Skip to content

Commit b37059d

Browse files
Merge pull request #521 from hshqwq/dev
rename check for special mark on windows
2 parents 51f5f8c + ef1687a commit b37059d

File tree

5 files changed

+148
-106
lines changed

5 files changed

+148
-106
lines changed
Lines changed: 113 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
1-
import { getFileIcon, getDirIcon, extractExtension } from "@/utils/getFileIcon";
2-
import { Popover, PopoverTrigger, Button, PopoverSurface, Input, Text, Subtitle1, Tooltip, Menu, MenuItem, MenuList, MenuPopover, MenuTrigger, Subtitle2, Select } from '@fluentui/react-components';
1+
import { checkFileName } from "@/utils/checkFileName";
2+
import { extractExtension, getDirIcon, getFileIcon } from "@/utils/getFileIcon";
3+
import { Button, Input, Popover, PopoverSurface, PopoverTrigger, Subtitle2, Text, Tooltip } from '@fluentui/react-components';
4+
import { bundleIcon, DeleteFilled, DeleteRegular, RenameFilled, RenameRegular } from "@fluentui/react-icons";
5+
import { t } from "@lingui/macro";
6+
import { useRef } from "react";
7+
import { useValue } from '../../hooks/useValue';
38
import IconWrapper from "../iconWrapper/IconWrapper";
49
import { IFile, IViewType } from "./Assets";
510
import styles from "./FileElement.module.scss";
6-
import { useValue } from '../../hooks/useValue';
7-
import { bundleIcon, RenameFilled, RenameRegular, DeleteFilled, DeleteRegular, DesktopMacFilled, DesktopMacRegular, MoreVerticalFilled, MoreVerticalRegular } from "@fluentui/react-icons";
8-
import { t } from "@lingui/macro";
9-
import { useRef } from "react";
1011

1112
const RenameIcon = bundleIcon(RenameFilled, RenameRegular);
1213
const DeleteIcon = bundleIcon(DeleteFilled, DeleteRegular);
1314

1415
export default function FileElement(
15-
{
16+
{
1617
rootPath,
1718
file,
1819
type,
@@ -24,17 +25,17 @@ export default function FileElement(
2425
handleDeleteFile,
2526
checkHasFile,
2627
}: {
27-
rootPath: string[],
28-
file: IFile,
29-
type: IViewType,
30-
selected?: boolean,
31-
desc?: string,
32-
isProtected?: boolean,
33-
handleOpenFile: (file: IFile) => Promise<void>,
34-
handleRenameFile: (source: string, newName: string) => Promise<void>,
35-
handleDeleteFile: (source: string) => Promise<void>,
36-
checkHasFile: (fileNmae: string) => boolean,
37-
}) {
28+
rootPath: string[],
29+
file: IFile,
30+
type: IViewType,
31+
selected?: boolean,
32+
desc?: string,
33+
isProtected?: boolean,
34+
handleOpenFile: (file: IFile) => Promise<void>,
35+
handleRenameFile: (source: string, newName: string) => Promise<void>,
36+
handleDeleteFile: (source: string) => Promise<void>,
37+
checkHasFile: (fileNmae: string) => boolean,
38+
}) {
3839
const newFileName = useValue(file.name);
3940
const FileItemSelfRef = useRef(null);
4041
const showTooltip = useValue(false);
@@ -43,11 +44,13 @@ export default function FileElement(
4344

4445
const is_picture = (extName: string) => extractExtension(extName) === 'image' ? true : false;
4546

47+
const isAccessible = checkFileName(newFileName.value);
48+
4649
return (
4750
<Tooltip
4851
content={
4952
<div
50-
style={{display: 'flex', flexDirection: 'column', padding: 0}}
53+
style={{ display: 'flex', flexDirection: 'column', padding: 0 }}
5154
onMouseEnter={() => showTooltip.set(false)}
5255
onMouseMove={() => showTooltip.set(false)}
5356
>
@@ -104,8 +107,8 @@ export default function FileElement(
104107
{
105108
!file.isDir && (
106109
is_picture(file.extName) && type === 'grid'
107-
? <img src={filePath} draggable='false' style={{ width: '100%', height: '100%', objectFit: 'contain',}}/>
108-
: <IconWrapper src={getFileIcon(file.name)} size={ type === 'grid' ? 44 : 22} iconSize={type === 'grid' ? 40 : 20} />
110+
? <img src={filePath} draggable='false' style={{ width: '100%', height: '100%', objectFit: 'contain', }} />
111+
: <IconWrapper src={getFileIcon(file.name)} size={type === 'grid' ? 44 : 22} iconSize={type === 'grid' ? 40 : 20} />
109112
)
110113
}
111114
{file.isDir && <IconWrapper src={getDirIcon(file.path)} size={type === 'grid' ? 44 : 22} iconSize={type === 'grid' ? 40 : 20} />}
@@ -136,93 +139,100 @@ export default function FileElement(
136139
<div className={styles.fileAction}>
137140
{
138141
!isProtected &&
139-
<>
140-
<Popover withArrow onOpenChange={() => newFileName.set(file.name)}>
141-
<PopoverTrigger>
142-
<Tooltip content={t`重命名`} relationship="label" positioning="below">
143-
<Button
144-
icon={<RenameIcon style={{ width: '16px' }} />}
145-
size='small'
146-
appearance='subtle'
147-
onClick={(e) => e.stopPropagation()}
148-
/>
149-
</Tooltip>
150-
</PopoverTrigger>
151-
<PopoverSurface
152-
onClick={(e) => e.stopPropagation()}
153-
onKeyDown={(e) => {
154-
e.stopPropagation();
155-
if((e.key === 'Enter') && !checkHasFile(newFileName.value)){
156-
handleRenameFile(filePath, newFileName.value.trim());
157-
};
158-
}}
159-
>
160-
<div style={{ display: "flex", flexFlow: "column", gap: "16px" }}>
161-
<Subtitle2>{t`重命名`}</Subtitle2>
162-
<Tooltip
163-
content={{ children: t`已存在文件或文件夹 ${newFileName.value},请输入其他名称`, style: { color: 'var(--danger)' } }}
164-
relationship="description"
165-
visible={checkHasFile(newFileName.value) && newFileName.value !== file.name}
166-
positioning="below"
167-
>
168-
<Input
169-
value={newFileName.value}
170-
className={checkHasFile(newFileName.value) && newFileName.value !== file.name ? styles.inputDanger : ''}
171-
onFocus={ev => {
172-
const el = ev.target;
173-
const dotPosition = el.value.indexOf('.');
174-
el?.setSelectionRange(0, dotPosition === -1 ? el.value.length : dotPosition);
142+
<>
143+
<Popover withArrow onOpenChange={() => newFileName.set(file.name)}>
144+
<PopoverTrigger>
145+
<Tooltip content={t`重命名`} relationship="label" positioning="below">
146+
<Button
147+
icon={<RenameIcon style={{ width: '16px' }} />}
148+
size='small'
149+
appearance='subtle'
150+
onClick={(e) => e.stopPropagation()}
151+
/>
152+
</Tooltip>
153+
</PopoverTrigger>
154+
<PopoverSurface
155+
onClick={(e) => e.stopPropagation()}
156+
onKeyDown={(e) => {
157+
e.stopPropagation();
158+
if ((e.key === 'Enter') && !(newFileName.value.trim() === '' || !isAccessible || checkHasFile(newFileName.value) && newFileName.value !== file.name)) {
159+
handleRenameFile(filePath, newFileName.value.trim());
160+
};
175161
}}
176-
onChange={(_, data) => {
177-
newFileName.set(data.value ?? "");
178-
}}
179-
/>
180-
</Tooltip>
181-
<Button
182-
appearance="primary"
183-
disabled={newFileName.value.trim() === '' || checkHasFile(newFileName.value) && newFileName.value !== file.name}
184-
onClick={() => handleRenameFile(filePath, newFileName.value.trim())}
185-
>{t`重命名`}</Button>
186-
</div>
187-
</PopoverSurface>
188-
</Popover>
162+
>
163+
<div style={{ display: "flex", flexFlow: "column", gap: "16px" }}>
164+
<Subtitle2>{t`重命名`}</Subtitle2>
165+
<Tooltip
166+
content={{ children: t`文件名不可包含特殊符号: '/\\:*?"<>|'`, style: { color: 'var(--danger)' } }}
167+
relationship="inaccessible"
168+
visible={!isAccessible}
169+
positioning="below"
170+
>
171+
<Tooltip
172+
content={{ children: t`已存在文件或文件夹 ${newFileName.value},请输入其他名称`, style: { color: 'var(--danger)' } }}
173+
relationship="description"
174+
visible={checkHasFile(newFileName.value) && newFileName.value !== file.name}
175+
positioning="below"
176+
>
177+
<Input
178+
value={newFileName.value}
179+
className={checkHasFile(newFileName.value) && newFileName.value !== file.name ? styles.inputDanger : ''}
180+
onFocus={ev => {
181+
const el = ev.target;
182+
const dotPosition = el.value.indexOf('.');
183+
el?.setSelectionRange(0, dotPosition === -1 ? el.value.length : dotPosition);
184+
}}
185+
onChange={(_, data) => {
186+
newFileName.set(data.value ?? "");
187+
}}
188+
/>
189+
</Tooltip>
190+
</Tooltip>
191+
<Button
192+
appearance="primary"
193+
disabled={newFileName.value.trim() === '' || !isAccessible || checkHasFile(newFileName.value) && newFileName.value !== file.name}
194+
onClick={() => handleRenameFile(filePath, newFileName.value.trim())}
195+
>{t`重命名`}</Button>
196+
</div>
197+
</PopoverSurface>
198+
</Popover>
189199

190-
<Popover withArrow>
191-
<PopoverTrigger>
192-
<Tooltip content={t`删除`} relationship="label" positioning="below">
193-
<Button
194-
icon={<DeleteIcon style={{ width: '16px' }} />}
195-
size='small'
196-
appearance='subtle'
197-
onClick={(e) => e.stopPropagation()}
198-
/>
199-
</Tooltip>
200-
</PopoverTrigger>
201-
<PopoverSurface
202-
onClick={(e) => e.stopPropagation()}
203-
onKeyDown={(e) => {
204-
e.stopPropagation();
205-
(e.key === 'Enter') && handleDeleteFile(filePath);
206-
}}
207-
>
208-
<div style={{ display: "flex", flexFlow: "column", gap: "16px" }}>
209-
<Subtitle2>{t`删除`}</Subtitle2>
210-
<Text>{t`是否要删除 "${file.name}" ?`}</Text>
211-
<Button
212-
appearance="primary"
213-
onClick={(e) => {
214-
e.stopPropagation();
215-
handleDeleteFile(filePath);
216-
}}
217-
>{t`删除`}</Button>
218-
</div>
219-
</PopoverSurface>
220-
</Popover>
221-
</>
200+
<Popover withArrow>
201+
<PopoverTrigger>
202+
<Tooltip content={t`删除`} relationship="label" positioning="below">
203+
<Button
204+
icon={<DeleteIcon style={{ width: '16px' }} />}
205+
size='small'
206+
appearance='subtle'
207+
onClick={(e) => e.stopPropagation()}
208+
/>
209+
</Tooltip>
210+
</PopoverTrigger>
211+
<PopoverSurface
212+
onClick={(e) => e.stopPropagation()}
213+
onKeyDown={(e) => {
214+
e.stopPropagation();
215+
(e.key === 'Enter') && handleDeleteFile(filePath);
216+
}}
217+
>
218+
<div style={{ display: "flex", flexFlow: "column", gap: "16px" }}>
219+
<Subtitle2>{t`删除`}</Subtitle2>
220+
<Text>{t`是否要删除 "${file.name}" ?`}</Text>
221+
<Button
222+
appearance="primary"
223+
onClick={(e) => {
224+
e.stopPropagation();
225+
handleDeleteFile(filePath);
226+
}}
227+
>{t`删除`}</Button>
228+
</div>
229+
</PopoverSurface>
230+
</Popover>
231+
</>
222232
}
223233
</div>
224234
</div>
225235
</div>
226-
</Tooltip>
236+
</Tooltip >
227237
);
228238
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function checkFileName(name: string): boolean {
2+
return name.search(/[\/\\\:\*\?"\<\>\|]/) === -1;
3+
}

packages/terre2/src/Modules/lsp/suggestionRules/getArgsKey.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,13 +131,27 @@ export function getArgsKey(
131131
return [whenKey];
132132
}
133133
case commandType.setAnimation: {
134-
return [whenKey, nextKey, continueKey, targetKey, writeDefaultKey, keepKey];
134+
return [
135+
whenKey,
136+
nextKey,
137+
continueKey,
138+
targetKey,
139+
writeDefaultKey,
140+
keepKey,
141+
];
135142
}
136143
case commandType.playEffect: {
137144
return [whenKey, volumeKey, idSoundKey];
138145
}
139146
case commandType.setTempAnimation: {
140-
return [whenKey, nextKey, continueKey, targetKey, writeDefaultKey, keepKey];
147+
return [
148+
whenKey,
149+
nextKey,
150+
continueKey,
151+
targetKey,
152+
writeDefaultKey,
153+
keepKey,
154+
];
141155
}
142156
case commandType.setTransform: {
143157
return [

packages/terre2/src/Modules/webgal-fs/webgal-fs.service.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ interface FileList {
2727
export class WebgalFsService {
2828
constructor(private readonly logger: ConsoleLogger) {}
2929

30+
static checkFileName(name: string): boolean {
31+
return name.search(/[\/\\\:\*\?"\<\>\|]/) === -1;
32+
}
33+
3034
greet() {
3135
this.logger.log('Welcome to WebGAl Files System Service!');
3236
}
@@ -86,6 +90,8 @@ export class WebgalFsService {
8690
* @param dirName 文件夹名称
8791
*/
8892
async mkdir(src, dirName) {
93+
if (!WebgalFsService.checkFileName(dirName)) return false;
94+
8995
return await fs
9096
.mkdir(join(decodeURI(src), decodeURI(dirName)))
9197
.catch(() => {
@@ -111,6 +117,7 @@ export class WebgalFsService {
111117
* @param newName 新文件名
112118
*/
113119
async renameFile(path: string, newName: string) {
120+
if (!WebgalFsService.checkFileName(newName)) return false;
114121
// 取出旧文件的路径
115122
const rawOldPath = decodeURI(path);
116123
let oldPath = join(...rawOldPath.split(/[\/\\]/g));
@@ -179,6 +186,7 @@ export class WebgalFsService {
179186
_path: string,
180187
newName: string,
181188
): Promise<boolean> {
189+
if (!WebgalFsService.checkFileName(newName)) return false;
182190
try {
183191
const path = decodeURI(_path);
184192

@@ -224,6 +232,13 @@ export class WebgalFsService {
224232
async createEmptyFile(path: string) {
225233
try {
226234
const decodedPath = decodeURI(path);
235+
if (
236+
decodedPath
237+
.replace(/\\/g, '/')
238+
.split('/')
239+
.every((name) => WebgalFsService.checkFileName(name))
240+
)
241+
throw new Error('There are unexpect marks in path');
227242
const directory = dirname(decodedPath);
228243

229244
if (!(await this.existsDir(directory))) {

packages/terre2/src/util/strings.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export function pprintJSON(obj: Object, needStringify = false) {
1+
export function pprintJSON(obj: unknown, needStringify = false) {
22
let jsonStr: string;
33

44
if (needStringify) {

0 commit comments

Comments
 (0)