code formatting with prettier

This commit is contained in:
coolcoala
2025-07-14 05:23:32 +03:00
parent eb1e4fe0c3
commit 5cdc5075f8
58 changed files with 5163 additions and 1846 deletions

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect, useMemo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import React, { useState, useEffect, useMemo, useCallback } from "react";
import { useTranslation } from "react-i18next";
import { cn } from "@root/lib/utils";
// Компоненты и иконки
@@ -9,23 +9,28 @@ import {
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { ChevronsUpDown, Timer, WholeWord } from 'lucide-react';
} from "@/components/ui/select";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { ChevronsUpDown, Timer, WholeWord } from "lucide-react";
// Логика
import { useVerge } from '@/hooks/use-verge';
import { useAppData } from '@/providers/app-data-provider';
import delayManager from '@/services/delay';
import { updateProxy, deleteConnection } from '@/services/api';
import { useVerge } from "@/hooks/use-verge";
import { useAppData } from "@/providers/app-data-provider";
import delayManager from "@/services/delay";
import { updateProxy, deleteConnection } from "@/services/api";
// --- Типы и константы ---
const STORAGE_KEY_GROUP = 'clash-verge-selected-proxy-group';
const STORAGE_KEY_SORT_TYPE = 'clash-verge-proxy-sort-type';
const STORAGE_KEY_GROUP = "clash-verge-selected-proxy-group";
const STORAGE_KEY_SORT_TYPE = "clash-verge-proxy-sort-type";
const presetList = ["DIRECT", "REJECT", "REJECT-DROP", "PASS", "COMPATIBLE"];
type ProxySortType = 'default' | 'delay' | 'name';
type ProxySortType = "default" | "delay" | "name";
interface IProxyGroup {
name: string;
type: string;
@@ -35,15 +40,25 @@ interface IProxyGroup {
}
// --- Вспомогательная функция для цвета задержки ---
function getDelayBadgeVariant(delayValue: number): 'default' | 'secondary' | 'destructive' | 'outline' {
if (delayValue < 0) return 'secondary';
if (delayValue >= 150) return 'destructive';
return 'default';
function getDelayBadgeVariant(
delayValue: number,
): "default" | "secondary" | "destructive" | "outline" {
if (delayValue < 0) return "secondary";
if (delayValue >= 150) return "destructive";
return "default";
}
// --- Дочерний компонент для элемента списка с "живым" обновлением пинга ---
const ProxySelectItem = ({ proxyName, groupName }: { proxyName: string, groupName: string }) => {
const [delay, setDelay] = useState(() => delayManager.getDelay(proxyName, groupName));
const ProxySelectItem = ({
proxyName,
groupName,
}: {
proxyName: string;
groupName: string;
}) => {
const [delay, setDelay] = useState(() =>
delayManager.getDelay(proxyName, groupName),
);
const [isJustUpdated, setIsJustUpdated] = useState(false);
useEffect(() => {
@@ -71,44 +86,62 @@ const ProxySelectItem = ({ proxyName, groupName }: { proxyName: string, groupNam
variant={getDelayBadgeVariant(delay)}
className={cn(
"ml-4 flex-shrink-0 px-2 h-5 justify-center transition-colors duration-300",
isJustUpdated && "bg-primary/20 border-primary/50"
isJustUpdated && "bg-primary/20 border-primary/50",
)}
>
{(delay < 0) || (delay > 10000) ? '---' : delay}
{delay < 0 || delay > 10000 ? "---" : delay}
</Badge>
</div>
</SelectItem>
);
};
export const ProxySelectors: React.FC = () => {
const { t } = useTranslation();
const { verge } = useVerge();
const { proxies, connections, clashConfig, refreshProxy } = useAppData();
const mode = clashConfig?.mode?.toLowerCase() || 'rule';
const isGlobalMode = mode === 'global';
const isDirectMode = mode ==='direct';
const mode = clashConfig?.mode?.toLowerCase() || "rule";
const isGlobalMode = mode === "global";
const isDirectMode = mode === "direct";
const [selectedGroup, setSelectedGroup] = useState<string>('');
const [selectedProxy, setSelectedProxy] = useState<string>('');
const [sortType, setSortType] = useState<ProxySortType>(() => (localStorage.getItem(STORAGE_KEY_SORT_TYPE) as ProxySortType) || 'default');
const [selectedGroup, setSelectedGroup] = useState<string>("");
const [selectedProxy, setSelectedProxy] = useState<string>("");
const [sortType, setSortType] = useState<ProxySortType>(
() =>
(localStorage.getItem(STORAGE_KEY_SORT_TYPE) as ProxySortType) ||
"default",
);
useEffect(() => {
if (!proxies?.groups) return;
if (isGlobalMode) { setSelectedGroup('GLOBAL'); return; }
if (isDirectMode) { setSelectedGroup('DIRECT'); return; }
if (isGlobalMode) {
setSelectedGroup("GLOBAL");
return;
}
if (isDirectMode) {
setSelectedGroup("DIRECT");
return;
}
const savedGroup = localStorage.getItem(STORAGE_KEY_GROUP);
const primaryGroup = proxies.groups.find((g: IProxyGroup) => g.type === 'Selector' && g.name.toLowerCase().includes('auto')) || proxies.groups.find((g: IProxyGroup) => g.type === 'Selector');
const primaryGroup =
proxies.groups.find(
(g: IProxyGroup) =>
g.type === "Selector" && g.name.toLowerCase().includes("auto"),
) || proxies.groups.find((g: IProxyGroup) => g.type === "Selector");
if (savedGroup && proxies.groups.some((g: IProxyGroup) => g.name === savedGroup)) {
if (
savedGroup &&
proxies.groups.some((g: IProxyGroup) => g.name === savedGroup)
) {
setSelectedGroup(savedGroup);
} else if (primaryGroup) {
setSelectedGroup(primaryGroup.name);
} else if (proxies.groups.length > 0) {
const firstSelector = proxies.groups.find((g: IProxyGroup) => g.type === 'Selector');
const firstSelector = proxies.groups.find(
(g: IProxyGroup) => g.type === "Selector",
);
if (firstSelector) {
setSelectedGroup(firstSelector.name);
}
@@ -117,38 +150,56 @@ export const ProxySelectors: React.FC = () => {
useEffect(() => {
if (!selectedGroup || !proxies) return;
if (isGlobalMode) { setSelectedProxy(proxies.global?.now || ''); return; }
if (isDirectMode) { setSelectedProxy('DIRECT'); return; }
const group = proxies.groups.find((g: IProxyGroup) => g.name === selectedGroup);
if (isGlobalMode) {
setSelectedProxy(proxies.global?.now || "");
return;
}
if (isDirectMode) {
setSelectedProxy("DIRECT");
return;
}
const group = proxies.groups.find(
(g: IProxyGroup) => g.name === selectedGroup,
);
if (group) {
const current = group.now;
const firstInList = typeof group.all?.[0] === 'string' ? group.all[0] : group.all?.[0]?.name;
setSelectedProxy(current || firstInList || '');
const firstInList =
typeof group.all?.[0] === "string"
? group.all[0]
: group.all?.[0]?.name;
setSelectedProxy(current || firstInList || "");
}
}, [selectedGroup, proxies, isGlobalMode, isDirectMode]);
const handleProxyListOpen = useCallback((isOpen: boolean) => {
if (!isOpen || isDirectMode) return;
const handleProxyListOpen = useCallback(
(isOpen: boolean) => {
if (!isOpen || isDirectMode) return;
const timeout = verge?.default_latency_timeout || 5000;
const timeout = verge?.default_latency_timeout || 5000;
if (isGlobalMode) {
const proxyList = proxies?.global?.all;
if (proxyList) {
const proxyNames = proxyList
.map((p: any) => (typeof p === 'string' ? p : p.name))
.filter((name: string) => name && !presetList.includes(name));
if (isGlobalMode) {
const proxyList = proxies?.global?.all;
if (proxyList) {
const proxyNames = proxyList
.map((p: any) => (typeof p === "string" ? p : p.name))
.filter((name: string) => name && !presetList.includes(name));
delayManager.checkListDelay(proxyNames, 'GLOBAL', timeout);
delayManager.checkListDelay(proxyNames, "GLOBAL", timeout);
}
} else {
const group = proxies?.groups?.find(
(g: IProxyGroup) => g.name === selectedGroup,
);
if (group && group.all) {
const proxyNames = group.all
.map((p: any) => (typeof p === "string" ? p : p.name))
.filter(Boolean);
delayManager.checkListDelay(proxyNames, selectedGroup, timeout);
}
}
} else {
const group = proxies?.groups?.find((g: IProxyGroup) => g.name === selectedGroup);
if (group && group.all) {
const proxyNames = group.all.map((p: any) => typeof p === 'string' ? p : p.name).filter(Boolean);
delayManager.checkListDelay(proxyNames, selectedGroup, timeout);
}
}
}, [selectedGroup, proxies, isGlobalMode, isDirectMode, verge]);
},
[selectedGroup, proxies, isGlobalMode, isDirectMode, verge],
);
const handleGroupChange = (newGroup: string) => {
if (isGlobalMode || isDirectMode) return;
@@ -176,7 +227,11 @@ export const ProxySelectors: React.FC = () => {
};
const handleSortChange = () => {
const nextSort: Record<ProxySortType, ProxySortType> = { default: 'delay', delay: 'name', name: 'default' };
const nextSort: Record<ProxySortType, ProxySortType> = {
default: "delay",
delay: "name",
name: "default",
};
const newSortType = nextSort[sortType];
setSortType(newSortType);
localStorage.setItem(STORAGE_KEY_SORT_TYPE, newSortType);
@@ -187,27 +242,31 @@ export const ProxySelectors: React.FC = () => {
const allowedTypes = ["Selector", "URLTest", "Fallback"];
return proxies.groups.filter((g: IProxyGroup) =>
allowedTypes.includes(g.type) &&
!g.hidden
return proxies.groups.filter(
(g: IProxyGroup) => allowedTypes.includes(g.type) && !g.hidden,
);
}, [proxies]);
const proxyOptions = useMemo(() => {
let options: { name: string }[] = [];
if (isDirectMode) return [{ name: "DIRECT" }];
const sourceList = isGlobalMode ? proxies?.global?.all : proxies?.groups?.find((g: IProxyGroup) => g.name === selectedGroup)?.all;
const sourceList = isGlobalMode
? proxies?.global?.all
: proxies?.groups?.find((g: IProxyGroup) => g.name === selectedGroup)
?.all;
if (sourceList) {
options = sourceList.map((proxy: any) => ({
name: typeof proxy === 'string' ? proxy : proxy.name,
})).filter((p: { name: string }) => p.name);
options = sourceList
.map((proxy: any) => ({
name: typeof proxy === "string" ? proxy : proxy.name,
}))
.filter((p: { name: string }) => p.name);
}
if (sortType === 'name') return options.sort((a, b) => a.name.localeCompare(b.name));
if (sortType === 'delay') {
if (sortType === "name")
return options.sort((a, b) => a.name.localeCompare(b.name));
if (sortType === "delay") {
return options.sort((a, b) => {
const delayA = delayManager.getDelay(a.name, selectedGroup);
const delayB = delayManager.getDelay(b.name, selectedGroup);
@@ -223,8 +282,14 @@ export const ProxySelectors: React.FC = () => {
<TooltipProvider>
<div className="flex justify-center flex-col gap-2 md:items-end">
<div className="flex flex-col items-start gap-2">
<label className="text-sm font-medium text-muted-foreground">{t("Group")}</label>
<Select value={selectedGroup} onValueChange={handleGroupChange} disabled={isGlobalMode || isDirectMode}>
<label className="text-sm font-medium text-muted-foreground">
{t("Group")}
</label>
<Select
value={selectedGroup}
onValueChange={handleGroupChange}
disabled={isGlobalMode || isDirectMode}
>
<SelectTrigger className="w-100">
<Tooltip>
<TooltipTrigger asChild>
@@ -239,7 +304,9 @@ export const ProxySelectors: React.FC = () => {
</SelectTrigger>
<SelectContent>
{selectorGroups.map((group: IProxyGroup) => (
<SelectItem key={group.name} value={group.name}>{group.name}</SelectItem>
<SelectItem key={group.name} value={group.name}>
{group.name}
</SelectItem>
))}
</SelectContent>
</Select>
@@ -247,26 +314,39 @@ export const ProxySelectors: React.FC = () => {
<div className="flex flex-col items-start gap-2">
<div className="flex justify-between items-center w-100">
<label className="text-sm font-medium text-muted-foreground">{t("Proxy")}</label>
<label className="text-sm font-medium text-muted-foreground">
{t("Proxy")}
</label>
<Tooltip>
<TooltipTrigger asChild>
<span className="truncate">
<Button variant="ghost" size="sm" onClick={handleSortChange} disabled={isDirectMode}>
{sortType === 'default' && <ChevronsUpDown className="h-4 w-4" />}
{sortType === 'delay' && <Timer className="h-4 w-4" />}
{sortType === 'name' && <WholeWord className="h-4 w-4" />}
</Button>
</span>
</TooltipTrigger>
<TooltipContent>
{sortType === 'default' && <p>{t("Sort by default")}</p>}
{sortType === 'delay' && <p>{t("Sort by delay")}</p>}
{sortType === 'name' && <p>{t("Sort by name")}</p>}
</TooltipContent>
</Tooltip>
<TooltipTrigger asChild>
<span className="truncate">
<Button
variant="ghost"
size="sm"
onClick={handleSortChange}
disabled={isDirectMode}
>
{sortType === "default" && (
<ChevronsUpDown className="h-4 w-4" />
)}
{sortType === "delay" && <Timer className="h-4 w-4" />}
{sortType === "name" && <WholeWord className="h-4 w-4" />}
</Button>
</span>
</TooltipTrigger>
<TooltipContent>
{sortType === "default" && <p>{t("Sort by default")}</p>}
{sortType === "delay" && <p>{t("Sort by delay")}</p>}
{sortType === "name" && <p>{t("Sort by name")}</p>}
</TooltipContent>
</Tooltip>
</div>
<Select value={selectedProxy} onValueChange={handleProxyChange} disabled={isDirectMode} onOpenChange={handleProxyListOpen}>
<Select
value={selectedProxy}
onValueChange={handleProxyChange}
disabled={isDirectMode}
onOpenChange={handleProxyListOpen}
>
<SelectTrigger className="w-100">
<Tooltip>
<TooltipTrigger asChild>
@@ -280,11 +360,11 @@ export const ProxySelectors: React.FC = () => {
</Tooltip>
</SelectTrigger>
<SelectContent>
{proxyOptions.map(proxy => (
{proxyOptions.map((proxy) => (
<ProxySelectItem
key={proxy.name}
proxyName={proxy.name}
groupName={selectedGroup}
key={proxy.name}
proxyName={proxy.name}
groupName={selectedGroup}
/>
))}
</SelectContent>