metacubexd/src/pages/Proxies.tsx

295 lines
9.1 KiB
TypeScript
Raw Normal View History

2023-09-02 00:09:41 +08:00
import { useI18n } from '@solid-primitives/i18n'
import { IconBrandSpeedtest, IconReload } from '@tabler/icons-solidjs'
import { For, Show, createSignal } from 'solid-js'
2023-09-05 15:38:42 +08:00
import { twMerge } from 'tailwind-merge'
import {
Button,
Collapse,
ForTwoColumns,
ProxyCardGroups,
ProxyNodePreview,
SubscriptionInfo,
} from '~/components'
import {
formatTimeFromNow,
sortProxiesByOrderingType,
useStringBooleanMap,
} from '~/helpers'
import { proxiesOrderingType, useProxies } from '~/signals'
2023-08-29 20:20:01 +08:00
import type { Proxy } from '~/types'
enum ActiveTab {
all = 'all',
proxyProviders = 'proxyProviders',
proxies = 'proxies',
}
2023-08-24 04:20:53 +08:00
export default () => {
2023-09-02 00:09:41 +08:00
const [t] = useI18n()
const {
proxies,
setProxyGroupByProxyName,
2023-09-03 05:40:39 +08:00
latencyTestByProxyGroupName,
latencyMap,
proxyProviders,
updateProviderByProviderName,
updateAllProvider,
healthCheckByProviderName,
} = useProxies()
2023-08-30 23:02:55 +08:00
2023-09-05 15:38:42 +08:00
const { map: collapsedMap, set: setCollapsedMap } = useStringBooleanMap()
const { map: latencyTestingMap, setWithCallback: setLatencyTestingMap } =
2023-09-05 15:38:42 +08:00
useStringBooleanMap()
2023-08-24 04:20:53 +08:00
2023-08-29 20:20:01 +08:00
const onProxyNodeClick = async (proxy: Proxy, proxyName: string) => {
2023-09-03 20:23:02 +08:00
void setProxyGroupByProxyName(proxy, proxyName)
2023-08-29 20:20:01 +08:00
}
2023-08-24 04:20:53 +08:00
const onLatencyTestClick = async (e: MouseEvent, name: string) => {
e.stopPropagation()
void setLatencyTestingMap(name, () => latencyTestByProxyGroupName(name))
}
const { map: healthCheckingMap, setWithCallback: setHealthCheckingMap } =
useStringBooleanMap()
const { map: updatingMap, setWithCallback: setUpdatingMap } =
useStringBooleanMap()
const [isAllProviderUpdating, setIsAllProviderUpdating] = createSignal(false)
const onHealthCheckClick = (e: MouseEvent, name: string) => {
e.stopPropagation()
void setHealthCheckingMap(name, () => healthCheckByProviderName(name))
}
const onUpdateProviderClick = (e: MouseEvent, name: string) => {
e.stopPropagation()
void setUpdatingMap(name, () => updateProviderByProviderName(name))
}
const onUpdateAllProviderClick = async (e: MouseEvent) => {
2023-09-05 15:38:42 +08:00
e.stopPropagation()
setIsAllProviderUpdating(true)
try {
await updateAllProvider()
} catch {}
setIsAllProviderUpdating(false)
2023-08-30 11:01:19 +08:00
}
const [activeTab, setActiveTab] = createSignal(ActiveTab.all)
const tabs = () => [
{
type: ActiveTab.all,
name: t('all'),
count: proxyProviders().length + proxies().length,
},
{
type: ActiveTab.proxyProviders,
name: t('proxyProviders'),
count: proxyProviders().length,
},
{
type: ActiveTab.proxies,
name: t('proxies'),
count: proxies().length,
},
]
2023-08-24 04:20:53 +08:00
return (
2023-09-06 22:47:02 +08:00
<div class="flex h-full flex-col gap-2">
<div class="flex items-center justify-between gap-2">
2023-09-07 10:34:54 +08:00
<div class="tabs tabs-boxed gap-2">
<For each={tabs()}>
{(tab) => (
<button
class={twMerge(
activeTab() === tab.type && 'tab-active',
2023-09-07 10:34:54 +08:00
'tab gap-2 px-2',
)}
onClick={() => setActiveTab(tab.type)}
>
<span>{tab.name}</span>
<div class="badge badge-sm">{tab.count}</div>
</button>
)}
</For>
</div>
<Button
2023-09-07 10:34:54 +08:00
class="btn btn-circle btn-sm"
2023-09-06 22:20:45 +08:00
disabled={isAllProviderUpdating()}
onClick={(e) => onUpdateAllProviderClick(e)}
>
<IconReload
class={twMerge(
isAllProviderUpdating() && 'animate-spin text-success',
)}
/>
</Button>
</div>
2023-09-06 22:47:02 +08:00
<div class="flex-1 overflow-y-auto">
<Show
when={
activeTab() === ActiveTab.all ||
activeTab() === ActiveTab.proxyProviders
}
>
<ForTwoColumns
subChild={proxyProviders().map((proxyProvider) => {
const sortedProxyNames = sortProxiesByOrderingType(
proxyProvider.proxies.map((i) => i.name) ?? [],
latencyMap(),
proxiesOrderingType(),
)
2023-09-06 22:47:02 +08:00
const title = (
<>
<div class="mr-8 flex items-center justify-between">
<div class="flex items-center gap-2">
<span>{proxyProvider.name}</span>
<div class="badge badge-sm">
{proxyProvider.proxies.length}
</div>
</div>
<div>
<Button
class="btn btn-circle btn-sm mr-2"
disabled={updatingMap()[proxyProvider.name]}
onClick={(e) =>
onUpdateProviderClick(e, proxyProvider.name)
}
>
<IconReload
class={twMerge(
updatingMap()[proxyProvider.name] &&
'animate-spin text-success',
)}
/>
</Button>
<Button
class="btn btn-circle btn-sm"
disabled={healthCheckingMap()[proxyProvider.name]}
onClick={(e) =>
onHealthCheckClick(e, proxyProvider.name)
}
>
<IconBrandSpeedtest
class={twMerge(
healthCheckingMap()[proxyProvider.name] &&
'animate-pulse text-success',
)}
/>
</Button>
</div>
</div>
2023-09-06 22:47:02 +08:00
<SubscriptionInfo
subscriptionInfo={proxyProvider.subscriptionInfo}
/>
<div class="text-sm text-slate-500">
{proxyProvider.vehicleType} :: {t('updated')}{' '}
{formatTimeFromNow(proxyProvider.updatedAt)}
</div>
<Show when={!collapsedMap()[proxyProvider.name]}>
<ProxyNodePreview proxyNameList={sortedProxyNames} />
</Show>
</>
)
const content = <ProxyCardGroups proxyNames={sortedProxyNames} />
return (
<Collapse
isOpen={collapsedMap()[proxyProvider.name]}
title={title}
content={content}
onCollapse={(val) => setCollapsedMap(proxyProvider.name, val)}
/>
)
})}
/>
</Show>
<Show when={activeTab() === ActiveTab.all}>
<div class="divider" />
</Show>
<Show
when={
activeTab() === ActiveTab.all || activeTab() === ActiveTab.proxies
}
>
<ForTwoColumns
subChild={proxies().map((proxy) => {
const sortedProxyNames = sortProxiesByOrderingType(
proxy.all ?? [],
latencyMap(),
proxiesOrderingType(),
)
const title = (
<>
<div class="mr-8 flex items-center justify-between">
<div class="flex items-center gap-2">
<span>{proxy.name}</span>
<div class="badge badge-sm">{proxy.all?.length}</div>
</div>
<Button
2023-09-06 22:47:02 +08:00
class="btn-circle btn-sm"
disabled={latencyTestingMap()[proxy.name]}
onClick={(e) => onLatencyTestClick(e, proxy.name)}
>
<IconBrandSpeedtest
class={twMerge(
2023-09-06 22:47:02 +08:00
latencyTestingMap()[proxy.name] &&
'animate-pulse text-success',
)}
/>
</Button>
</div>
2023-09-06 22:47:02 +08:00
<div class="text-sm text-slate-500">
{proxy.type} {proxy.now?.length > 0 && ` :: ${proxy.now}`}
</div>
2023-09-06 22:47:02 +08:00
<Show when={!collapsedMap()[proxy.name]}>
<ProxyNodePreview
proxyNameList={sortedProxyNames}
now={proxy.now}
2023-09-05 15:38:42 +08:00
/>
2023-09-06 22:47:02 +08:00
</Show>
</>
)
2023-09-02 16:58:22 +08:00
2023-09-06 22:47:02 +08:00
const content = (
<ProxyCardGroups
proxyNames={sortedProxyNames}
now={proxy.now}
onClick={(name) => {
void onProxyNodeClick(proxy, name)
}}
/>
)
2023-09-02 16:58:22 +08:00
2023-09-06 22:47:02 +08:00
return (
<Collapse
isOpen={collapsedMap()[proxy.name]}
title={title}
content={content}
onCollapse={(val) => setCollapsedMap(proxy.name, val)}
/>
)
})}
/>
</Show>
</div>
</div>
2023-08-24 04:20:53 +08:00
)
}