Skip to content

Commit 3e709df

Browse files
feat: add invalid filename check before send to server (#256)
1 parent 62352f0 commit 3e709df

File tree

5 files changed

+147
-18
lines changed

5 files changed

+147
-18
lines changed

src/components/ModalInput.tsx

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
onCleanup,
1919
} from "solid-js"
2020
import { useT } from "~/hooks"
21-
import { notify } from "~/utils"
21+
import { notify, validateFilename } from "~/utils"
2222
export type ModalInputProps = {
2323
opened: boolean
2424
onClose: () => void
@@ -36,6 +36,7 @@ export type ModalInputProps = {
3636
}
3737
export const ModalInput = (props: ModalInputProps) => {
3838
const [value, setValue] = createSignal(props.defaultValue ?? "")
39+
const [validationError, setValidationError] = createSignal<string>("")
3940
const t = useT()
4041

4142
let inputRef: HTMLInputElement | HTMLTextAreaElement
@@ -77,13 +78,20 @@ export const ModalInput = (props: ModalInputProps) => {
7778
})
7879

7980
const submit = () => {
80-
if (!value()) {
81-
notify.warning(t("global.empty_input"))
81+
const validation = validateFilename(value())
82+
if (!validation.valid) {
83+
notify.warning(t(`global.${validation.error}`))
8284
return
8385
}
8486
props.onSubmit?.(value())
8587
}
8688

89+
const handleInput = (newValue: string) => {
90+
setValue(newValue)
91+
const validation = validateFilename(newValue)
92+
setValidationError(validation.valid ? "" : validation.error || "")
93+
}
94+
8795
return (
8896
<Modal
8997
blockScrollOnMount={false}
@@ -105,8 +113,9 @@ export const ModalInput = (props: ModalInputProps) => {
105113
type={props.type}
106114
value={value()}
107115
ref={(el) => (inputRef = el)}
116+
invalid={!!validationError()}
108117
onInput={(e) => {
109-
setValue(e.currentTarget.value)
118+
handleInput(e.currentTarget.value)
110119
}}
111120
onFocus={handleFocus}
112121
onKeyDown={(e) => {
@@ -121,12 +130,18 @@ export const ModalInput = (props: ModalInputProps) => {
121130
id="modal-input"
122131
value={value()}
123132
ref={(el) => (inputRef = el)}
133+
invalid={!!validationError()}
124134
onInput={(e) => {
125-
setValue(e.currentTarget.value)
135+
handleInput(e.currentTarget.value)
126136
}}
127137
onFocus={handleFocus}
128138
/>
129139
</Show>
140+
<Show when={validationError()}>
141+
<FormHelperText color="$danger9">
142+
{t(`global.${validationError()}`)}
143+
</FormHelperText>
144+
</Show>
130145
<Show when={props.tips}>
131146
<FormHelperText>{props.tips}</FormHelperText>
132147
</Show>

src/components/ModalTwoInput.tsx

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
} from "@hope-ui/solid"
1414
import { createSignal, JSXElement, Show } from "solid-js"
1515
import { useT } from "~/hooks"
16-
import { notify } from "~/utils"
16+
import { notify, validateFilename } from "~/utils"
1717
export type ModalTwoInputProps = {
1818
opened: boolean
1919
onClose: () => void
@@ -29,11 +29,32 @@ export type ModalTwoInputProps = {
2929
export const ModalTwoInput = (props: ModalTwoInputProps) => {
3030
const [value1, setValue1] = createSignal(props.defaultValue1 ?? "") // Update value and setValue to value1 and setValue1
3131
const [value2, setValue2] = createSignal(props.defaultValue2 ?? "") // Add value2 and setValue2 for second input
32+
const [validationError1, setValidationError1] = createSignal<string>("")
33+
const [validationError2, setValidationError2] = createSignal<string>("")
3234
const t = useT()
35+
36+
const handleInput1 = (newValue: string) => {
37+
setValue1(newValue)
38+
const validation = validateFilename(newValue)
39+
setValidationError1(validation.valid ? "" : validation.error || "")
40+
}
41+
42+
const handleInput2 = (newValue: string) => {
43+
setValue2(newValue)
44+
const validation = validateFilename(newValue)
45+
setValidationError2(validation.valid ? "" : validation.error || "")
46+
}
47+
3348
const submit = () => {
34-
if (!value1() || !value2()) {
35-
// Check if both input values are not empty
36-
notify.warning(t("global.empty_input"))
49+
const validation1 = validateFilename(value1())
50+
const validation2 = validateFilename(value2())
51+
52+
if (!validation1.valid) {
53+
notify.warning(t(`global.${validation1.error}`))
54+
return
55+
}
56+
if (!validation2.valid) {
57+
notify.warning(t(`global.${validation2.error}`))
3758
return
3859
}
3960
props.onSubmit?.(value1(), value2()) // Update onSubmit to pass both input values
@@ -59,47 +80,71 @@ export const ModalTwoInput = (props: ModalTwoInputProps) => {
5980
id="modal-input1" // Update id to "modal-input1" for first input
6081
type={props.type}
6182
value={value1()} // Update value to value1 for first input
83+
invalid={!!validationError1()}
6284
onInput={(e) => {
63-
setValue1(e.currentTarget.value)
85+
handleInput1(e.currentTarget.value)
6486
}}
6587
onKeyDown={(e) => {
6688
if (e.key === "Enter") {
6789
submit()
6890
}
6991
}}
7092
/>
93+
<Show when={validationError1()}>
94+
<FormHelperText color="$danger9">
95+
{t(`global.${validationError1()}`)}
96+
</FormHelperText>
97+
</Show>
7198
<Input
7299
id="modal-input2" // Add second input with id "modal-input2"
73100
type={props.type}
74101
value={value2()} // Bind value to value2 for second input
102+
invalid={!!validationError2()}
75103
onInput={(e) => {
76-
setValue2(e.currentTarget.value)
104+
handleInput2(e.currentTarget.value)
77105
}}
78106
onKeyDown={(e) => {
79107
if (e.key === "Enter") {
80108
submit()
81109
}
82110
}}
83111
/>
112+
<Show when={validationError2()}>
113+
<FormHelperText color="$danger9">
114+
{t(`global.${validationError2()}`)}
115+
</FormHelperText>
116+
</Show>
84117
</VStack>
85118
}
86119
>
87-
<div>
120+
<VStack spacing="$2">
88121
<Textarea
89122
id="modal-input1" // Update id to "modal-input1" for first input
90123
value={value1()} // Update value to value1 for first input
124+
invalid={!!validationError1()}
91125
onInput={(e) => {
92-
setValue1(e.currentTarget.value)
126+
handleInput1(e.currentTarget.value)
93127
}}
94128
/>
129+
<Show when={validationError1()}>
130+
<FormHelperText color="$danger9">
131+
{t(`global.${validationError1()}`)}
132+
</FormHelperText>
133+
</Show>
95134
<Textarea
96135
id="modal-input2" // Add second input with id "modal-input2"
97136
value={value2()} // Bind value to value2 for second input
137+
invalid={!!validationError2()}
98138
onInput={(e) => {
99-
setValue2(e.currentTarget.value)
139+
handleInput2(e.currentTarget.value)
100140
}}
101141
/>
102-
</div>
142+
<Show when={validationError2()}>
143+
<FormHelperText color="$danger9">
144+
{t(`global.${validationError2()}`)}
145+
</FormHelperText>
146+
</Show>
147+
</VStack>
103148
</Show>
104149
<Show when={props.tips}>
105150
<FormHelperText>{props.tips}</FormHelperText>

src/lang/en/global.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"close": "Close",
2929
"no_support_now": "Not currently supported",
3030
"empty_input": "Please enter",
31+
"invalid_filename_chars": "Filename cannot contain: / \\ ? < > * : | \"",
3132
"name": "Name",
3233
"go_to_storages": "Go to Storages"
3334
}

src/pages/home/toolbar/BatchRename.tsx

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
fsBatchRename,
2121
handleRespWithNotifySuccess,
2222
notify,
23+
validateFilename,
2324
} from "~/utils"
2425
import { createSignal, For, onCleanup, Show } from "solid-js"
2526
import { selectedObjs } from "~/store"
@@ -41,8 +42,30 @@ export const BatchRename = () => {
4142
const [newName, setNewName] = createSignal("")
4243
const [newNameType, setNewNameType] = createSignal("string")
4344
const [matchNames, setMatchNames] = createSignal<RenameObj[]>([])
45+
const [validationErrorSrc, setValidationErrorSrc] = createSignal<string>("")
46+
const [validationErrorNew, setValidationErrorNew] = createSignal<string>("")
4447
const t = useT()
4548

49+
const handleInputSrc = (newValue: string) => {
50+
setSrcName(newValue)
51+
if (type() === "2" || type() === "3") {
52+
const validation = validateFilename(newValue)
53+
setValidationErrorSrc(validation.valid ? "" : validation.error || "")
54+
} else {
55+
setValidationErrorSrc("")
56+
}
57+
}
58+
59+
const handleInputNew = (newValue: string) => {
60+
setNewName(newValue)
61+
if (type() === "2" || type() === "3") {
62+
const validation = validateFilename(newValue)
63+
setValidationErrorNew(validation.valid ? "" : validation.error || "")
64+
} else {
65+
setValidationErrorNew("")
66+
}
67+
}
68+
4669
const itemProps = () => {
4770
return {
4871
fontWeight: "bold",
@@ -68,6 +91,18 @@ export const BatchRename = () => {
6891
notify.warning(t("global.empty_input"))
6992
return
7093
}
94+
if (type() === "2" || type() === "3") {
95+
const validationSrc = validateFilename(srcName())
96+
if (!validationSrc.valid) {
97+
notify.warning(t(`global.${validationSrc.error}`))
98+
return
99+
}
100+
const validationNew = validateFilename(newName())
101+
if (!validationNew.valid) {
102+
notify.warning(t(`global.${validationNew.error}`))
103+
return
104+
}
105+
}
71106
const replaceRegexp = new RegExp(srcName(), "g")
72107

73108
let matchNames: RenameObj[]
@@ -217,35 +252,49 @@ export const BatchRename = () => {
217252
id="modal-input1" // Update id to "modal-input1" for first input
218253
type={"string"}
219254
value={srcName()} // Update value to value1 for first input
255+
invalid={!!validationErrorSrc()}
220256
onInput={(e) => {
221-
setSrcName(e.currentTarget.value)
257+
handleInputSrc(e.currentTarget.value)
222258
}}
223259
onKeyDown={(e) => {
224260
if (e.key === "Enter") {
225261
submit()
226262
}
227263
}}
228264
/>
265+
<Show when={validationErrorSrc()}>
266+
<Text color="$danger9" fontSize="$sm">
267+
{t(`global.${validationErrorSrc()}`)}
268+
</Text>
269+
</Show>
229270
<Input
230271
id="modal-input2" // Add second input with id "modal-input2"
231272
type={newNameType()}
232273
value={newName()} // Bind value to value2 for second input
274+
invalid={!!validationErrorNew()}
233275
onInput={(e) => {
234-
setNewName(e.currentTarget.value)
276+
handleInputNew(e.currentTarget.value)
235277
}}
236278
onKeyDown={(e) => {
237279
if (e.key === "Enter") {
238280
submit()
239281
}
240282
}}
241283
/>
284+
<Show when={validationErrorNew()}>
285+
<Text color="$danger9" fontSize="$sm">
286+
{t(`global.${validationErrorNew()}`)}
287+
</Text>
288+
</Show>
242289
</VStack>
243290
</ModalBody>
244291
<ModalFooter display="flex" gap="$2">
245292
<Button
246293
onClick={() => {
247294
setType("1")
248295
setNewNameType("string")
296+
setValidationErrorSrc("")
297+
setValidationErrorNew("")
249298
onClose()
250299
}}
251300
colorScheme="neutral"
@@ -256,7 +305,7 @@ export const BatchRename = () => {
256305
onClick={() => submit()}
257306
disabled={
258307
type() === "2" || type() === "3"
259-
? !srcName() || !newName()
308+
? !srcName() || !newName() || !!validationErrorSrc() || !!validationErrorNew()
260309
: !srcName()
261310
}
262311
>
@@ -297,6 +346,8 @@ export const BatchRename = () => {
297346
setMatchNames([])
298347
setType("1")
299348
setNewNameType("string")
349+
setValidationErrorSrc("")
350+
setValidationErrorNew("")
300351
closePreviewModal()
301352
onClose()
302353
}}
@@ -324,6 +375,8 @@ export const BatchRename = () => {
324375
setNewName("")
325376
setType("1")
326377
setNewNameType("string")
378+
setValidationErrorSrc("")
379+
setValidationErrorNew("")
327380
refresh()
328381
onClose()
329382
closePreviewModal()

src/utils/str.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,3 +176,18 @@ export const matchTemplate = (
176176
const template = Handlebars.compile(templateStr)
177177
return template(data)
178178
}
179+
180+
181+
export const validateFilename = (
182+
name: string,
183+
): { valid: boolean; error?: string } => {
184+
if (!name || name.trim().length === 0) {
185+
return { valid: false, error: "empty_input" }
186+
}
187+
const INVALID_CHARS = /[\/\\?<>*:|"]/
188+
if (INVALID_CHARS.test(name)) {
189+
return { valid: false, error: "invalid_filename_chars" }
190+
}
191+
192+
return { valid: true }
193+
}

0 commit comments

Comments
 (0)