mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-09 03:42:06 +08:00
chore: trim excessive comments
This commit is contained in:
@@ -26,7 +26,6 @@ import { useAppRefreshers, useProxiesData } from '@/providers/app-data-context'
|
||||
import { showNotice } from '@/services/notice-service'
|
||||
import parseTraffic from '@/utils/parse-traffic'
|
||||
|
||||
// 样式化组件 - 类型框
|
||||
const TypeBox = styled(Box)<{ component?: React.ElementType }>(({ theme }) => ({
|
||||
display: 'inline-block',
|
||||
border: '1px solid #ccc',
|
||||
@@ -39,7 +38,6 @@ const TypeBox = styled(Box)<{ component?: React.ElementType }>(({ theme }) => ({
|
||||
lineHeight: 1.25,
|
||||
}))
|
||||
|
||||
// 解析过期时间
|
||||
const parseExpire = (expire?: number) => {
|
||||
if (!expire) return '-'
|
||||
return dayjs(expire * 1000).format('YYYY-MM-DD')
|
||||
@@ -54,15 +52,12 @@ export const ProviderButton = () => {
|
||||
const providers = proxyView?.providers ?? []
|
||||
const providerUnavailable = proxyView?.providerState === 'unavailable'
|
||||
|
||||
// 更新单个代理提供者
|
||||
const updateProvider = useLockFn(async (name: string) => {
|
||||
try {
|
||||
// 设置更新状态
|
||||
setUpdating((prev) => ({ ...prev, [name]: true }))
|
||||
|
||||
await updateProxyProvider(name)
|
||||
|
||||
// 刷新数据
|
||||
await refreshProxy()
|
||||
|
||||
showNotice.success(
|
||||
@@ -77,22 +72,18 @@ export const ProviderButton = () => {
|
||||
message: String(err),
|
||||
})
|
||||
} finally {
|
||||
// 清除更新状态
|
||||
setUpdating((prev) => ({ ...prev, [name]: false }))
|
||||
}
|
||||
})
|
||||
|
||||
// 更新所有代理提供者
|
||||
const updateAllProviders = useLockFn(async () => {
|
||||
try {
|
||||
// 获取所有provider的名称
|
||||
const allProviders = providers.map(({ name }) => name)
|
||||
if (allProviders.length === 0) {
|
||||
showNotice.info('proxies.feedback.notifications.provider.none')
|
||||
return
|
||||
}
|
||||
|
||||
// 设置所有provider为更新中状态
|
||||
const newUpdating = allProviders.reduce(
|
||||
(acc, key) => {
|
||||
acc[key] = true
|
||||
@@ -102,19 +93,15 @@ export const ProviderButton = () => {
|
||||
)
|
||||
setUpdating(newUpdating)
|
||||
|
||||
// 改为串行逐个更新所有provider
|
||||
for (const name of allProviders) {
|
||||
try {
|
||||
await updateProxyProvider(name)
|
||||
// 每个更新完成后更新状态
|
||||
setUpdating((prev) => ({ ...prev, [name]: false }))
|
||||
} catch (err) {
|
||||
console.error(`更新 ${name} 失败`, err)
|
||||
// 继续执行下一个,不中断整体流程
|
||||
}
|
||||
}
|
||||
|
||||
// 刷新数据
|
||||
await refreshProxy()
|
||||
|
||||
showNotice.success('proxies.feedback.notifications.provider.allUpdated')
|
||||
@@ -123,7 +110,6 @@ export const ProviderButton = () => {
|
||||
message: String(err),
|
||||
})
|
||||
} finally {
|
||||
// 清除所有更新状态
|
||||
setUpdating({})
|
||||
}
|
||||
})
|
||||
@@ -190,7 +176,6 @@ export const ProviderButton = () => {
|
||||
: null
|
||||
const isUpdating = updating[key]
|
||||
|
||||
// 订阅信息
|
||||
const sub = provider.subscriptionInfo
|
||||
const hasSubInfo = !!sub
|
||||
const upload = sub?.upload || 0
|
||||
@@ -198,7 +183,6 @@ export const ProviderButton = () => {
|
||||
const total = sub?.total || 0
|
||||
const expire = sub?.expire || 0
|
||||
|
||||
// 流量使用进度
|
||||
const progress =
|
||||
total > 0
|
||||
? Math.min(
|
||||
@@ -272,7 +256,6 @@ export const ProviderButton = () => {
|
||||
}
|
||||
secondary={
|
||||
<>
|
||||
{/* 订阅信息 */}
|
||||
{hasSubInfo && (
|
||||
<>
|
||||
<Box
|
||||
@@ -296,7 +279,6 @@ export const ProviderButton = () => {
|
||||
</span>
|
||||
</Box>
|
||||
|
||||
{/* 进度条 */}
|
||||
<LinearProgress
|
||||
variant="determinate"
|
||||
value={progress}
|
||||
|
||||
@@ -23,14 +23,7 @@ interface ProxyListStateInput {
|
||||
const isSubscriptionProfile = (item: { type?: string }): boolean =>
|
||||
item.type === 'remote' || item.type === 'local'
|
||||
|
||||
/**
|
||||
* What the proxy page should show.
|
||||
*
|
||||
* Deliberately does *not* answer "is there anything to render". That question is answered by
|
||||
* the list itself, in `hasRenderableItems`, because predicting it from beside the renderer is
|
||||
* how the two came to disagree. This decides only the things knowable without building a list:
|
||||
* direct mode, still-loading, and having no usable subscription to render from.
|
||||
*/
|
||||
/** Decides only pre-render states; the built list owns whether it has renderable content. */
|
||||
export type ProxyListState =
|
||||
| { kind: 'direct' }
|
||||
| { kind: 'loading' }
|
||||
@@ -67,12 +60,7 @@ export const resolveProxyListState = ({
|
||||
return { kind: 'render' }
|
||||
}
|
||||
|
||||
/**
|
||||
* Why a list that turned out to be empty is empty.
|
||||
*
|
||||
* Only reachable once the renderer has built its list and found nothing, so it explains an
|
||||
* observed emptiness rather than predicting one.
|
||||
*/
|
||||
/** Explains an empty list only after the renderer observes it. */
|
||||
export const resolveEmptyListReason = ({
|
||||
runningMode,
|
||||
isProxyViewError,
|
||||
|
||||
@@ -65,11 +65,6 @@ interface Props {
|
||||
chainConfigData?: string | null
|
||||
}
|
||||
|
||||
/**
|
||||
* The empty state to draw when the render list turns out to contain nothing.
|
||||
*
|
||||
* Shared by both list components so the observation and its explanation stay together.
|
||||
*/
|
||||
function useEmptyRenderList() {
|
||||
const { isProxyViewError } = useProxiesData()
|
||||
const { runningMode } = useSystemData()
|
||||
@@ -103,7 +98,6 @@ function useProxyRenderState(
|
||||
|
||||
const timeout = verge?.default_latency_timeout || 10000
|
||||
|
||||
// 测全部延迟
|
||||
const handleCheckAll = useStableCallback(
|
||||
useLockFn(async (groupName: string) => {
|
||||
debugLog(`[ProxyGroups] 开始测试所有延迟,组: ${groupName}`)
|
||||
@@ -133,8 +127,6 @@ function useProxyRenderState(
|
||||
} catch (error) {
|
||||
console.error(`[ProxyGroups] 延迟测试出错,组: ${groupName}`, error)
|
||||
} finally {
|
||||
// Re-sorting is no longer poked from here: the delay store announces that the test
|
||||
// settled and the render list recomputes from that.
|
||||
onProxies()
|
||||
}
|
||||
}),
|
||||
@@ -254,7 +246,6 @@ function ChainProxyGroups(props: {
|
||||
const virtualItems = virtualizer.getVirtualItems()
|
||||
const activeStickyIndex = activeStickyIndexRef.current
|
||||
|
||||
// 从 localStorage 恢复滚动位置
|
||||
useLayoutEffect(() => {
|
||||
if (renderList.length === 0) return
|
||||
const node = parentRef.current
|
||||
@@ -335,7 +326,6 @@ function ChainProxyGroups(props: {
|
||||
}
|
||||
})
|
||||
|
||||
// The list is built; whether it holds anything is now an observation, not a guess.
|
||||
if (!hasRenderableItems(renderList)) return emptyList
|
||||
|
||||
return (
|
||||
@@ -376,11 +366,9 @@ function NormalProxyGroups(props: { mode: string }) {
|
||||
} = useProxyRenderState(mode, false, null)
|
||||
const emptyList = useEmptyRenderList()
|
||||
const renderFirstRef = useRef(true)
|
||||
// 恢复滚动位置期间设为 true,避免程序化滚动触发的 scroll 事件把中间值写回存储
|
||||
// Do not persist intermediate positions produced while restoring virtual scroll.
|
||||
const isRestoringRef = useRef(false)
|
||||
|
||||
// 目前无法使用 StickyVirtualList 的 initialOffset 值设置初始化,具体原因需排查
|
||||
// 从 localStorage 恢复滚动位置
|
||||
useLayoutEffect(() => {
|
||||
if (renderList.length === 0) return
|
||||
if (!renderFirstRef.current) return
|
||||
@@ -388,15 +376,12 @@ function NormalProxyGroups(props: { mode: string }) {
|
||||
if (!node) return
|
||||
|
||||
const savedPosition = getScrollPosition()
|
||||
// 未保存过位置或位置为 0(顶部)时无需恢复
|
||||
if (!savedPosition) {
|
||||
renderFirstRef.current = false
|
||||
return
|
||||
}
|
||||
|
||||
// 虚拟列表初始使用预估高度,真实高度测量完成后总高度才会稳定。
|
||||
// 尤其是过滤后节点数变少时,预估总高度常常不足以一次性滚动到目标位置,
|
||||
// 因此跨帧重试,直到到达目标位置(或内容确实不够高)为止。
|
||||
// Retry across frames until virtual-list measurements can reach the saved offset.
|
||||
isRestoringRef.current = true
|
||||
let rafId = 0
|
||||
let attempts = 0
|
||||
@@ -436,7 +421,6 @@ function NormalProxyGroups(props: { mode: string }) {
|
||||
|
||||
const handleScroll = useCallback(
|
||||
(event: Event) => {
|
||||
// 恢复位置过程中产生的滚动不写回存储,避免中间的钳制值覆盖真实位置
|
||||
if (isRestoringRef.current) return
|
||||
const target = event.target as HTMLElement | null
|
||||
const nextScrollTop = target?.scrollTop ?? 0
|
||||
@@ -480,7 +464,6 @@ function NormalProxyGroups(props: { mode: string }) {
|
||||
[handleProxyGroupChange],
|
||||
)
|
||||
|
||||
// 滚到对应的节点
|
||||
const handleLocation = useStableCallback((group: ProxyGroupView) => {
|
||||
if (!group) return
|
||||
const { name, now } = group
|
||||
@@ -501,7 +484,6 @@ function NormalProxyGroups(props: { mode: string }) {
|
||||
}
|
||||
})
|
||||
|
||||
// 定位到指定的代理组
|
||||
const handleGroupLocationByName = useCallback(
|
||||
(groupName: string) => {
|
||||
const index = renderList.findIndex(
|
||||
@@ -525,7 +507,6 @@ function NormalProxyGroups(props: { mode: string }) {
|
||||
return Array.from(new Set(names))
|
||||
}, [renderList])
|
||||
|
||||
// 点击代理组改变展开状态,先滚动到sticky的代理组位置,再收起展开状态
|
||||
const handleGroupToggle = useCallback(
|
||||
async (group: ProxyGroupView) => {
|
||||
const index = renderList.findIndex(
|
||||
@@ -589,7 +570,6 @@ function NormalProxyGroups(props: { mode: string }) {
|
||||
[handleChangeProxy, handleCheckAll, onHeadState, handleLocation],
|
||||
)
|
||||
|
||||
// The list is built; whether it holds anything is now an observation, not a guess.
|
||||
if (!hasRenderableItems(renderList)) return emptyList
|
||||
|
||||
return (
|
||||
@@ -605,7 +585,6 @@ function NormalProxyGroups(props: { mode: string }) {
|
||||
renderItem={renderProxyItem}
|
||||
/>
|
||||
|
||||
{/* 代理组导航栏 */}
|
||||
{mode === 'rule' && (
|
||||
<ProxyGroupNavigator
|
||||
proxyGroupNames={proxyGroupNames}
|
||||
@@ -640,7 +619,6 @@ export const ProxyGroups = (props: Props) => {
|
||||
case 'empty':
|
||||
return <ProxyEmptyState reason={listState.reason} />
|
||||
case 'render':
|
||||
// Whether there is anything to draw is the list's own answer, given below.
|
||||
return isChainMode ? (
|
||||
<ChainProxyGroups mode={mode} chainConfigData={chainConfigData} />
|
||||
) : (
|
||||
|
||||
@@ -5,7 +5,6 @@ import { compileStringMatcher } from '@/utils/search-matcher'
|
||||
|
||||
import type { ResolvedMemberOccurrence } from './use-render-list'
|
||||
|
||||
// default | delay | alphabet
|
||||
export type ProxySortType = 0 | 1 | 2
|
||||
|
||||
export type ProxySearchState = {
|
||||
@@ -27,16 +26,9 @@ export function filterSort(
|
||||
return sp
|
||||
}
|
||||
|
||||
/**
|
||||
* 可以通过延迟数/节点类型 过滤
|
||||
*/
|
||||
const regex1 = /delay([=<>])(\d+|timeout|error)/i
|
||||
const regex2 = /type=(.*)/i
|
||||
|
||||
/**
|
||||
* filter the proxy
|
||||
* according to the regular conditions
|
||||
*/
|
||||
function filterProxies(
|
||||
proxies: ResolvedMemberOccurrence[],
|
||||
groupName: string,
|
||||
@@ -90,9 +82,6 @@ function filterProxies(
|
||||
return proxies.filter(({ member }) => compiled.matcher(member.ref.name))
|
||||
}
|
||||
|
||||
/**
|
||||
* sort the proxy
|
||||
*/
|
||||
function sortProxies(
|
||||
proxies: ResolvedMemberOccurrence[],
|
||||
groupName: string,
|
||||
|
||||
@@ -5,21 +5,18 @@ import { hasRenderableItems, type IRenderItem } from './use-render-list'
|
||||
const group = (hidden = false) =>
|
||||
({ name: 'Proxies', hidden }) as unknown as IRenderItem['group']
|
||||
|
||||
/** A group header row. On its own it is only content when the group is visible. */
|
||||
const header = (hidden = false): IRenderItem => ({
|
||||
type: 0,
|
||||
key: 'header',
|
||||
group: group(hidden),
|
||||
})
|
||||
|
||||
/** A member row — in chain mode the list is made entirely of these. */
|
||||
const member = (key: string): IRenderItem => ({
|
||||
type: 2,
|
||||
key,
|
||||
group: group(),
|
||||
})
|
||||
|
||||
/** A column of members, which is how the chain list renders at column counts above one. */
|
||||
const memberColumn = (key: string): IRenderItem => ({
|
||||
type: 4,
|
||||
key,
|
||||
@@ -32,9 +29,7 @@ describe('hasRenderableItems', () => {
|
||||
})
|
||||
|
||||
test('chain mode is answered by the chain list itself', () => {
|
||||
// This is the case the old prediction got wrong in both directions: it asked whether any
|
||||
// Selector or URLTest group existed, which is unrelated to what the chain list is built
|
||||
// from. Here there are rows, so there is content, whatever groups happen to exist.
|
||||
// Rendered rows, not group types, determine whether chain mode is empty.
|
||||
expect(hasRenderableItems([member('chain:a')])).toBe(true)
|
||||
expect(hasRenderableItems([memberColumn('chain-col:a')])).toBe(true)
|
||||
})
|
||||
@@ -54,8 +49,7 @@ describe('hasRenderableItems', () => {
|
||||
})
|
||||
|
||||
test('rows inside a hidden but expanded group are still rows on screen', () => {
|
||||
// Truthfulness over tidiness: the user can see these, so calling the page empty would be
|
||||
// a lie. The old predicate said "empty" here.
|
||||
// Visible member rows mean the page is not empty even without a visible group header.
|
||||
expect(hasRenderableItems([header(true), member('member:a')])).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user