mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-05 16:22:08 +08:00
* fix(webdav): allow clearing saved configuration Close #8495 * chore(deps): update cargo dependencies * fix(ci): resolve Rust dependency update failures * chore(deps): remove unrelated changelog entries --------- Co-authored-by: Tunglies <77394545+Tunglies@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
184 lines
4.9 KiB
TypeScript
184 lines
4.9 KiB
TypeScript
import { TextField } from '@mui/material'
|
|
import { useLockFn } from 'ahooks'
|
|
import { nanoid } from 'nanoid'
|
|
import { forwardRef, useImperativeHandle, useState } from 'react'
|
|
import { useForm, Controller } from 'react-hook-form'
|
|
import { useTranslation } from 'react-i18next'
|
|
|
|
import { BaseDialog } from '@/components/base'
|
|
import { useVerge } from '@/hooks/use-verge'
|
|
import type {
|
|
IVergeTestItem_Deserialize,
|
|
IVergeTestItem_Serialize,
|
|
} from '@/services/bindings'
|
|
import { showNotice } from '@/services/notice-service'
|
|
|
|
interface Props {
|
|
onChange: (uid: string, patch?: Partial<IVergeTestItem>) => void
|
|
}
|
|
|
|
export interface TestViewerRef {
|
|
create: () => void
|
|
edit: (item: IVergeTestItem) => void
|
|
}
|
|
|
|
// create or edit the test item
|
|
export const TestViewer = forwardRef<TestViewerRef, Props>(
|
|
({ onChange }, ref) => {
|
|
const { t } = useTranslation()
|
|
const [open, setOpen] = useState(false)
|
|
const [openType, setOpenType] = useState<'new' | 'edit'>('new')
|
|
const [loading, setLoading] = useState(false)
|
|
const { verge, patchVerge } = useVerge()
|
|
const testList = verge?.test_list ?? []
|
|
const { control, ...formIns } = useForm<IVergeTestItem_Deserialize>({
|
|
defaultValues: {
|
|
name: '',
|
|
icon: '',
|
|
url: '',
|
|
},
|
|
})
|
|
|
|
const patchTestList = async (
|
|
uid: string,
|
|
patch: Partial<IVergeTestItem_Deserialize>,
|
|
) => {
|
|
const newList = testList.map((x) => {
|
|
if (x.uid === uid) {
|
|
return {
|
|
uid: x.uid,
|
|
name: patch.name === undefined ? x.name : patch.name,
|
|
icon: patch.icon === undefined ? x.icon : patch.icon,
|
|
url: patch.url === undefined ? x.url : patch.url,
|
|
}
|
|
}
|
|
return x
|
|
})
|
|
await patchVerge({ test_list: newList })
|
|
}
|
|
|
|
useImperativeHandle(ref, () => ({
|
|
create: () => {
|
|
setOpenType('new')
|
|
setOpen(true)
|
|
},
|
|
edit: (item) => {
|
|
if (item) {
|
|
Object.entries(item).forEach(([key, value]) => {
|
|
formIns.setValue(key as any, value)
|
|
})
|
|
}
|
|
setOpenType('edit')
|
|
setOpen(true)
|
|
},
|
|
}))
|
|
|
|
const handleOk = useLockFn(
|
|
formIns.handleSubmit(async (form) => {
|
|
setLoading(true)
|
|
try {
|
|
if (!form.name) {
|
|
throw new Error(t('tests.modals.test.errors.nameRequired'))
|
|
}
|
|
if (!form.url) {
|
|
throw new Error(t('tests.modals.test.errors.urlRequired'))
|
|
}
|
|
|
|
if (openType === 'new') {
|
|
const uid = nanoid()
|
|
const item: IVergeTestItem_Serialize = {
|
|
uid,
|
|
name: form.name ?? null,
|
|
icon: form.icon ?? null,
|
|
url: form.url ?? null,
|
|
}
|
|
const newList = [...testList, item]
|
|
await patchVerge({ test_list: newList })
|
|
onChange(uid)
|
|
} else {
|
|
if (!form.uid) {
|
|
throw new Error(t('tests.modals.test.errors.uidMissing'))
|
|
}
|
|
const uid = form.uid
|
|
|
|
await patchTestList(uid, form)
|
|
onChange(uid, form)
|
|
}
|
|
setOpen(false)
|
|
setLoading(false)
|
|
setTimeout(() => formIns.reset(), 500)
|
|
} catch (err: any) {
|
|
showNotice.error('tests.modals.test.errors.saveFailed', err)
|
|
setLoading(false)
|
|
}
|
|
}),
|
|
)
|
|
|
|
const handleClose = () => {
|
|
setOpen(false)
|
|
setTimeout(() => formIns.reset(), 500)
|
|
}
|
|
|
|
const text = {
|
|
fullWidth: true,
|
|
size: 'small',
|
|
margin: 'normal',
|
|
variant: 'outlined',
|
|
autoComplete: 'off',
|
|
autoCorrect: 'off',
|
|
} as const
|
|
|
|
return (
|
|
<BaseDialog
|
|
open={open}
|
|
title={
|
|
openType === 'new'
|
|
? t('tests.modals.test.title.create')
|
|
: t('tests.modals.test.title.edit')
|
|
}
|
|
contentSx={{ width: 375, pb: 0, maxHeight: '80%' }}
|
|
okBtn={t('shared.actions.save')}
|
|
cancelBtn={t('shared.actions.cancel')}
|
|
onClose={handleClose}
|
|
onCancel={handleClose}
|
|
onOk={handleOk}
|
|
loading={loading}
|
|
>
|
|
<Controller
|
|
name="name"
|
|
control={control}
|
|
render={({ field }) => (
|
|
<TextField {...text} {...field} label={t('shared.labels.name')} />
|
|
)}
|
|
/>
|
|
<Controller
|
|
name="icon"
|
|
control={control}
|
|
render={({ field }) => (
|
|
<TextField
|
|
{...text}
|
|
{...field}
|
|
multiline
|
|
maxRows={5}
|
|
label={t('shared.labels.icon')}
|
|
/>
|
|
)}
|
|
/>
|
|
<Controller
|
|
name="url"
|
|
control={control}
|
|
render={({ field }) => (
|
|
<TextField
|
|
{...text}
|
|
{...field}
|
|
multiline
|
|
maxRows={3}
|
|
label={t('tests.modals.test.fields.url')}
|
|
/>
|
|
)}
|
|
/>
|
|
</BaseDialog>
|
|
)
|
|
},
|
|
)
|