import { useCallback, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { Server, RefreshCw, Loader2 } from "lucide-react"; import { Button } from "@/components/button.tsx"; import { Dialog, DialogContent, DialogHeader, DialogTitle, } from "@/components/dialog.tsx"; import { RemoteSyncServerPicker } from "./RemoteSyncServerPicker.tsx"; import { ElectronLoginForm } from "@/auth/ElectronLoginForm.tsx"; interface RemoteSyncConfig { serverUrl: string; connectedAt: string; lastSyncedAt?: string | null; lastSyncStatus?: "ok" | "error" | "never"; lastSyncError?: string | null; } interface RemoteSyncStatus { connected: boolean; syncing: boolean; lastSyncedAt: string | null; lastError: string | null; needsReauth: boolean; } type DesktopSettings = { defaultConnectionOrigin: "local" | "remote" }; export function RemoteSyncPanel() { const { t } = useTranslation(); const [config, setConfig] = useState(null); const [status, setStatus] = useState(null); const [desktopSettings, setDesktopSettings] = useState({ defaultConnectionOrigin: "local", }); const [step, setStep] = useState<"idle" | "picker" | "login">("idle"); const [pendingServerUrl, setPendingServerUrl] = useState(""); const [syncingNow, setSyncingNow] = useState(false); const refresh = useCallback(async () => { const [cfg, st, settings] = await Promise.all([ window.electronAPI?.invoke?.( "get-remote-sync-config", ) as Promise, window.electronAPI?.invoke?.( "get-remote-sync-status", ) as Promise, window.electronAPI?.invoke?.( "get-desktop-settings", ) as Promise, ]); setConfig(cfg ?? null); setStatus(st ?? null); if (settings) setDesktopSettings(settings); }, []); useEffect(() => { refresh(); const unsubscribe = window.electronAPI?.onRemoteSyncStatusChanged?.( (nextStatus: RemoteSyncStatus) => setStatus(nextStatus), ); return () => unsubscribe?.(); }, [refresh]); const handleConnectClick = () => setStep("picker"); const handleServerConfigured = (serverUrl: string) => { setPendingServerUrl(serverUrl); setStep("login"); }; const handleAuthSuccess = async () => { setStep("idle"); await refresh(); }; const handleDisconnect = async () => { await window.electronAPI?.invoke?.("clear-remote-sync-config"); await refresh(); }; const handleSyncNow = async () => { setSyncingNow(true); try { await window.electronAPI?.invoke?.("remote-sync-now"); } finally { setSyncingNow(false); await refresh(); } }; const handleOriginChange = async (origin: "local" | "remote") => { const next = { ...desktopSettings, defaultConnectionOrigin: origin }; setDesktopSettings(next); await window.electronAPI?.invoke?.("save-desktop-settings", next); }; const isConnected = !!config?.serverUrl; return (

{t("remoteSync.title")}

{t("remoteSync.description")}

{isConnected ? ( <> {t("remoteSync.connectedTo", { url: config.serverUrl })} {status?.needsReauth ? t("remoteSync.needsReauth") : status?.lastError ? t("remoteSync.syncError", { message: status.lastError, }) : status?.lastSyncedAt ? t("remoteSync.lastSynced", { time: new Date( status.lastSyncedAt, ).toLocaleString(), }) : t("remoteSync.neverSynced")} ) : ( {t("remoteSync.notConnected")} )}
{isConnected ? ( <> {status?.needsReauth && ( )} ) : ( )}

{t("remoteSync.originTitle")}

{t("remoteSync.originDescription")}

!open && setStep("idle")} > {t("remoteSync.title")} setStep("idle")} /> !open && setStep("idle")} > {t("remoteSync.signInTitle", { url: pendingServerUrl })}
setStep("picker")} />
); }