From f809b975f317093c1e5d0e03d6d48fa56ff6c3e6 Mon Sep 17 00:00:00 2001 From: zhom <2717306+zhom@users.noreply.github.com> Date: Wed, 13 Aug 2025 16:28:08 +0400 Subject: [PATCH] refactor: better ui handling for proxy changes --- src/components/create-profile-dialog.tsx | 2 + src/components/profile-data-table.tsx | 21 ++++++ src/components/proxy-management-dialog.tsx | 86 +++++++++++++++++----- 3 files changed, 90 insertions(+), 19 deletions(-) diff --git a/src/components/create-profile-dialog.tsx b/src/components/create-profile-dialog.tsx index 2ac36e4..0255d90 100644 --- a/src/components/create-profile-dialog.tsx +++ b/src/components/create-profile-dialog.tsx @@ -1,6 +1,7 @@ "use client"; import { invoke } from "@tauri-apps/api/core"; +import { emit } from "@tauri-apps/api/event"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { GoPlus } from "react-icons/go"; import { LoadingButton } from "@/components/loading-button"; @@ -634,6 +635,7 @@ export function CreateProfileDialog({ onSave={(proxy) => { setStoredProxies((prev) => [...prev, proxy]); setSelectedProxyId(proxy.id); + void emit("stored-proxies-changed"); }} /> diff --git a/src/components/profile-data-table.tsx b/src/components/profile-data-table.tsx index 1a0ac35..f14c496 100644 --- a/src/components/profile-data-table.tsx +++ b/src/components/profile-data-table.tsx @@ -9,6 +9,7 @@ import { useReactTable, } from "@tanstack/react-table"; import { invoke } from "@tauri-apps/api/core"; +import { emit, listen } from "@tauri-apps/api/event"; import * as React from "react"; import { CiCircleCheck } from "react-icons/ci"; import { IoEllipsisHorizontal } from "react-icons/io5"; @@ -136,6 +137,8 @@ export function ProfilesDataTable({ proxyId, }); setProxyOverrides((prev) => ({ ...prev, [profileName]: proxyId })); + // Notify other parts of the app so usage counts and lists refresh + await emit("profile-updated"); } catch (error) { console.error("Failed to update proxy settings:", error); } finally { @@ -179,6 +182,24 @@ export function ProfilesDataTable({ } }, [browserState.isClient, loadStoredProxies]); + // Keep stored proxies up-to-date by listening for changes emitted elsewhere in the app + React.useEffect(() => { + if (!browserState.isClient) return; + let unlisten: (() => void) | undefined; + (async () => { + try { + unlisten = await listen("stored-proxies-changed", () => { + void loadStoredProxies(); + }); + } catch (_err) { + // Best-effort only + } + })(); + return () => { + if (unlisten) unlisten(); + }; + }, [browserState.isClient, loadStoredProxies]); + // Automatically deselect profiles that become running, updating, launching, or stopping React.useEffect(() => { setSelectedProfiles((prev) => { diff --git a/src/components/proxy-management-dialog.tsx b/src/components/proxy-management-dialog.tsx index 3488edd..2dafd33 100644 --- a/src/components/proxy-management-dialog.tsx +++ b/src/components/proxy-management-dialog.tsx @@ -1,10 +1,11 @@ "use client"; import { invoke } from "@tauri-apps/api/core"; -import { listen } from "@tauri-apps/api/event"; +import { emit, listen } from "@tauri-apps/api/event"; import { useCallback, useEffect, useState } from "react"; import { FiEdit2, FiPlus, FiTrash2, FiWifi } from "react-icons/fi"; 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"; @@ -38,6 +39,8 @@ export function ProxyManagementDialog({ const [showProxyForm, setShowProxyForm] = useState(false); const [editingProxy, setEditingProxy] = useState(null); const [proxyUsage, setProxyUsage] = useState>({}); + const [proxyToDelete, setProxyToDelete] = useState(null); + const [isDeleting, setIsDeleting] = useState(false); const loadStoredProxies = useCallback(async () => { try { @@ -91,22 +94,46 @@ export function ProxyManagementDialog({ }; }, [isOpen, loadProxyUsage]); - const handleDeleteProxy = useCallback(async (proxy: StoredProxy) => { - if ( - !confirm(`Are you sure you want to delete the proxy "${proxy.name}"?`) - ) { - return; - } + // Keep list in sync with external changes (e.g., created from CreateProfileDialog) + useEffect(() => { + let unlisten: (() => void) | undefined; + const setup = async () => { + try { + unlisten = await listen("stored-proxies-changed", () => { + void loadStoredProxies(); + void loadProxyUsage(); + }); + } catch (_err) { + // ignore non-critical errors + } + }; + if (isOpen) void setup(); + return () => { + if (unlisten) unlisten(); + }; + }, [isOpen, loadStoredProxies, loadProxyUsage]); + 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: proxy.id }); - setStoredProxies((prev) => prev.filter((p) => p.id !== proxy.id)); + await invoke("delete_stored_proxy", { proxyId: proxyToDelete.id }); + setStoredProxies((prev) => prev.filter((p) => p.id !== 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); @@ -133,6 +160,7 @@ export function ProxyManagementDialog({ }); setShowProxyForm(false); setEditingProxy(null); + void emit("stored-proxies-changed"); }, []); const handleProxyFormClose = useCallback(() => { @@ -241,17 +269,28 @@ export function ProxyManagementDialog({ - + + + -

Delete proxy

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

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

+ ) : ( +

Delete proxy

+ )}
@@ -274,6 +313,15 @@ export function ProxyManagementDialog({ onSave={handleProxySaved} editingProxy={editingProxy} /> + 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} + /> ); }