import { useCallback, useEffect, useState } from "react"; import { useSettingsStore } from "@hooks/stores"; import { JsonRpcResponse, useJsonRpc } from "@hooks/useJsonRpc"; import { useDeviceUiNavigation } from "@hooks/useAppNavigation"; import { Button } from "@components/Button"; import Checkbox from "@components/Checkbox"; import { ConfirmDialog } from "@components/ConfirmDialog"; import { GridCard } from "@components/Card"; import { SettingsItem } from "@components/SettingsItem"; import { SettingsPageHeader } from "@components/SettingsPageheader"; import { NestedSettingsGroup } from "@components/NestedSettingsGroup"; import { TextAreaWithLabel } from "@components/TextArea"; import { InputFieldWithLabel } from "@components/InputField"; import { SelectMenuBasic } from "@components/SelectMenuBasic"; import { isOnDevice } from "@/main"; import notifications from "@/notifications"; import { m } from "@localizations/messages.js"; export default function SettingsAdvancedRoute() { const { send } = useJsonRpc(); const { navigateTo } = useDeviceUiNavigation(); const [sshKey, setSSHKey] = useState(""); const { setDeveloperMode } = useSettingsStore(); const [devChannel, setDevChannel] = useState(false); const [usbEmulationEnabled, setUsbEmulationEnabled] = useState(false); const [showLoopbackWarning, setShowLoopbackWarning] = useState(false); const [localLoopbackOnly, setLocalLoopbackOnly] = useState(false); const [updateTarget, setUpdateTarget] = useState("app"); const [appVersion, setAppVersion] = useState(""); const [systemVersion, setSystemVersion] = useState(""); const settings = useSettingsStore(); useEffect(() => { send("getDevModeState", {}, (resp: JsonRpcResponse) => { if ("error" in resp) return; const result = resp.result as { enabled: boolean }; setDeveloperMode(result.enabled); }); send("getSSHKeyState", {}, (resp: JsonRpcResponse) => { if ("error" in resp) return; setSSHKey(resp.result as string); }); send("getUsbEmulationState", {}, (resp: JsonRpcResponse) => { if ("error" in resp) return; setUsbEmulationEnabled(resp.result as boolean); }); send("getDevChannelState", {}, (resp: JsonRpcResponse) => { if ("error" in resp) return; setDevChannel(resp.result as boolean); }); send("getLocalLoopbackOnly", {}, (resp: JsonRpcResponse) => { if ("error" in resp) return; setLocalLoopbackOnly(resp.result as boolean); }); }, [send, setDeveloperMode]); const getUsbEmulationState = useCallback(() => { send("getUsbEmulationState", {}, (resp: JsonRpcResponse) => { if ("error" in resp) return; setUsbEmulationEnabled(resp.result as boolean); }); }, [send]); const handleUsbEmulationToggle = useCallback( (enabled: boolean) => { send("setUsbEmulationState", { enabled: enabled }, (resp: JsonRpcResponse) => { if ("error" in resp) { notifications.error( enabled ? m.advanced_error_usb_emulation_enable({ error: resp.error.data || m.unknown_error() }) : m.advanced_error_usb_emulation_disable({ error: resp.error.data || m.unknown_error() }) ); return; } setUsbEmulationEnabled(enabled); getUsbEmulationState(); }); }, [getUsbEmulationState, send], ); const handleResetConfig = useCallback(() => { send("resetConfig", {}, (resp: JsonRpcResponse) => { if ("error" in resp) { notifications.error( m.advanced_error_reset_config({ error: resp.error.data || m.unknown_error() }) ); return; } notifications.success(m.advanced_success_reset_config()); }); }, [send]); const handleUpdateSSHKey = useCallback(() => { send("setSSHKeyState", { sshKey }, (resp: JsonRpcResponse) => { if ("error" in resp) { notifications.error( m.advanced_error_update_ssh_key({ error: resp.error.data || m.unknown_error() }) ); return; } notifications.success(m.advanced_success_update_ssh_key()); }); }, [send, sshKey]); const handleDevModeChange = useCallback( (developerMode: boolean) => { send("setDevModeState", { enabled: developerMode }, (resp: JsonRpcResponse) => { if ("error" in resp) { notifications.error( m.advanced_error_set_dev_mode({ error: resp.error.data || m.unknown_error() }) ); return; } setDeveloperMode(developerMode); }); }, [send, setDeveloperMode], ); const handleDevChannelChange = useCallback( (enabled: boolean) => { send("setDevChannelState", { enabled }, (resp: JsonRpcResponse) => { if ("error" in resp) { notifications.error( m.advanced_error_set_dev_channel({ error: resp.error.data || m.unknown_error() }) ); return; } setDevChannel(enabled); }); }, [send, setDevChannel], ); const applyLoopbackOnlyMode = useCallback( (enabled: boolean) => { send("setLocalLoopbackOnly", { enabled }, (resp: JsonRpcResponse) => { if ("error" in resp) { notifications.error( enabled ? m.advanced_error_loopback_enable({ error: resp.error.data || m.unknown_error() }) : m.advanced_error_loopback_disable({ error: resp.error.data || m.unknown_error() }) ); return; } setLocalLoopbackOnly(enabled); if (enabled) { notifications.success(m.advanced_success_loopback_enabled()); } else { notifications.success(m.advanced_success_loopback_disabled()); } }); }, [send, setLocalLoopbackOnly], ); const handleLoopbackOnlyModeChange = useCallback( (enabled: boolean) => { // If trying to enable loopback-only mode, show warning first if (enabled) { setShowLoopbackWarning(true); } else { // If disabling, just proceed applyLoopbackOnlyMode(false); } }, [applyLoopbackOnlyMode, setShowLoopbackWarning], ); const confirmLoopbackModeEnable = useCallback(() => { applyLoopbackOnlyMode(true); setShowLoopbackWarning(false); }, [applyLoopbackOnlyMode, setShowLoopbackWarning]); const handleVersionUpdate = useCallback(() => { const params = { components: { app: appVersion, system: systemVersion, }, includePreRelease: devChannel, checkOnly: true, }; send("tryUpdateComponents", params, (resp: JsonRpcResponse) => { if ("error" in resp) { notifications.error( m.advanced_error_version_update({ error: resp.error.data || m.unknown_error() }) ); return; } // Navigate to update page navigateTo("/settings/general/update"); }); }, [appVersion, systemVersion, devChannel, send, navigateTo]); return (
{ handleDevChannelChange(e.target.checked); }} /> handleDevModeChange(e.target.checked)} /> {settings.developerMode ? (

{m.advanced_developer_mode_enabled_title()}

  • {m.advanced_developer_mode_warning_security()}
  • {m.advanced_developer_mode_warning_risks()}
{m.advanced_developer_mode_warning_advanced()}
{isOnDevice && (
setSSHKey(e.target.value)} placeholder={m.advanced_ssh_public_key_placeholder()} />

{m.advanced_ssh_default_user()}root.

)}
setUpdateTarget(e.target.value)} /> {(updateTarget === "app" || updateTarget === "both") && ( setAppVersion(e.target.value)} /> )} {(updateTarget === "system" || updateTarget === "both") && ( setSystemVersion(e.target.value)} /> )}

{m.advanced_version_update_helper()}{" "} {m.advanced_version_update_github_link()}

) : null} handleLoopbackOnlyModeChange(e.target.checked)} /> { settings.setDebugMode(e.target.checked); }} /> {settings.debugMode && (
{ setShowLoopbackWarning(false); }} title={m.advanced_loopback_warning_title()} description={ <>

{m.advanced_loopback_warning_description()}

{m.advanced_loopback_warning_before()}

  • {m.advanced_loopback_warning_ssh()}
  • {m.advanced_loopback_warning_cloud()}
} variant="warning" confirmText={m.advanced_loopback_warning_confirm()} onConfirm={confirmLoopbackModeEnable} />
); }