Files
clash-verge-rev/src/components/proxy/proxy-head.tsx
T
wonfen 5e6a696087 revert(tray): revert per-group latency testing (#8173)
This reverts commit cb751052a4.

Remove the tray latency test and its dashboard URL persistence and cache
changes because the two entry points have diverging behavior.

Preserve the subsequent IPC binding migration and remove generated fields,
input projections, and locale entries that depended on the reverted feature.
2026-10-21 05:37:18 +08:00

207 lines
5.7 KiB
TypeScript

import {
AccessTimeRounded,
MyLocationRounded,
NetworkCheckRounded,
SearchOffRounded,
SearchRounded,
VisibilityRounded,
VisibilityOffRounded,
WifiTetheringRounded,
WifiTetheringOffRounded,
SortByAlphaRounded,
SortRounded,
} from '@mui/icons-material'
import { Box, IconButton, TextField, type SxProps } from '@mui/material'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { BaseSearchBox } from '@/components/base'
import { useVerge } from '@/hooks/use-verge'
import delayManager from '@/services/delay'
import { showNotice } from '@/services/notice-service'
import { debugLog } from '@/utils/debug'
import { isValidUrl } from '@/utils/network'
import type { ProxySortType } from './use-filter-sort'
import type { HeadState } from './use-head-state'
interface Props {
sx?: SxProps
url?: string
groupName: string
headState: HeadState
onLocation: () => void
onCheckDelay: () => void
onHeadState: (val: Partial<HeadState>) => void
}
const defaultSx: SxProps = {}
export const ProxyHead = ({
sx = defaultSx,
url,
groupName,
headState,
onHeadState,
onLocation,
onCheckDelay,
}: Props) => {
const {
showType,
sortType,
filterText,
textState,
testUrl,
filterMatchCase,
filterMatchWholeWord,
filterUseRegularExpression,
} = headState
const { t } = useTranslation()
const [autoFocus, setAutoFocus] = useState(false)
useEffect(() => {
// fix the focus conflict
const timer = setTimeout(() => setAutoFocus(true), 100)
return () => clearTimeout(timer)
}, [])
const { verge } = useVerge()
const defaultLatencyUrl =
verge?.default_latency_test?.trim() ||
'http://cp.cloudflare.com/generate_204'
useEffect(() => {
delayManager.setUrl(groupName, testUrl?.trim() || url || defaultLatencyUrl)
}, [groupName, testUrl, defaultLatencyUrl, url])
return (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, ...sx }}>
<IconButton
size="small"
color="inherit"
title={t('proxies.page.tooltips.locate')}
onClick={onLocation}
>
<MyLocationRounded />
</IconButton>
<IconButton
size="small"
color="inherit"
title={t('proxies.page.tooltips.delayCheck')}
onClick={() => {
debugLog(`[ProxyHead] 点击延迟测试按钮,组: ${groupName}`)
// Remind the user that it is custom test url
if (testUrl?.trim() && textState !== 'filter') {
debugLog(`[ProxyHead] 使用自定义测试URL: ${testUrl}`)
onHeadState({ textState: 'url' })
}
if (testUrl?.trim() && !isValidUrl(testUrl)) {
showNotice.warning('proxies.feedback.warnings.invalidTestUrl')
return
}
onCheckDelay()
}}
>
<NetworkCheckRounded />
</IconButton>
<IconButton
size="small"
color="inherit"
title={
[
t('proxies.page.tooltips.sortDefault'),
t('proxies.page.tooltips.sortDelay'),
t('proxies.page.tooltips.sortName'),
][sortType]
}
onClick={() =>
onHeadState({ sortType: ((sortType + 1) % 3) as ProxySortType })
}
>
{sortType !== 1 && sortType !== 2 && <SortRounded />}
{sortType === 1 && <AccessTimeRounded />}
{sortType === 2 && <SortByAlphaRounded />}
</IconButton>
<IconButton
size="small"
color="inherit"
title={t('proxies.page.tooltips.delayCheckUrl')}
onClick={() =>
onHeadState({ textState: textState === 'url' ? null : 'url' })
}
>
{textState === 'url' ? (
<WifiTetheringRounded />
) : (
<WifiTetheringOffRounded />
)}
</IconButton>
<IconButton
size="small"
color="inherit"
title={
showType
? t('proxies.page.tooltips.showBasic')
: t('proxies.page.tooltips.showDetail')
}
onClick={() => onHeadState({ showType: !showType })}
>
{showType ? <VisibilityRounded /> : <VisibilityOffRounded />}
</IconButton>
<IconButton
size="small"
color="inherit"
title={t('proxies.page.tooltips.filter')}
onClick={() =>
onHeadState({ textState: textState === 'filter' ? null : 'filter' })
}
>
{textState === 'filter' ? <SearchOffRounded /> : <SearchRounded />}
</IconButton>
{textState === 'filter' && (
<Box sx={{ ml: 0.5, flex: '1 1 auto' }}>
<BaseSearchBox
autoFocus={autoFocus}
value={filterText}
searchState={{
matchCase: filterMatchCase,
matchWholeWord: filterMatchWholeWord,
useRegularExpression: filterUseRegularExpression,
}}
onSearch={(_, state) =>
onHeadState({
filterText: state.text,
filterMatchCase: state.matchCase,
filterMatchWholeWord: state.matchWholeWord,
filterUseRegularExpression: state.useRegularExpression,
})
}
/>
</Box>
)}
{textState === 'url' && (
<TextField
autoComplete="new-password"
autoFocus={autoFocus}
hiddenLabel
autoSave="off"
value={testUrl}
size="small"
variant="outlined"
placeholder={t('proxies.page.placeholders.delayCheckUrl')}
onChange={(e) => onHeadState({ testUrl: e.target.value })}
sx={{ ml: 0.5, flex: '1 1 auto', input: { py: 0.65, px: 1 } }}
/>
)}
</Box>
)
}