mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-03 13:52:09 +03:00
Integrated into release/v3.8.25 — feat(mimocode): per-account proxy for multi-account round-robin (runWithProxyContext per account, keyed by fingerprint). Orphan test relocated to a collected vitest path (14/14 green).
75 lines
2.3 KiB
TypeScript
75 lines
2.3 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useCallback, useRef, useEffect } from "react";
|
|
|
|
type ButtonState = "idle" | "distributing" | "complete";
|
|
|
|
interface DistributeProxiesButtonProps {
|
|
/** Async callback that performs the actual proxy distribution */
|
|
onDistribute: () => Promise<void>;
|
|
/** Whether the button should be disabled (e.g., during batch testing) */
|
|
disabled?: boolean;
|
|
/** Button label override */
|
|
label?: string;
|
|
/** Size variant */
|
|
size?: "sm" | "md";
|
|
}
|
|
|
|
export default function DistributeProxiesButton({
|
|
onDistribute,
|
|
disabled = false,
|
|
label = "Distribute Proxies",
|
|
size = "md",
|
|
}: DistributeProxiesButtonProps) {
|
|
const [state, setState] = useState<ButtonState>("idle");
|
|
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
|
|
useEffect(() => {
|
|
return () => {
|
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
};
|
|
}, []);
|
|
|
|
const handleClick = useCallback(async () => {
|
|
if (disabled || state === "distributing") return;
|
|
setState("distributing");
|
|
try {
|
|
await onDistribute();
|
|
setState("complete");
|
|
timerRef.current = setTimeout(() => setState("idle"), 1500);
|
|
} catch {
|
|
setState("idle");
|
|
}
|
|
}, [onDistribute, disabled, state]);
|
|
|
|
const isDisabled = disabled || state === "distributing";
|
|
|
|
const sizeClasses =
|
|
size === "sm" ? "px-2 py-1 text-[11px]" : "px-3 py-1.5 text-xs";
|
|
|
|
const stateClasses =
|
|
state === "distributing"
|
|
? "bg-primary/20 border-primary/40 text-primary animate-pulse"
|
|
: state === "complete"
|
|
? "bg-green-500/15 border-green-500/40 text-green-500"
|
|
: "bg-bg-subtle border-border text-text-muted hover:text-text-primary hover:border-primary/40";
|
|
|
|
const icon = state === "distributing" ? "sync" : state === "complete" ? "check" : "swap_horiz";
|
|
const displayLabel = state === "distributing" ? "Distributing..." : state === "complete" ? "Complete" : label;
|
|
|
|
return (
|
|
<button
|
|
onClick={handleClick}
|
|
disabled={isDisabled}
|
|
className={`flex items-center gap-1.5 rounded-lg font-medium border transition-colors ${sizeClasses} ${stateClasses}`}
|
|
title={displayLabel}
|
|
aria-label={displayLabel}
|
|
>
|
|
<span className={`material-symbols-outlined text-[14px] ${state === "distributing" ? "animate-spin" : ""}`}>
|
|
{icon}
|
|
</span>
|
|
{displayLabel}
|
|
</button>
|
|
);
|
|
}
|