Skip to content

Commit

Permalink
feat: draggable & editable source ip label
Browse files Browse the repository at this point in the history
  • Loading branch information
Zephyruso committed Feb 13, 2025
1 parent bbbf52f commit 36d19a9
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 66 deletions.
24 changes: 12 additions & 12 deletions src/components/settings/ProxiesSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,6 @@
:clearable="true"
/>
</div>
<div class="flex w-full items-center gap-2">
<span> {{ $t('independentLatencyTest') }} </span>
<input
class="toggle"
type="checkbox"
v-model="independentLatencyTest"
/>
<QuestionMarkCircleIcon
class="h-4 w-4"
@mouseenter="independentLatencyTestTip"
/>
</div>
<div class="flex w-full items-center gap-2">
<span> {{ $t('speedtestTimeout') }} </span>
<input
Expand Down Expand Up @@ -52,6 +40,18 @@
/>
ms
</div>
<div class="flex w-full items-center gap-2">
<span> {{ $t('independentLatencyTest') }} </span>
<input
class="toggle"
type="checkbox"
v-model="independentLatencyTest"
/>
<QuestionMarkCircleIcon
class="h-4 w-4"
@mouseenter="independentLatencyTestTip"
/>
</div>
<div class="flex items-center gap-2">
{{ $t('ipv6Test') }}
<input
Expand Down
113 changes: 79 additions & 34 deletions src/components/settings/SourceIPLabels.vue
Original file line number Diff line number Diff line change
@@ -1,36 +1,55 @@
<template>
<div>{{ $t('sourceIPLabels') }}</div>
<div class="flex flex-col gap-2 p-1 pr-0">
<div
v-for="ip in sourceIPs"
:key="ip"
class="flex items-center gap-2"
<Draggable
class="flex flex-1 flex-col gap-2"
v-model="sourceIPLabelList"
group="list"
:animation="150"
:handle="'.drag-handle'"
:item-key="'uuid'"
@start="disableSwipe = true"
@end="disableSwipe = false"
>
<TagIcon class="h-4 w-4 shrink-0" />
<span class="min-w-24 break-all">
{{ ip }}
</span>
<TextInput
class="w-40 min-w-24 max-sm:flex-1"
v-model="sourceIPLabelMap[ip]"
/>
<button
class="btn btn-circle btn-sm"
@click="() => handlerLabelRemove(ip)"
>
<XMarkIcon class="h-4 w-4" />
</button>
</div>
<template #item="{ element: { id, key, label } }">
<div
:key="id"
class="flex items-center gap-2"
>
<ChevronUpDownIcon class="drag-handle h-4 w-4 shrink-0 cursor-grab" />
<input
class="input input-sm input-bordered w-36 sm:w-64"
:value="key"
@input="(e) => handlerLabelKeyChange(e, id, 'key')"
@click="handlerIPInputFocus"
/>
<ArrowRightCircleIcon class="h-4 w-4 shrink-0" />
<input
class="input input-sm input-bordered w-0 max-w-40 flex-1"
:value="label"
@input="(e) => handlerLabelKeyChange(e, id, 'label')"
/>
<button
class="btn btn-circle btn-sm"
@click="() => handlerLabelRemove(id)"
>
<XMarkIcon class="h-4 w-4" />
</button>
</div>
</template>
</Draggable>

<div class="flex w-full items-center gap-2">
<TextInput
class="w-36 flex-1 sm:max-w-40"
<TagIcon class="h-4 w-4 shrink-0" />
<input
class="input input-sm input-bordered w-36 sm:w-64"
v-model="newLabelForIP.ip"
@click="handlerIPInputFocus"
placeholder="IP / eui64 / Regex"
/>
<ArrowRightCircleIcon class="h-4 w-4 shrink-0" />
<TextInput
class="w-24 sm:w-32"
<input
class="input input-sm input-bordered w-0 max-w-40 flex-1"
v-model="newLabelForIP.label"
@keypress.enter="handlerLabelAdd"
:placeholder="$t('label')"
Expand All @@ -46,13 +65,21 @@
</template>

<script setup lang="ts">
import { disableSwipe } from '@/composables/swipe'
import { useTooltip } from '@/helper/tooltip'
import { connections } from '@/store/connections'
import { sourceIPLabelMap } from '@/store/settings'
import { ArrowRightCircleIcon, PlusIcon, TagIcon, XMarkIcon } from '@heroicons/vue/24/outline'
import { sourceIPLabelList } from '@/store/settings'
import {
ArrowRightCircleIcon,
ChevronUpDownIcon,
PlusIcon,
TagIcon,
XMarkIcon,
} from '@heroicons/vue/24/outline'
import { uniq } from 'lodash'
import { computed, reactive } from 'vue'
import TextInput from '../common/TextInput.vue'
import { v4 as uuid } from 'uuid'
import { reactive } from 'vue'
import Draggable from 'vuedraggable'
const { showTip, destroyTip } = useTooltip()
Expand Down Expand Up @@ -86,23 +113,41 @@ const handlerIPInputFocus = (e: Event) => {
arrow: false,
})
}
const sourceIPs = computed(() => {
return Object.keys(sourceIPLabelMap.value).sort()
})
const newLabelForIP = reactive({
ip: '',
label: '',
})
const handlerLabelAdd = () => {
sourceIPLabelMap.value[newLabelForIP.ip] = newLabelForIP.label
if (!newLabelForIP.ip || !newLabelForIP.label) {
return
}
sourceIPLabelList.value.push({
key: newLabelForIP.ip,
label: newLabelForIP.label,
id: uuid(),
})
newLabelForIP.ip = ''
newLabelForIP.label = ''
}
const handlerLabelRemove = (ip: string) => {
Reflect.deleteProperty(sourceIPLabelMap.value, ip)
sourceIPLabelMap.value = { ...sourceIPLabelMap.value }
const handlerLabelRemove = (id: string) => {
sourceIPLabelList.value.splice(
sourceIPLabelList.value.findIndex((item) => item.id === id),
1,
)
}
const handlerLabelKeyChange = (e: Event, id: string, path: 'key' | 'label') => {
const target = e.target as HTMLInputElement
const key = target.value
const source = sourceIPLabelList.value.find((item) => item.id === id)
if (source) {
source[path] = key
}
}
</script>
9 changes: 8 additions & 1 deletion src/composables/swipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { useRoute, useRouter } from 'vue-router'
import { useProxies } from './proxies'
import { rulesTabShow } from './rules'

export const disableSwipe = ref(false)

export const useSwipeRouter = () => {
const route = useRoute()
const router = useRouter()
Expand Down Expand Up @@ -86,7 +88,12 @@ export const useSwipeRouter = () => {
}

watch(direction, () => {
if (document.querySelector('dialog:modal') || window.getSelection()?.toString()?.length) return
if (
document.querySelector('dialog:modal') ||
window.getSelection()?.toString()?.length ||
disableSwipe.value
)
return
if (direction.value === 'right') {
getPrevRouteName()
} else if (direction.value === 'left') {
Expand Down
8 changes: 4 additions & 4 deletions src/helper/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
lowLatency,
mediumLatency,
proxySortType,
sourceIPLabelMap,
sourceIPLabelList,
splitOverviewPage,
} from '@/store/settings'
import type { Backend, Connection } from '@/types'
Expand Down Expand Up @@ -99,15 +99,15 @@ export const getIPLabelFromMap = (ip: string) => {
}
const isIPv6 = ip.includes(':')

for (const key in sourceIPLabelMap.value) {
for (const { key, label } of sourceIPLabelList.value) {
if (key.startsWith('/')) {
const regex = new RegExp(key, 'i')

if (regex.test(ip)) {
return sourceIPLabelMap.value[key]
return label
}
} else if (ip === key || (isIPv6 && ip.endsWith(key))) {
return sourceIPLabelMap.value[key]
return label
}
}
return ip
Expand Down
12 changes: 6 additions & 6 deletions src/store/logs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useStorage } from '@vueuse/core'
import dayjs from 'dayjs'
import { throttle } from 'lodash'
import { ref, watch } from 'vue'
import { language, logRetentionLimit, sourceIPLabelMap } from './settings'
import { language, logRetentionLimit, sourceIPLabelList } from './settings'

export const logs = ref<LogWithSeq[]>([])
export const logFilter = ref('')
Expand All @@ -23,16 +23,16 @@ const sliceLogs = throttle(() => {
const ipSourceMatchs: [RegExp, string][] = []
const restructMatchs = () => {
ipSourceMatchs.length = 0
for (const ip in sourceIPLabelMap.value) {
if (ip.startsWith('/')) continue
const regex = new RegExp(ip + ':', 'ig')
for (const { key, label } of sourceIPLabelList.value) {
if (key.startsWith('/')) continue
const regex = new RegExp(key + ':', 'ig')

ipSourceMatchs.push([regex, `${ip} (${sourceIPLabelMap.value[ip]}) :`])
ipSourceMatchs.push([regex, `${key} (${label}) :`])
}
}

watch(
sourceIPLabelMap,
sourceIPLabelList,
() => {
restructMatchs()
},
Expand Down
17 changes: 16 additions & 1 deletion src/store/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ import {
TABLE_WIDTH_MODE,
} from '@/constant'
import { isMiddleScreen } from '@/helper/utils'
import type { SourceIPLabel } from '@/types'
import { useStorage } from '@vueuse/core'
import { isEmpty } from 'lodash'
import { v4 as uuid } from 'uuid'
import { computed, ref } from 'vue'

// global
Expand Down Expand Up @@ -115,7 +118,19 @@ connectionCardLines.value = connectionCardLines.value.map((lines) =>
lines.filter(filterLegacyDetailsOpt),
)

export const sourceIPLabelMap = useStorage<Record<string, string>>('config/source-ip-label-map', {})
const sourceIPLabelMap = useStorage<Record<string, string>>('config/source-ip-label-map', {})

export const sourceIPLabelList = useStorage<SourceIPLabel[]>('config/source-ip-label-list', () => {
const oldMap = sourceIPLabelMap.value

if (isEmpty(oldMap)) {
return []
}

return Object.entries(oldMap)
.sort((prev, next) => prev[0].localeCompare(next[0]))
.map(([key, label]) => ({ key, label, id: uuid() }))
})

// logs
export const logRetentionLimit = useStorage<number>('config/log-retention-limit', 1000)
6 changes: 6 additions & 0 deletions src/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,9 @@ export type DNSQuery = {
type: number
}[]
}

export type SourceIPLabel = {
key: string
label: string
id: string
}
10 changes: 2 additions & 8 deletions src/views/SetupPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,7 @@
:key="element.uuid"
class="flex items-center gap-2"
>
<button class="btn btn-ghost btn-sm cursor-grab">
<AdjustmentsVerticalIcon class="h-4 w-4" />
</button>
<ChevronUpDownIcon class="h-4 w-4 cursor-grab" />
<button
class="btn btn-sm flex-1"
@click="selectBackend(element.uuid)"
Expand Down Expand Up @@ -131,11 +129,7 @@ import { getUrlFromBackend } from '@/helper'
import router from '@/router'
import { activeUuid, addBackend, backendList, removeBackend } from '@/store/setup'
import type { Backend } from '@/types'
import {
AdjustmentsVerticalIcon,
QuestionMarkCircleIcon,
XMarkIcon,
} from '@heroicons/vue/24/outline'
import { ChevronUpDownIcon, QuestionMarkCircleIcon, XMarkIcon } from '@heroicons/vue/24/outline'
import { reactive } from 'vue'
import Draggable from 'vuedraggable'
Expand Down

0 comments on commit 36d19a9

Please sign in to comment.