mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-08-10 05:00:43 +02:00
refactor: cleanup, korean translation
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FiWifi } from "react-icons/fi";
|
||||
import { LuLayers, LuPuzzle, LuShield, LuUsers } from "react-icons/lu";
|
||||
import { LoadingButton } from "@/components/loading-button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
@@ -19,6 +22,8 @@ interface UnsyncedEntityCounts {
|
||||
proxies: number;
|
||||
groups: number;
|
||||
vpns: number;
|
||||
extensions: number;
|
||||
extension_groups: number;
|
||||
}
|
||||
|
||||
interface SyncAllDialogProps {
|
||||
@@ -67,27 +72,55 @@ export function SyncAllDialog({ isOpen, onClose }: SyncAllDialogProps) {
|
||||
}
|
||||
}, [onClose, t]);
|
||||
|
||||
const totalCount =
|
||||
(counts?.proxies ?? 0) + (counts?.groups ?? 0) + (counts?.vpns ?? 0);
|
||||
const items = useMemo(() => {
|
||||
if (!counts) return [];
|
||||
return [
|
||||
{
|
||||
key: "proxies",
|
||||
count: counts.proxies,
|
||||
label: t("syncAll.labels.proxies"),
|
||||
Icon: FiWifi,
|
||||
},
|
||||
{
|
||||
key: "vpns",
|
||||
count: counts.vpns,
|
||||
label: t("syncAll.labels.vpns"),
|
||||
Icon: LuShield,
|
||||
},
|
||||
{
|
||||
key: "groups",
|
||||
count: counts.groups,
|
||||
label: t("syncAll.labels.groups"),
|
||||
Icon: LuUsers,
|
||||
},
|
||||
{
|
||||
key: "extensions",
|
||||
count: counts.extensions,
|
||||
label: t("syncAll.labels.extensions"),
|
||||
Icon: LuPuzzle,
|
||||
},
|
||||
{
|
||||
key: "extensionGroups",
|
||||
count: counts.extension_groups,
|
||||
label: t("syncAll.labels.extensionGroups"),
|
||||
Icon: LuLayers,
|
||||
},
|
||||
].filter((item) => item.count > 0);
|
||||
}, [counts, t]);
|
||||
|
||||
// Don't show if there's nothing to sync
|
||||
const totalCount = items.reduce((sum, item) => sum + item.count, 0);
|
||||
|
||||
// Don't render anything when there's nothing to sync — the parent
|
||||
// mounts this dialog eagerly after login, so silent-close is correct.
|
||||
if (!isLoading && totalCount === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const parts: string[] = [];
|
||||
if (counts?.proxies && counts.proxies > 0) {
|
||||
parts.push(t("syncAll.proxies", { count: counts.proxies }));
|
||||
}
|
||||
if (counts?.groups && counts.groups > 0) {
|
||||
parts.push(t("syncAll.groups", { count: counts.groups }));
|
||||
}
|
||||
if (counts?.vpns && counts.vpns > 0) {
|
||||
parts.push(t("syncAll.vpns", { count: counts.vpns }));
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen && totalCount > 0} onOpenChange={onClose}>
|
||||
<Dialog
|
||||
open={isOpen && (isLoading || totalCount > 0)}
|
||||
onOpenChange={onClose}
|
||||
>
|
||||
<DialogContent className="max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t("syncAll.title")}</DialogTitle>
|
||||
@@ -99,10 +132,26 @@ export function SyncAllDialog({ isOpen, onClose }: SyncAllDialogProps) {
|
||||
<div className="size-6 rounded-full border-2 border-current animate-spin border-t-transparent" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="py-4">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t("syncAll.itemsList", { items: parts.join(", ") })}
|
||||
</p>
|
||||
<div className="grid grid-cols-2 gap-2 py-2">
|
||||
{items.map(({ key, count, label, Icon }) => (
|
||||
<div
|
||||
key={key}
|
||||
className="flex items-center gap-3 rounded-lg border border-border/60 bg-card/50 p-3 transition-colors hover:bg-card"
|
||||
>
|
||||
<div className="flex size-9 shrink-0 items-center justify-center rounded-md bg-primary/10 text-primary">
|
||||
<Icon className="size-4" />
|
||||
</div>
|
||||
<div className="min-w-0 flex-1 text-sm font-medium truncate">
|
||||
{label}
|
||||
</div>
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="shrink-0 tabular-nums px-2"
|
||||
>
|
||||
{count}
|
||||
</Badge>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user