mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-04-23 04:16:29 +02:00
169 lines
5.0 KiB
TypeScript
169 lines
5.0 KiB
TypeScript
"use client";
|
|
|
|
import { invoke } from "@tauri-apps/api/core";
|
|
import * as React from "react";
|
|
import { FiCheck } from "react-icons/fi";
|
|
import { toast } from "sonner";
|
|
import { FlagIcon } from "@/components/flag-icon";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
Tooltip,
|
|
TooltipContent,
|
|
TooltipTrigger,
|
|
} from "@/components/ui/tooltip";
|
|
import { formatRelativeTime } from "@/lib/flag-utils";
|
|
import type { ProxyCheckResult, StoredProxy } from "@/types";
|
|
|
|
interface ProxyCheckButtonProps {
|
|
proxy: StoredProxy;
|
|
profileId: string;
|
|
checkingProfileId: string | null;
|
|
cachedResult?: ProxyCheckResult;
|
|
onCheckComplete?: (result: ProxyCheckResult) => void;
|
|
onCheckFailed?: (result: ProxyCheckResult) => void;
|
|
disabled?: boolean;
|
|
setCheckingProfileId?: (id: string | null) => void;
|
|
}
|
|
|
|
export function ProxyCheckButton({
|
|
proxy,
|
|
profileId,
|
|
checkingProfileId,
|
|
cachedResult,
|
|
onCheckComplete,
|
|
onCheckFailed,
|
|
disabled = false,
|
|
setCheckingProfileId,
|
|
}: ProxyCheckButtonProps) {
|
|
const [localResult, setLocalResult] = React.useState<
|
|
ProxyCheckResult | undefined
|
|
>(cachedResult);
|
|
|
|
React.useEffect(() => {
|
|
setLocalResult(cachedResult);
|
|
}, [cachedResult]);
|
|
|
|
const handleCheck = React.useCallback(async () => {
|
|
if (checkingProfileId === profileId) return;
|
|
|
|
setCheckingProfileId?.(profileId);
|
|
try {
|
|
const result = await invoke<ProxyCheckResult>("check_proxy_validity", {
|
|
proxyId: proxy.id,
|
|
proxySettings: proxy.dynamic_proxy_url
|
|
? undefined
|
|
: proxy.proxy_settings,
|
|
});
|
|
setLocalResult(result);
|
|
onCheckComplete?.(result);
|
|
|
|
// Show toast with location
|
|
const locationParts: string[] = [];
|
|
if (result.city) locationParts.push(result.city);
|
|
if (result.country) locationParts.push(result.country);
|
|
const location =
|
|
locationParts.length > 0 ? locationParts.join(", ") : "Unknown";
|
|
|
|
toast.success(
|
|
<div className="flex flex-col">
|
|
Your proxy location is:
|
|
<div className="flex items-center whitespace-nowrap">
|
|
{location}
|
|
{result.country_code && (
|
|
<FlagIcon
|
|
countryCode={result.country_code}
|
|
className="ml-1 text-sm"
|
|
/>
|
|
)}
|
|
</div>
|
|
</div>,
|
|
);
|
|
} catch (error) {
|
|
const errorMessage =
|
|
error instanceof Error ? error.message : String(error);
|
|
toast.error(`Proxy check failed: ${errorMessage}`);
|
|
|
|
// Save failed check result
|
|
const failedResult: ProxyCheckResult = {
|
|
ip: "",
|
|
city: undefined,
|
|
country: undefined,
|
|
country_code: undefined,
|
|
timestamp: Math.floor(Date.now() / 1000),
|
|
is_valid: false,
|
|
};
|
|
setLocalResult(failedResult);
|
|
onCheckFailed?.(failedResult);
|
|
} finally {
|
|
setCheckingProfileId?.(null);
|
|
}
|
|
}, [
|
|
proxy,
|
|
profileId,
|
|
checkingProfileId,
|
|
onCheckComplete,
|
|
onCheckFailed,
|
|
setCheckingProfileId,
|
|
]);
|
|
|
|
const isCurrentlyChecking = checkingProfileId === profileId;
|
|
const result = localResult;
|
|
|
|
return (
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
className="h-7 w-7 p-0"
|
|
onClick={handleCheck}
|
|
disabled={isCurrentlyChecking || disabled}
|
|
>
|
|
{isCurrentlyChecking ? (
|
|
<div className="w-3 h-3 rounded-full border border-current animate-spin border-t-transparent" />
|
|
) : result?.is_valid && result.country_code ? (
|
|
<span className="relative inline-flex items-center justify-center">
|
|
<FlagIcon countryCode={result.country_code} className="h-2.5" />
|
|
<FiCheck className="absolute bottom-[-6px] right-[-4px]" />
|
|
</span>
|
|
) : result && !result.is_valid ? (
|
|
<span className="text-destructive text-sm">✕</span>
|
|
) : (
|
|
<FiCheck className="w-3 h-3" />
|
|
)}
|
|
</Button>
|
|
</TooltipTrigger>
|
|
<TooltipContent>
|
|
{isCurrentlyChecking ? (
|
|
<p>Checking proxy...</p>
|
|
) : result?.is_valid ? (
|
|
<div className="space-y-1">
|
|
<p className="flex items-center gap-1">
|
|
{result.country_code && (
|
|
<FlagIcon countryCode={result.country_code} />
|
|
)}
|
|
{[result.city, result.country].filter(Boolean).join(", ") ||
|
|
"Unknown"}
|
|
</p>
|
|
<p className="text-xs text-primary-foreground/70">
|
|
IP: {result.ip}
|
|
</p>
|
|
<p className="text-xs text-primary-foreground/70">
|
|
Checked {formatRelativeTime(result.timestamp)}
|
|
</p>
|
|
</div>
|
|
) : result && !result.is_valid ? (
|
|
<div>
|
|
<p>Proxy check failed</p>
|
|
<p className="text-xs text-primary-foreground/70">
|
|
Failed {formatRelativeTime(result.timestamp)}
|
|
</p>
|
|
</div>
|
|
) : (
|
|
<p>Check proxy validity</p>
|
|
)}
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
);
|
|
}
|