mirror of
https://github.com/pompurin404/mihomo-party.git
synced 2024-11-16 11:42:19 +08:00
auto pin global group
Some checks are pending
Build / windows (arm64) (push) Waiting to run
Build / windows (ia32) (push) Waiting to run
Build / windows (x64) (push) Waiting to run
Build / windows7 (ia32) (push) Waiting to run
Build / windows7 (x64) (push) Waiting to run
Build / linux (arm64) (push) Waiting to run
Build / linux (x64) (push) Waiting to run
Build / macos (arm64) (push) Waiting to run
Build / macos (x64) (push) Waiting to run
Build / artifact (push) Blocked by required conditions
Build / updater (push) Blocked by required conditions
Build / aur-release-updater (mihomo-party) (push) Blocked by required conditions
Build / aur-release-updater (mihomo-party-bin) (push) Blocked by required conditions
Build / aur-release-updater (mihomo-party-electron) (push) Blocked by required conditions
Build / aur-release-updater (mihomo-party-electron-bin) (push) Blocked by required conditions
Build / aur-git-updater (push) Waiting to run
Build / Update WinGet Package (push) Blocked by required conditions
Build / Update Homebrew cask (push) Blocked by required conditions
Some checks are pending
Build / windows (arm64) (push) Waiting to run
Build / windows (ia32) (push) Waiting to run
Build / windows (x64) (push) Waiting to run
Build / windows7 (ia32) (push) Waiting to run
Build / windows7 (x64) (push) Waiting to run
Build / linux (arm64) (push) Waiting to run
Build / linux (x64) (push) Waiting to run
Build / macos (arm64) (push) Waiting to run
Build / macos (x64) (push) Waiting to run
Build / artifact (push) Blocked by required conditions
Build / updater (push) Blocked by required conditions
Build / aur-release-updater (mihomo-party) (push) Blocked by required conditions
Build / aur-release-updater (mihomo-party-bin) (push) Blocked by required conditions
Build / aur-release-updater (mihomo-party-electron) (push) Blocked by required conditions
Build / aur-release-updater (mihomo-party-electron-bin) (push) Blocked by required conditions
Build / aur-git-updater (push) Waiting to run
Build / Update WinGet Package (push) Blocked by required conditions
Build / Update Homebrew cask (push) Blocked by required conditions
This commit is contained in:
parent
b4a9da92f6
commit
dcb59e767c
|
@ -147,7 +147,8 @@ export async function startCore(detached = false): Promise<Promise<void>[]> {
|
||||||
child.stdout?.on('data', async (data) => {
|
child.stdout?.on('data', async (data) => {
|
||||||
if (data.toString().includes('Start initial Compatible provider default')) {
|
if (data.toString().includes('Start initial Compatible provider default')) {
|
||||||
try {
|
try {
|
||||||
mainWindow?.webContents.send('coreRestart')
|
mainWindow?.webContents.send('groupsUpdated')
|
||||||
|
mainWindow?.webContents.send('rulesUpdated')
|
||||||
await uploadRuntimeConfig()
|
await uploadRuntimeConfig()
|
||||||
} catch {
|
} catch {
|
||||||
// ignore
|
// ignore
|
||||||
|
|
|
@ -76,6 +76,8 @@ export const mihomoProxies = async (): Promise<IMihomoProxies> => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const mihomoGroups = async (): Promise<IMihomoMixedGroup[]> => {
|
export const mihomoGroups = async (): Promise<IMihomoMixedGroup[]> => {
|
||||||
|
const { mode = 'rule' } = await getControledMihomoConfig()
|
||||||
|
if (mode === 'direct') return []
|
||||||
const proxies = await mihomoProxies()
|
const proxies = await mihomoProxies()
|
||||||
const runtime = await getRuntimeConfig()
|
const runtime = await getRuntimeConfig()
|
||||||
const groups: IMihomoMixedGroup[] = []
|
const groups: IMihomoMixedGroup[] = []
|
||||||
|
@ -95,6 +97,10 @@ export const mihomoGroups = async (): Promise<IMihomoMixedGroup[]> => {
|
||||||
groups.push({ ...newGlobal, all: newAll })
|
groups.push({ ...newGlobal, all: newAll })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (mode === 'global') {
|
||||||
|
const global = groups.findIndex((group) => group.name === 'GLOBAL')
|
||||||
|
groups.unshift(groups.splice(global, 1)[0])
|
||||||
|
}
|
||||||
return groups
|
return groups
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -111,6 +111,7 @@ export const buildContextMenu = async (): Promise<Menu> => {
|
||||||
await patchControledMihomoConfig({ mode: 'rule' })
|
await patchControledMihomoConfig({ mode: 'rule' })
|
||||||
await patchMihomoConfig({ mode: 'rule' })
|
await patchMihomoConfig({ mode: 'rule' })
|
||||||
mainWindow?.webContents.send('controledMihomoConfigUpdated')
|
mainWindow?.webContents.send('controledMihomoConfigUpdated')
|
||||||
|
mainWindow?.webContents.send('groupsUpdated')
|
||||||
ipcMain.emit('updateTrayMenu')
|
ipcMain.emit('updateTrayMenu')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -124,6 +125,7 @@ export const buildContextMenu = async (): Promise<Menu> => {
|
||||||
await patchControledMihomoConfig({ mode: 'global' })
|
await patchControledMihomoConfig({ mode: 'global' })
|
||||||
await patchMihomoConfig({ mode: 'global' })
|
await patchMihomoConfig({ mode: 'global' })
|
||||||
mainWindow?.webContents.send('controledMihomoConfigUpdated')
|
mainWindow?.webContents.send('controledMihomoConfigUpdated')
|
||||||
|
mainWindow?.webContents.send('groupsUpdated')
|
||||||
ipcMain.emit('updateTrayMenu')
|
ipcMain.emit('updateTrayMenu')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -137,6 +139,7 @@ export const buildContextMenu = async (): Promise<Menu> => {
|
||||||
await patchControledMihomoConfig({ mode: 'direct' })
|
await patchControledMihomoConfig({ mode: 'direct' })
|
||||||
await patchMihomoConfig({ mode: 'direct' })
|
await patchMihomoConfig({ mode: 'direct' })
|
||||||
mainWindow?.webContents.send('controledMihomoConfigUpdated')
|
mainWindow?.webContents.send('controledMihomoConfigUpdated')
|
||||||
|
mainWindow?.webContents.send('groupsUpdated')
|
||||||
ipcMain.emit('updateTrayMenu')
|
ipcMain.emit('updateTrayMenu')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -22,7 +22,7 @@ const CollapseInput: React.FC<CollapseInputProps> = (props) => {
|
||||||
}}
|
}}
|
||||||
endContent={
|
endContent={
|
||||||
<div
|
<div
|
||||||
className="cursor-pointer p-2 text-lg text-default-500"
|
className="cursor-pointer p-2 text-lg text-foreground-500"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
inputRef.current?.focus()
|
inputRef.current?.focus()
|
||||||
|
|
|
@ -51,7 +51,7 @@ const ConnectionItem: React.FC<Props> = (props) => {
|
||||||
info.metadata.destinationIP ||
|
info.metadata.destinationIP ||
|
||||||
info.metadata.remoteDestination}
|
info.metadata.remoteDestination}
|
||||||
</div>
|
</div>
|
||||||
<small className="whitespace-nowrap text-default-500">
|
<small className="whitespace-nowrap text-foreground-500">
|
||||||
{dayjs(info.start).fromNow()}
|
{dayjs(info.start).fromNow()}
|
||||||
</small>
|
</small>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
|
|
@ -16,7 +16,7 @@ const LogItem: React.FC<IMihomoLogInfo & { index: number }> = (props) => {
|
||||||
<div className={`mr-2 text-lg font-bold text-${colorMap[type]}`}>
|
<div className={`mr-2 text-lg font-bold text-${colorMap[type]}`}>
|
||||||
{props.type.toUpperCase()}
|
{props.type.toUpperCase()}
|
||||||
</div>
|
</div>
|
||||||
<small className="text-default-500">{time}</small>
|
<small className="text-foreground-500">{time}</small>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardBody className="pt-0 text-sm">{payload}</CardBody>
|
<CardBody className="pt-0 text-sm">{payload}</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
|
@ -34,7 +34,7 @@ const EditFileModal: React.FC<Props> = (props) => {
|
||||||
<ModalHeader className="flex pb-0 app-drag">
|
<ModalHeader className="flex pb-0 app-drag">
|
||||||
<div className="flex justify-start">
|
<div className="flex justify-start">
|
||||||
<div className="flex items-center">编辑订阅</div>
|
<div className="flex items-center">编辑订阅</div>
|
||||||
<small className="ml-2 text-default-500">
|
<small className="ml-2 text-foreground-500">
|
||||||
注意:此处编辑配置更新订阅后会还原,如需要自定义配置请使用
|
注意:此处编辑配置更新订阅后会还原,如需要自定义配置请使用
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|
|
@ -63,7 +63,7 @@ const ProxyItem: React.FC<Props> = (props) => {
|
||||||
{proxy.name}
|
{proxy.name}
|
||||||
</div>
|
</div>
|
||||||
{proxyDisplayMode === 'full' && (
|
{proxyDisplayMode === 'full' && (
|
||||||
<div className="inline ml-2 text-default-500" title={proxy.type}>
|
<div className="inline ml-2 text-foreground-500" title={proxy.type}>
|
||||||
{proxy.type}
|
{proxy.type}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -71,7 +71,7 @@ const ProxyProvider: React.FC = () => {
|
||||||
divider={!provider.subscriptionInfo && index !== providers.length - 1}
|
divider={!provider.subscriptionInfo && index !== providers.length - 1}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
<div className="flex h-[32px] leading-[32px] text-default-500">
|
<div className="flex h-[32px] leading-[32px] text-foreground-500">
|
||||||
<div>{dayjs(provider.updatedAt).fromNow()}</div>
|
<div>{dayjs(provider.updatedAt).fromNow()}</div>
|
||||||
<Button
|
<Button
|
||||||
isIconOnly
|
isIconOnly
|
||||||
|
@ -90,14 +90,14 @@ const ProxyProvider: React.FC = () => {
|
||||||
<SettingItem
|
<SettingItem
|
||||||
divider={index !== providers.length - 1}
|
divider={index !== providers.length - 1}
|
||||||
title={
|
title={
|
||||||
<div className="text-default-500">{`${calcTraffic(
|
<div className="text-foreground-500">{`${calcTraffic(
|
||||||
provider.subscriptionInfo.Upload + provider.subscriptionInfo.Download
|
provider.subscriptionInfo.Upload + provider.subscriptionInfo.Download
|
||||||
)}
|
)}
|
||||||
/${calcTraffic(provider.subscriptionInfo.Total)}`}</div>
|
/${calcTraffic(provider.subscriptionInfo.Total)}`}</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{provider.subscriptionInfo && (
|
{provider.subscriptionInfo && (
|
||||||
<div className="h-[32px] leading-[32px] text-default-500">
|
<div className="h-[32px] leading-[32px] text-foreground-500">
|
||||||
{provider.subscriptionInfo.Expire
|
{provider.subscriptionInfo.Expire
|
||||||
? dayjs.unix(provider.subscriptionInfo.Expire).format('YYYY-MM-DD')
|
? dayjs.unix(provider.subscriptionInfo.Expire).format('YYYY-MM-DD')
|
||||||
: '长期有效'}
|
: '长期有效'}
|
||||||
|
|
|
@ -65,7 +65,7 @@ const RuleProvider: React.FC = () => {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
<div className="flex h-[32px] leading-[32px] text-default-500">
|
<div className="flex h-[32px] leading-[32px] text-foreground-500">
|
||||||
<div>{dayjs(provider.updatedAt).fromNow()}</div>
|
<div>{dayjs(provider.updatedAt).fromNow()}</div>
|
||||||
<Button
|
<Button
|
||||||
isIconOnly
|
isIconOnly
|
||||||
|
@ -81,10 +81,10 @@ const RuleProvider: React.FC = () => {
|
||||||
}
|
}
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
<SettingItem
|
<SettingItem
|
||||||
title={<div className="text-default-500">{provider.format}</div>}
|
title={<div className="text-foreground-500">{provider.format}</div>}
|
||||||
divider={index !== providers.length - 1}
|
divider={index !== providers.length - 1}
|
||||||
>
|
>
|
||||||
<div className="h-[32px] leading-[32px] text-default-500">
|
<div className="h-[32px] leading-[32px] text-foreground-500">
|
||||||
{provider.vehicleType}::{provider.behavior}
|
{provider.vehicleType}::{provider.behavior}
|
||||||
</div>
|
</div>
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
|
|
|
@ -10,7 +10,7 @@ const RuleItem: React.FC<IMihomoRulesDetail & { index: number }> = (props) => {
|
||||||
<div title={payload} className="text-ellipsis whitespace-nowrap overflow-hidden">
|
<div title={payload} className="text-ellipsis whitespace-nowrap overflow-hidden">
|
||||||
{payload}
|
{payload}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-start text-default-500">
|
<div className="flex justify-start text-foreground-500">
|
||||||
<div>{type}</div>
|
<div>{type}</div>
|
||||||
<div className="ml-2">{proxy}</div>
|
<div className="ml-2">{proxy}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import { Tabs, Tab } from '@nextui-org/react'
|
import { Tabs, Tab } from '@nextui-org/react'
|
||||||
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
||||||
import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config'
|
import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config'
|
||||||
|
import { useGroups } from '@renderer/hooks/use-groups'
|
||||||
import { mihomoCloseAllConnections, patchMihomoConfig } from '@renderer/utils/ipc'
|
import { mihomoCloseAllConnections, patchMihomoConfig } from '@renderer/utils/ipc'
|
||||||
import { Key } from 'react'
|
import { Key } from 'react'
|
||||||
|
|
||||||
const OutboundModeSwitcher: React.FC = () => {
|
const OutboundModeSwitcher: React.FC = () => {
|
||||||
const { controledMihomoConfig, patchControledMihomoConfig } = useControledMihomoConfig()
|
const { controledMihomoConfig, patchControledMihomoConfig } = useControledMihomoConfig()
|
||||||
|
const { mutate: mutateGroups } = useGroups()
|
||||||
const { appConfig } = useAppConfig()
|
const { appConfig } = useAppConfig()
|
||||||
const { autoCloseConnection = true } = appConfig || {}
|
const { autoCloseConnection = true } = appConfig || {}
|
||||||
const { mode } = controledMihomoConfig || {}
|
const { mode } = controledMihomoConfig || {}
|
||||||
|
@ -16,6 +18,7 @@ const OutboundModeSwitcher: React.FC = () => {
|
||||||
if (autoCloseConnection) {
|
if (autoCloseConnection) {
|
||||||
await mihomoCloseAllConnections()
|
await mihomoCloseAllConnections()
|
||||||
}
|
}
|
||||||
|
mutateGroups()
|
||||||
window.electron.ipcRenderer.send('updateTrayMenu')
|
window.electron.ipcRenderer.send('updateTrayMenu')
|
||||||
}
|
}
|
||||||
if (!mode) return null
|
if (!mode) return null
|
||||||
|
|
|
@ -16,11 +16,11 @@ export const GroupsProvider: React.FC<{ children: ReactNode }> = ({ children })
|
||||||
})
|
})
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
window.electron.ipcRenderer.on('coreRestart', () => {
|
window.electron.ipcRenderer.on('groupsUpdated', () => {
|
||||||
mutate()
|
mutate()
|
||||||
})
|
})
|
||||||
return (): void => {
|
return (): void => {
|
||||||
window.electron.ipcRenderer.removeAllListeners('coreRestart')
|
window.electron.ipcRenderer.removeAllListeners('groupsUpdated')
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
|
|
@ -16,11 +16,11 @@ export const RulesProvider: React.FC<{ children: ReactNode }> = ({ children }) =
|
||||||
})
|
})
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
window.electron.ipcRenderer.on('coreRestart', () => {
|
window.electron.ipcRenderer.on('rulesUpdated', () => {
|
||||||
mutate()
|
mutate()
|
||||||
})
|
})
|
||||||
return (): void => {
|
return (): void => {
|
||||||
window.electron.ipcRenderer.removeAllListeners('coreRestart')
|
window.electron.ipcRenderer.removeAllListeners('rulesUpdated')
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
|
|
@ -15,12 +15,15 @@ import { useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { GroupedVirtuoso, GroupedVirtuosoHandle } from 'react-virtuoso'
|
import { GroupedVirtuoso, GroupedVirtuosoHandle } from 'react-virtuoso'
|
||||||
import ProxyItem from '@renderer/components/proxies/proxy-item'
|
import ProxyItem from '@renderer/components/proxies/proxy-item'
|
||||||
import { IoIosArrowBack } from 'react-icons/io'
|
import { IoIosArrowBack } from 'react-icons/io'
|
||||||
import { MdOutlineSpeed } from 'react-icons/md'
|
import { MdDoubleArrow, MdOutlineSpeed } from 'react-icons/md'
|
||||||
import { useGroups } from '@renderer/hooks/use-groups'
|
import { useGroups } from '@renderer/hooks/use-groups'
|
||||||
import CollapseInput from '@renderer/components/base/collapse-input'
|
import CollapseInput from '@renderer/components/base/collapse-input'
|
||||||
import { includesIgnoreCase } from '@renderer/utils/includes'
|
import { includesIgnoreCase } from '@renderer/utils/includes'
|
||||||
|
import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config'
|
||||||
|
|
||||||
const Proxies: React.FC = () => {
|
const Proxies: React.FC = () => {
|
||||||
|
const { controledMihomoConfig } = useControledMihomoConfig()
|
||||||
|
const { mode = 'rule' } = controledMihomoConfig || {}
|
||||||
const { groups = [], mutate } = useGroups()
|
const { groups = [], mutate } = useGroups()
|
||||||
const { appConfig, patchAppConfig } = useAppConfig()
|
const { appConfig, patchAppConfig } = useAppConfig()
|
||||||
const {
|
const {
|
||||||
|
@ -197,184 +200,193 @@ const Proxies: React.FC = () => {
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="h-[calc(100vh-50px)]">
|
{mode === 'direct' ? (
|
||||||
<GroupedVirtuoso
|
<div className="h-full w-full flex justify-center items-center">
|
||||||
ref={virtuosoRef}
|
<div className="flex flex-col items-center">
|
||||||
groupCounts={groupCounts}
|
<MdDoubleArrow className="text-foreground-500 text-[100px]" />
|
||||||
groupContent={(index) => {
|
<h2 className="text-foreground-500 text-[20px]">直连模式</h2>
|
||||||
if (
|
</div>
|
||||||
groups[index] &&
|
</div>
|
||||||
groups[index].icon &&
|
) : (
|
||||||
groups[index].icon.startsWith('http') &&
|
<div className="h-[calc(100vh-50px)]">
|
||||||
!localStorage.getItem(groups[index].icon)
|
<GroupedVirtuoso
|
||||||
) {
|
ref={virtuosoRef}
|
||||||
getImageDataURL(groups[index].icon).then((dataURL) => {
|
groupCounts={groupCounts}
|
||||||
localStorage.setItem(groups[index].icon, dataURL)
|
groupContent={(index) => {
|
||||||
mutate()
|
if (
|
||||||
})
|
groups[index] &&
|
||||||
}
|
groups[index].icon &&
|
||||||
return groups[index] ? (
|
groups[index].icon.startsWith('http') &&
|
||||||
<div
|
!localStorage.getItem(groups[index].icon)
|
||||||
className={`w-full pt-2 ${index === groupCounts.length - 1 && !isOpen[index] ? 'pb-2' : ''} px-2`}
|
) {
|
||||||
>
|
getImageDataURL(groups[index].icon).then((dataURL) => {
|
||||||
<Card
|
localStorage.setItem(groups[index].icon, dataURL)
|
||||||
isPressable
|
mutate()
|
||||||
fullWidth
|
})
|
||||||
onClick={() => {
|
}
|
||||||
setIsOpen((prev) => {
|
return groups[index] ? (
|
||||||
const newOpen = [...prev]
|
<div
|
||||||
newOpen[index] = !prev[index]
|
className={`w-full pt-2 ${index === groupCounts.length - 1 && !isOpen[index] ? 'pb-2' : ''} px-2`}
|
||||||
return newOpen
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<CardBody className="w-full">
|
<Card
|
||||||
<div className="flex justify-between">
|
isPressable
|
||||||
<div className="flex text-ellipsis overflow-hidden whitespace-nowrap">
|
fullWidth
|
||||||
{groups[index].icon ? (
|
onClick={() => {
|
||||||
<Avatar
|
setIsOpen((prev) => {
|
||||||
className="bg-transparent mr-2"
|
const newOpen = [...prev]
|
||||||
size="sm"
|
newOpen[index] = !prev[index]
|
||||||
radius="sm"
|
return newOpen
|
||||||
src={
|
})
|
||||||
groups[index].icon.startsWith('<svg')
|
}}
|
||||||
? `data:image/svg+xml;utf8,${groups[index].icon}`
|
>
|
||||||
: localStorage.getItem(groups[index].icon) || groups[index].icon
|
<CardBody className="w-full">
|
||||||
}
|
<div className="flex justify-between">
|
||||||
/>
|
<div className="flex text-ellipsis overflow-hidden whitespace-nowrap">
|
||||||
) : null}
|
{groups[index].icon ? (
|
||||||
<div className="text-ellipsis overflow-hidden whitespace-nowrap">
|
<Avatar
|
||||||
<div
|
className="bg-transparent mr-2"
|
||||||
title={groups[index].name}
|
size="sm"
|
||||||
className="inline flag-emoji h-[32px] text-md leading-[32px]"
|
radius="sm"
|
||||||
>
|
src={
|
||||||
{groups[index].name}
|
groups[index].icon.startsWith('<svg')
|
||||||
</div>
|
? `data:image/svg+xml;utf8,${groups[index].icon}`
|
||||||
{proxyDisplayMode === 'full' && (
|
: localStorage.getItem(groups[index].icon) || groups[index].icon
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
<div className="text-ellipsis overflow-hidden whitespace-nowrap">
|
||||||
<div
|
<div
|
||||||
title={groups[index].type}
|
title={groups[index].name}
|
||||||
className="inline ml-2 text-sm text-default-500"
|
className="inline flag-emoji h-[32px] text-md leading-[32px]"
|
||||||
>
|
>
|
||||||
{groups[index].type}
|
{groups[index].name}
|
||||||
</div>
|
</div>
|
||||||
)}
|
{proxyDisplayMode === 'full' && (
|
||||||
|
<div
|
||||||
|
title={groups[index].type}
|
||||||
|
className="inline ml-2 text-sm text-foreground-500"
|
||||||
|
>
|
||||||
|
{groups[index].type}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{proxyDisplayMode === 'full' && (
|
||||||
|
<div className="inline flag-emoji ml-2 text-sm text-foreground-500">
|
||||||
|
{groups[index].now}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex">
|
||||||
{proxyDisplayMode === 'full' && (
|
{proxyDisplayMode === 'full' && (
|
||||||
<div className="inline flag-emoji ml-2 text-sm text-default-500">
|
<Chip size="sm" className="my-1 mr-2">
|
||||||
{groups[index].now}
|
{groups[index].all.length}
|
||||||
</div>
|
</Chip>
|
||||||
)}
|
)}
|
||||||
|
<CollapseInput
|
||||||
|
title="搜索节点"
|
||||||
|
value={searchValue[index]}
|
||||||
|
onValueChange={(v) => {
|
||||||
|
setSearchValue((prev) => {
|
||||||
|
const newSearchValue = [...prev]
|
||||||
|
newSearchValue[index] = v
|
||||||
|
return newSearchValue
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
title="定位到当前节点"
|
||||||
|
variant="light"
|
||||||
|
size="sm"
|
||||||
|
isIconOnly
|
||||||
|
onPress={() => {
|
||||||
|
if (!isOpen[index]) {
|
||||||
|
setIsOpen((prev) => {
|
||||||
|
const newOpen = [...prev]
|
||||||
|
newOpen[index] = true
|
||||||
|
return newOpen
|
||||||
|
})
|
||||||
|
}
|
||||||
|
let i = 0
|
||||||
|
for (let j = 0; j < index; j++) {
|
||||||
|
i += groupCounts[j]
|
||||||
|
}
|
||||||
|
i += Math.floor(
|
||||||
|
allProxies[index].findIndex(
|
||||||
|
(proxy) => proxy.name === groups[index].now
|
||||||
|
) / cols
|
||||||
|
)
|
||||||
|
virtuosoRef.current?.scrollToIndex({
|
||||||
|
index: Math.floor(i),
|
||||||
|
align: 'start'
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FaLocationCrosshairs className="text-lg text-foreground-500" />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
title="延迟测试"
|
||||||
|
variant="light"
|
||||||
|
isLoading={delaying[index]}
|
||||||
|
size="sm"
|
||||||
|
isIconOnly
|
||||||
|
onPress={() => {
|
||||||
|
onGroupDelay(index)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MdOutlineSpeed className="text-lg text-foreground-500" />
|
||||||
|
</Button>
|
||||||
|
<IoIosArrowBack
|
||||||
|
className={`transition duration-200 ml-2 h-[32px] text-lg text-foreground-500 ${isOpen[index] ? '-rotate-90' : ''}`}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex">
|
</CardBody>
|
||||||
{proxyDisplayMode === 'full' && (
|
</Card>
|
||||||
<Chip size="sm" className="my-1 mr-2">
|
</div>
|
||||||
{groups[index].all.length}
|
) : (
|
||||||
</Chip>
|
<div>Never See This</div>
|
||||||
)}
|
)
|
||||||
<CollapseInput
|
}}
|
||||||
title="搜索节点"
|
itemContent={(index, groupIndex) => {
|
||||||
value={searchValue[index]}
|
let innerIndex = index
|
||||||
onValueChange={(v) => {
|
groupCounts.slice(0, groupIndex).forEach((count) => {
|
||||||
setSearchValue((prev) => {
|
innerIndex -= count
|
||||||
const newSearchValue = [...prev]
|
})
|
||||||
newSearchValue[index] = v
|
return allProxies[groupIndex] ? (
|
||||||
return newSearchValue
|
<div
|
||||||
})
|
style={
|
||||||
}}
|
proxyCols !== 'auto'
|
||||||
/>
|
? { gridTemplateColumns: `repeat(${proxyCols}, minmax(0, 1fr))` }
|
||||||
<Button
|
: {}
|
||||||
title="定位到当前节点"
|
}
|
||||||
variant="light"
|
className={`grid ${proxyCols === 'auto' ? 'sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5' : ''} ${groupIndex === groupCounts.length - 1 && innerIndex === groupCounts[groupIndex] - 1 ? 'pb-2' : ''} gap-2 pt-2 mx-2`}
|
||||||
size="sm"
|
>
|
||||||
isIconOnly
|
{Array.from({ length: cols }).map((_, i) => {
|
||||||
onPress={() => {
|
if (!allProxies[groupIndex][innerIndex * cols + i]) return null
|
||||||
if (!isOpen[index]) {
|
return (
|
||||||
setIsOpen((prev) => {
|
<ProxyItem
|
||||||
const newOpen = [...prev]
|
key={allProxies[groupIndex][innerIndex * cols + i].name}
|
||||||
newOpen[index] = true
|
mutateProxies={mutate}
|
||||||
return newOpen
|
onProxyDelay={onProxyDelay}
|
||||||
})
|
onSelect={onChangeProxy}
|
||||||
}
|
proxy={allProxies[groupIndex][innerIndex * cols + i]}
|
||||||
let i = 0
|
group={groups[groupIndex]}
|
||||||
for (let j = 0; j < index; j++) {
|
proxyDisplayMode={proxyDisplayMode}
|
||||||
i += groupCounts[j]
|
selected={
|
||||||
}
|
allProxies[groupIndex][innerIndex * cols + i]?.name ===
|
||||||
i += Math.floor(
|
groups[groupIndex].now
|
||||||
allProxies[index].findIndex(
|
}
|
||||||
(proxy) => proxy.name === groups[index].now
|
/>
|
||||||
) / cols
|
)
|
||||||
)
|
})}
|
||||||
virtuosoRef.current?.scrollToIndex({
|
</div>
|
||||||
index: Math.floor(i),
|
) : (
|
||||||
align: 'start'
|
<div>Never See This</div>
|
||||||
})
|
)
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
<FaLocationCrosshairs className="text-lg text-default-500" />
|
</div>
|
||||||
</Button>
|
)}
|
||||||
<Button
|
|
||||||
title="延迟测试"
|
|
||||||
variant="light"
|
|
||||||
isLoading={delaying[index]}
|
|
||||||
size="sm"
|
|
||||||
isIconOnly
|
|
||||||
onPress={() => {
|
|
||||||
onGroupDelay(index)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<MdOutlineSpeed className="text-lg text-default-500" />
|
|
||||||
</Button>
|
|
||||||
<IoIosArrowBack
|
|
||||||
className={`transition duration-200 ml-2 h-[32px] text-lg text-default-500 ${isOpen[index] ? '-rotate-90' : ''}`}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div>Never See This</div>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
itemContent={(index, groupIndex) => {
|
|
||||||
let innerIndex = index
|
|
||||||
groupCounts.slice(0, groupIndex).forEach((count) => {
|
|
||||||
innerIndex -= count
|
|
||||||
})
|
|
||||||
return allProxies[groupIndex] ? (
|
|
||||||
<div
|
|
||||||
style={
|
|
||||||
proxyCols !== 'auto'
|
|
||||||
? { gridTemplateColumns: `repeat(${proxyCols}, minmax(0, 1fr))` }
|
|
||||||
: {}
|
|
||||||
}
|
|
||||||
className={`grid ${proxyCols === 'auto' ? 'sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5' : ''} ${groupIndex === groupCounts.length - 1 && innerIndex === groupCounts[groupIndex] - 1 ? 'pb-2' : ''} gap-2 pt-2 mx-2`}
|
|
||||||
>
|
|
||||||
{Array.from({ length: cols }).map((_, i) => {
|
|
||||||
if (!allProxies[groupIndex][innerIndex * cols + i]) return null
|
|
||||||
return (
|
|
||||||
<ProxyItem
|
|
||||||
key={allProxies[groupIndex][innerIndex * cols + i].name}
|
|
||||||
mutateProxies={mutate}
|
|
||||||
onProxyDelay={onProxyDelay}
|
|
||||||
onSelect={onChangeProxy}
|
|
||||||
proxy={allProxies[groupIndex][innerIndex * cols + i]}
|
|
||||||
group={groups[groupIndex]}
|
|
||||||
proxyDisplayMode={proxyDisplayMode}
|
|
||||||
selected={
|
|
||||||
allProxies[groupIndex][innerIndex * cols + i]?.name ===
|
|
||||||
groups[groupIndex].now
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div>Never See This</div>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</BasePage>
|
</BasePage>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user