import { useEffect, useState } from "react"; import { api, type ProxySettings, type ProxySettingsInput, type StatisticsStorage } from "../api"; import { PageContent } from "../components/layout/PageContent"; import { AppLifecycleSettingsCard } from "../components/settings/AppLifecycleSettingsCard"; import { ProxySettingsCard } from "../components/settings/ProxySettingsCard"; import { Button } from "../components/ui/Button"; import { Checkbox } from "../components/ui/Checkbox"; import { FormField, TextInput } from "../components/ui/FormControls"; import { Modal } from "../components/ui/Modal"; import { Select } from "../components/ui/Select"; import { TitledCard } from "../components/ui/TitledCard"; import { setLocalePreference, useI18n, type LocalePreference } from "../i18n/store"; import { useMessage } from "../components/ui/message"; import { appStore, useAppStore } from "../store/appStore"; import { themeOptions } from "../theme/theme"; import styles from "./SettingsPage.module.scss"; export function SettingsPage() { const { detailed, ports, theme } = useAppStore(); const { preference, locale } = useI18n(); const message = useMessage(); const [proxyPort, setProxyPort] = useState(String(ports.proxy_port)); const [servicePort, setServicePort] = useState(String(ports.service_port)); const [editingPorts, setEditingPorts] = useState(false); const [savingPorts, setSavingPorts] = useState(false); const [storage, setStorage] = useState(null); const [confirmClear, setConfirmClear] = useState(false); const [clearing, setClearing] = useState(false); const [outboundProxy, setOutboundProxy] = useState(null); const [proxyDraft, setProxyDraft] = useState({ mode: "system", address: "", auth_enabled: false, username: "", password: "" }); const [editingProxy, setEditingProxy] = useState(false); const [savingProxy, setSavingProxy] = useState(false); useEffect(() => { void Promise.all([api.statisticsStorage(), api.proxySettings()]).then(([nextStorage, nextProxy]) => { setStorage(nextStorage); setOutboundProxy(nextProxy); setProxyDraft({ mode: nextProxy.mode, address: nextProxy.address, auth_enabled: nextProxy.auth_enabled, username: nextProxy.username, password: "" }); }).catch((cause) => message(cause instanceof Error ? cause.message : String(cause))); }, [message]); useEffect(() => { setProxyPort(String(ports.proxy_port)); setServicePort(String(ports.service_port)); }, [ports.proxy_port, ports.service_port]); const parsePort = (value: string, label: string) => { const port = Number(value); if (!Number.isInteger(port) || port < 0 || port > 65_535) { throw new Error(`${label}${t("必须是 0–65535 之间的整数")}`); } return port; }; const savePorts = async () => { try { const next = { proxy_port: parsePort(proxyPort, t("代理端口")), service_port: parsePort(servicePort, t("服务端口")), }; setSavingPorts(true); if (await appStore.updatePorts(next)) { setEditingPorts(false); message(t("端口设置已保存,重启软件后生效"), { duration: 4_000 }); } } catch (cause) { message(cause instanceof Error ? cause.message : String(cause)); } finally { setSavingPorts(false); } }; const editPorts = () => { setProxyPort(String(ports.proxy_port)); setServicePort(String(ports.service_port)); setEditingPorts(true); }; const cancelPortEdit = () => { setProxyPort(String(ports.proxy_port)); setServicePort(String(ports.service_port)); setEditingPorts(false); }; const clearStorage = async () => { try { setClearing(true); setStorage(await api.clearStatisticsStorage()); setConfirmClear(false); await appStore.refresh(); message(t("统计数据已清理")); } catch (cause) { message(cause instanceof Error ? cause.message : String(cause)); } finally { setClearing(false); } }; const editProxy = () => { if (!outboundProxy) return; setProxyDraft({ mode: outboundProxy.mode, address: outboundProxy.address, auth_enabled: outboundProxy.auth_enabled, username: outboundProxy.username, password: "" }); setEditingProxy(true); }; const cancelProxyEdit = () => { if (outboundProxy) { setProxyDraft({ mode: outboundProxy.mode, address: outboundProxy.address, auth_enabled: outboundProxy.auth_enabled, username: outboundProxy.username, password: "" }); } setEditingProxy(false); }; const saveProxy = async () => { try { setSavingProxy(true); const saved = await api.setProxySettings({ ...proxyDraft, password: proxyDraft.password || undefined }); setOutboundProxy(saved); setProxyDraft({ mode: saved.mode, address: saved.address, auth_enabled: saved.auth_enabled, username: saved.username, password: "" }); setEditingProxy(false); message(t("代理设置已保存")); } catch (cause) { message(cause instanceof Error ? cause.message : String(cause)); } finally { setSavingProxy(false); } }; const formatBytes = (bytes: number) => { if (bytes < 1024) return `${bytes} B`; const units = ["KB", "MB", "GB", "TB"]; let value = bytes / 1024; let unit = 0; while (value >= 1024 && unit < units.length - 1) { value /= 1024; unit += 1; } return `${value < 10 ? value.toFixed(1) : Math.round(value)} ${units[unit]}`; }; const content = (
{t("详细模式")} {t("额外保存完整请求和流响应;默认只保存时间、状态与用量。")}
void appStore.updateDetailed(checked)} />
) : ( )}>
{editingPorts ? <> setProxyPort(event.target.value)} /> setServicePort(event.target.value)} /> : <>
{t("代理端口")}{ports.proxy_port}
{t("服务端口")}{ports.service_port}
}
{t( "端口被占用时会自动选择新的随机端口并保存。修改后需要重启软件才会生效。", )}
void saveProxy()} />
{t("界面语言")} {t("默认跟随操作系统;不支持的系统语言使用英文。当前:{language}", { language: locale === "zh-CN" ? "简体中文" : "English" })}