refactor: update project source code layout

This commit is contained in:
kunish 2023-09-03 03:26:29 +08:00
parent fec64133fa
commit 461488c19f
No known key found for this signature in database
GPG Key ID: 647A12B4F782C430
34 changed files with 318 additions and 191 deletions

View File

@ -15,9 +15,11 @@
"@felte/solid": "^1.2.11", "@felte/solid": "^1.2.11",
"@felte/validator-zod": "^1.0.17", "@felte/validator-zod": "^1.0.17",
"@fontsource/fira-sans": "^5.0.11", "@fontsource/fira-sans": "^5.0.11",
"@solid-primitives/clipboard": "^1.5.7",
"@solid-primitives/event-listener": "^2.3.0", "@solid-primitives/event-listener": "^2.3.0",
"@solid-primitives/i18n": "^1.4.1", "@solid-primitives/i18n": "^1.4.1",
"@solid-primitives/keyed": "^1.2.0", "@solid-primitives/keyed": "^1.2.0",
"@solid-primitives/media": "^2.2.5",
"@solid-primitives/storage": "^2.1.1", "@solid-primitives/storage": "^2.1.1",
"@solid-primitives/websocket": "^1.1.0", "@solid-primitives/websocket": "^1.1.0",
"@solidjs/router": "^0.8.3", "@solidjs/router": "^0.8.3",

View File

@ -17,6 +17,9 @@ dependencies:
'@fontsource/fira-sans': '@fontsource/fira-sans':
specifier: ^5.0.11 specifier: ^5.0.11
version: 5.0.11 version: 5.0.11
'@solid-primitives/clipboard':
specifier: ^1.5.7
version: 1.5.7(solid-js@1.7.11)
'@solid-primitives/event-listener': '@solid-primitives/event-listener':
specifier: ^2.3.0 specifier: ^2.3.0
version: 2.3.0(solid-js@1.7.11) version: 2.3.0(solid-js@1.7.11)
@ -26,6 +29,9 @@ dependencies:
'@solid-primitives/keyed': '@solid-primitives/keyed':
specifier: ^1.2.0 specifier: ^1.2.0
version: 1.2.0(solid-js@1.7.11) version: 1.2.0(solid-js@1.7.11)
'@solid-primitives/media':
specifier: ^2.2.5
version: 2.2.5(solid-js@1.7.11)
'@solid-primitives/storage': '@solid-primitives/storage':
specifier: ^2.1.1 specifier: ^2.1.1
version: 2.1.1(solid-js@1.7.11) version: 2.1.1(solid-js@1.7.11)
@ -2420,6 +2426,18 @@ packages:
rollup: 2.79.1 rollup: 2.79.1
dev: false dev: false
/@solid-primitives/clipboard@1.5.7(solid-js@1.7.11):
resolution:
{
integrity: sha512-MGTykW9739tMm2wS3P/Pb9W7BIZvgBLnC+Vhlx3s0DGm9CcEeXJ8lHyUp736UE57HKtCsqzfcCkpmIprbidIbQ==,
}
peerDependencies:
solid-js: ^1.6.12
dependencies:
'@solid-primitives/utils': 6.2.1(solid-js@1.7.11)
solid-js: 1.7.11
dev: false
/@solid-primitives/context@0.2.1(solid-js@1.7.11): /@solid-primitives/context@0.2.1(solid-js@1.7.11):
resolution: resolution:
{ {
@ -2466,6 +2484,45 @@ packages:
solid-js: 1.7.11 solid-js: 1.7.11
dev: false dev: false
/@solid-primitives/media@2.2.5(solid-js@1.7.11):
resolution:
{
integrity: sha512-wTESNFteSwOZsNIBPLMIVLuOHIIzt2AIZdaCYYxfsJIr/xjDqSomlmdFlAmxfJD3ondO7fwtWfc0rcmAvjoPCA==,
}
peerDependencies:
solid-js: ^1.6.12
dependencies:
'@solid-primitives/event-listener': 2.3.0(solid-js@1.7.11)
'@solid-primitives/rootless': 1.4.2(solid-js@1.7.11)
'@solid-primitives/static-store': 0.0.5(solid-js@1.7.11)
'@solid-primitives/utils': 6.2.1(solid-js@1.7.11)
solid-js: 1.7.11
dev: false
/@solid-primitives/rootless@1.4.2(solid-js@1.7.11):
resolution:
{
integrity: sha512-ynI/2aEOPyc14IKCX6yDBqnsAYCoLbaP9V/jejEWMVKOT2ZdV2ZxdftaLimOpWPpvjyti5DUJIGTOfLaNb7jlg==,
}
peerDependencies:
solid-js: ^1.6.12
dependencies:
'@solid-primitives/utils': 6.2.1(solid-js@1.7.11)
solid-js: 1.7.11
dev: false
/@solid-primitives/static-store@0.0.5(solid-js@1.7.11):
resolution:
{
integrity: sha512-ssQ+s/wrlFAEE4Zw8GV499yBfvWx7SMm+ZVc11wvao4T5xg9VfXCL9Oa+x4h+vPMvSV/Knv5LrsLiUa+wlJUXQ==,
}
peerDependencies:
solid-js: ^1.6.12
dependencies:
'@solid-primitives/utils': 6.2.1(solid-js@1.7.11)
solid-js: 1.7.11
dev: false
/@solid-primitives/storage@2.1.1(solid-js@1.7.11): /@solid-primitives/storage@2.1.1(solid-js@1.7.11):
resolution: resolution:
{ {

View File

@ -1,10 +1,14 @@
import { Navigate, Route, Routes, useNavigate } from '@solidjs/router' import { Navigate, Route, Routes, useNavigate } from '@solidjs/router'
import { Show, createEffect, lazy, onMount } from 'solid-js' import { Show, createEffect, lazy, onMount } from 'solid-js'
import { Header } from '~/components/Header' import { Header } from '~/components'
import { curTheme, endpoint, selectedEndpoint } from '~/signals' import { ROUTE } from '~/constants'
import { ROUTE } from './config/enum' import {
import { useAutoSwitchTheme } from './signals/config' curTheme,
import { useProxies } from './signals/proxies' endpoint,
selectedEndpoint,
useAutoSwitchTheme,
useProxies,
} from '~/signals'
const Setup = lazy(() => import('~/pages/Setup')) const Setup = lazy(() => import('~/pages/Setup'))
const Overview = lazy(() => import('~/pages/Overview')) const Overview = lazy(() => import('~/pages/Overview'))

View File

@ -1,6 +1,6 @@
import { JSX, ParentComponent, Show, createMemo } from 'solid-js' import { JSX, ParentComponent, Show, createMemo } from 'solid-js'
import { twMerge } from 'tailwind-merge' import { twMerge } from 'tailwind-merge'
import { renderInTwoColumn } from '~/signals/config' import { renderInTwoColumn } from '~/signals'
type Props = { type Props = {
title: JSX.Element title: JSX.Element
@ -9,7 +9,7 @@ type Props = {
onCollapse: (collapsed: boolean) => void onCollapse: (collapsed: boolean) => void
} }
const Collapse: ParentComponent<Props> = (props) => { export const Collapse: ParentComponent<Props> = (props) => {
const { title, content, onCollapse } = props const { title, content, onCollapse } = props
const getCollapseClassName = () => { const getCollapseClassName = () => {
@ -57,5 +57,3 @@ const Collapse: ParentComponent<Props> = (props) => {
</div> </div>
) )
} }
export default Collapse

View File

@ -14,17 +14,13 @@ import {
useDragDropContext, useDragDropContext,
} from '@thisbeyond/solid-dnd' } from '@thisbeyond/solid-dnd'
import { For, Show, createSignal } from 'solid-js' import { For, Show, createSignal } from 'solid-js'
import { Button } from '~/components/Button' import { Button } from '~/components'
import { import { AccessorKey, initColumnOrder, initColumnVisibility } from '~/constants'
AccessorKey,
initColumnOrder,
initColumnVisibility,
} from '~/config/enum'
type ColumnVisibility = Partial<Record<AccessorKey, boolean>> type ColumnVisibility = Partial<Record<AccessorKey, boolean>>
type ColumnOrder = AccessorKey[] type ColumnOrder = AccessorKey[]
export default (props: { export const ConnectionsModal = (props: {
order: ColumnOrder order: ColumnOrder
visible: ColumnVisibility visible: ColumnVisibility
onOrderChange: (value: ColumnOrder) => void onOrderChange: (value: ColumnOrder) => void

View File

@ -1,8 +1,8 @@
import { Show, createEffect, createMemo, createSignal } from 'solid-js' import { Show, createEffect, createMemo, createSignal } from 'solid-js'
import { DELAY } from '~/config/enum' import { DELAY } from '~/constants'
import { useProxies } from '~/signals/proxies' import { useProxies } from '~/signals'
const Delay = (props: { name?: string }) => { export const Delay = (props: { name?: string }) => {
const { delayMap } = useProxies() const { delayMap } = useProxies()
const [textClassName, setTextClassName] = createSignal('') const [textClassName, setTextClassName] = createSignal('')
const delay = createMemo(() => { const delay = createMemo(() => {
@ -29,5 +29,3 @@ const Delay = (props: { name?: string }) => {
</> </>
) )
} }
export default Delay

View File

@ -1,5 +1,5 @@
import { JSX, Show, createMemo, createSignal } from 'solid-js' import { JSX, Show, createMemo, createSignal } from 'solid-js'
import { renderInTwoColumn } from '~/signals/config' import { renderInTwoColumn } from '~/signals'
const [windowWidth, setWindowWidth] = createSignal(0) const [windowWidth, setWindowWidth] = createSignal(0)
@ -9,7 +9,7 @@ window.addEventListener('resize', () => {
setWindowWidth(document.body.clientWidth) setWindowWidth(document.body.clientWidth)
}) })
const ForTwoColumns = (props: { subChild: JSX.Element[] }) => { export const ForTwoColumns = (props: { subChild: JSX.Element[] }) => {
const isShowTwoColumns = createMemo( const isShowTwoColumns = createMemo(
() => windowWidth() >= 640 && renderInTwoColumn(), () => windowWidth() >= 640 && renderInTwoColumn(),
) // 640 is sm size in daisyui ) // 640 is sm size in daisyui
@ -29,5 +29,3 @@ const ForTwoColumns = (props: { subChild: JSX.Element[] }) => {
</div> </div>
) )
} }
export default ForTwoColumns

View File

@ -15,11 +15,9 @@ import {
} from '@tabler/icons-solidjs' } from '@tabler/icons-solidjs'
import { For, ParentComponent, Show, createMemo, createSignal } from 'solid-js' import { For, ParentComponent, Show, createMemo, createSignal } from 'solid-js'
import { twMerge } from 'tailwind-merge' import { twMerge } from 'tailwind-merge'
import { Button } from '~/components/Button' import { Button } from '~/components'
import { LANG, ROUTE } from '~/config/enum' import { LANG, ROUTE, themes } from '~/constants'
import { themes } from '~/constants' import { setCurTheme, setSelectedEndpoint, useProxies } from '~/signals'
import { setCurTheme, setSelectedEndpoint } from '~/signals'
import { useProxies } from '~/signals/proxies'
const Nav: ParentComponent<{ href: string; tooltip: string }> = ({ const Nav: ParentComponent<{ href: string; tooltip: string }> = ({
href, href,

View File

@ -2,7 +2,7 @@ import InfiniteScroll from 'solid-infinite-scroll'
import { createMemo, createSignal } from 'solid-js' import { createMemo, createSignal } from 'solid-js'
import ProxyNodeCard from './ProxyNodeCard' import ProxyNodeCard from './ProxyNodeCard'
export default (props: { export const ProxyCardGroups = (props: {
proxies: string[] proxies: string[]
now?: string now?: string
onClick?: (name: string) => void onClick?: (name: string) => void

View File

@ -1,7 +1,7 @@
import { createMemo } from 'solid-js' import { createMemo } from 'solid-js'
import { twMerge } from 'tailwind-merge' import { twMerge } from 'tailwind-merge'
import Delay from '~/components/Delay' import { Delay } from '~/components'
import { useProxies } from '~/signals/proxies' import { useProxies } from '~/signals'
export default (props: { export default (props: {
proxyName: string proxyName: string

View File

@ -1,10 +1,12 @@
import { Show, createMemo } from 'solid-js' import { Show, createMemo } from 'solid-js'
import { PROXIES_PREVIEW_TYPE } from '~/config/enum' import { ProxyPreviewBar, ProxyPreviewDots } from '~/components'
import { proxiesPreviewType } from '~/signals/config' import { PROXIES_PREVIEW_TYPE } from '~/constants'
import ProxyPreviewBar from './ProxyPreviewBar' import { proxiesPreviewType } from '~/signals'
import ProxyPreviewDots from './ProxyPreviewDots'
export default (props: { proxyNameList: string[]; now?: string }) => { export const ProxyNodePreview = (props: {
proxyNameList: string[]
now?: string
}) => {
const off = () => proxiesPreviewType() === PROXIES_PREVIEW_TYPE.OFF const off = () => proxiesPreviewType() === PROXIES_PREVIEW_TYPE.OFF
const isSmallGroup = createMemo(() => props.proxyNameList.length <= 30) const isSmallGroup = createMemo(() => props.proxyNameList.length <= 30)

View File

@ -1,9 +1,12 @@
import { createMemo } from 'solid-js' import { createMemo } from 'solid-js'
import Delay from '~/components/Delay' import { Delay } from '~/components'
import { DELAY } from '~/config/enum' import { DELAY } from '~/constants'
import { useProxies } from '~/signals/proxies' import { useProxies } from '~/signals'
export default (props: { proxyNameList: string[]; now?: string }) => { export const ProxyPreviewBar = (props: {
proxyNameList: string[]
now?: string
}) => {
const { delayMap } = useProxies() const { delayMap } = useProxies()
const delayList = createMemo(() => const delayList = createMemo(() =>
props.proxyNameList.map((i) => delayMap()[i]), props.proxyNameList.map((i) => delayMap()[i]),

View File

@ -1,7 +1,7 @@
import { For } from 'solid-js' import { For } from 'solid-js'
import { twMerge } from 'tailwind-merge' import { twMerge } from 'tailwind-merge'
import { DELAY } from '~/config/enum' import { DELAY } from '~/constants'
import { useProxies } from '~/signals/proxies' import { useProxies } from '~/signals'
const DelayDots = (p: { delay: number | undefined; selected: boolean }) => { const DelayDots = (p: { delay: number | undefined; selected: boolean }) => {
let dotClassName = p.selected let dotClassName = p.selected
@ -23,7 +23,10 @@ const DelayDots = (p: { delay: number | undefined; selected: boolean }) => {
return <div class={twMerge('m-1 h-4 w-4 rounded-full', dotClassName)}></div> return <div class={twMerge('m-1 h-4 w-4 rounded-full', dotClassName)}></div>
} }
export default (props: { proxyNameList: string[]; now?: string }) => { export const ProxyPreviewDots = (props: {
proxyNameList: string[]
now?: string
}) => {
const { delayMap } = useProxies() const { delayMap } = useProxies()
return ( return (

View File

@ -1,8 +1,8 @@
import byteSize from 'byte-size' import byteSize from 'byte-size'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import type { SubscriptionInfo } from '~/types' import type { SubscriptionInfo as ISubscriptionInfo } from '~/types'
const getSubscriptionsInfo = (subscriptionInfo: SubscriptionInfo) => { const getSubscriptionsInfo = (subscriptionInfo: ISubscriptionInfo) => {
const total = byteSize(subscriptionInfo.Total, { units: 'iec' }) const total = byteSize(subscriptionInfo.Total, { units: 'iec' })
const used = byteSize(subscriptionInfo.Download + subscriptionInfo.Upload, { const used = byteSize(subscriptionInfo.Download + subscriptionInfo.Upload, {
units: 'iec', units: 'iec',
@ -28,7 +28,9 @@ const getSubscriptionsInfo = (subscriptionInfo: SubscriptionInfo) => {
} }
} }
export default (props: { subscriptionInfo: SubscriptionInfo }) => { export const SubscriptionInfo = (props: {
subscriptionInfo: ISubscriptionInfo
}) => {
if (!props.subscriptionInfo) { if (!props.subscriptionInfo) {
return return
} }

12
src/components/index.ts Normal file
View File

@ -0,0 +1,12 @@
export * from './Button'
export * from './Collpase'
export * from './ConnectionsModal'
export * from './Delay'
export * from './ForTwoColumns'
export * from './Header'
export * from './ProxyCardGroups'
export * from './ProxyNodeCard'
export * from './ProxyNodePreview'
export * from './ProxyPreviewBar'
export * from './ProxyPreviewDots'
export * from './SubscriptionInfo'

View File

@ -1,49 +0,0 @@
export enum ROUTE {
Overview = '/overview',
Proxies = '/proxies',
Proxyprovider = '/proxyprovider',
Rules = '/rules',
Conns = '/conns',
Log = '/logs',
Config = '/config',
}
export enum AccessorKey {
Close = 'close',
ID = 'ID',
Type = 'type',
Process = 'process',
Host = 'host',
Rule = 'rules',
Chains = 'chains',
DlSpeed = 'dlSpeed',
ULSpeed = 'ulSpeed',
Download = 'dl',
Upload = 'ul',
Source = 'source',
Destination = 'destination',
}
export enum DELAY {
NOT_CONNECTED = 0,
MEDIUM = 200,
HIGH = 500,
}
export enum PROXIES_PREVIEW_TYPE {
OFF = 'off',
DOTS = 'dots',
BAR = 'bar',
Auto = 'auto',
}
export enum LANG {
EN = 'en-US',
ZH = 'zh-CN',
}
export const initColumnOrder = Object.values(AccessorKey)
export const initColumnVisibility = {
...Object.fromEntries(initColumnOrder.map((i) => [i, true])),
[AccessorKey.ID]: false,
}

View File

@ -29,3 +29,61 @@ export const themes = [
'coffee', 'coffee',
'winter', 'winter',
] ]
export enum ROUTE {
Overview = '/overview',
Proxies = '/proxies',
Proxyprovider = '/proxyprovider',
Rules = '/rules',
Conns = '/conns',
Log = '/logs',
Config = '/config',
}
export enum AccessorKey {
Close = 'close',
ID = 'ID',
Type = 'type',
Process = 'process',
Host = 'host',
Rule = 'rules',
Chains = 'chains',
DlSpeed = 'dlSpeed',
ULSpeed = 'ulSpeed',
Download = 'dl',
Upload = 'ul',
Source = 'source',
Destination = 'destination',
}
export enum DELAY {
NOT_CONNECTED = 0,
MEDIUM = 200,
HIGH = 500,
}
export enum PROXIES_PREVIEW_TYPE {
OFF = 'off',
DOTS = 'dots',
BAR = 'bar',
Auto = 'auto',
}
export enum PROXIES_SORTING_TYPE {
NATURAL = 'orderNatural',
LATENCY_ASC = 'orderLatency_asc',
LATENCY_DESC = 'orderLatency_desc',
NAME_ASC = 'orderName_asc',
NAME_DESC = 'orderName_desc',
}
export enum LANG {
EN = 'en-US',
ZH = 'zh-CN',
}
export const initColumnOrder = Object.values(AccessorKey)
export const initColumnVisibility = {
...Object.fromEntries(initColumnOrder.map((i) => [i, true])),
[AccessorKey.ID]: false,
}

1
src/helpers/index.ts Normal file
View File

@ -0,0 +1 @@
export * from './proxies'

View File

@ -3,11 +3,11 @@ import relativeTime from 'dayjs/plugin/relativeTime'
dayjs.extend(relativeTime) dayjs.extend(relativeTime)
export function formatTimeFromNow(time: number | string) { export const formatTimeFromNow = (time: number | string) => {
return dayjs(time).fromNow() return dayjs(time).fromNow()
} }
export function getBtnElFromClickEvent(event: MouseEvent) { export const getBtnElFromClickEvent = (event: MouseEvent) => {
let el = event.target as HTMLElement let el = event.target as HTMLElement
while (el && !el.classList.contains('btn')) { while (el && !el.classList.contains('btn')) {

View File

@ -1,4 +1,4 @@
import { LANG } from '~/config/enum' import { LANG } from '~/constants'
import en from './en' import en from './en'
import zh from './zh' import zh from './zh'

View File

@ -48,4 +48,10 @@ export default {
updateGEODatabases: 'Update GEO Databases', updateGEODatabases: 'Update GEO Databases',
restartCore: 'Restart Core', restartCore: 'Restart Core',
upgradeCore: 'Upgrade Core', upgradeCore: 'Upgrade Core',
proxiesSorting: 'Proxies Sorting',
orderNatural: 'Original order in config file',
orderLatency_asc: 'By latency from low to high',
orderLatency_desc: 'By latency from high to low',
orderName_asc: 'By name alphabetically (A-Z)',
orderName_desc: 'By name alphabetically (Z-A)',
} }

View File

@ -1,7 +1,7 @@
import { I18nContext, createI18nContext, useI18n } from '@solid-primitives/i18n' import { I18nContext, createI18nContext, useI18n } from '@solid-primitives/i18n'
import { makePersisted } from '@solid-primitives/storage' import { makePersisted } from '@solid-primitives/storage'
import { ParentComponent, createEffect, createSignal } from 'solid-js' import { ParentComponent, createEffect, createSignal } from 'solid-js'
import { LANG } from '~/config/enum' import { LANG } from '~/constants'
import dict from './dict' import dict from './dict'
const useLanguage = () => { const useLanguage = () => {

View File

@ -48,4 +48,10 @@ export default {
updateGEODatabases: '更新 GEO 数据库文件', updateGEODatabases: '更新 GEO 数据库文件',
restartCore: '重启核心', restartCore: '重启核心',
upgradeCore: '更新核心', upgradeCore: '更新核心',
proxiesSorting: '节点排序',
orderNatural: '原配置文件中的排序',
orderLatency_asc: '按延迟从低到高',
orderLatency_desc: '按延迟从高到低',
orderName_asc: '按名称字母排序 (A-Z)',
orderName_desc: '按名称字母排序 (Z-A)',
} }

View File

@ -3,10 +3,8 @@ import { validator } from '@felte/validator-zod'
import { useI18n } from '@solid-primitives/i18n' import { useI18n } from '@solid-primitives/i18n'
import { For, Show, createSignal, onMount } from 'solid-js' import { For, Show, createSignal, onMount } from 'solid-js'
import { z } from 'zod' import { z } from 'zod'
import { Button } from '~/components/Button' import { Button } from '~/components'
import { PROXIES_PREVIEW_TYPE } from '~/config/enum' import { PROXIES_PREVIEW_TYPE, PROXIES_SORTING_TYPE, themes } from '~/constants'
import { themes } from '~/constants'
import { useRequest } from '~/signals'
import { import {
applyThemeByMode, applyThemeByMode,
autoCloseConns, autoCloseConns,
@ -14,16 +12,19 @@ import {
favDayTheme, favDayTheme,
favNightTheme, favNightTheme,
proxiesPreviewType, proxiesPreviewType,
proxiesSortingType,
renderInTwoColumn, renderInTwoColumn,
setAutoCloseConns, setAutoCloseConns,
setAutoSwitchTheme, setAutoSwitchTheme,
setFavDayTheme, setFavDayTheme,
setFavNightTheme, setFavNightTheme,
setProxiesPreviewType, setProxiesPreviewType,
setProxiesSortingType,
setRenderInTwoColumn, setRenderInTwoColumn,
setUrlForDelayTest, setUrlForDelayTest,
urlForDelayTest, urlForDelayTest,
} from '~/signals/config' useRequest,
} from '~/signals'
import type { DNSQuery, Config as IConfig } from '~/types' import type { DNSQuery, Config as IConfig } from '~/types'
const dnsQueryFormSchema = z.object({ const dnsQueryFormSchema = z.object({
@ -261,8 +262,10 @@ const ConfigForXd = () => {
</select> </select>
</div> </div>
</Show> </Show>
<div> <div>
<div class="pb-4">{t('proxiesPreviewType')}</div> <div class="pb-4">{t('proxiesPreviewType')}</div>
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<For each={Object.values(PROXIES_PREVIEW_TYPE)}> <For each={Object.values(PROXIES_PREVIEW_TYPE)}>
{(value) => ( {(value) => (
@ -282,6 +285,28 @@ const ConfigForXd = () => {
</div> </div>
</div> </div>
<div>
<div class="pb-4">{t('proxiesSorting')}</div>
<div class="flex flex-col gap-4">
<For each={Object.values(PROXIES_SORTING_TYPE)}>
{(value) => (
<label class="flex items-center gap-2">
<span>{t(value)}</span>
<input
class="radio"
aria-label={value}
type="radio"
checked={value === proxiesSortingType()}
onChange={() => setProxiesSortingType(value)}
/>
</label>
)}
</For>
</div>
</div>
<div> <div>
<div class="pb-4">{t('autoCloseConns')}</div> <div class="pb-4">{t('autoCloseConns')}</div>

View File

@ -20,13 +20,8 @@ import byteSize from 'byte-size'
import { isIPv6 } from 'is-ip' import { isIPv6 } from 'is-ip'
import { For, createEffect, createSignal } from 'solid-js' import { For, createEffect, createSignal } from 'solid-js'
import { twMerge } from 'tailwind-merge' import { twMerge } from 'tailwind-merge'
import { Button } from '~/components/Button' import { Button, ConnectionsModal } from '~/components'
import ConnectionsModal from '~/components/ConnectionsModal' import { AccessorKey, initColumnOrder, initColumnVisibility } from '~/constants'
import {
AccessorKey,
initColumnOrder,
initColumnVisibility,
} from '~/config/enum'
import { secret, useRequest, wsEndpointURL } from '~/signals' import { secret, useRequest, wsEndpointURL } from '~/signals'
import type { Connection } from '~/types' import type { Connection } from '~/types'

View File

@ -1,14 +1,16 @@
import { useI18n } from '@solid-primitives/i18n' import { useI18n } from '@solid-primitives/i18n'
import { IconBrandSpeedtest } from '@tabler/icons-solidjs' import { IconBrandSpeedtest } from '@tabler/icons-solidjs'
import { Show, createSignal } from 'solid-js' import { Show, createSignal } from 'solid-js'
import { Button } from '~/components/Button' import {
import Collapse from '~/components/Collpase' Button,
import ForTwoColumns from '~/components/ForTwoColumns' Collapse,
import ProxyCardGroups from '~/components/ProxyCardGroups' ForTwoColumns,
import ProxyNodePreview from '~/components/ProxyNodePreview' ProxyCardGroups,
import { useProxies } from '~/signals/proxies' ProxyNodePreview,
} from '~/components'
import { getBtnElFromClickEvent } from '~/helpers'
import { useProxies } from '~/signals'
import type { Proxy } from '~/types' import type { Proxy } from '~/types'
import { getBtnElFromClickEvent } from '~/utils/proxies'
export default () => { export default () => {
const [t] = useI18n() const [t] = useI18n()

View File

@ -1,14 +1,16 @@
import { useI18n } from '@solid-primitives/i18n' import { useI18n } from '@solid-primitives/i18n'
import { IconBrandSpeedtest, IconReload } from '@tabler/icons-solidjs' import { IconBrandSpeedtest, IconReload } from '@tabler/icons-solidjs'
import { Show, createSignal } from 'solid-js' import { Show, createSignal } from 'solid-js'
import { Button } from '~/components/Button' import {
import Collapse from '~/components/Collpase' Button,
import ForTwoColumns from '~/components/ForTwoColumns' Collapse,
import ProxyCardGroups from '~/components/ProxyCardGroups' ForTwoColumns,
import ProxyNodePreview from '~/components/ProxyNodePreview' ProxyCardGroups,
import SubscriptionInfo from '~/components/SubscriptionInfo' ProxyNodePreview,
import { useProxies } from '~/signals/proxies' SubscriptionInfo,
import { formatTimeFromNow, getBtnElFromClickEvent } from '~/utils/proxies' } from '~/components'
import { formatTimeFromNow, getBtnElFromClickEvent } from '~/helpers'
import { useProxies } from '~/signals'
export default () => { export default () => {
const [t] = useI18n() const [t] = useI18n()

View File

@ -2,9 +2,9 @@ import { useI18n } from '@solid-primitives/i18n'
import { IconReload } from '@tabler/icons-solidjs' import { IconReload } from '@tabler/icons-solidjs'
import InfiniteScroll from 'solid-infinite-scroll' import InfiniteScroll from 'solid-infinite-scroll'
import { For, Show, createMemo, createSignal, onMount } from 'solid-js' import { For, Show, createMemo, createSignal, onMount } from 'solid-js'
import { Button } from '~/components/Button' import { Button } from '~/components'
import { useRules } from '~/signals/rules' import { formatTimeFromNow, getBtnElFromClickEvent } from '~/helpers'
import { formatTimeFromNow, getBtnElFromClickEvent } from '~/utils/proxies' import { useRules } from '~/signals'
export default () => { export default () => {
const [t] = useI18n() const [t] = useI18n()

View File

@ -7,7 +7,7 @@ import ky from 'ky'
import { For, onMount } from 'solid-js' import { For, onMount } from 'solid-js'
import { v4 as uuid } from 'uuid' import { v4 as uuid } from 'uuid'
import { z } from 'zod' import { z } from 'zod'
import { Button } from '~/components/Button' import { Button } from '~/components'
import { endpointList, setEndpointList, setSelectedEndpoint } from '~/signals' import { endpointList, setEndpointList, setSelectedEndpoint } from '~/signals'
const schema = z.object({ const schema = z.object({

View File

@ -1,12 +1,16 @@
import { makePersisted } from '@solid-primitives/storage' import { makePersisted } from '@solid-primitives/storage'
import { createSignal } from 'solid-js' import { createSignal } from 'solid-js'
import { PROXIES_PREVIEW_TYPE } from '~/config/enum' import { PROXIES_PREVIEW_TYPE, PROXIES_SORTING_TYPE } from '~/constants'
import { setCurTheme } from '~/signals' import { setCurTheme } from '~/signals'
export const [proxiesPreviewType, setProxiesPreviewType] = makePersisted( export const [proxiesPreviewType, setProxiesPreviewType] = makePersisted(
createSignal(PROXIES_PREVIEW_TYPE.BAR), createSignal(PROXIES_PREVIEW_TYPE.Auto),
{ name: 'proxiesPreviewType', storage: localStorage }, { name: 'proxiesPreviewType', storage: localStorage },
) )
export const [proxiesSortingType, setProxiesSortingType] = makePersisted(
createSignal(PROXIES_SORTING_TYPE.NATURAL),
{ name: 'proxiesSortingType', storage: localStorage },
)
export const [urlForDelayTest, setUrlForDelayTest] = makePersisted( export const [urlForDelayTest, setUrlForDelayTest] = makePersisted(
createSignal('https://www.gstatic.com/generate_204'), createSignal('https://www.gstatic.com/generate_204'),
{ name: 'urlForDelayTest', storage: localStorage }, { name: 'urlForDelayTest', storage: localStorage },
@ -42,15 +46,13 @@ const setTheme = (isDark: boolean) => {
} }
} }
export function applyThemeByMode() { export const applyThemeByMode = () =>
setTheme(window.matchMedia('(prefers-color-scheme: dark)').matches) setTheme(window.matchMedia('(prefers-color-scheme: dark)').matches)
}
export function useAutoSwitchTheme() { export const useAutoSwitchTheme = () => {
applyThemeByMode() applyThemeByMode()
window window
.matchMedia('(prefers-color-scheme: dark)') .matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', (event) => { .addEventListener('change', (event) => setTheme(event.matches))
setTheme(event.matches)
})
} }

View File

@ -1,46 +1,5 @@
import { makePersisted } from '@solid-primitives/storage' export * from './config'
import ky from 'ky' export * from './proxies'
import { createSignal } from 'solid-js' export * from './request'
import { themes } from '~/constants' export * from './rules'
export * from './theme'
export const useRequest = () => {
const e = endpoint()
return ky.create({
prefixUrl: e?.url,
headers: {
Authorization: e?.secret ? `Bearer ${e.secret}` : '',
},
})
}
export const [selectedEndpoint, setSelectedEndpoint] = makePersisted(
createSignal(''),
{
name: 'selectedEndpoint',
storage: localStorage,
},
)
export const [endpointList, setEndpointList] = makePersisted(
createSignal<
{
id: string
url: string
secret: string
}[]
>([]),
{ name: 'endpointList', storage: localStorage },
)
export const [curTheme, setCurTheme] = makePersisted(
createSignal<(typeof themes)[number]>('halloween'),
{ name: 'theme', storage: localStorage },
)
export const endpoint = () =>
endpointList().find(({ id }) => id === selectedEndpoint())
export const secret = () => endpoint()?.secret
export const wsEndpointURL = () => endpoint()?.url.replace('http', 'ws')

View File

@ -1,6 +1,5 @@
import { createSignal } from 'solid-js' import { createSignal } from 'solid-js'
import { useRequest } from '~/signals' import { autoCloseConns, urlForDelayTest, useRequest } from '~/signals'
import { autoCloseConns, urlForDelayTest } from '~/signals/config'
import type { Proxy, ProxyNode, ProxyProvider } from '~/types' import type { Proxy, ProxyNode, ProxyProvider } from '~/types'
type ProxyInfo = { type ProxyInfo = {
@ -17,8 +16,9 @@ const [proxyNodeMap, setProxyNodeMap] = createSignal<Record<string, ProxyInfo>>(
{}, {},
) )
export function useProxies() { export const useProxies = () => {
const request = useRequest() const request = useRequest()
const setProxyInfoByProixes = (proxies: Proxy[] | ProxyNode[]) => { const setProxyInfoByProixes = (proxies: Proxy[] | ProxyNode[]) => {
proxies.forEach((proxy) => { proxies.forEach((proxy) => {
const delay = proxy.history.at(-1)?.delay ?? 0 const delay = proxy.history.at(-1)?.delay ?? 0
@ -85,7 +85,6 @@ export function useProxies() {
proxyGroup.now = proxyName proxyGroup.now = proxyName
setProxies(proxyGroupList) setProxies(proxyGroupList)
// queueMicrotask(updateProxy)
} }
const delayTestByProxyGroupName = async (proxyGroupName: string) => { const delayTestByProxyGroupName = async (proxyGroupName: string) => {
@ -122,7 +121,7 @@ export function useProxies() {
const healthCheckByProviderName = async (providerName: string) => { const healthCheckByProviderName = async (providerName: string) => {
await request.get(`providers/proxies/${providerName}/healthcheck`, { await request.get(`providers/proxies/${providerName}/healthcheck`, {
timeout: 30 * 1000, // thie api was a little bit slow sometimes... timeout: 30 * 1000, // thie api is a little bit slow sometimes...
}) })
await updateProxy() await updateProxy()
} }

40
src/signals/request.ts Normal file
View File

@ -0,0 +1,40 @@
import { makePersisted } from '@solid-primitives/storage'
import ky from 'ky'
import { createSignal } from 'solid-js'
export const [selectedEndpoint, setSelectedEndpoint] = makePersisted(
createSignal(''),
{
name: 'selectedEndpoint',
storage: localStorage,
},
)
export const [endpointList, setEndpointList] = makePersisted(
createSignal<
{
id: string
url: string
secret: string
}[]
>([]),
{ name: 'endpointList', storage: localStorage },
)
export const useRequest = () => {
const e = endpoint()
return ky.create({
prefixUrl: e?.url,
headers: {
Authorization: e?.secret ? `Bearer ${e.secret}` : '',
},
})
}
export const endpoint = () =>
endpointList().find(({ id }) => id === selectedEndpoint())
export const secret = () => endpoint()?.secret
export const wsEndpointURL = () => endpoint()?.url.replace('http', 'ws')

8
src/signals/theme.ts Normal file
View File

@ -0,0 +1,8 @@
import { makePersisted } from '@solid-primitives/storage'
import { createSignal } from 'solid-js'
import { themes } from '~/constants'
export const [curTheme, setCurTheme] = makePersisted(
createSignal<(typeof themes)[number]>('halloween'),
{ name: 'theme', storage: localStorage },
)