mirror of
https://github.com/MetaCubeX/metacubexd.git
synced 2024-12-25 19:14:12 +08:00
feat(proxies): overhaul the look of proxies page, drop renderInTwoColumns support
This commit is contained in:
parent
2e92dd20cf
commit
698362f1c7
@ -29,7 +29,7 @@
|
||||
"@solid-primitives/timer": "^1.3.10",
|
||||
"@solid-primitives/websocket": "^1.2.2",
|
||||
"@solidjs/meta": "^0.29.4",
|
||||
"@solidjs/router": "^0.14.7",
|
||||
"@solidjs/router": "^0.14.8",
|
||||
"@tabler/icons-solidjs": "^3.19.0",
|
||||
"@tanstack/match-sorter-utils": "^8.19.4",
|
||||
"@tanstack/solid-table": "^8.20.5",
|
||||
|
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
@ -53,8 +53,8 @@ importers:
|
||||
specifier: ^0.29.4
|
||||
version: 0.29.4(solid-js@1.9.2)
|
||||
'@solidjs/router':
|
||||
specifier: ^0.14.7
|
||||
version: 0.14.7(solid-js@1.9.2)
|
||||
specifier: ^0.14.8
|
||||
version: 0.14.8(solid-js@1.9.2)
|
||||
'@tabler/icons-solidjs':
|
||||
specifier: ^3.19.0
|
||||
version: 3.19.0(solid-js@1.9.2)
|
||||
@ -1984,10 +1984,10 @@ packages:
|
||||
peerDependencies:
|
||||
solid-js: '>=1.8.4'
|
||||
|
||||
'@solidjs/router@0.14.7':
|
||||
'@solidjs/router@0.14.8':
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-agLf8AUz5XnW6+F64a4Iq+QQQobI5zGHQ/gUYd/WHSwcbnFpavbsiwRLob3YhWMXVX3sQyn4ekUN+uchMCRupw==,
|
||||
integrity: sha512-S+rD5Twp0820cM03wEIYtb7/4KN7Cfr3BP+qPIqb7IXO/SZ72tWqHEMQsmcjDbr4yVfpA+5Sq0Y+xcq09y1gQA==,
|
||||
}
|
||||
peerDependencies:
|
||||
solid-js: ^1.8.6
|
||||
@ -7577,7 +7577,7 @@ snapshots:
|
||||
dependencies:
|
||||
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:
|
||||
solid-js: 1.9.2
|
||||
|
||||
|
@ -19,6 +19,7 @@ interface ButtonWithoutIconProps extends ButtonBaseProps {
|
||||
export const Button: ParentComponent<
|
||||
ButtonWithIconProps | ButtonWithoutIconProps
|
||||
> = (props) => {
|
||||
// @ts-expect-error Expression produces a union type that is too complex to represent
|
||||
const [local, others] = splitProps(props, ['class', 'loading', 'icon'])
|
||||
|
||||
return (
|
||||
|
@ -1,6 +1,5 @@
|
||||
import type { JSX, ParentComponent } from 'solid-js'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { renderProxiesInTwoColumns } from '~/signals'
|
||||
|
||||
type Props = {
|
||||
title: JSX.Element
|
||||
@ -25,14 +24,6 @@ export const Collapse: ParentComponent<Props> = (props) => {
|
||||
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 (
|
||||
<div
|
||||
class={twMerge(
|
||||
@ -50,8 +41,7 @@ export const Collapse: ParentComponent<Props> = (props) => {
|
||||
<div
|
||||
class={twMerge(
|
||||
getCollapseContentClassName(),
|
||||
gridStyle(),
|
||||
'collapse-content grid grid-cols-2 gap-2 transition-opacity duration-1000',
|
||||
'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',
|
||||
)}
|
||||
>
|
||||
<Show when={props.isOpen}>{children(() => props.children)()}</Show>
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
@ -11,7 +11,6 @@ import {
|
||||
latencyTestTimeoutDuration,
|
||||
proxiesOrderingType,
|
||||
proxiesPreviewType,
|
||||
renderProxiesInTwoColumns,
|
||||
setAutoCloseConns,
|
||||
setHideUnAvailableProxies,
|
||||
setIconHeight,
|
||||
@ -19,7 +18,6 @@ import {
|
||||
setLatencyTestTimeoutDuration,
|
||||
setProxiesOrderingType,
|
||||
setProxiesPreviewType,
|
||||
setRenderProxiesInTwoColumns,
|
||||
setUrlForLatencyTest,
|
||||
setUrlIPv6SupportTest,
|
||||
urlForIPv6SupportTest,
|
||||
@ -107,7 +105,7 @@ export const ProxiesSettingsModal: Component<{
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<ConfigTitle withDivider>{t('hideUnAvailableProxies')}</ConfigTitle>
|
||||
<ConfigTitle withDivider>{t('hideUnavailableProxies')}</ConfigTitle>
|
||||
|
||||
<div class="flex w-full justify-center">
|
||||
<input
|
||||
@ -119,19 +117,6 @@ export const ProxiesSettingsModal: Component<{
|
||||
</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>
|
||||
<ConfigTitle withDivider>{t('proxiesPreviewType')}</ConfigTitle>
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { IconBrandSpeedtest } from '@tabler/icons-solidjs'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { Button, IPv6Support, Latency } from '~/components'
|
||||
import { Button, Latency } from '~/components'
|
||||
import { filterSpecialProxyType, formatProxyType } from '~/helpers'
|
||||
import { useProxies } from '~/signals'
|
||||
|
||||
@ -9,10 +9,19 @@ export const ProxyNodeCard = (props: {
|
||||
isSelected?: boolean
|
||||
onClick?: () => void
|
||||
}) => {
|
||||
const { proxyLatencyTest, proxyLatencyTestingMap } = useProxies()
|
||||
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 specialType = () =>
|
||||
filterSpecialProxyType(proxyNode()?.type)
|
||||
? proxyNode()?.xudp
|
||||
@ -25,55 +34,61 @@ export const ProxyNodeCard = (props: {
|
||||
return (
|
||||
<div
|
||||
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 &&
|
||||
'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',
|
||||
)}
|
||||
data-tip={proxyName}
|
||||
onClick={onClick}
|
||||
title={proxyName}
|
||||
>
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<span class="break-all text-left text-sm">{proxyName}</span>
|
||||
<div class="card-body">
|
||||
<span class="card-title truncate text-sm">{proxyName}</span>
|
||||
|
||||
<span class="flex items-center gap-1">
|
||||
<IPv6Support name={props.proxyName} />
|
||||
<Button
|
||||
class="btn-circle btn-ghost h-auto min-h-0 w-auto"
|
||||
icon={
|
||||
<IconBrandSpeedtest
|
||||
size={20}
|
||||
class={twMerge(
|
||||
proxyLatencyTestingMap()[proxyName] &&
|
||||
'animate-pulse text-success',
|
||||
)}
|
||||
/>
|
||||
}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<Show when={specialType()}>
|
||||
<div class="badge badge-primary badge-sm font-bold uppercase">
|
||||
{formatProxyType(proxyNode()?.type)}
|
||||
</div>
|
||||
|
||||
void proxyLatencyTest(proxyName, proxyNode().provider)
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<Show when={specialType()}>
|
||||
<div class="badge badge-secondary badge-sm">
|
||||
{specialType()}
|
||||
</div>
|
||||
</Show>
|
||||
</Show>
|
||||
|
||||
<div class="flex items-center justify-between gap-1">
|
||||
<div
|
||||
class={twMerge(
|
||||
'text-xs text-slate-500',
|
||||
isSelected && 'text-primary-content',
|
||||
)}
|
||||
>
|
||||
{formatProxyType(proxyNode()?.type)}
|
||||
<Show when={supportIPv6()}>
|
||||
<div class="badge badge-accent badge-sm">IPv6</div>
|
||||
</Show>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
|
||||
<Latency
|
||||
name={props.proxyName}
|
||||
class={twMerge(isSelected && 'badge badge-sm px-1')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
@ -4,7 +4,6 @@ export * from './ConfigTitle'
|
||||
export * from './ConnectionsSettingsModal'
|
||||
export * from './ConnectionsTableDetailsModal'
|
||||
export * from './Header'
|
||||
export * from './IPv6Support'
|
||||
export * from './Latency'
|
||||
export * from './LogoText'
|
||||
export * from './LogsSettingsModal'
|
||||
@ -14,5 +13,4 @@ export * from './ProxyNodeCard'
|
||||
export * from './ProxyNodePreview'
|
||||
export * from './ProxyPreviewBar'
|
||||
export * from './ProxyPreviewDots'
|
||||
export * from './RenderInTwoColumns'
|
||||
export * from './SubscriptionInfo'
|
||||
|
@ -47,15 +47,14 @@ export default {
|
||||
bar: 'Bar',
|
||||
auto: 'Auto',
|
||||
off: 'Off',
|
||||
proxiesPreviewType: 'Proxies preview type',
|
||||
urlForIPv6SupportTest: 'URL for IPv6 support test',
|
||||
urlForLatencyTest: 'URL for latency test',
|
||||
autoCloseConns: 'Automatically Close connections',
|
||||
proxiesPreviewType: 'Proxies Preview Type',
|
||||
urlForIPv6SupportTest: 'URL for IPv6 Support Test',
|
||||
urlForLatencyTest: 'URL for Latency Test',
|
||||
autoCloseConns: 'Automatically Close Connections',
|
||||
useTwemoji: 'Use Twemoji Mozilla Font',
|
||||
autoSwitchTheme: 'Automatically switch theme',
|
||||
favDayTheme: 'Favorite light theme',
|
||||
favNightTheme: 'Favorite dark theme',
|
||||
renderInTwoColumns: 'Render in two columns',
|
||||
updateGEODatabases: 'Update GEO Databases',
|
||||
restartCore: 'Restart Core',
|
||||
upgradeCore: 'Upgrade Core',
|
||||
@ -95,7 +94,7 @@ export default {
|
||||
active: 'Active',
|
||||
closed: 'Closed',
|
||||
sort: 'Sort',
|
||||
hideUnAvailableProxies: 'Hide UnAvailable Proxies',
|
||||
hideUnavailableProxies: 'Hide Unavailable Proxies',
|
||||
reloadConfig: 'Reload Config',
|
||||
flushFakeIP: 'Flush Fake-IP',
|
||||
tagClientSourceIPWithName: 'Tag Client Source IP With Name',
|
||||
|
@ -57,7 +57,6 @@ export default {
|
||||
autoSwitchTheme: '自动切换主题',
|
||||
favDayTheme: '浅色主题偏好',
|
||||
favNightTheme: '深色主题偏好',
|
||||
renderInTwoColumns: '双列渲染',
|
||||
updateGEODatabases: '更新 GEO 数据库',
|
||||
restartCore: '重启核心',
|
||||
upgradeCore: '更新核心',
|
||||
@ -97,7 +96,7 @@ export default {
|
||||
active: '活动',
|
||||
closed: '已关闭',
|
||||
sort: '排序',
|
||||
hideUnAvailableProxies: '隐藏不可用节点',
|
||||
hideUnavailableProxies: '隐藏不可用节点',
|
||||
reloadConfig: '重载配置',
|
||||
flushFakeIP: '清空 Fake-IP',
|
||||
tagClientSourceIPWithName: '为客户端源 IP 地址添加名称标记',
|
||||
|
@ -27,7 +27,6 @@ import {
|
||||
iconHeight,
|
||||
iconMarginRight,
|
||||
proxiesOrderingType,
|
||||
renderProxiesInTwoColumns,
|
||||
useConnections,
|
||||
useProxies,
|
||||
} from '~/signals'
|
||||
@ -174,14 +173,7 @@ export default () => {
|
||||
|
||||
<div class="flex-1 overflow-y-auto">
|
||||
<Show when={activeTab() === ActiveTab.proxies}>
|
||||
<div
|
||||
class={twMerge(
|
||||
'grid grid-cols-1 place-items-start gap-2',
|
||||
renderProxiesInTwoColumns()
|
||||
? 'sm:grid-cols-2'
|
||||
: 'sm:grid-cols-1',
|
||||
)}
|
||||
>
|
||||
<div class="flex flex-col gap-2">
|
||||
<For each={renderProxies()}>
|
||||
{(proxyGroup) => {
|
||||
const sortedProxyNames = createMemo(() =>
|
||||
@ -195,7 +187,7 @@ export default () => {
|
||||
)
|
||||
|
||||
const title = (
|
||||
<>
|
||||
<div class="space-y-2">
|
||||
<div class="flex items-center justify-between pr-8">
|
||||
<div class="flex items-center">
|
||||
<Show when={proxyGroup.icon}>
|
||||
@ -233,18 +225,21 @@ export default () => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between text-sm text-slate-500">
|
||||
<span>
|
||||
{proxyGroup.type}{' '}
|
||||
{proxyGroup.now?.length > 0 &&
|
||||
` :: ${proxyGroup.now}`}
|
||||
</span>
|
||||
<span>
|
||||
<div class="flex flex-wrap items-center justify-between gap-2">
|
||||
<div class="badge badge-primary badge-sm">
|
||||
<span class="font-bold">{proxyGroup.type}</span>
|
||||
|
||||
<Show when={proxyGroup.now?.length > 0}>
|
||||
<pre>{` :: ${proxyGroup.now}`}</pre>
|
||||
</Show>
|
||||
</div>
|
||||
|
||||
<div class="badge badge-secondary badge-sm">
|
||||
{byteSize(
|
||||
speedGroupByName()[proxyGroup.name] ?? 0,
|
||||
).toString()}
|
||||
/s
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Show when={!collapsedMap()[proxyGroup.name]}>
|
||||
@ -253,7 +248,7 @@ export default () => {
|
||||
now={proxyGroup.now}
|
||||
/>
|
||||
</Show>
|
||||
</>
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
@ -283,14 +278,7 @@ export default () => {
|
||||
</Show>
|
||||
|
||||
<Show when={activeTab() === ActiveTab.proxyProviders}>
|
||||
<div
|
||||
class={twMerge(
|
||||
'grid grid-cols-1 place-items-start gap-2',
|
||||
renderProxiesInTwoColumns()
|
||||
? 'sm:grid-cols-2'
|
||||
: 'sm:grid-cols-1',
|
||||
)}
|
||||
>
|
||||
<div class="flex flex-col gap-2">
|
||||
<For each={proxyProviders()}>
|
||||
{(proxyProvider) => {
|
||||
const sortedProxyNames = createMemo(() =>
|
||||
@ -305,6 +293,7 @@ export default () => {
|
||||
<div class="flex items-center justify-between pr-8">
|
||||
<div class="flex items-center gap-2">
|
||||
<span>{proxyProvider.name}</span>
|
||||
|
||||
<div class="badge badge-sm">
|
||||
{proxyProvider.proxies.length}
|
||||
</div>
|
||||
|
@ -31,12 +31,6 @@ export const [hideUnAvailableProxies, setHideUnAvailableProxies] =
|
||||
storage: localStorage,
|
||||
})
|
||||
|
||||
export const [renderProxiesInTwoColumns, setRenderProxiesInTwoColumns] =
|
||||
makePersisted(createSignal(true), {
|
||||
name: 'renderProxiesInTwoColumns',
|
||||
storage: localStorage,
|
||||
})
|
||||
|
||||
export const [urlForLatencyTest, setUrlForLatencyTest] = makePersisted(
|
||||
createSignal('https://www.gstatic.com/generate_204'),
|
||||
{ name: 'urlForLatencyTest', storage: localStorage },
|
||||
|
Loading…
x
Reference in New Issue
Block a user