feat: add desktop UI password setting
Adds optional desktop UI password protection Starts Electron with the saved UI password Explains login session duration in settings
This commit is contained in:
@@ -2,6 +2,7 @@ import * as React from 'react';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { getDesktopLanAddress, isDesktopLocalOriginActive, isDesktopShell, restartDesktopApp } from '@/lib/desktop';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
@@ -10,6 +11,8 @@ export const DesktopNetworkSettings: React.FC = () => {
|
||||
const isLocalDesktop = isDesktopShell() && isDesktopLocalOriginActive();
|
||||
const [savedValue, setSavedValue] = React.useState(false);
|
||||
const [draftValue, setDraftValue] = React.useState(false);
|
||||
const [savedPassword, setSavedPassword] = React.useState('');
|
||||
const [draftPassword, setDraftPassword] = React.useState('');
|
||||
const [isLoading, setIsLoading] = React.useState(true);
|
||||
const [isSaving, setIsSaving] = React.useState(false);
|
||||
const [error, setError] = React.useState<string | null>(null);
|
||||
@@ -32,14 +35,20 @@ export const DesktopNetworkSettings: React.FC = () => {
|
||||
throw new Error(t('settings.openchamber.desktopNetwork.error.loadFailed'));
|
||||
}
|
||||
|
||||
const data = (await response.json().catch(() => null)) as null | { desktopLanAccessEnabled?: unknown };
|
||||
const data = (await response.json().catch(() => null)) as null | {
|
||||
desktopLanAccessEnabled?: unknown;
|
||||
desktopUiPassword?: unknown;
|
||||
};
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const enabled = data?.desktopLanAccessEnabled === true;
|
||||
const password = typeof data?.desktopUiPassword === 'string' ? data.desktopUiPassword : '';
|
||||
setSavedValue(enabled);
|
||||
setDraftValue(enabled);
|
||||
setSavedPassword(password);
|
||||
setDraftPassword(password);
|
||||
setError(null);
|
||||
} catch (cause) {
|
||||
if (!cancelled) {
|
||||
@@ -77,7 +86,7 @@ export const DesktopNetworkSettings: React.FC = () => {
|
||||
};
|
||||
}, [draftValue, isLocalDesktop]);
|
||||
|
||||
const isDirty = draftValue !== savedValue;
|
||||
const isDirty = draftValue !== savedValue || draftPassword !== savedPassword;
|
||||
const currentPort = React.useMemo(() => {
|
||||
if (typeof window === 'undefined') {
|
||||
return null;
|
||||
@@ -107,7 +116,10 @@ export const DesktopNetworkSettings: React.FC = () => {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ desktopLanAccessEnabled: draftValue }),
|
||||
body: JSON.stringify({
|
||||
desktopLanAccessEnabled: draftValue,
|
||||
desktopUiPassword: draftPassword,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
@@ -115,6 +127,7 @@ export const DesktopNetworkSettings: React.FC = () => {
|
||||
}
|
||||
|
||||
setSavedValue(draftValue);
|
||||
setSavedPassword(draftPassword);
|
||||
|
||||
const restarted = await restartDesktopApp();
|
||||
if (!restarted) {
|
||||
@@ -124,7 +137,7 @@ export const DesktopNetworkSettings: React.FC = () => {
|
||||
setError(cause instanceof Error ? cause.message : t('settings.openchamber.desktopNetwork.error.saveFailed'));
|
||||
setIsSaving(false);
|
||||
}
|
||||
}, [draftValue, isDirty, t]);
|
||||
}, [draftPassword, draftValue, isDirty, t]);
|
||||
|
||||
if (!isLocalDesktop) {
|
||||
return null;
|
||||
@@ -137,6 +150,24 @@ export const DesktopNetworkSettings: React.FC = () => {
|
||||
</div>
|
||||
|
||||
<section className="space-y-2 px-2 pb-2 pt-0">
|
||||
<div className="space-y-1 py-1.5">
|
||||
<label className="typography-ui-label text-foreground" htmlFor="desktop-ui-password">
|
||||
{t('settings.openchamber.desktopPassword.field.password')}
|
||||
</label>
|
||||
<Input
|
||||
id="desktop-ui-password"
|
||||
type="password"
|
||||
className="h-7 max-w-sm"
|
||||
value={draftPassword}
|
||||
onChange={(event) => setDraftPassword(event.target.value)}
|
||||
placeholder={t('settings.openchamber.desktopPassword.field.passwordPlaceholder')}
|
||||
disabled={isLoading || isSaving}
|
||||
/>
|
||||
<div className="typography-micro text-muted-foreground/70">
|
||||
{t('settings.openchamber.desktopPassword.field.passwordDescription')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="group flex cursor-pointer items-start gap-2 py-1.5"
|
||||
role="button"
|
||||
|
||||
Reference in New Issue
Block a user