"use client"; import { invoke } from "@tauri-apps/api/core"; import { emit, listen } from "@tauri-apps/api/event"; import { useCallback, useEffect, useState } from "react"; import { GoPlus } from "react-icons/go"; import { LuPencil, LuTrash2 } from "react-icons/lu"; import { toast } from "sonner"; import { DeleteConfirmationDialog } from "@/components/delete-confirmation-dialog"; import { ProxyFormDialog } from "@/components/proxy-form-dialog"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Checkbox } from "@/components/ui/checkbox"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Label } from "@/components/ui/label"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/ui/tooltip"; import { useProxyEvents } from "@/hooks/use-proxy-events"; import { showErrorToast, showSuccessToast } from "@/lib/toast-utils"; import type { ProxyCheckResult, StoredProxy } from "@/types"; import { ProxyCheckButton } from "./proxy-check-button"; import { RippleButton } from "./ui/ripple"; type SyncStatus = "disabled" | "syncing" | "synced" | "error" | "waiting"; function getSyncStatusDot( proxy: StoredProxy, liveStatus: SyncStatus | undefined, ): { color: string; tooltip: string; animate: boolean } { const status = liveStatus ?? (proxy.sync_enabled ? "synced" : "disabled"); switch (status) { case "syncing": return { color: "bg-yellow-500", tooltip: "Syncing...", animate: true }; case "synced": return { color: "bg-green-500", tooltip: proxy.last_sync ? `Synced ${new Date(proxy.last_sync * 1000).toLocaleString()}` : "Synced", animate: false, }; case "waiting": return { color: "bg-yellow-500", tooltip: "Waiting to sync", animate: false, }; case "error": return { color: "bg-red-500", tooltip: "Sync error", animate: false }; default: return { color: "bg-gray-400", tooltip: "Not synced", animate: false }; } } interface ProxyManagementDialogProps { isOpen: boolean; onClose: () => void; } export function ProxyManagementDialog({ isOpen, onClose, }: ProxyManagementDialogProps) { const [showProxyForm, setShowProxyForm] = useState(false); const [editingProxy, setEditingProxy] = useState(null); const [proxyToDelete, setProxyToDelete] = useState(null); const [isDeleting, setIsDeleting] = useState(false); const [checkingProxyId, setCheckingProxyId] = useState(null); const [proxyCheckResults, setProxyCheckResults] = useState< Record >({}); const [proxySyncStatus, setProxySyncStatus] = useState< Record >({}); const [proxyInUse, setProxyInUse] = useState>({}); const [isTogglingSync, setIsTogglingSync] = useState>( {}, ); const { storedProxies, proxyUsage, isLoading } = useProxyEvents(); // Listen for proxy sync status events useEffect(() => { let unlisten: (() => void) | undefined; const setupListener = async () => { unlisten = await listen<{ id: string; status: string }>( "proxy-sync-status", (event) => { const { id, status } = event.payload; setProxySyncStatus((prev) => ({ ...prev, [id]: status as SyncStatus, })); }, ); }; void setupListener(); return () => { unlisten?.(); }; }, []); // Load cached check results on mount and when proxies change useEffect(() => { const loadCachedResults = async () => { const results: Record = {}; const inUse: Record = {}; for (const proxy of storedProxies) { try { const cached = await invoke( "get_cached_proxy_check", { proxyId: proxy.id }, ); if (cached) { results[proxy.id] = cached; } const inUseBySynced = await invoke( "is_proxy_in_use_by_synced_profile", { proxyId: proxy.id }, ); inUse[proxy.id] = inUseBySynced; } catch (_error) { // Ignore errors } } setProxyCheckResults(results); setProxyInUse(inUse); }; if (storedProxies.length > 0) { void loadCachedResults(); } }, [storedProxies]); const handleDeleteProxy = useCallback((proxy: StoredProxy) => { // Open in-app confirmation dialog setProxyToDelete(proxy); }, []); const handleConfirmDelete = useCallback(async () => { if (!proxyToDelete) return; setIsDeleting(true); try { await invoke("delete_stored_proxy", { proxyId: proxyToDelete.id }); toast.success("Proxy deleted successfully"); await emit("stored-proxies-changed"); } catch (error) { console.error("Failed to delete proxy:", error); toast.error("Failed to delete proxy"); } finally { setIsDeleting(false); setProxyToDelete(null); } }, [proxyToDelete]); const handleCreateProxy = useCallback(() => { setEditingProxy(null); setShowProxyForm(true); }, []); const handleEditProxy = useCallback((proxy: StoredProxy) => { setEditingProxy(proxy); setShowProxyForm(true); }, []); const handleProxyFormClose = useCallback(() => { setShowProxyForm(false); setEditingProxy(null); }, []); const handleToggleSync = useCallback(async (proxy: StoredProxy) => { setIsTogglingSync((prev) => ({ ...prev, [proxy.id]: true })); try { await invoke("set_proxy_sync_enabled", { proxyId: proxy.id, enabled: !proxy.sync_enabled, }); showSuccessToast(proxy.sync_enabled ? "Sync disabled" : "Sync enabled"); await emit("stored-proxies-changed"); } catch (error) { console.error("Failed to toggle sync:", error); showErrorToast( error instanceof Error ? error.message : "Failed to update sync", ); } finally { setIsTogglingSync((prev) => ({ ...prev, [proxy.id]: false })); } }, []); return ( <> Proxy Management Manage your saved proxy configurations for reuse across profiles
{/* Create new proxy button */}
Create
{/* Proxies list */} {isLoading ? (
Loading proxies...
) : storedProxies.length === 0 ? (
No proxies created yet. Create your first proxy using the button above.
) : (
Name Usage Sync Actions {storedProxies.map((proxy) => { const syncDot = getSyncStatusDot( proxy, proxySyncStatus[proxy.id], ); return (

{syncDot.tooltip}

{proxy.name}
{proxyUsage[proxy.id] ?? 0}
handleToggleSync(proxy) } disabled={ isTogglingSync[proxy.id] || proxyInUse[proxy.id] } />
{proxyInUse[proxy.id] ? (

Sync cannot be disabled while this proxy is used by synced profiles

) : (

{proxy.sync_enabled ? "Disable sync" : "Enable sync"}

)}
{ setProxyCheckResults((prev) => ({ ...prev, [proxy.id]: result, })); }} onCheckFailed={(result) => { setProxyCheckResults((prev) => ({ ...prev, [proxy.id]: result, })); }} />

Edit proxy

{(proxyUsage[proxy.id] ?? 0) > 0 ? (

Cannot delete: in use by{" "} {proxyUsage[proxy.id]} profile {proxyUsage[proxy.id] > 1 ? "s" : ""}

) : (

Delete proxy

)}
); })}
)}
Close
setProxyToDelete(null)} onConfirm={handleConfirmDelete} title="Delete Proxy" description={`This action cannot be undone. This will permanently delete the proxy "${proxyToDelete?.name ?? ""}".`} confirmButtonText="Delete" isLoading={isDeleting} /> ); }