refactor: use a map for update state

This commit is contained in:
Zephyruso 2023-09-05 15:38:42 +08:00
parent 6cd9ee74ba
commit c4a7163786
6 changed files with 108 additions and 77 deletions

View File

@ -1,27 +1,35 @@
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { createSignal } from 'solid-js'
import { PROXIES_ORDERING_TYPE } from '~/constants' import { PROXIES_ORDERING_TYPE } from '~/constants'
export const formatTimeFromNow = (time: number | string) => { export const formatTimeFromNow = (time: number | string) => {
return dayjs(time).fromNow() return dayjs(time).fromNow()
} }
export const useStringBooleanMap = () => {
export const handleAnimatedBtnClickWithCallback = async ( const [map, setMap] = createSignal<Record<string, boolean>>({})
event: MouseEvent, const set = (name: string, value: boolean) => {
callback: () => Promise<void>, setMap({
className = 'animate-spin', ...map(),
) => { [name]: value,
let el = event.target as HTMLElement })
event.stopPropagation()
while (el && !el.classList.contains('btn')) {
el = el.parentElement!
} }
el.classList.add(className) const setWithCallback = async (
try { name: string,
await callback() callback: () => Promise<void>,
} catch {} ) => {
el.classList.remove(className) set(name, true)
try {
await callback()
} catch {}
set(name, false)
}
return {
map,
set,
setWithCallback,
}
} }
export const formatProxyType = (type = '') => { export const formatProxyType = (type = '') => {

View File

@ -1,6 +1,7 @@
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 { createSignal, Show } from 'solid-js' import { Show } from 'solid-js'
import { twMerge } from 'tailwind-merge'
import { import {
Button, Button,
Collapse, Collapse,
@ -8,10 +9,7 @@ import {
ProxyCardGroups, ProxyCardGroups,
ProxyNodePreview, ProxyNodePreview,
} from '~/components' } from '~/components'
import { import { sortProxiesByOrderingType, useStringBooleanMap } from '~/helpers'
handleAnimatedBtnClickWithCallback,
sortProxiesByOrderingType,
} from '~/helpers'
import { import {
proxiesOrderingType, proxiesOrderingType,
renderProxiesInSamePage, renderProxiesInSamePage,
@ -29,19 +27,19 @@ export default () => {
latencyMap, latencyMap,
} = useProxies() } = useProxies()
const [collapsedMap, setCollapsedMap] = createSignal<Record<string, boolean>>( const { map: collapsedMap, set: setCollapsedMap } = useStringBooleanMap()
{}, const { map: speedTestingMap, setWithCallback: setSpeedTestingMap } =
) useStringBooleanMap()
const onProxyNodeClick = async (proxy: Proxy, proxyName: string) => { const onProxyNodeClick = async (proxy: Proxy, proxyName: string) => {
void setProxyGroupByProxyName(proxy, proxyName) void setProxyGroupByProxyName(proxy, proxyName)
} }
const onSpeedTestClick = (e: MouseEvent, name: string) => { const onSpeedTestClick = async (e: MouseEvent, name: string) => {
handleAnimatedBtnClickWithCallback( e.stopPropagation()
e, setSpeedTestingMap(
latencyTestByProxyGroupName.bind(null, name), name,
'animate-pulse', async () => await latencyTestByProxyGroupName(name),
) )
} }
@ -67,7 +65,11 @@ export default () => {
class="btn-circle btn-sm" class="btn-circle btn-sm"
onClick={(e) => onSpeedTestClick(e, proxy.name)} onClick={(e) => onSpeedTestClick(e, proxy.name)}
> >
<IconBrandSpeedtest /> <IconBrandSpeedtest
class={twMerge(
speedTestingMap()[proxy.name] && 'animate-pulse',
)}
/>
</Button> </Button>
</div> </div>
<div class="text-sm text-slate-500"> <div class="text-sm text-slate-500">
@ -98,10 +100,7 @@ export default () => {
title={title} title={title}
content={content} content={content}
onCollapse={(val) => onCollapse={(val) =>
setCollapsedMap({ setCollapsedMap(`group-${proxy.name}`, val)
...collapsedMap(),
[`group-${proxy.name}`]: val,
})
} }
/> />
) )

View File

@ -1,6 +1,7 @@
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 { twMerge } from 'tailwind-merge'
import { import {
Button, Button,
Collapse, Collapse,
@ -11,8 +12,8 @@ import {
} from '~/components' } from '~/components'
import { import {
formatTimeFromNow, formatTimeFromNow,
handleAnimatedBtnClickWithCallback,
sortProxiesByOrderingType, sortProxiesByOrderingType,
useStringBooleanMap,
} from '~/helpers' } from '~/helpers'
import { proxiesOrderingType, useProxies } from '~/signals' import { proxiesOrderingType, useProxies } from '~/signals'
@ -26,27 +27,33 @@ export default () => {
latencyMap, latencyMap,
} = useProxies() } = useProxies()
const [collapsedMap, setCollapsedMap] = createSignal<Record<string, boolean>>( const { map: collapsedMap, set: setCollapsedMap } = useStringBooleanMap()
{}, const { map: healthCheckingMap, setWithCallback: setHealthCheckingMap } =
) useStringBooleanMap()
const { map: updateingMap, setWithCallback: setUpdateingMap } =
useStringBooleanMap()
const [allProviderIsUpdating, setAllProviderIsUpdating] = createSignal(false)
const onHealthCheckClick = (e: MouseEvent, name: string) => { const onHealthCheckClick = async (e: MouseEvent, name: string) => {
handleAnimatedBtnClickWithCallback( e.stopPropagation()
e, setHealthCheckingMap(
healthCheckByProviderName.bind(null, name), name,
'animate-pulse', async () => await healthCheckByProviderName(name),
) )
} }
const onUpdateProviderClick = (e: MouseEvent, name: string) => { const onUpdateProviderClick = async (e: MouseEvent, name: string) => {
handleAnimatedBtnClickWithCallback( e.stopPropagation()
e, setUpdateingMap(name, async () => await updateProviderByProviderName(name))
updateProviderByProviderName.bind(null, name),
)
} }
const onUpdateAllProviderClick = (e: MouseEvent) => { const onUpdateAllProviderClick = async (e: MouseEvent) => {
handleAnimatedBtnClickWithCallback(e, updateAllProvider) e.stopPropagation()
setAllProviderIsUpdating(true)
try {
await updateAllProvider()
} catch {}
setAllProviderIsUpdating(false)
} }
return ( return (
@ -58,7 +65,9 @@ export default () => {
class="btn-circle btn-ghost btn-sm ml-2" class="btn-circle btn-ghost btn-sm ml-2"
onClick={(e) => onUpdateAllProviderClick(e)} onClick={(e) => onUpdateAllProviderClick(e)}
> >
<IconReload /> <IconReload
class={twMerge(allProviderIsUpdating() && 'animate-spin')}
/>
</Button> </Button>
</h1> </h1>
<ForTwoColumns <ForTwoColumns
@ -80,14 +89,23 @@ export default () => {
onUpdateProviderClick(e, proxyProvider.name) onUpdateProviderClick(e, proxyProvider.name)
} }
> >
<IconReload /> <IconReload
class={twMerge(
updateingMap()[proxyProvider.name] && 'animate-spin',
)}
/>
</Button> </Button>
<Button <Button
class="btn btn-circle btn-sm" class="btn btn-circle btn-sm"
onClick={(e) => onHealthCheckClick(e, proxyProvider.name)} onClick={(e) => onHealthCheckClick(e, proxyProvider.name)}
> >
<IconBrandSpeedtest /> <IconBrandSpeedtest
class={twMerge(
healthCheckingMap()[proxyProvider.name] &&
'animate-pulse',
)}
/>
</Button> </Button>
</div> </div>
</div> </div>
@ -112,10 +130,7 @@ export default () => {
title={title} title={title}
content={content} content={content}
onCollapse={(val) => onCollapse={(val) =>
setCollapsedMap({ setCollapsedMap(`provider-${proxyProvider.name}`, val)
...collapsedMap(),
[`provider-${proxyProvider.name}`]: val,
})
} }
/> />
) )

View File

@ -2,11 +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 { twMerge } from 'tailwind-merge'
import { Button } from '~/components' import { Button } from '~/components'
import { import { formatTimeFromNow, useStringBooleanMap } from '~/helpers'
formatTimeFromNow,
handleAnimatedBtnClickWithCallback,
} from '~/helpers'
import { useRules } from '~/signals' import { useRules } from '~/signals'
export default () => { export default () => {
@ -23,15 +21,22 @@ export default () => {
onMount(updateRules) onMount(updateRules)
const { map: updateingMap, setWithCallback: setUpdateingMap } =
useStringBooleanMap()
const [allProviderIsUpdating, setAllProviderIsUpdating] = createSignal(false)
const onUpdateProviderClick = (e: MouseEvent, name: string) => { const onUpdateProviderClick = (e: MouseEvent, name: string) => {
handleAnimatedBtnClickWithCallback( e.stopPropagation()
e, setUpdateingMap(name, async () => await updateRuleProviderByName(name))
updateRuleProviderByName.bind(null, name),
)
} }
const onUpdateAllProviderClick = (e: MouseEvent) => { const onUpdateAllProviderClick = async (e: MouseEvent) => {
handleAnimatedBtnClickWithCallback(e, updateAllRuleProvider) e.stopPropagation()
setAllProviderIsUpdating(true)
try {
await updateAllRuleProvider()
} catch {}
setAllProviderIsUpdating(false)
} }
return ( return (
@ -68,7 +73,9 @@ export default () => {
class="btn-circle btn-ghost btn-sm ml-2" class="btn-circle btn-ghost btn-sm ml-2"
onClick={(e) => onUpdateAllProviderClick(e)} onClick={(e) => onUpdateAllProviderClick(e)}
> >
<IconReload /> <IconReload
class={twMerge(allProviderIsUpdating() && 'animate-spin')}
/>
</Button> </Button>
</h1> </h1>
@ -86,7 +93,11 @@ export default () => {
onUpdateProviderClick(e, rulesProvider.name) onUpdateProviderClick(e, rulesProvider.name)
} }
> >
<IconReload /> <IconReload
class={twMerge(
updateingMap()[rulesProvider.name] && 'animate-spin',
)}
/>
</Button> </Button>
</div> </div>
</div> </div>

View File

@ -9,16 +9,14 @@ export const useRules = () => {
const [rulesProviders, setRulesProviders] = createSignal<RuleProvider[]>([]) const [rulesProviders, setRulesProviders] = createSignal<RuleProvider[]>([])
const updateRules = async () => { const updateRules = async () => {
const { rules } = await request const [{ rules }, { providers }] = await Promise.all([
.get('rules') request.get('rules').json<{ rules: Record<string, Rule> }>(),
.json<{ rules: Record<string, Rule> }>() request
.get('providers/rules')
.json<{ providers: Record<string, RuleProvider> }>(),
])
setRules(Object.values(rules)) setRules(Object.values(rules))
const { providers } = await request
.get('providers/rules')
.json<{ providers: Record<string, RuleProvider> }>()
setRulesProviders(Object.values(providers)) setRulesProviders(Object.values(providers))
} }