"use client" import { useCallback, useEffect, useMemo, useState } from "react" import { ArrowUpCircle, Languages, Loader2, RefreshCw, Save, Wifi, } from "lucide-react" import type { Update } from "@tauri-apps/plugin-updater" import { useLocale, useTranslations } from "next-intl" import { toast } from "sonner" import { useAppI18n } from "@/components/i18n-provider" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { getSystemProxySettings, updateSystemLanguageSettings, updateSystemProxySettings, } from "@/lib/tauri" import type { AppLocale } from "@/lib/types" import { checkAppUpdate, closeAppUpdate, getCurrentAppVersion, installAppUpdate, relaunchApp, } from "@/lib/updater" const PROXY_EXAMPLE = "http://127.0.0.1:7890" const APP_LANGUAGE_VALUES = ["en", "zh_cn", "zh_tw"] as const type LanguageSelectValue = "system" | AppLocale function isAppLocale(value: string): value is AppLocale { return APP_LANGUAGE_VALUES.includes(value as AppLocale) } export function SystemNetworkSettings() { const t = useTranslations("SystemSettings") const tLanguage = useTranslations("Language") const locale = useLocale() const { languageSettings, languageSettingsLoaded, setLanguageSettings } = useAppI18n() const [loading, setLoading] = useState(true) const [saving, setSaving] = useState(false) const [savingLanguage, setSavingLanguage] = useState(false) const [enabled, setEnabled] = useState(false) const [proxyUrl, setProxyUrl] = useState("") const [loadError, setLoadError] = useState(null) const [currentVersion, setCurrentVersion] = useState("") const [availableUpdate, setAvailableUpdate] = useState(null) const [checkingUpdate, setCheckingUpdate] = useState(false) const [installingUpdate, setInstallingUpdate] = useState(false) const [updateError, setUpdateError] = useState(null) const [lastCheckedAt, setLastCheckedAt] = useState(null) const [appLanguage, setAppLanguage] = useState( languageSettings.mode === "system" ? "system" : languageSettings.language ) useEffect(() => { setAppLanguage( languageSettings.mode === "system" ? "system" : languageSettings.language ) }, [languageSettings]) const languageLabels = useMemo( () => ({ en: tLanguage("english"), zh_cn: tLanguage("simplifiedChinese"), zh_tw: tLanguage("traditionalChinese"), }), [tLanguage] ) const formattedLastCheckedAt = useMemo(() => { if (!lastCheckedAt) return null return new Intl.DateTimeFormat(locale, { dateStyle: "medium", timeStyle: "short", }).format(lastCheckedAt) }, [lastCheckedAt, locale]) const loadSettings = useCallback(async () => { setLoading(true) setLoadError(null) try { const [proxySettings, version] = await Promise.all([ getSystemProxySettings(), getCurrentAppVersion(), ]) setEnabled(proxySettings.enabled) setProxyUrl(proxySettings.proxy_url ?? "") setCurrentVersion(version) } catch (err) { const message = err instanceof Error ? err.message : String(err) setLoadError(message) console.error("[Settings] load system settings failed:", err) } finally { setLoading(false) } }, []) useEffect(() => { loadSettings().catch((err) => { console.error("[Settings] load system settings failed:", err) }) }, [loadSettings]) useEffect(() => { return () => { if (!availableUpdate) return closeAppUpdate(availableUpdate).catch((err) => { console.error("[Settings] release updater resource failed:", err) }) } }, [availableUpdate]) const saveSettings = useCallback(async () => { if (enabled && !proxyUrl.trim()) { toast.error(t("proxyRequired")) return } setSaving(true) try { const next = await updateSystemProxySettings({ enabled, proxy_url: proxyUrl.trim() || null, }) setEnabled(next.enabled) setProxyUrl(next.proxy_url ?? "") toast.success(t("saveSuccess")) } catch (err) { const message = err instanceof Error ? err.message : String(err) toast.error(t("saveFailed", { message })) } finally { setSaving(false) } }, [enabled, proxyUrl, t]) const saveLanguage = useCallback(async () => { setSavingLanguage(true) try { const next = await updateSystemLanguageSettings({ mode: appLanguage === "system" ? "system" : "manual", language: appLanguage === "system" ? languageSettings.language : appLanguage, }) setLanguageSettings(next) toast.success(t("languageSaveSuccess")) } catch (err) { const message = err instanceof Error ? err.message : String(err) toast.error(t("languageSaveFailed", { message })) } finally { setSavingLanguage(false) } }, [appLanguage, languageSettings.language, setLanguageSettings, t]) const checkForUpdates = useCallback(async () => { setCheckingUpdate(true) setUpdateError(null) try { const previousUpdate = availableUpdate const result = await checkAppUpdate() setCurrentVersion(result.currentVersion) setLastCheckedAt(new Date()) if (result.update) { setAvailableUpdate(result.update) toast.success(t("foundUpdate", { version: result.update.version })) } else { setAvailableUpdate(null) toast.success(t("alreadyLatest")) } if (previousUpdate && previousUpdate !== result.update) { await closeAppUpdate(previousUpdate) } } catch (err) { const message = err instanceof Error ? err.message : String(err) setUpdateError(message) toast.error(t("checkUpdateFailed", { message })) } finally { setCheckingUpdate(false) } }, [availableUpdate, t]) const installUpdate = useCallback(async () => { if (!availableUpdate) return setInstallingUpdate(true) setUpdateError(null) try { await installAppUpdate(availableUpdate) toast.success(t("installSuccess")) await relaunchApp() } catch (err) { const message = err instanceof Error ? err.message : String(err) setUpdateError(message) toast.error(t("installFailed", { message })) } finally { setInstallingUpdate(false) } }, [availableUpdate, t]) if (loading) { return (
{t("loading")}
) } return (

{t("sectionTitle")}

{t("sectionDescription")}

{t("proxyTitle")}

{t("proxyDescription")}

{loadError && (
{t("loadFailed", { message: loadError })}
)}
setProxyUrl(event.target.value)} placeholder={PROXY_EXAMPLE} />

{t("proxyHint", { example: PROXY_EXAMPLE })}

{t("languageTitle")}

{t("languageDescription")}

{t("updateTitle")}

{t("updateDescription")}

{t("currentVersion")}
{currentVersion ? `v${currentVersion}` : "-"}
{t("upgradableVersion")}
{availableUpdate ? `v${availableUpdate.version}` : t("none")}
{formattedLastCheckedAt && (

{t("lastChecked", { time: formattedLastCheckedAt })}

)} {updateError && (
{t("updateError", { message: updateError })}
)}
{availableUpdate && ( )}
) }