2023-11-18 19:27:19 +01:00
|
|
|
import classNames from "classnames";
|
2023-11-24 18:39:40 +01:00
|
|
|
import { useCallback, useEffect, useMemo } from "react";
|
2023-11-18 17:28:10 +01:00
|
|
|
import { useAsyncFn } from "react-use";
|
2023-10-27 21:51:14 +02:00
|
|
|
|
2023-11-24 18:39:40 +01:00
|
|
|
import { base64ToBuffer, decryptData } from "@/backend/accounts/crypto";
|
2023-11-18 17:28:10 +01:00
|
|
|
import { getSessions } from "@/backend/accounts/sessions";
|
2023-11-24 18:39:40 +01:00
|
|
|
import { updateSettings } from "@/backend/accounts/settings";
|
2023-11-24 17:11:00 +01:00
|
|
|
import { Button } from "@/components/Button";
|
2023-10-25 18:05:40 +02:00
|
|
|
import { WideContainer } from "@/components/layout/WideContainer";
|
2023-11-18 17:28:10 +01:00
|
|
|
import { Heading1 } from "@/components/utils/Text";
|
|
|
|
import { useBackendUrl } from "@/hooks/auth/useBackendUrl";
|
2023-11-18 19:27:19 +01:00
|
|
|
import { useIsMobile } from "@/hooks/useIsMobile";
|
2023-11-24 17:11:00 +01:00
|
|
|
import { useSettingsState } from "@/hooks/useSettingsState";
|
|
|
|
import { AccountActionsPart } from "@/pages/parts/settings/AccountActionsPart";
|
|
|
|
import { AccountEditPart } from "@/pages/parts/settings/AccountEditPart";
|
|
|
|
import { CaptionsPart } from "@/pages/parts/settings/CaptionsPart";
|
2023-11-24 18:39:40 +01:00
|
|
|
import { ConnectionsPart } from "@/pages/parts/settings/ConnectionsPart";
|
2023-11-24 17:11:00 +01:00
|
|
|
import { DeviceListPart } from "@/pages/parts/settings/DeviceListPart";
|
|
|
|
import { RegisterCalloutPart } from "@/pages/parts/settings/RegisterCalloutPart";
|
|
|
|
import { SidebarPart } from "@/pages/parts/settings/SidebarPart";
|
|
|
|
import { ThemePart } from "@/pages/parts/settings/ThemePart";
|
2023-11-18 17:28:10 +01:00
|
|
|
import { AccountWithToken, useAuthStore } from "@/stores/auth";
|
2023-11-24 17:11:00 +01:00
|
|
|
import { useLanguageStore } from "@/stores/language";
|
|
|
|
import { useSubtitleStore } from "@/stores/subtitles";
|
2023-11-18 17:28:10 +01:00
|
|
|
import { useThemeStore } from "@/stores/theme";
|
2023-10-25 18:05:40 +02:00
|
|
|
|
|
|
|
import { SubPageLayout } from "./layouts/SubPageLayout";
|
2023-11-24 17:11:00 +01:00
|
|
|
import { LocalePart } from "./parts/settings/LocalePart";
|
2023-10-25 18:05:40 +02:00
|
|
|
|
|
|
|
function SettingsLayout(props: { children: React.ReactNode }) {
|
2023-11-18 19:27:19 +01:00
|
|
|
const { isMobile } = useIsMobile();
|
|
|
|
|
2023-10-25 18:05:40 +02:00
|
|
|
return (
|
2023-11-24 17:11:00 +01:00
|
|
|
<WideContainer ultraWide classNames="overflow-visible">
|
2023-11-18 19:27:19 +01:00
|
|
|
<div
|
|
|
|
className={classNames(
|
|
|
|
"grid gap-12",
|
|
|
|
isMobile ? "grid-cols-1" : "lg:grid-cols-[260px,1fr]"
|
|
|
|
)}
|
|
|
|
>
|
2023-11-18 17:28:10 +01:00
|
|
|
<SidebarPart />
|
2023-11-20 19:36:35 +01:00
|
|
|
<div>{props.children}</div>
|
2023-10-25 18:05:40 +02:00
|
|
|
</div>
|
|
|
|
</WideContainer>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-11-18 17:28:10 +01:00
|
|
|
export function AccountSettings(props: { account: AccountWithToken }) {
|
|
|
|
const url = useBackendUrl();
|
|
|
|
const { account } = props;
|
|
|
|
const [sessionsResult, execSessions] = useAsyncFn(() => {
|
|
|
|
return getSessions(url, account);
|
|
|
|
}, [account, url]);
|
|
|
|
useEffect(() => {
|
|
|
|
execSessions();
|
|
|
|
}, [execSessions]);
|
2023-10-27 21:51:14 +02:00
|
|
|
|
|
|
|
return (
|
2023-11-18 17:28:10 +01:00
|
|
|
<>
|
|
|
|
<AccountEditPart />
|
|
|
|
<DeviceListPart
|
|
|
|
error={!!sessionsResult.error}
|
|
|
|
loading={sessionsResult.loading}
|
|
|
|
sessions={sessionsResult.value ?? []}
|
|
|
|
onChange={execSessions}
|
|
|
|
/>
|
|
|
|
<AccountActionsPart />
|
|
|
|
</>
|
2023-10-27 21:51:14 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-10-25 18:05:40 +02:00
|
|
|
export function SettingsPage() {
|
2023-11-18 17:28:10 +01:00
|
|
|
const activeTheme = useThemeStore((s) => s.theme);
|
|
|
|
const setTheme = useThemeStore((s) => s.setTheme);
|
2023-11-24 17:11:00 +01:00
|
|
|
|
|
|
|
const appLanguage = useLanguageStore((s) => s.language);
|
2023-11-24 18:39:40 +01:00
|
|
|
const setAppLanguage = useLanguageStore((s) => s.setLanguage);
|
2023-11-24 17:11:00 +01:00
|
|
|
|
|
|
|
const subStyling = useSubtitleStore((s) => s.styling);
|
2023-11-24 18:39:40 +01:00
|
|
|
const setSubStyling = useSubtitleStore((s) => s.updateStyling);
|
2023-11-24 17:11:00 +01:00
|
|
|
|
2023-11-24 18:39:40 +01:00
|
|
|
const account = useAuthStore((s) => s.account);
|
|
|
|
const decryptedName = useMemo(() => {
|
|
|
|
if (!account) return "";
|
|
|
|
return decryptData(account.deviceName, base64ToBuffer(account.seed));
|
|
|
|
}, [account]);
|
|
|
|
|
|
|
|
const backendUrl = useBackendUrl();
|
2023-11-24 17:11:00 +01:00
|
|
|
|
2023-11-18 17:28:10 +01:00
|
|
|
const user = useAuthStore();
|
|
|
|
|
2023-11-24 17:11:00 +01:00
|
|
|
const state = useSettingsState(
|
|
|
|
activeTheme,
|
|
|
|
appLanguage,
|
|
|
|
subStyling,
|
2023-11-24 18:39:40 +01:00
|
|
|
decryptedName
|
2023-11-24 17:11:00 +01:00
|
|
|
);
|
|
|
|
|
2023-11-24 18:39:40 +01:00
|
|
|
const saveChanges = useCallback(async () => {
|
|
|
|
console.log(state);
|
|
|
|
|
|
|
|
if (account) {
|
|
|
|
await updateSettings(backendUrl, account, {
|
|
|
|
applicationLanguage: state.appLanguage.state,
|
|
|
|
applicationTheme: state.theme.state ?? undefined,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
setAppLanguage(state.appLanguage.state);
|
|
|
|
setTheme(state.theme.state);
|
|
|
|
setSubStyling(state.subtitleStyling.state);
|
|
|
|
}, [state, account, backendUrl, setAppLanguage, setTheme, setSubStyling]);
|
2023-10-25 18:05:40 +02:00
|
|
|
return (
|
|
|
|
<SubPageLayout>
|
|
|
|
<SettingsLayout>
|
2023-11-18 19:27:19 +01:00
|
|
|
<div id="settings-account">
|
|
|
|
<Heading1 border className="!mb-0">
|
|
|
|
Account
|
|
|
|
</Heading1>
|
|
|
|
{user.account ? (
|
|
|
|
<AccountSettings account={user.account} />
|
|
|
|
) : (
|
|
|
|
<RegisterCalloutPart />
|
|
|
|
)}
|
|
|
|
</div>
|
2023-11-20 19:36:35 +01:00
|
|
|
<div id="settings-locale" className="mt-48">
|
2023-11-24 17:11:00 +01:00
|
|
|
<LocalePart
|
|
|
|
language={state.appLanguage.state}
|
|
|
|
setLanguage={state.appLanguage.set}
|
|
|
|
/>
|
2023-11-18 20:55:46 +01:00
|
|
|
</div>
|
2023-11-20 19:36:35 +01:00
|
|
|
<div id="settings-appearance" className="mt-48">
|
2023-11-24 17:11:00 +01:00
|
|
|
<ThemePart active={state.theme.state} setTheme={state.theme.set} />
|
2023-11-18 19:27:19 +01:00
|
|
|
</div>
|
2023-11-20 19:36:35 +01:00
|
|
|
<div id="settings-captions" className="mt-48">
|
2023-11-24 17:11:00 +01:00
|
|
|
<CaptionsPart
|
|
|
|
styling={state.subtitleStyling.state}
|
2023-11-24 18:39:40 +01:00
|
|
|
setStyling={state.subtitleStyling.set}
|
2023-11-24 17:11:00 +01:00
|
|
|
/>
|
2023-11-18 19:27:19 +01:00
|
|
|
</div>
|
2023-11-24 18:39:40 +01:00
|
|
|
<div id="settings-connection" className="mt-48">
|
|
|
|
<ConnectionsPart />
|
|
|
|
</div>
|
2023-10-25 18:05:40 +02:00
|
|
|
</SettingsLayout>
|
2023-11-24 18:39:40 +01:00
|
|
|
<div
|
|
|
|
className={`bg-settings-saveBar-background border-t border-settings-card-border/50 py-4 transition-opacity w-full fixed bottom-0 flex justify-between px-8 items-center ${
|
|
|
|
state.changed ? "opacity-100" : "opacity-0"
|
|
|
|
}`}
|
|
|
|
>
|
|
|
|
<p className="text-type-danger">You have unsaved changes</p>
|
|
|
|
<div className="space-x-6">
|
|
|
|
<Button theme="secondary" onClick={state.reset}>
|
|
|
|
Reset
|
|
|
|
</Button>
|
|
|
|
<Button theme="purple" onClick={saveChanges}>
|
|
|
|
Save
|
|
|
|
</Button>
|
2023-11-24 17:11:00 +01:00
|
|
|
</div>
|
2023-11-24 18:39:40 +01:00
|
|
|
</div>
|
2023-10-25 18:05:40 +02:00
|
|
|
</SubPageLayout>
|
|
|
|
);
|
|
|
|
}
|