diff --git a/src/helpers/proxies.ts b/src/helpers/proxies.ts index cf07a97..c075505 100644 --- a/src/helpers/proxies.ts +++ b/src/helpers/proxies.ts @@ -9,12 +9,8 @@ export const formatTimeFromNow = (time: number | string) => { export const formatProxyType = (type = '') => { const t = type.toLowerCase() - if (t === 'shadowsocks') { - return 'ss' - } - - if (t === 'shadowsocksr') { - return 'ssr' + if (t.includes('shadowsocks')) { + return type.replace('shadowsocks', 'ss') // for both ss and ssr } if (t === 'hysteria') { diff --git a/src/i18n/en.ts b/src/i18n/en.ts index 3bc74e0..64e17be 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -42,7 +42,7 @@ export default { off: 'Off', proxiesPreviewType: 'Proxies preview type', urlForLatencyTest: 'URL for latency test', - autoCloseConns: 'Automatically close all connections', + autoCloseConns: 'Automatically close connections when proxy is selected', useTwemoji: 'Use Twemoji Mozilla Font', autoSwitchTheme: 'Automatically switch theme', favDayTheme: 'Favorite light theme', diff --git a/src/i18n/zh.ts b/src/i18n/zh.ts index db49aff..5348546 100644 --- a/src/i18n/zh.ts +++ b/src/i18n/zh.ts @@ -42,7 +42,7 @@ export default { off: '关闭', proxiesPreviewType: '节点组预览样式', urlForLatencyTest: '测速链接', - autoCloseConns: '切换代理时自动断开全部连接', + autoCloseConns: '切换代理时自动断开连接', useTwemoji: '使用 Twemoji Mozilla 字体', autoSwitchTheme: '自动切换主题', favDayTheme: '浅色主题偏好', diff --git a/src/pages/Connections.tsx b/src/pages/Connections.tsx index 5e9d07d..4d733ea 100644 --- a/src/pages/Connections.tsx +++ b/src/pages/Connections.tsx @@ -25,8 +25,7 @@ import { } from '@tanstack/solid-table' import byteSize from 'byte-size' import dayjs from 'dayjs' -import { differenceWith } from 'lodash' -import { For, createEffect, createMemo, createSignal } from 'solid-js' +import { For, createMemo, createSignal } from 'solid-js' import { twMerge } from 'tailwind-merge' import { Button, ConnectionsTableOrderingModal } from '~/components' import { @@ -38,8 +37,8 @@ import { formatTimeFromNow } from '~/helpers' import { tableSize, tableSizeClassName, + useConnections, useRequest, - useWsRequest, } from '~/signals' import type { Connection } from '~/types' @@ -62,63 +61,12 @@ export default () => { const [search, setSearch] = createSignal('') const [activeTab, setActiveTab] = createSignal(ActiveTab.activeConnections) - - const connections = useWsRequest<{ connections: Connection[] }>('connections') - - const [closedConnectionsWithSpeed, setClosedConnectionsWithSpeed] = - createSignal([], { equals: () => paused() }) - - const [activeConnectionsWithSpeed, setActiveConnectionsWithSpeed] = - createSignal([], { equals: () => paused() }) - - const [paused, setPaused] = createSignal(false) - - const updateConnectionsWithSpeed = - (connections: Connection[]) => (prevConnections: ConnectionWithSpeed[]) => { - const prevMap = new Map() - prevConnections.forEach((prev) => prevMap.set(prev.id, prev)) - - const connectionWithSpeed = connections.map((connection) => { - const prevConn = prevMap.get(connection.id) - - if (!prevConn) { - return { ...connection, downloadSpeed: 0, uploadSpeed: 0 } - } - - return { - ...connection, - downloadSpeed: - connection.download - (prevConn.download ?? connection.download), - uploadSpeed: - connection.upload - (prevConn.upload ?? connection.upload), - } - }) - - const closedConnections = differenceWith( - prevConnections, - connectionWithSpeed, - (a, b) => a.id === b.id, - ) - - setClosedConnectionsWithSpeed((prev) => - [...prev, ...closedConnections].slice(-1000), - ) - - return connectionWithSpeed.slice(-200) - } - - createEffect(() => { - const connection = connections()?.connections - - if (!connection) { - return - } - - const updater = updateConnectionsWithSpeed(connection) - - setActiveConnectionsWithSpeed(updater) - }) - + const { + activeConnectionsWithSpeed, + closedConnectionsWithSpeed, + paused, + setPaused, + } = useConnections() const onCloseConnection = (id: string) => request.delete(`connections/${id}`) const [columnVisibility, setColumnVisibility] = makePersisted( diff --git a/src/pages/Overview.tsx b/src/pages/Overview.tsx index d0e5ef7..148c556 100644 --- a/src/pages/Overview.tsx +++ b/src/pages/Overview.tsx @@ -13,8 +13,7 @@ import { createSignal, } from 'solid-js' import { CHART_MAX_XAXIS, DEFAULT_CHART_OPTIONS } from '~/constants' -import { useWsRequest } from '~/signals' -import type { Connection } from '~/types' +import { connections, useWsRequest } from '~/signals' const TrafficWidget: ParentComponent<{ label: JSX.Element }> = (props) => (
@@ -84,12 +83,6 @@ export default () => { { name: t('memory'), data: memories() }, ]) - const connection = useWsRequest<{ - downloadTotal: number - uploadTotal: number - connections: Connection[] - }>('connections') - return (
@@ -102,15 +95,15 @@ export default () => { - {byteSize(connection()?.uploadTotal || 0).toString()} + {byteSize(connections()?.uploadTotal || 0).toString()} - {byteSize(connection()?.downloadTotal || 0).toString()} + {byteSize(connections()?.downloadTotal || 0).toString()} - {connection()?.connections.length || 0} + {connections()?.connections.length || 0} diff --git a/src/signals/connections.ts b/src/signals/connections.ts new file mode 100644 index 0000000..6b538f2 --- /dev/null +++ b/src/signals/connections.ts @@ -0,0 +1,113 @@ +import { differenceWith, unionWith } from 'lodash' +import { + createEffect, + createMemo, + createResource, + createSignal, + untrack, +} from 'solid-js' +import { Connection, ConnectionWithSpeed } from '~/types' +import { selectedEndpoint, useWsRequest } from './request' + +type WsMsg = { + connections: Connection[] + uploadTotal: number + downloadTotal: number +} | null + +// we make allconnections global so we can keep track of connections when user in proxy page +// when user selects proxy and close some connections they can back and check connections +// they closed +const [allConnectionsWithSpeed, setAllConnectionsWithSpeed] = createSignal< + ConnectionWithSpeed[] +>([]) + +const [latestConnectionWsMessage] = createResource(async () => { + await new Promise((resolve) => { + createEffect(() => { + if (selectedEndpoint()) { + resolve() + } + }) + }) + + return useWsRequest('connections') +}) + +export const connections = createMemo(() => latestConnectionWsMessage()?.()) + +export const useConnections = () => { + const [closedConnectionsWithSpeed, setClosedConnectionsWithSpeed] = + createSignal([]) + const [activeConnectionsWithSpeed, setActiveConnectionsWithSpeed] = + createSignal([]) + const [paused, setPaused] = createSignal(false) + + const updateConnectionsWithSpeed = (connections: Connection[]) => { + const prevActiveConnections = activeConnectionsWithSpeed() + const prevMap = new Map() + prevActiveConnections.forEach((prev) => prevMap.set(prev.id, prev)) + + const activeConnnections: ConnectionWithSpeed[] = connections.map( + (connection) => { + const prevConn = prevMap.get(connection.id) + + if (!prevConn) { + return { ...connection, downloadSpeed: 0, uploadSpeed: 0 } + } + + return { + ...connection, + downloadSpeed: + connection.download - (prevConn.download ?? connection.download), + uploadSpeed: + connection.upload - (prevConn.upload ?? connection.upload), + } + }, + ) + + const allConnections = unionWith( + allConnectionsWithSpeed(), + activeConnnections, + (a, b) => a.id === b.id, + ) + const closedConnections = differenceWith( + allConnections, + activeConnnections, + (a, b) => a.id === b.id, + ) + + return { + activeConns: activeConnnections.slice(-200), + closedConns: closedConnections.slice(-200), + allConns: allConnections.slice(-400), + } + } + + createEffect(() => { + const connection = connections()?.connections + + if (!connection) { + return + } + + untrack(() => { + const { activeConns, closedConns, allConns } = + updateConnectionsWithSpeed(connection) + + if (!paused()) { + setActiveConnectionsWithSpeed(activeConns) + setClosedConnectionsWithSpeed(closedConns) + } + + setAllConnectionsWithSpeed(allConns) + }) + }) + + return { + closedConnectionsWithSpeed, + activeConnectionsWithSpeed, + paused, + setPaused, + } +} diff --git a/src/signals/index.ts b/src/signals/index.ts index 12bcf39..2e37a89 100644 --- a/src/signals/index.ts +++ b/src/signals/index.ts @@ -1,4 +1,5 @@ export * from './config' +export * from './connections' export * from './proxies' export * from './request' export * from './rules' diff --git a/src/signals/proxies.ts b/src/signals/proxies.ts index 157d595..085002a 100644 --- a/src/signals/proxies.ts +++ b/src/signals/proxies.ts @@ -6,6 +6,7 @@ import { useRequest, } from '~/signals' import type { Proxy, ProxyNode, ProxyProvider } from '~/types' +import { useConnections } from './connections' type ProxyInfo = { name: string @@ -46,6 +47,7 @@ const setProxiesInfo = (proxies: (Proxy | ProxyNode)[]) => { export const useProxies = () => { const request = useRequest() + const { activeConnectionsWithSpeed } = useConnections() const updateProxies = async () => { const [{ providers }, { proxies }] = await Promise.all([ @@ -80,7 +82,13 @@ export const useProxies = () => { const proxyGroupList = proxies().slice() const proxyGroup = proxyGroupList.find((i) => i.name === proxy.name)! - if (autoCloseConns()) request.delete('connections') + if (autoCloseConns()) { + activeConnectionsWithSpeed().forEach(({ id, chains }) => { + if (chains.includes(proxy.name)) { + request.delete(`connections/${id}`) + } + }) + } await request.put(`proxies/${proxy.name}`, { body: JSON.stringify({ diff --git a/src/types/index.d.ts b/src/types/index.d.ts index bda0cf1..24fac7f 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -100,6 +100,11 @@ export type Connection = { } } +export type ConnectionWithSpeed = Connection & { + downloadSpeed: number + uploadSpeed: number +} + export type Log = { type: string payload: string