import { useEffect, useState } from "preact/hooks"; import { t } from "../../i18n"; import { api } from "../../features/customize/api"; import { custTab } from "../../features/customize/actions"; import { asList, asString, hint } from "../../features/customize/host"; import { createTelemetryDrain, readTelemetryConsent, } from "../../features/customize/telemetry"; import { useAlive } from "./use-timer-lease"; import { markCustomizeFailed, markCustomizeLoaded } from "../../features/customize/load"; import { Hdr, Pill, Toggle } from "./ui"; import { DoubaoSearchCard } from "./vendors/doubao"; export function NetworkTab() { const alive = useAlive(); const [err, setErr] = useState(null); const [allow, setAllow] = useState<{ enabled: boolean; groups: Array>; }>({ enabled: false, groups: [] }); const [doubao, setDoubao] = useState<{ config: Record; error: unknown; }>({ config: {}, error: null }); const [search, setSearch] = useState>({}); const [searchKey, setSearchKey] = useState(""); const [savingSearch, setSavingSearch] = useState(false); useEffect(() => { void (async () => { try { const [d, db] = await Promise.all([ api("/preferences/builtin-allowlist"), api("/doubao-search/config") .then((config) => ({ config, error: null as unknown })) .catch((error) => ({ config: {} as Record, error })), ]); if (!alive()) return; setAllow({ enabled: !!d.enabled, groups: asList(d.groups) as Array>, }); setDoubao(db); markCustomizeLoaded(); try { const sc = await api("/search/config"); if (alive()) setSearch(sc); } catch { /* original swallowed */ } } catch (e) { if (!alive()) return; const message = t("versions.load.err", (e as Error).message); setErr(message); markCustomizeFailed(message); } })(); }, [alive]); if (err) return
{err}
; return (
{t("cust.network.allowName")}
{allow.enabled ? t("cust.network.enabledDesc") : t("cust.network.disabledDesc")}
{ const on = !allow.enabled; setAllow((prev) => ({ ...prev, enabled: on })); try { const r = await api("/network/status", { method: "PUT", body: JSON.stringify({ enabled: on }), }); hint(r.enabled ? t("toast.network.enabled") : t("toast.network.disabled")); } catch { setAllow((prev) => ({ ...prev, enabled: !on })); } }} />
{t("cust.search.name")}
{(search.api_key_configured ? t("cust.search.set") : t("cust.search.unset")) + " ยท " + (asString(search.endpoint) || "https://api.tavily.com/search")}
setSearchKey((e.target as HTMLInputElement).value)} />
{allow.groups.map((g, i) => (
{asString(g.name || g.label)}
{asList(g.domains) .slice(0, 12) .map((dm) => ( {String(dm)} ))}
))}
); } function TelemetryToggle({ alive }: { alive: () => boolean }) { const [consent, setConsent] = useState<{ enabled: boolean; env_locked: boolean; } | null>(null); const [on, setOn] = useState(false); useEffect(() => { void (async () => { try { const d = await api("/telemetry/consent"); if (!alive()) return; const next = readTelemetryConsent(d); setConsent(next); setOn(next.enabled); } catch { /* original returns */ } })(); }, [alive]); if (!consent) return null; if (consent.env_locked) { return (
{t("cust.telemetry.name")}
{t("cust.telemetry.envlock")}
{}} />
); } return ( ); } function LiveTelemetry({ initial, on, setOn, alive, }: { initial: boolean; on: boolean; setOn: (v: boolean) => void; alive: () => boolean; }) { const [drain] = useState(() => createTelemetryDrain(initial, (next) => setOn(next), { alive }), ); return (
{t("cust.telemetry.name")}
{on ? t("cust.telemetry.on") : t("cust.telemetry.off")}
drain.onclick()} />
); }