Skip to content

Commit 26eedec

Browse files
修复导入的图片卸载时机不对的问题
1 parent 2120192 commit 26eedec

File tree

4 files changed

+59
-61
lines changed

4 files changed

+59
-61
lines changed

src/contestEditor/configPanel.tsx

Lines changed: 39 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
type Dispatch,
1818
type SetStateAction,
1919
} from "react";
20-
import { useImmer, type Updater } from "use-immer";
20+
import { type Updater } from "use-immer";
2121
import type { DateArr, ImmerContestData } from "@/types/contestData";
2222
import dayjs from "dayjs";
2323
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
@@ -52,23 +52,9 @@ const ConfigPanel: FC<{
5252
const { modal, message } = App.useApp();
5353
const formRef = useRef<HTMLFormElement>(null);
5454
const [width, setWidth] = useState(220);
55-
const [images, updateImages] = useImmer<
56-
{
57-
name: string;
58-
url: string;
59-
editMode: boolean;
60-
}[]
61-
>([]);
62-
const imagesRef = useRef(images);
63-
64-
useEffect(() => {
65-
imagesRef.current = images;
66-
}, [images]);
67-
useEffect(
68-
() => () => imagesRef.current.forEach((x) => URL.revokeObjectURL(x.url)),
69-
[],
55+
const [imageEditModeId, setImageEditModeId] = useState<number | undefined>(
56+
undefined
7057
);
71-
7258
useEffect(() => {
7359
if (!formRef.current) return;
7460
const form = formRef.current;
@@ -98,24 +84,32 @@ const ConfigPanel: FC<{
9884
}
9985
function updateLang(
10086
index: number,
101-
cb: (x: ImmerContestData["support_languages"][number]) => void,
87+
cb: (x: ImmerContestData["support_languages"][number]) => void
10288
) {
10389
updateContestData((x) => {
10490
cb(x.support_languages[index]);
10591
});
10692
}
10793
function updateProblemData(
10894
index: number,
109-
cb: (x: ImmerContestData["problems"][number]) => void,
95+
cb: (x: ImmerContestData["problems"][number]) => void
11096
) {
11197
updateContestData((x) => {
11298
cb(x.problems[index]);
11399
});
114100
}
101+
function updateImages(
102+
index: number,
103+
cb: (x: ImmerContestData["images"][number]) => void
104+
) {
105+
updateContestData((x) => {
106+
cb(x.images[index]);
107+
});
108+
}
115109
const removeProblem = removeProblemCallback(
116110
modal,
117111
setPanel,
118-
updateContestData,
112+
updateContestData
119113
);
120114
return (
121115
<form className="contest-editor-config" ref={formRef}>
@@ -277,7 +271,7 @@ const ConfigPanel: FC<{
277271
onClick={() =>
278272
updateContestData((x) => {
279273
const i = x.support_languages.findIndex(
280-
(v) => v.key === lang.key,
274+
(v) => v.key === lang.key
281275
);
282276
if (i === -1)
283277
throw new Error("Target language not found");
@@ -305,7 +299,7 @@ const ConfigPanel: FC<{
305299
for (const p of x.problems)
306300
if (!(newLang.name in p.submit_filename))
307301
p.submit_filename.push(
308-
p.name + `.lang${x.support_languages.length + 1}`,
302+
p.name + `.lang${x.support_languages.length + 1}`
309303
);
310304
x.support_languages.push(newLang);
311305
})
@@ -386,7 +380,7 @@ const ConfigPanel: FC<{
386380
onChange={(e) =>
387381
updateProblemData(
388382
index,
389-
(x) => (x.title = e.target.value),
383+
(x) => (x.title = e.target.value)
390384
)
391385
}
392386
/>
@@ -412,7 +406,7 @@ const ConfigPanel: FC<{
412406
onChange={(e) =>
413407
updateProblemData(
414408
index,
415-
(x) => (x.dir = e.target.value),
409+
(x) => (x.dir = e.target.value)
416410
)
417411
}
418412
className="contest-editor-config-monoinput"
@@ -426,7 +420,7 @@ const ConfigPanel: FC<{
426420
onChange={(e) =>
427421
updateProblemData(
428422
index,
429-
(x) => (x.exec = e.target.value),
423+
(x) => (x.exec = e.target.value)
430424
)
431425
}
432426
className="contest-editor-config-monoinput"
@@ -444,7 +438,7 @@ const ConfigPanel: FC<{
444438
onChange={(e) =>
445439
updateProblemData(
446440
index,
447-
(x) => (x.input = e.target.value),
441+
(x) => (x.input = e.target.value)
448442
)
449443
}
450444
className="contest-editor-config-monoinput"
@@ -458,7 +452,7 @@ const ConfigPanel: FC<{
458452
onChange={(e) =>
459453
updateProblemData(
460454
index,
461-
(x) => (x.output = e.target.value),
455+
(x) => (x.output = e.target.value)
462456
)
463457
}
464458
className="contest-editor-config-monoinput"
@@ -475,7 +469,7 @@ const ConfigPanel: FC<{
475469
onChange={(e) =>
476470
updateProblemData(
477471
index,
478-
(x) => (x.time_limit = e.target.value),
472+
(x) => (x.time_limit = e.target.value)
479473
)
480474
}
481475
/>
@@ -488,7 +482,7 @@ const ConfigPanel: FC<{
488482
onChange={(e) =>
489483
updateProblemData(
490484
index,
491-
(x) => (x.memory_limit = e.target.value),
485+
(x) => (x.memory_limit = e.target.value)
492486
)
493487
}
494488
/>
@@ -503,7 +497,7 @@ const ConfigPanel: FC<{
503497
onChange={(e) =>
504498
updateProblemData(
505499
index,
506-
(x) => (x.testcase = e.target.value),
500+
(x) => (x.testcase = e.target.value)
507501
)
508502
}
509503
/>
@@ -517,7 +511,7 @@ const ConfigPanel: FC<{
517511
onChange={(e) =>
518512
updateProblemData(
519513
index,
520-
(x) => (x.point_equal = e.target.value),
514+
(x) => (x.point_equal = e.target.value)
521515
)
522516
}
523517
/>
@@ -532,7 +526,7 @@ const ConfigPanel: FC<{
532526
onChange={(e) =>
533527
updateProblemData(
534528
index,
535-
(x) => (x.pretestcase = e.target.value),
529+
(x) => (x.pretestcase = e.target.value)
536530
)
537531
}
538532
/>
@@ -551,7 +545,7 @@ const ConfigPanel: FC<{
551545
onChange={(e) =>
552546
updateProblemData(
553547
index,
554-
(x) => (x.submit_filename[findex] = e.target.value),
548+
(x) => (x.submit_filename[findex] = e.target.value)
555549
)
556550
}
557551
className="contest-editor-config-monoinput"
@@ -577,24 +571,20 @@ const ConfigPanel: FC<{
577571
<div className="contest-editor-config-label contest-editor-config-image">
578572
<div>本地图片</div>
579573
<div>
580-
{images.map((img, index) => (
574+
{contestData.images.map((img, index) => (
581575
<Card
582576
key={img.url}
583577
classNames={{ body: "contest-editor-config-image-card" }}
584578
>
585579
<Image src={img.url} alt={img.name} height={150} />
586580
<div>
587-
{!img.editMode ? (
581+
{imageEditModeId !== index ? (
588582
<>
589583
<div>{img.name}</div>
590584
<Button
591585
type="text"
592586
icon={<FontAwesomeIcon icon={faPenToSquare} />}
593-
onClick={() =>
594-
updateImages((x) => {
595-
x[index].editMode = true;
596-
})
597-
}
587+
onClick={() => setImageEditModeId(index)}
598588
/>
599589
</>
600590
) : (
@@ -603,15 +593,11 @@ const ConfigPanel: FC<{
603593
value={img.name}
604594
autoFocus
605595
onChange={(e) =>
606-
updateImages((x) => {
607-
x[index].name = e.target.value;
608-
})
609-
}
610-
onBlur={() =>
611-
updateImages((x) => {
612-
x[index].editMode = false;
596+
updateImages(index, (x) => {
597+
x.name = e.target.value;
613598
})
614599
}
600+
onBlur={() => setImageEditModeId(undefined)}
615601
/>
616602
)}
617603
</div>
@@ -628,18 +614,18 @@ const ConfigPanel: FC<{
628614
console.error("Error when copy.", e);
629615
message.error(
630616
"复制失败:" +
631-
(e instanceof Error ? e.message : String(e)),
617+
(e instanceof Error ? e.message : String(e))
632618
);
633-
},
619+
}
634620
)
635621
}
636622
/>
637623
<Button
638624
type="text"
639625
icon={<FontAwesomeIcon icon={faTrashCan} />}
640626
onClick={() =>
641-
updateImages((imgs) => {
642-
imgs.splice(index, 1);
627+
updateContestData((x) => {
628+
x.images.splice(index, 1);
643629
})
644630
}
645631
/>
@@ -666,11 +652,10 @@ const ConfigPanel: FC<{
666652
console.log(options);
667653
const file = options.file;
668654
if (!(file instanceof File)) throw new Error("Invalid file");
669-
updateImages((imgs) => {
670-
imgs.push({
655+
updateContestData((x) => {
656+
x.images.push({
671657
name: file.name,
672658
url: URL.createObjectURL(file),
673-
editMode: false,
674659
});
675660
});
676661
}}

src/contestEditor/index.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { type FC, useEffect, useState } from "react";
1+
import { type FC, useEffect, useRef, useState } from "react";
22
import { useImmer } from "use-immer";
33
import type { ImmerContestData } from "@/types/contestData";
44
import exampleStatements from "./exampleStatements";
@@ -17,10 +17,18 @@ import { compileToPdf, typstInitPromise } from "@/compiler";
1717

1818
const ContestEditor: FC = () => {
1919
const [contestData, updateContestData] = useImmer<ImmerContestData>(
20-
toImmerContestData(exampleStatements["SupportedGrammer"]),
20+
toImmerContestData(exampleStatements["SupportedGrammer"])
2121
);
2222
const [panel, setPanel] = useState("config");
2323
const [exportDisabled, setExportDisabled] = useState(true);
24+
const imgsUrlRef = useRef<string[]>(contestData.images.map((img) => img.url));
25+
useEffect(() => {
26+
imgsUrlRef.current = contestData.images.map((img) => img.url);
27+
}, [contestData.images]);
28+
useEffect(
29+
() => () => imgsUrlRef.current.forEach((x) => URL.revokeObjectURL(x)),
30+
[]
31+
);
2432
const { modal, notification } = App.useApp();
2533
const items: TabsProps["items"] = [
2634
{
@@ -58,7 +66,7 @@ const ContestEditor: FC = () => {
5866
const removeProblem = removeProblemCallback(
5967
modal,
6068
setPanel,
61-
updateContestData,
69+
updateContestData
6270
);
6371
return (
6472
<div className="contest-editor">

src/types/contestData.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,8 @@ export interface ImmerContestData extends ContestData<{ withMarkdown: true }> {
6363
}>["support_languages"][number] & {
6464
key: import("crypto").UUID;
6565
})[];
66+
images: {
67+
name: string;
68+
url: string;
69+
}[];
6670
}

src/utils/contestDataUtils.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type React from "react";
55
import type { Updater } from "use-immer";
66

77
export function toImmerContestData(
8-
data: ContestData<{ withMarkdown: true }>,
8+
data: ContestData<{ withMarkdown: true }>
99
): ImmerContestData {
1010
return {
1111
...data,
@@ -17,11 +17,12 @@ export function toImmerContestData(
1717
...problem,
1818
key: crypto.randomUUID(),
1919
})),
20+
images: [],
2021
};
2122
}
2223

2324
export function newProblem(
24-
contestData: ImmerContestData,
25+
contestData: ImmerContestData
2526
): ImmerContestData["problems"][number] {
2627
return {
2728
key: crypto.randomUUID(),
@@ -37,7 +38,7 @@ export function newProblem(
3738
testcase: "10",
3839
point_equal: "是",
3940
submit_filename: contestData.support_languages.map(
40-
(lang) => "problem." + lang.name,
41+
(lang) => "problem." + lang.name
4142
),
4243
pretestcase: "10",
4344
statementMarkdown: "",
@@ -47,7 +48,7 @@ export function newProblem(
4748
export function removeProblemCallback(
4849
modal: ModalHookAPI,
4950
setPanel: React.Dispatch<React.SetStateAction<string>>,
50-
updateContestData: Updater<ImmerContestData>,
51+
updateContestData: Updater<ImmerContestData>
5152
) {
5253
return async (e: string) => {
5354
if (

0 commit comments

Comments
 (0)