feat: quick filter for connections (#856)

* feat: quick filter for connections

* feat: make persisted for collapse map status

* fix: config error when tun is disabled in sing-box
This commit is contained in:
YetAnotherZephyruso 2024-07-24 20:40:21 +08:00 committed by GitHub
parent b419bed6b1
commit 916a661011
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 81 additions and 16 deletions

View File

@ -31,8 +31,10 @@ import {
allConnections, allConnections,
clientSourceIPTags, clientSourceIPTags,
connectionsTableSize, connectionsTableSize,
quickFilterRegex,
setClientSourceIPTags, setClientSourceIPTags,
setConnectionsTableSize, setConnectionsTableSize,
setQuickFilterRegex,
} from '~/signals' } from '~/signals'
import { import {
ConnectionsTableColumnOrder, ConnectionsTableColumnOrder,
@ -205,6 +207,16 @@ export const ConnectionsSettingsModal = (props: {
} }
> >
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<div>
<ConfigTitle withDivider>{t('quickFilter')}</ConfigTitle>
<input
type="text"
class="input input-bordered w-full"
onInput={(e) => setQuickFilterRegex(e.target.value)}
value={quickFilterRegex()}
></input>
</div>
<div> <div>
<ConfigTitle withDivider>{t('tableSize')}</ConfigTitle> <ConfigTitle withDivider>{t('tableSize')}</ConfigTitle>

View File

@ -112,4 +112,5 @@ export default {
en: 'English', en: 'English',
zh: 'Chinese', zh: 'Chinese',
port: '{{ name }} Port', port: '{{ name }} Port',
quickFilter: 'Quick Filter',
} }

View File

@ -114,4 +114,5 @@ export default {
en: '英文', en: '英文',
zh: '中文', zh: '中文',
port: '{{ name }} 端口', port: '{{ name }} 端口',
quickFilter: '快速过滤',
} satisfies Dict } satisfies Dict

View File

@ -244,7 +244,7 @@ const ConfigForm: Component<{ backendVersion: Accessor<string> }> = ({
id="enable-tun-device" id="enable-tun-device"
type="checkbox" type="checkbox"
class="toggle" class="toggle"
checked={configsData()?.tun.enable} checked={configsData()?.tun?.enable}
onChange={(e) => onChange={(e) =>
void updateBackendConfigAPI( void updateBackendConfigAPI(
'tun', 'tun',
@ -263,7 +263,7 @@ const ConfigForm: Component<{ backendVersion: Accessor<string> }> = ({
<select <select
id="tun-ip-stack" id="tun-ip-stack"
class="select select-bordered flex-1" class="select select-bordered flex-1"
value={configsData()?.tun.stack} value={configsData()?.tun?.stack}
onChange={(e) => onChange={(e) =>
void updateBackendConfigAPI( void updateBackendConfigAPI(
'tun', 'tun',
@ -287,7 +287,7 @@ const ConfigForm: Component<{ backendVersion: Accessor<string> }> = ({
<input <input
id="device-name" id="device-name"
class="input input-bordered min-w-0" class="input input-bordered min-w-0"
value={configsData()?.tun.device} value={configsData()?.tun?.device}
onChange={(e) => onChange={(e) =>
void updateBackendConfigAPI( void updateBackendConfigAPI(
'tun', 'tun',

View File

@ -45,6 +45,7 @@ import {
connectionsTableSize, connectionsTableSize,
endpoint, endpoint,
formatTimeFromNow, formatTimeFromNow,
quickFilterRegex,
setConnectionsTableColumnOrder, setConnectionsTableColumnOrder,
setConnectionsTableColumnVisibility, setConnectionsTableColumnVisibility,
tableSizeClassName, tableSizeClassName,
@ -89,7 +90,13 @@ export default () => {
useConnections() useConnections()
const [globalFilter, setGlobalFilter] = createSignal('') const [globalFilter, setGlobalFilter] = createSignal('')
const [enableQuickFilter, setEnableQuickFilter] = makePersisted(
createSignal(false),
{
name: 'enableQuickFilter',
storage: localStorage,
},
)
const [selectedConnectionID, setSelectedConnectionID] = createSignal<string>() const [selectedConnectionID, setSelectedConnectionID] = createSignal<string>()
const columns: ColumnDef<Connection>[] = [ const columns: ColumnDef<Connection>[] = [
@ -283,9 +290,24 @@ export default () => {
}, },
}, },
get data() { get data() {
return activeTab() === ActiveTab.activeConnections const connections =
? activeConnections() activeTab() === ActiveTab.activeConnections
: closedConnections() ? activeConnections()
: closedConnections()
connections.sort((a, b) => {
return a.id.localeCompare(b.id)
})
if (!enableQuickFilter()) {
return connections
}
const reg = new RegExp(quickFilterRegex(), 'i')
return connections.filter(
(connection) => !reg.test(connection.chains.join('')),
)
}, },
sortDescFirst: true, sortDescFirst: true,
enableHiding: true, enableHiding: true,
@ -353,6 +375,16 @@ export default () => {
</Index> </Index>
</div> </div>
<div class="flex items-center">
<span class="mr-2 hidden lg:inline-block">{t('quickFilter')}:</span>
<input
type="checkbox"
class="toggle"
checked={enableQuickFilter()}
onChange={(e) => setEnableQuickFilter(e.target.checked)}
/>
</div>
<select <select
class="select select-bordered select-primary select-sm w-full max-w-full flex-1 sm:select-md" class="select select-bordered select-primary select-sm w-full max-w-full flex-1 sm:select-md"
onChange={(e) => setSourceIPFilter(e.target.value)} onChange={(e) => setSourceIPFilter(e.target.value)}

View File

@ -87,8 +87,8 @@ export default () => {
]) ])
return ( return (
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2 lg:h-full">
<div class="stats stats-vertical w-full grid-cols-2 bg-primary shadow lg:stats-horizontal lg:flex"> <div class="stats stats-vertical w-full flex-shrink-0 grid-cols-2 bg-gradient-to-br from-primary to-secondary shadow lg:stats-horizontal lg:flex">
<TrafficWidget label={t('upload')}> <TrafficWidget label={t('upload')}>
{byteSize(traffic()?.up || 0).toString()}/s {byteSize(traffic()?.up || 0).toString()}/s
</TrafficWidget> </TrafficWidget>

View File

@ -1,3 +1,4 @@
import { makePersisted } from '@solid-primitives/storage'
import { import {
IconBrandSpeedtest, IconBrandSpeedtest,
IconReload, IconReload,
@ -55,14 +56,24 @@ export default () => {
updateAllProvider, updateAllProvider,
proxyGroupLatencyTest, proxyGroupLatencyTest,
proxyProviderLatencyTest, proxyProviderLatencyTest,
collapsedMap,
setCollapsedMap,
proxyGroupLatencyTestingMap, proxyGroupLatencyTestingMap,
proxyProviderLatencyTestingMap, proxyProviderLatencyTestingMap,
isAllProviderUpdating, isAllProviderUpdating,
updatingMap, updatingMap,
} = useProxies() } = useProxies()
const [collapsedMap, setCollapsedMap] = makePersisted(
createSignal<Record<string, boolean>>({}),
{
name: 'collapsedMap',
storage: localStorage,
},
)
const setCollapsedMapByKey = (key: string, value: boolean) => {
setCollapsedMap((prev) => ({ ...prev, [key]: value }))
}
onMount(fetchProxies) onMount(fetchProxies)
const onProxyGroupLatencyTestClick = async ( const onProxyGroupLatencyTestClick = async (
@ -221,7 +232,9 @@ export default () => {
<Collapse <Collapse
isOpen={collapsedMap()[proxyGroup.name]} isOpen={collapsedMap()[proxyGroup.name]}
title={title} title={title}
onCollapse={(val) => setCollapsedMap(proxyGroup.name, val)} onCollapse={(val) =>
setCollapsedMapByKey(proxyGroup.name, val)
}
> >
<For each={sortedProxyNames()}> <For each={sortedProxyNames()}>
{(proxyName) => ( {(proxyName) => (
@ -330,7 +343,7 @@ export default () => {
isOpen={collapsedMap()[proxyProvider.name]} isOpen={collapsedMap()[proxyProvider.name]}
title={title} title={title}
onCollapse={(val) => onCollapse={(val) =>
setCollapsedMap(proxyProvider.name, val) setCollapsedMapByKey(proxyProvider.name, val)
} }
> >
<For each={sortedProxyNames()}> <For each={sortedProxyNames()}>

View File

@ -1,3 +1,4 @@
import { makePersisted } from '@solid-primitives/storage'
import { differenceWith, isNumber, unionWith } from 'lodash' import { differenceWith, isNumber, unionWith } from 'lodash'
import { CONNECTIONS_TABLE_MAX_CLOSED_ROWS } from '~/constants' import { CONNECTIONS_TABLE_MAX_CLOSED_ROWS } from '~/constants'
import { Connection, ConnectionRawMessage } from '~/types' import { Connection, ConnectionRawMessage } from '~/types'
@ -8,6 +9,14 @@ export type WsMsg = {
downloadTotal: number downloadTotal: number
} | null } | null
export const [quickFilterRegex, setQuickFilterRegex] = makePersisted(
createSignal<string>('Direct|direct|dns-out'),
{
name: 'quickFilterRegex',
storage: localStorage,
},
)
// we make connections global, so we can keep track of connections when user in proxy page // we make connections 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 // when user selects proxy and close some connections they can back and check connections
// they closed // they closed

View File

@ -32,7 +32,6 @@ type ProxyInfo = {
export type ProxyWithProvider = Proxy & { provider?: string } export type ProxyWithProvider = Proxy & { provider?: string }
export type ProxyNodeWithProvider = ProxyNode & { provider?: string } export type ProxyNodeWithProvider = ProxyNode & { provider?: string }
const { map: collapsedMap, set: setCollapsedMap } = useStringBooleanMap()
const { const {
map: proxyLatencyTestingMap, map: proxyLatencyTestingMap,
setWithCallback: setProxyLatencyTestingMap, setWithCallback: setProxyLatencyTestingMap,
@ -322,8 +321,6 @@ export const useProxies = () => {
}) })
return { return {
collapsedMap,
setCollapsedMap,
proxyIPv6SupportMap, proxyIPv6SupportMap,
proxyLatencyTestingMap, proxyLatencyTestingMap,
proxyGroupLatencyTestingMap, proxyGroupLatencyTestingMap,