feat(proxies): overhaul the look of proxies page, drop renderInTwoColumns support

This commit is contained in:
kunish 2024-10-10 18:52:24 +08:00
parent 2e92dd20cf
commit 698362f1c7
No known key found for this signature in database
GPG Key ID: 647A12B4F782C430
13 changed files with 88 additions and 159 deletions

View File

@ -29,7 +29,7 @@
"@solid-primitives/timer": "^1.3.10", "@solid-primitives/timer": "^1.3.10",
"@solid-primitives/websocket": "^1.2.2", "@solid-primitives/websocket": "^1.2.2",
"@solidjs/meta": "^0.29.4", "@solidjs/meta": "^0.29.4",
"@solidjs/router": "^0.14.7", "@solidjs/router": "^0.14.8",
"@tabler/icons-solidjs": "^3.19.0", "@tabler/icons-solidjs": "^3.19.0",
"@tanstack/match-sorter-utils": "^8.19.4", "@tanstack/match-sorter-utils": "^8.19.4",
"@tanstack/solid-table": "^8.20.5", "@tanstack/solid-table": "^8.20.5",

10
pnpm-lock.yaml generated
View File

@ -53,8 +53,8 @@ importers:
specifier: ^0.29.4 specifier: ^0.29.4
version: 0.29.4(solid-js@1.9.2) version: 0.29.4(solid-js@1.9.2)
'@solidjs/router': '@solidjs/router':
specifier: ^0.14.7 specifier: ^0.14.8
version: 0.14.7(solid-js@1.9.2) version: 0.14.8(solid-js@1.9.2)
'@tabler/icons-solidjs': '@tabler/icons-solidjs':
specifier: ^3.19.0 specifier: ^3.19.0
version: 3.19.0(solid-js@1.9.2) version: 3.19.0(solid-js@1.9.2)
@ -1984,10 +1984,10 @@ packages:
peerDependencies: peerDependencies:
solid-js: '>=1.8.4' solid-js: '>=1.8.4'
'@solidjs/router@0.14.7': '@solidjs/router@0.14.8':
resolution: resolution:
{ {
integrity: sha512-agLf8AUz5XnW6+F64a4Iq+QQQobI5zGHQ/gUYd/WHSwcbnFpavbsiwRLob3YhWMXVX3sQyn4ekUN+uchMCRupw==, integrity: sha512-S+rD5Twp0820cM03wEIYtb7/4KN7Cfr3BP+qPIqb7IXO/SZ72tWqHEMQsmcjDbr4yVfpA+5Sq0Y+xcq09y1gQA==,
} }
peerDependencies: peerDependencies:
solid-js: ^1.8.6 solid-js: ^1.8.6
@ -7577,7 +7577,7 @@ snapshots:
dependencies: dependencies:
solid-js: 1.9.2 solid-js: 1.9.2
'@solidjs/router@0.14.7(solid-js@1.9.2)': '@solidjs/router@0.14.8(solid-js@1.9.2)':
dependencies: dependencies:
solid-js: 1.9.2 solid-js: 1.9.2

View File

@ -19,6 +19,7 @@ interface ButtonWithoutIconProps extends ButtonBaseProps {
export const Button: ParentComponent< export const Button: ParentComponent<
ButtonWithIconProps | ButtonWithoutIconProps ButtonWithIconProps | ButtonWithoutIconProps
> = (props) => { > = (props) => {
// @ts-expect-error Expression produces a union type that is too complex to represent
const [local, others] = splitProps(props, ['class', 'loading', 'icon']) const [local, others] = splitProps(props, ['class', 'loading', 'icon'])
return ( return (

View File

@ -1,6 +1,5 @@
import type { JSX, ParentComponent } from 'solid-js' import type { JSX, ParentComponent } from 'solid-js'
import { twMerge } from 'tailwind-merge' import { twMerge } from 'tailwind-merge'
import { renderProxiesInTwoColumns } from '~/signals'
type Props = { type Props = {
title: JSX.Element title: JSX.Element
@ -25,14 +24,6 @@ export const Collapse: ParentComponent<Props> = (props) => {
return props.isOpen ? openedClassName : closedClassName return props.isOpen ? openedClassName : closedClassName
} }
const gridStyle = createMemo(() => {
if (renderProxiesInTwoColumns()) {
return 'lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5'
}
return 'sm:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 2xl:grid-cols-10'
})
return ( return (
<div <div
class={twMerge( class={twMerge(
@ -50,8 +41,7 @@ export const Collapse: ParentComponent<Props> = (props) => {
<div <div
class={twMerge( class={twMerge(
getCollapseContentClassName(), getCollapseContentClassName(),
gridStyle(), 'collapse-content grid grid-cols-1 gap-2 transition-opacity duration-1000 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5',
'collapse-content grid grid-cols-2 gap-2 transition-opacity duration-1000',
)} )}
> >
<Show when={props.isOpen}>{children(() => props.children)()}</Show> <Show when={props.isOpen}>{children(() => props.children)()}</Show>

View File

@ -1,15 +0,0 @@
import { useProxies } from '~/signals'
export const IPv6Support = (props: { name?: string }) => {
const { getNowProxyNodeName, proxyIPv6SupportMap } = useProxies()
const support = createMemo(() => {
return proxyIPv6SupportMap()[getNowProxyNodeName(props.name || '')]
})
return (
<Show when={support()}>
<span class={`text-xs`}>IPv6</span>
</Show>
)
}

View File

@ -11,7 +11,6 @@ import {
latencyTestTimeoutDuration, latencyTestTimeoutDuration,
proxiesOrderingType, proxiesOrderingType,
proxiesPreviewType, proxiesPreviewType,
renderProxiesInTwoColumns,
setAutoCloseConns, setAutoCloseConns,
setHideUnAvailableProxies, setHideUnAvailableProxies,
setIconHeight, setIconHeight,
@ -19,7 +18,6 @@ import {
setLatencyTestTimeoutDuration, setLatencyTestTimeoutDuration,
setProxiesOrderingType, setProxiesOrderingType,
setProxiesPreviewType, setProxiesPreviewType,
setRenderProxiesInTwoColumns,
setUrlForLatencyTest, setUrlForLatencyTest,
setUrlIPv6SupportTest, setUrlIPv6SupportTest,
urlForIPv6SupportTest, urlForIPv6SupportTest,
@ -107,7 +105,7 @@ export const ProxiesSettingsModal: Component<{
</div> </div>
<div> <div>
<ConfigTitle withDivider>{t('hideUnAvailableProxies')}</ConfigTitle> <ConfigTitle withDivider>{t('hideUnavailableProxies')}</ConfigTitle>
<div class="flex w-full justify-center"> <div class="flex w-full justify-center">
<input <input
@ -119,19 +117,6 @@ export const ProxiesSettingsModal: Component<{
</div> </div>
</div> </div>
<div>
<ConfigTitle withDivider>{t('renderInTwoColumns')}</ConfigTitle>
<div class="flex w-full justify-center">
<input
class="toggle"
type="checkbox"
checked={renderProxiesInTwoColumns()}
onChange={(e) => setRenderProxiesInTwoColumns(e.target.checked)}
/>
</div>
</div>
<div> <div>
<ConfigTitle withDivider>{t('proxiesPreviewType')}</ConfigTitle> <ConfigTitle withDivider>{t('proxiesPreviewType')}</ConfigTitle>

View File

@ -1,6 +1,6 @@
import { IconBrandSpeedtest } from '@tabler/icons-solidjs' import { IconBrandSpeedtest } from '@tabler/icons-solidjs'
import { twMerge } from 'tailwind-merge' import { twMerge } from 'tailwind-merge'
import { Button, IPv6Support, Latency } from '~/components' import { Button, Latency } from '~/components'
import { filterSpecialProxyType, formatProxyType } from '~/helpers' import { filterSpecialProxyType, formatProxyType } from '~/helpers'
import { useProxies } from '~/signals' import { useProxies } from '~/signals'
@ -9,10 +9,19 @@ export const ProxyNodeCard = (props: {
isSelected?: boolean isSelected?: boolean
onClick?: () => void onClick?: () => void
}) => { }) => {
const { proxyLatencyTest, proxyLatencyTestingMap } = useProxies()
const { proxyName, isSelected, onClick } = props const { proxyName, isSelected, onClick } = props
const { proxyNodeMap } = useProxies() const {
getNowProxyNodeName,
proxyIPv6SupportMap,
proxyNodeMap,
proxyLatencyTest,
proxyLatencyTestingMap,
} = useProxies()
const supportIPv6 = createMemo(
() => proxyIPv6SupportMap()[getNowProxyNodeName(props.proxyName || '')],
)
const proxyNode = createMemo(() => proxyNodeMap()[proxyName]) const proxyNode = createMemo(() => proxyNodeMap()[proxyName])
const specialType = () => const specialType = () =>
filterSpecialProxyType(proxyNode()?.type) filterSpecialProxyType(proxyNode()?.type)
? proxyNode()?.xudp ? proxyNode()?.xudp
@ -25,55 +34,61 @@ export const ProxyNodeCard = (props: {
return ( return (
<div <div
class={twMerge( class={twMerge(
'border-neutral-focus card card-bordered tooltip-bottom flex flex-col justify-between gap-1 bg-neutral p-2 text-neutral-content', 'card tooltip card-compact tooltip-accent bg-neutral text-neutral-content',
isSelected && isSelected &&
'bg-gradient-to-br from-primary to-secondary text-primary-content', 'bg-gradient-to-l from-primary to-secondary text-primary-content',
onClick && 'cursor-pointer', onClick && 'cursor-pointer',
)} )}
data-tip={proxyName}
onClick={onClick} onClick={onClick}
title={proxyName}
> >
<div class="flex items-center justify-between gap-2"> <div class="card-body">
<span class="break-all text-left text-sm">{proxyName}</span> <span class="card-title truncate text-sm">{proxyName}</span>
<span class="flex items-center gap-1"> <div class="flex items-center justify-between gap-2">
<IPv6Support name={props.proxyName} /> <div class="flex items-center gap-2">
<Button <Show when={specialType()}>
class="btn-circle btn-ghost h-auto min-h-0 w-auto" <div class="badge badge-primary badge-sm font-bold uppercase">
icon={ {formatProxyType(proxyNode()?.type)}
<IconBrandSpeedtest </div>
size={20}
class={twMerge(
proxyLatencyTestingMap()[proxyName] &&
'animate-pulse text-success',
)}
/>
}
onClick={(e) => {
e.stopPropagation()
void proxyLatencyTest(proxyName, proxyNode().provider) <Show when={specialType()}>
}} <div class="badge badge-secondary badge-sm">
/> {specialType()}
</span> </div>
</div> </Show>
</Show>
<div class="flex items-center justify-between gap-1"> <Show when={supportIPv6()}>
<div <div class="badge badge-accent badge-sm">IPv6</div>
class={twMerge( </Show>
'text-xs text-slate-500', </div>
isSelected && 'text-primary-content',
)}
>
{formatProxyType(proxyNode()?.type)}
<Show when={specialType()}>{` :: ${specialType()}`}</Show> <div class="flex items-center gap-2">
<Latency
name={props.proxyName}
class={twMerge(isSelected && 'badge')}
/>
<Button
class="btn-square btn-sm"
icon={
<IconBrandSpeedtest
class={twMerge(
'size-6',
proxyLatencyTestingMap()[proxyName] &&
'animate-pulse text-success',
)}
/>
}
onClick={(e) => {
e.stopPropagation()
void proxyLatencyTest(proxyName, proxyNode().provider)
}}
/>
</div>
</div> </div>
<Latency
name={props.proxyName}
class={twMerge(isSelected && 'badge badge-sm px-1')}
/>
</div> </div>
</div> </div>
) )

View File

@ -1,26 +0,0 @@
import { createWindowSize } from '@solid-primitives/resize-observer'
import type { ParentComponent } from 'solid-js'
export const RenderInTwoColumns: ParentComponent = (props) => {
const resolvedChildren = children(() => props.children)
const windowSize = createWindowSize()
// 640 is sm size in default tailwindcss breakpoint
const showTwoColumns = createMemo(() => windowSize.width >= 640)
const leftColumns = createMemo(() =>
resolvedChildren.toArray().filter((_, index) => index % 2 === 0),
)
const rightColumns = createMemo(() =>
resolvedChildren.toArray().filter((_, index) => index % 2 === 1),
)
return (
<div class="flex flex-col gap-2 sm:flex-row">
<Show when={showTwoColumns()} fallback={props.children}>
<div class="flex flex-1 flex-col gap-2">{leftColumns()}</div>
<div class="flex flex-1 flex-col gap-2">{rightColumns()}</div>
</Show>
</div>
)
}

View File

@ -4,7 +4,6 @@ export * from './ConfigTitle'
export * from './ConnectionsSettingsModal' export * from './ConnectionsSettingsModal'
export * from './ConnectionsTableDetailsModal' export * from './ConnectionsTableDetailsModal'
export * from './Header' export * from './Header'
export * from './IPv6Support'
export * from './Latency' export * from './Latency'
export * from './LogoText' export * from './LogoText'
export * from './LogsSettingsModal' export * from './LogsSettingsModal'
@ -14,5 +13,4 @@ export * from './ProxyNodeCard'
export * from './ProxyNodePreview' export * from './ProxyNodePreview'
export * from './ProxyPreviewBar' export * from './ProxyPreviewBar'
export * from './ProxyPreviewDots' export * from './ProxyPreviewDots'
export * from './RenderInTwoColumns'
export * from './SubscriptionInfo' export * from './SubscriptionInfo'

View File

@ -47,15 +47,14 @@ export default {
bar: 'Bar', bar: 'Bar',
auto: 'Auto', auto: 'Auto',
off: 'Off', off: 'Off',
proxiesPreviewType: 'Proxies preview type', proxiesPreviewType: 'Proxies Preview Type',
urlForIPv6SupportTest: 'URL for IPv6 support test', urlForIPv6SupportTest: 'URL for IPv6 Support Test',
urlForLatencyTest: 'URL for latency test', urlForLatencyTest: 'URL for Latency Test',
autoCloseConns: 'Automatically Close connections', autoCloseConns: 'Automatically Close Connections',
useTwemoji: 'Use Twemoji Mozilla Font', useTwemoji: 'Use Twemoji Mozilla Font',
autoSwitchTheme: 'Automatically switch theme', autoSwitchTheme: 'Automatically switch theme',
favDayTheme: 'Favorite light theme', favDayTheme: 'Favorite light theme',
favNightTheme: 'Favorite dark theme', favNightTheme: 'Favorite dark theme',
renderInTwoColumns: 'Render in two columns',
updateGEODatabases: 'Update GEO Databases', updateGEODatabases: 'Update GEO Databases',
restartCore: 'Restart Core', restartCore: 'Restart Core',
upgradeCore: 'Upgrade Core', upgradeCore: 'Upgrade Core',
@ -95,7 +94,7 @@ export default {
active: 'Active', active: 'Active',
closed: 'Closed', closed: 'Closed',
sort: 'Sort', sort: 'Sort',
hideUnAvailableProxies: 'Hide UnAvailable Proxies', hideUnavailableProxies: 'Hide Unavailable Proxies',
reloadConfig: 'Reload Config', reloadConfig: 'Reload Config',
flushFakeIP: 'Flush Fake-IP', flushFakeIP: 'Flush Fake-IP',
tagClientSourceIPWithName: 'Tag Client Source IP With Name', tagClientSourceIPWithName: 'Tag Client Source IP With Name',

View File

@ -57,7 +57,6 @@ export default {
autoSwitchTheme: '自动切换主题', autoSwitchTheme: '自动切换主题',
favDayTheme: '浅色主题偏好', favDayTheme: '浅色主题偏好',
favNightTheme: '深色主题偏好', favNightTheme: '深色主题偏好',
renderInTwoColumns: '双列渲染',
updateGEODatabases: '更新 GEO 数据库', updateGEODatabases: '更新 GEO 数据库',
restartCore: '重启核心', restartCore: '重启核心',
upgradeCore: '更新核心', upgradeCore: '更新核心',
@ -97,7 +96,7 @@ export default {
active: '活动', active: '活动',
closed: '已关闭', closed: '已关闭',
sort: '排序', sort: '排序',
hideUnAvailableProxies: '隐藏不可用节点', hideUnavailableProxies: '隐藏不可用节点',
reloadConfig: '重载配置', reloadConfig: '重载配置',
flushFakeIP: '清空 Fake-IP', flushFakeIP: '清空 Fake-IP',
tagClientSourceIPWithName: '为客户端源 IP 地址添加名称标记', tagClientSourceIPWithName: '为客户端源 IP 地址添加名称标记',

View File

@ -27,7 +27,6 @@ import {
iconHeight, iconHeight,
iconMarginRight, iconMarginRight,
proxiesOrderingType, proxiesOrderingType,
renderProxiesInTwoColumns,
useConnections, useConnections,
useProxies, useProxies,
} from '~/signals' } from '~/signals'
@ -174,14 +173,7 @@ export default () => {
<div class="flex-1 overflow-y-auto"> <div class="flex-1 overflow-y-auto">
<Show when={activeTab() === ActiveTab.proxies}> <Show when={activeTab() === ActiveTab.proxies}>
<div <div class="flex flex-col gap-2">
class={twMerge(
'grid grid-cols-1 place-items-start gap-2',
renderProxiesInTwoColumns()
? 'sm:grid-cols-2'
: 'sm:grid-cols-1',
)}
>
<For each={renderProxies()}> <For each={renderProxies()}>
{(proxyGroup) => { {(proxyGroup) => {
const sortedProxyNames = createMemo(() => const sortedProxyNames = createMemo(() =>
@ -195,7 +187,7 @@ export default () => {
) )
const title = ( const title = (
<> <div class="space-y-2">
<div class="flex items-center justify-between pr-8"> <div class="flex items-center justify-between pr-8">
<div class="flex items-center"> <div class="flex items-center">
<Show when={proxyGroup.icon}> <Show when={proxyGroup.icon}>
@ -233,18 +225,21 @@ export default () => {
/> />
</div> </div>
<div class="flex items-center justify-between text-sm text-slate-500"> <div class="flex flex-wrap items-center justify-between gap-2">
<span> <div class="badge badge-primary badge-sm">
{proxyGroup.type}{' '} <span class="font-bold">{proxyGroup.type}</span>
{proxyGroup.now?.length > 0 &&
` :: ${proxyGroup.now}`} <Show when={proxyGroup.now?.length > 0}>
</span> <pre>{` :: ${proxyGroup.now}`}</pre>
<span> </Show>
</div>
<div class="badge badge-secondary badge-sm">
{byteSize( {byteSize(
speedGroupByName()[proxyGroup.name] ?? 0, speedGroupByName()[proxyGroup.name] ?? 0,
).toString()} ).toString()}
/s /s
</span> </div>
</div> </div>
<Show when={!collapsedMap()[proxyGroup.name]}> <Show when={!collapsedMap()[proxyGroup.name]}>
@ -253,7 +248,7 @@ export default () => {
now={proxyGroup.now} now={proxyGroup.now}
/> />
</Show> </Show>
</> </div>
) )
return ( return (
@ -283,14 +278,7 @@ export default () => {
</Show> </Show>
<Show when={activeTab() === ActiveTab.proxyProviders}> <Show when={activeTab() === ActiveTab.proxyProviders}>
<div <div class="flex flex-col gap-2">
class={twMerge(
'grid grid-cols-1 place-items-start gap-2',
renderProxiesInTwoColumns()
? 'sm:grid-cols-2'
: 'sm:grid-cols-1',
)}
>
<For each={proxyProviders()}> <For each={proxyProviders()}>
{(proxyProvider) => { {(proxyProvider) => {
const sortedProxyNames = createMemo(() => const sortedProxyNames = createMemo(() =>
@ -305,6 +293,7 @@ export default () => {
<div class="flex items-center justify-between pr-8"> <div class="flex items-center justify-between pr-8">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span>{proxyProvider.name}</span> <span>{proxyProvider.name}</span>
<div class="badge badge-sm"> <div class="badge badge-sm">
{proxyProvider.proxies.length} {proxyProvider.proxies.length}
</div> </div>

View File

@ -31,12 +31,6 @@ export const [hideUnAvailableProxies, setHideUnAvailableProxies] =
storage: localStorage, storage: localStorage,
}) })
export const [renderProxiesInTwoColumns, setRenderProxiesInTwoColumns] =
makePersisted(createSignal(true), {
name: 'renderProxiesInTwoColumns',
storage: localStorage,
})
export const [urlForLatencyTest, setUrlForLatencyTest] = makePersisted( export const [urlForLatencyTest, setUrlForLatencyTest] = makePersisted(
createSignal('https://www.gstatic.com/generate_204'), createSignal('https://www.gstatic.com/generate_204'),
{ name: 'urlForLatencyTest', storage: localStorage }, { name: 'urlForLatencyTest', storage: localStorage },