import React, { useEffect, useState } from "react"; import { ChangePassword, CreateGroupApi, CreateUserAccount, DeleteGroupApi, DeleteTheme, FetchGroups, FetchLocks, FetchProfileColourOptions, FetchSettings, FetchTheme, FetchThemeNames, FetchUsers, GetAvatarUrl, Logout, RemoveLock, SaveSettings, SaveTheme, SetGroupMembersApi, UpdateProfileColour, UpdateUsername, UploadAvatar, } from "../Api.js"; import Switch from "./Switch.jsx"; import PromptModal from "./PromptModal.jsx"; import ConfirmModal from "./ConfirmModal.jsx"; import { PaletteRoles } from "../ThemeRoles.js"; export default function SettingsPanel({ CurrentUser, OnClose, OnSettingsChanged, OnLoggedOut, OnProfileUpdated, }) { const [Settings, SetSettings] = useState(null); const [ThemeNames, SetThemeNames] = useState([]); const [SaveError, SetSaveError] = useState(null); const [IsSaving, SetIsSaving] = useState(false); const [IsCreatingTheme, SetIsCreatingTheme] = useState(false); const [NewThemeName, SetNewThemeName] = useState(""); const [NewThemePalette, SetNewThemePalette] = useState(null); const [ThemeError, SetThemeError] = useState(null); const [IsRenamingTheme, SetIsRenamingTheme] = useState(false); const [IsConfirmingThemeDelete, SetIsConfirmingThemeDelete] = useState(false); const [Users, SetUsers] = useState(null); const [NewUsername, SetNewUsername] = useState(""); const [NewUserPassword, SetNewUserPassword] = useState(""); const [NewUserIsAdmin, SetNewUserIsAdmin] = useState(false); const [NewUserColour, SetNewUserColour] = useState("Purple"); const [UserError, SetUserError] = useState(null); const [ColourOptions, SetColourOptions] = useState([]); const [ProfileError, SetProfileError] = useState(null); const [AvatarNonce, SetAvatarNonce] = useState(0); const [UsernameField, SetUsernameField] = useState(""); const [UsernameError, SetUsernameError] = useState(null); const [CurrentPasswordField, SetCurrentPasswordField] = useState(""); const [NewPasswordField, SetNewPasswordField] = useState(""); const [PasswordError, SetPasswordError] = useState(null); const [PasswordChanged, SetPasswordChanged] = useState(false); const [Locks, SetLocksList] = useState(null); const [LockError, SetLockError] = useState(null); const [Groups, SetGroupsList] = useState(null); const [NewGroupName, SetNewGroupName] = useState(""); const [GroupError, SetGroupError] = useState(null); const [ExpandedGroup, SetExpandedGroup] = useState(null); const [ConfirmingDeleteGroupName, SetConfirmingDeleteGroupName] = useState(null); useEffect(() => { FetchSettings().then(SetSettings); FetchThemeNames().then(SetThemeNames); FetchProfileColourOptions().then(SetColourOptions); if (CurrentUser) { SetUsernameField(CurrentUser.Username); } if (CurrentUser && CurrentUser.IsAdmin) { FetchUsers().then(SetUsers); FetchLocks().then(SetLocksList); FetchGroups().then(SetGroupsList); } }, [CurrentUser]); if (!Settings) { return null; } function UpdateField(Field, Value) { SetSettings({ ...Settings, [Field]: Value }); } async function HandleSave() { SetIsSaving(true); SetSaveError(null); try { const Saved = await SaveSettings(Settings); OnSettingsChanged(Saved); OnClose(); } catch (Error) { SetSaveError(Error.message); } finally { SetIsSaving(false); } } async function HandleStartCreateTheme() { SetThemeError(null); const BasePalette = await FetchTheme(Settings.ThemeName); SetNewThemePalette(BasePalette); SetNewThemeName(""); SetIsCreatingTheme(true); } async function HandleSaveNewTheme() { if (!NewThemeName.trim()) return; try { await SaveTheme(NewThemeName.trim(), NewThemePalette); const Updated = await FetchThemeNames(); SetThemeNames(Updated); UpdateField("ThemeName", NewThemeName.trim()); SetIsCreatingTheme(false); } catch (Error) { SetThemeError(Error.message); } } function HandleRenameThemeClick() { SetIsRenamingTheme(true); } async function HandleRenameThemeConfirm(NewName) { SetIsRenamingTheme(false); if (NewName === Settings.ThemeName) return; SetThemeError(null); try { const Palette = await FetchTheme(Settings.ThemeName); await SaveTheme(NewName, Palette); await DeleteTheme(Settings.ThemeName); const Updated = await FetchThemeNames(); SetThemeNames(Updated); UpdateField("ThemeName", NewName); } catch (Error) { SetThemeError(Error.message); } } function HandleDeleteThemeClick() { if (ThemeNames.length <= 1) { SetThemeError("Can't delete the only remaining theme."); return; } SetIsConfirmingThemeDelete(true); } async function HandleDeleteThemeConfirm() { SetIsConfirmingThemeDelete(false); SetThemeError(null); try { await DeleteTheme(Settings.ThemeName); const Updated = await FetchThemeNames(); SetThemeNames(Updated); UpdateField("ThemeName", Updated[0]); } catch (Error) { SetThemeError(Error.message); } } async function HandleLogout() { await Logout(); OnLoggedOut(); } async function HandleCreateUser() { SetUserError(null); try { await CreateUserAccount(NewUsername.trim(), NewUserPassword, NewUserIsAdmin, NewUserColour); const Updated = await FetchUsers(); SetUsers(Updated); SetNewUsername(""); SetNewUserPassword(""); SetNewUserIsAdmin(false); SetNewUserColour("Purple"); } catch (Error) { SetUserError(Error.message); } } async function HandlePickColour(Colour) { SetProfileError(null); try { const Updated = await UpdateProfileColour(Colour); OnProfileUpdated(Updated); } catch (Error) { SetProfileError(Error.message); } } async function HandleAvatarChange(Event) { const File = Event.target.files && Event.target.files[0]; if (!File) return; SetProfileError(null); try { await UploadAvatar(File); const NextNonce = Date.now(); SetAvatarNonce(NextNonce); OnProfileUpdated({ AvatarNonce: NextNonce }); } catch (Error) { SetProfileError(Error.message); } } async function HandleChangeUsername() { SetUsernameError(null); if (!UsernameField.trim() || UsernameField.trim() === CurrentUser.Username) return; try { const Updated = await UpdateUsername(UsernameField.trim()); OnProfileUpdated(Updated); } catch (Error) { SetUsernameError(Error.message); } } async function HandleChangePassword() { SetPasswordError(null); SetPasswordChanged(false); try { await ChangePassword(CurrentPasswordField, NewPasswordField); SetCurrentPasswordField(""); SetNewPasswordField(""); SetPasswordChanged(true); } catch (Error) { SetPasswordError(Error.message); } } async function HandleUnlock(Lock) { SetLockError(null); try { await RemoveLock(Lock.Root, Lock.Path); const Updated = await FetchLocks(); SetLocksList(Updated); } catch (Error) { SetLockError(Error.message); } } async function HandleCreateGroup() { SetGroupError(null); try { await CreateGroupApi(NewGroupName.trim()); const Updated = await FetchGroups(); SetGroupsList(Updated); SetNewGroupName(""); } catch (Error) { SetGroupError(Error.message); } } function HandleDeleteGroupClick(Name) { SetConfirmingDeleteGroupName(Name); } async function HandleDeleteGroupConfirm() { const Name = ConfirmingDeleteGroupName; SetConfirmingDeleteGroupName(null); SetGroupError(null); try { await DeleteGroupApi(Name); const Updated = await FetchGroups(); SetGroupsList(Updated); } catch (Error) { SetGroupError(Error.message); } } async function HandleToggleGroupMember(Group, Username) { const CurrentMembers = Group.Members || []; const NewMembers = CurrentMembers.includes(Username) ? CurrentMembers.filter((Member) => Member !== Username) : [...CurrentMembers, Username]; SetGroupError(null); try { await SetGroupMembersApi(Group.Name, NewMembers); const Updated = await FetchGroups(); SetGroupsList(Updated); } catch (Error) { SetGroupError(Error.message); } } return (