summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLarvan2 <[email protected]>2026-07-03 14:42:55 +0800
committerLarvan2 <[email protected]>2026-07-03 14:42:55 +0800
commit37367efe2a385114adac21f9bb044bc82c8789d5 (patch)
tree9c12c4b270cd5bd171e6dcf4d7120534a9197a7c
parent5f9935312b1703561caf1187fa94abed1d046894 (diff)
feat(proxies): add fixed selection feature and related translations
-rw-r--r--src/components/CollapsibleSectionHeader.module.scss8
-rw-r--r--src/components/CollapsibleSectionHeader.tsx13
-rw-r--r--src/components/proxies/ProxyGroup.tsx12
-rw-r--r--src/components/shared/FeatherIcons.ts1
-rw-r--r--src/i18n/en.ts1
-rw-r--r--src/i18n/zh-cn.ts1
-rw-r--r--src/i18n/zh-tw.ts1
-rw-r--r--src/store/types.ts3
8 files changed, 36 insertions, 4 deletions
diff --git a/src/components/CollapsibleSectionHeader.module.scss b/src/components/CollapsibleSectionHeader.module.scss
index f0797dd..3fd8bd3 100644
--- a/src/components/CollapsibleSectionHeader.module.scss
+++ b/src/components/CollapsibleSectionHeader.module.scss
@@ -27,6 +27,14 @@
margin-left: 5px;
}
+.lock {
+ display: inline-flex;
+ align-items: center;
+ margin-left: 6px;
+ color: var(--color-latency-medium, #d4b75c);
+ cursor: help;
+}
+
/* TODO duplicate with connQty in Connections.module.css */
.qty {
font-family: var(--font-normal);
diff --git a/src/components/CollapsibleSectionHeader.tsx b/src/components/CollapsibleSectionHeader.tsx
index e243950..729f072 100644
--- a/src/components/CollapsibleSectionHeader.tsx
+++ b/src/components/CollapsibleSectionHeader.tsx
@@ -1,7 +1,9 @@
import * as React from 'react';
+import { useTranslation } from 'react-i18next';
import s from './CollapsibleSectionHeader.module.scss';
import { SectionNameType } from './shared/Basic';
+import { Lock } from './shared/FeatherIcons';
type Props = {
name: string;
@@ -9,9 +11,12 @@ type Props = {
qty?: number | string;
toggle?: () => void;
isOpen?: boolean;
+ // URLTest/Fallback group has a manually-fixed selection
+ fixed?: boolean;
};
-export default function Header({ name, type, toggle, qty }: Props) {
+export default function Header({ name, type, toggle, qty, fixed }: Props) {
+ const { t } = useTranslation();
const handleKeyDown = React.useCallback(
(e: React.KeyboardEvent) => {
e.preventDefault();
@@ -34,6 +39,12 @@ export default function Header({ name, type, toggle, qty }: Props) {
<SectionNameType name={name} type={type} />
</div>
+ {fixed ? (
+ <span className={s.lock} title={t('group_fixed_tip')}>
+ <Lock size={13} />
+ </span>
+ ) : null}
+
{typeof qty === 'number' ? <span className={s.qty}>{qty}</span> : null}
</div>
);
diff --git a/src/components/proxies/ProxyGroup.tsx b/src/components/proxies/ProxyGroup.tsx
index f436944..d838685 100644
--- a/src/components/proxies/ProxyGroup.tsx
+++ b/src/components/proxies/ProxyGroup.tsx
@@ -109,8 +109,8 @@ export const ProxyGroup = memo(function ProxyGroup({
dispatch,
proxyGroupByProvider = false,
}: Props) {
- const group = proxies[name] as ProxyItem & { all?: string[]; now?: string };
- const { all: allItems = [], type, now } = group || {};
+ const group = proxies[name] as ProxyItem & { all?: string[]; now?: string; fixed?: string };
+ const { all: allItems = [], type, now, fixed } = group || {};
const all = useFilteredAndSorted(allItems, delay, hideUnavailableProxies, proxySortBy, proxies);
const nowChain = useMemo(() => buildNowChain(proxies, name), [proxies, name]);
@@ -179,7 +179,13 @@ export const ProxyGroup = memo(function ProxyGroup({
return (
<div className={s0.group}>
<div className={s0.groupHeader}>
- <CollapsibleSectionHeader name={name} type={type} toggle={toggle} qty={qtyLabel} />
+ <CollapsibleSectionHeader
+ name={name}
+ type={type}
+ toggle={toggle}
+ qty={qtyLabel}
+ fixed={!!fixed}
+ />
<div className={s0.btnGroup}>
<Button
kind="minimal"
diff --git a/src/components/shared/FeatherIcons.ts b/src/components/shared/FeatherIcons.ts
index ee1f410..ca9c359 100644
--- a/src/components/shared/FeatherIcons.ts
+++ b/src/components/shared/FeatherIcons.ts
@@ -17,6 +17,7 @@ export {
Hash,
Info,
Link,
+ Lock,
LogOut,
Menu,
Monitor,
diff --git a/src/i18n/en.ts b/src/i18n/en.ts
index 1f42f3c..39c1d04 100644
--- a/src/i18n/en.ts
+++ b/src/i18n/en.ts
@@ -91,4 +91,5 @@ export const data = {
disconnect: 'Close Connection',
internel: 'Internal Connection',
Clear: 'Clear',
+ group_fixed_tip: 'This group has a manually fixed selection; run a latency test to release it',
};
diff --git a/src/i18n/zh-cn.ts b/src/i18n/zh-cn.ts
index ff6ceac..b9c8ddf 100644
--- a/src/i18n/zh-cn.ts
+++ b/src/i18n/zh-cn.ts
@@ -92,4 +92,5 @@ export const data = {
disconnect: '断开连接',
internel: '内部链接',
Clear: '清空',
+ group_fixed_tip: '该组已手动固定选择,点击测速可解除固定',
};
diff --git a/src/i18n/zh-tw.ts b/src/i18n/zh-tw.ts
index c3ea79f..ba3a9c6 100644
--- a/src/i18n/zh-tw.ts
+++ b/src/i18n/zh-tw.ts
@@ -85,4 +85,5 @@ export const data = {
disconnect: '斷開連線',
internel: '內部連線',
Clear: '清空',
+ group_fixed_tip: '該組已手動固定選擇,點擊測速可解除固定',
};
diff --git a/src/store/types.ts b/src/store/types.ts
index 2d4459b..eaefaf1 100644
--- a/src/store/types.ts
+++ b/src/store/types.ts
@@ -75,6 +75,9 @@ export type ProxyItem = {
now?: string;
hidden?: boolean;
// group-only fields (Selector/URLTest/Fallback/LoadBalance) from GET /proxies
+ // set by URLTest/Fallback groups to the manually-fixed member name; cleared by the
+ // backend when the group is latency-tested via /group/{name}/delay
+ fixed?: string;
testUrl?: string;
expectedStatus?: string;
// extra latency history keyed by test URL — its keys are the URLs the backend tests against