/* ============================================================ SELECT PAGE — Universal full-screen option picker Design: iOS Settings style selection list Usage: Navigate to /select with state object State params: title - Page title shown in header options - Array of option strings selected - Currently selected index (number) back - Route to navigate back to after selection key - Identifier for which setting this controls ============================================================ */ import { useLocation } from "wouter"; import { ChevronLeft, Check } from "lucide-react"; import { useDevice } from "@/contexts/DeviceContext"; import { cn } from "@/lib/utils"; import { useEffect, useMemo, useState } from "react"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; import localeZh from "@/locales/data-zh.json"; import localeZhHK from "@/locales/data-zh-HK.json"; import localeEn from "@/locales/data-en.json"; const iisModeImageUrl = (index: number) => `${import.meta.env.BASE_URL}images/IIS/iis_app${index + 1}.png`; type DacVolumeDirectLocale = { label?: string; confirmTitle?: string; confirm0dB?: string; confirm12dB?: string; confirmOk?: string; confirmCancel?: string; }; type LocaleRoot = { dac?: { dacVolumeDirect?: DacVolumeDirectLocale }; }; const LOCALE_BY_LANG: Record = { 0: localeEn as LocaleRoot, 1: localeZhHK as LocaleRoot, 2: localeZh as LocaleRoot, }; /* ── Key → API field mapping ── */ const KEY_TO_SETTING: Record = { effect_value: "effect_value", crossfeed_value: "crossfeed_value", subwoofer_rate: "subwoofer_rate", subwoofer_mix_type: "subwoofer_mix_type", language: "language", analogGain: "analogGain", soundStep: "soundStep", bootSound: "bootSound", filterCharacteristic: "pcm", mutePolar: "hdmimutepolar", IISMode: "hdmiType", xlr: "xlr", dacGain: "dacGain", dacImpedance: "dacImpedance", dacVolumeDirect: "dacVolumeDirect", dacArc: "dacArc", vu: "vu", input: "input", output: "output", screenLight: "screenLight", screenOff: "screenOff", buttonLight: "buttonLight", autoHome: "autoHome", sleep: "sleep", headphone: "peqSelect", }; // Global state for select page (to avoid URL params) export let selectPageState: { title: string; options: string[]; selected: number; back: string; key: string; } | null = null; // Last select result for pages that need local state update export let selectPageResult: { key: string; selected: number } | null = null; export function consumeSelectPageResult() { const result = selectPageResult; selectPageResult = null; return result; } // Helper function to set select page state export function navigateToSelect(title: string, options: string[], selected: number, back: string, key: string) { selectPageState = { title, options, selected, back, key }; } export default function SelectPage() { const [location, setLocation] = useLocation(); const { updateSetting, deviceState } = useDevice(); const [state, setState] = useState<{ title: string; options: string[]; selected: number; back: string; key: string; } | null>(() => selectPageState); const [saving, setSaving] = useState(false); const [passthroughConfirmIdx, setPassthroughConfirmIdx] = useState(null); const langIdx = deviceState?.language ?? 2; const dacVolumeDirectText = (LOCALE_BY_LANG[langIdx] ?? localeZh as LocaleRoot).dac?.dacVolumeDirect; const passthroughConfirmCopy = useMemo(() => { if (passthroughConfirmIdx === null) return null; const message = passthroughConfirmIdx === 1 ? dacVolumeDirectText?.confirm0dB : passthroughConfirmIdx === 2 ? dacVolumeDirectText?.confirm12dB : undefined; return { title: dacVolumeDirectText?.confirmTitle ?? "提示", message: message ?? (passthroughConfirmIdx === 1 ? "直通模式会将音量调到最大,并且不可调节" : "直通模式会将音量调到-12dB,并且不可调节"), ok: dacVolumeDirectText?.confirmOk ?? "确认", cancel: dacVolumeDirectText?.confirmCancel ?? "取消", }; }, [passthroughConfirmIdx, dacVolumeDirectText]); // Re-read global state whenever we enter /select (avoids stale key/options after remount) useEffect(() => { if (location === "/select" && selectPageState) { setState({ ...selectPageState }); } }, [location]); if (!state) { return null; } const { title, options, selected: selectedIdx, back, key } = state; const applySelect = async (idx: number) => { if (saving) return; selectPageResult = { key, selected: idx }; const apiField = KEY_TO_SETTING[key]; if (apiField) { setSaving(true); try { await updateSetting({ [apiField]: idx }); } finally { setSaving(false); } } selectPageState = null; setLocation(back); }; const handleSelect = async (idx: number) => { if (saving) return; if (key === "dacVolumeDirect" && (idx === 1 || idx === 2) && idx !== selectedIdx) { setPassthroughConfirmIdx(idx); return; } await applySelect(idx); }; const handlePassthroughConfirm = async () => { if (passthroughConfirmIdx === null) return; const idx = passthroughConfirmIdx; setPassthroughConfirmIdx(null); await applySelect(idx); }; return (
{/* ── Header ── */}

{title}

{key === "IISMode" ? (
{options.map((opt, idx) => { const isSelected = idx === selectedIdx; return ( ); })}
) : (
{options.map((opt, idx) => { const isSelected = idx === selectedIdx; return ( ); })}
)}
{ if (!open) setPassthroughConfirmIdx(null); }} > {passthroughConfirmCopy?.title} {passthroughConfirmCopy?.message} {passthroughConfirmCopy?.cancel} void handlePassthroughConfirm()} > {passthroughConfirmCopy?.ok}
); }