Skip to content

Commit 79878f7

Browse files
authored
Merge pull request #371 from qa-guru/QAGDEV-717
QAGDEV-717 - При удалении одного файла/картинки из s3 - удаляются все, которые прикреплены к комментарию
2 parents a9a4785 + 9fa7f48 commit 79878f7

File tree

12 files changed

+179
-108
lines changed

12 files changed

+179
-108
lines changed

src/features/edit-training/views/edit-lecture/edit-lecture.tsx

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import {
1818
useLectureHomeworkFileDelete,
1919
useLectureHomeworkFileUpload,
2020
} from "shared/hooks";
21-
import { extractFileId } from "shared/helpers";
2221

2322
import { SelectLectors } from "../../containers";
2423
import {
@@ -43,6 +42,13 @@ const EditLecture: FC<IEditLecture> = ({
4342
const { enqueueSnackbar } = useSnackbar();
4443
const navigate = useNavigate();
4544
const [pendingFiles, setPendingFiles] = useState<PendingFile[]>([]);
45+
const [deletedLectureFileIds, setDeletedLectureFileIds] = useState<string[]>(
46+
[]
47+
);
48+
const [deletedHomeworkFileIds, setDeletedHomeworkFileIds] = useState<
49+
string[]
50+
>([]);
51+
4652
const { uploadLectureFile } = useLectureFileUpload();
4753
const { uploadLectureHomeworkFile } = useLectureHomeworkFileUpload();
4854
const { deleteLectureFile } = useLectureFileDelete();
@@ -130,7 +136,14 @@ const EditLecture: FC<IEditLecture> = ({
130136
variables: {
131137
input: submissionData,
132138
},
133-
onCompleted: () => {
139+
onCompleted: async () => {
140+
for (const fileId of deletedLectureFileIds) {
141+
await deleteLectureFile(lectureId, fileId);
142+
}
143+
for (const fileId of deletedHomeworkFileIds) {
144+
await deleteLectureHomeworkFile(lectureId, fileId);
145+
}
146+
134147
enqueueSnackbar("Урок обновлен", { variant: "success" });
135148
},
136149
onError: () => {
@@ -142,6 +155,8 @@ const EditLecture: FC<IEditLecture> = ({
142155
});
143156

144157
setPendingFiles([]);
158+
setDeletedLectureFileIds([]);
159+
setDeletedHomeworkFileIds([]);
145160
rteRefContent.current?.editor?.commands.clearContent();
146161
rteRefContentHomeWork.current?.editor?.commands.clearContent();
147162
};
@@ -158,24 +173,12 @@ const EditLecture: FC<IEditLecture> = ({
158173
})();
159174
};
160175

161-
const handleDeleteLectureFiles = async (content: string) => {
162-
const fileIds = extractFileId(content);
163-
164-
for (const fileId of fileIds) {
165-
if (lectureId) {
166-
await deleteLectureFile(lectureId, fileId);
167-
}
168-
}
176+
const handleDeleteLectureFile = async (fileId: string) => {
177+
setDeletedLectureFileIds((prev) => [...prev, fileId]);
169178
};
170179

171-
const handleDeleteHomeworkFiles = async (content: string) => {
172-
const fileIds = extractFileId(content);
173-
174-
for (const fileId of fileIds) {
175-
if (lectureId) {
176-
await deleteLectureHomeworkFile(lectureId, fileId);
177-
}
178-
}
180+
const handleDeleteHomeworkFile = async (fileId: string) => {
181+
setDeletedHomeworkFileIds((prev) => [...prev, fileId]);
179182
};
180183

181184
return (
@@ -215,7 +218,7 @@ const EditLecture: FC<IEditLecture> = ({
215218
rteRef={rteRefContent}
216219
setPendingFiles={setPendingFiles}
217220
source="lecture"
218-
handleDeleteFile={handleDeleteLectureFiles}
221+
handleDeleteFile={handleDeleteLectureFile}
219222
/>
220223
</StyledInfoStack>
221224
</StyledPaper>
@@ -227,7 +230,7 @@ const EditLecture: FC<IEditLecture> = ({
227230
rteRef={rteRefContentHomeWork}
228231
setPendingFiles={setPendingFiles}
229232
source="lectureHomework"
230-
handleDeleteFile={handleDeleteHomeworkFiles}
233+
handleDeleteFile={handleDeleteHomeworkFile}
231234
/>
232235
</StyledInfoStack>
233236
</StyledPaper>

src/shared/components/text-editor/comment-editor/comment-editor.tsx

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { Lock, LockOpen, TextFields } from "@mui/icons-material";
22
import { Box, Stack } from "@mui/material";
33
import type { EditorOptions } from "@tiptap/core";
4-
import { EditorView } from "@tiptap/pm/view";
54
import { FC, useCallback, useState } from "react";
65

76
import {
@@ -11,7 +10,6 @@ import {
1110
RichTextEditor,
1211
} from "shared/lib/mui-tiptap";
1312
import { TableBubbleMenu, MenuButton } from "shared/lib/mui-tiptap/controls";
14-
import { extractFileId } from "shared/helpers";
1513

1614
import { EditorMenuControls } from "./ui";
1715
import { fileListToImageFiles } from "../utils/file-list-to-image-files";
@@ -27,6 +25,9 @@ const CommentEditor: FC<ITextEditor> = ({
2725
}) => {
2826
const extensions = useExtensions({
2927
placeholder: "Введите текст...",
28+
onFileDelete: async (fileId: string) => {
29+
await handleDeleteFile?.(fileId);
30+
},
3031
});
3132
const [isEditable, setIsEditable] = useState(true);
3233
const [showMenuBar, setShowMenuBar] = useState(true);
@@ -112,25 +113,6 @@ const CommentEditor: FC<ITextEditor> = ({
112113
[rteRef, setPendingFiles]
113114
);
114115

115-
const handleKeyDown = useCallback(
116-
(view: EditorView, event: { key: string }) => {
117-
if (event.key === "Backspace" || event.key === "Delete") {
118-
const content = rteRef.current?.editor?.getHTML();
119-
120-
if (content) {
121-
const fileIds = extractFileId(content);
122-
123-
if (fileIds.length > 0) {
124-
handleDeleteFile?.(content);
125-
}
126-
}
127-
}
128-
129-
return false;
130-
},
131-
[handleDeleteFile]
132-
);
133-
134116
return (
135117
<>
136118
<Box
@@ -150,9 +132,6 @@ const CommentEditor: FC<ITextEditor> = ({
150132
editorProps={{
151133
handleDrop,
152134
handlePaste,
153-
handleDOMEvents: {
154-
keydown: handleKeyDown,
155-
},
156135
}}
157136
renderControls={() => (
158137
<EditorMenuControls

src/shared/components/text-editor/editor/editor.tsx

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@ import { Lock, LockOpen, TextFields } from "@mui/icons-material";
22
import { Box, Stack } from "@mui/material";
33
import type { EditorOptions } from "@tiptap/core";
44
import { FC, useCallback, useState } from "react";
5-
import { EditorView } from "@tiptap/pm/view";
65

76
import { insertFiles, insertImages } from "shared/lib/mui-tiptap/utils";
87
import { LinkBubbleMenu, RichTextEditor } from "shared/lib/mui-tiptap";
98
import { TableBubbleMenu, MenuButton } from "shared/lib/mui-tiptap/controls";
10-
import { extractFileId } from "shared/helpers";
119

1210
import { EditorMenuControls } from "./ui";
1311
import { ITextEditor } from "../types";
@@ -23,7 +21,11 @@ const Editor: FC<ITextEditor> = ({
2321
}) => {
2422
const extensions = useExtensions({
2523
placeholder: "Введите текст...",
24+
onFileDelete: async (fileId: string) => {
25+
await handleDeleteFile?.(fileId);
26+
},
2627
});
28+
2729
const [isEditable, setIsEditable] = useState(true);
2830
const [showMenuBar, setShowMenuBar] = useState(true);
2931

@@ -137,25 +139,6 @@ const Editor: FC<ITextEditor> = ({
137139
[handleNewImageFiles, handleNewFiles]
138140
);
139141

140-
const handleKeyDown = useCallback(
141-
(view: EditorView, event: { key: string }) => {
142-
if (event.key === "Backspace" || event.key === "Delete") {
143-
const content = rteRef.current?.editor?.getHTML();
144-
145-
if (content) {
146-
const fileIds = extractFileId(content);
147-
148-
if (fileIds.length > 0) {
149-
handleDeleteFile?.(content);
150-
}
151-
}
152-
}
153-
154-
return false;
155-
},
156-
[handleDeleteFile]
157-
);
158-
159142
return (
160143
<>
161144
<Box
@@ -175,9 +158,6 @@ const Editor: FC<ITextEditor> = ({
175158
editorProps={{
176159
handleDrop,
177160
handlePaste,
178-
handleDOMEvents: {
179-
keydown: handleKeyDown,
180-
},
181161
}}
182162
renderControls={() => (
183163
<EditorMenuControls

src/shared/components/text-editor/hooks/use-extensions.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import {
4242
ResizableImage,
4343
} from "shared/lib/mui-tiptap/extensions";
4444
import { HeadingWithAnchor } from "shared/lib/mui-tiptap/hooks";
45+
import { FileDeletionTracker } from "shared/lib/mui-tiptap/extensions/file-deletion-tracker";
4546

4647
import { mentionSuggestionOptions } from "../utils/mention-suggestion-options";
4748

@@ -160,6 +161,7 @@ export const FileNode = Node.create<FileNodeOptions>({
160161

161162
export type UseExtensionsOptions = {
162163
placeholder?: string;
164+
onFileDelete?: (fileId: string) => void;
163165
};
164166

165167
const CustomLinkExtension = Link.extend({
@@ -204,6 +206,7 @@ const CustomResizableImage = ResizableImage.extend({
204206

205207
export default function useExtensions({
206208
placeholder,
209+
onFileDelete,
207210
}: UseExtensionsOptions = {}): EditorOptions["extensions"] {
208211
return useMemo(() => {
209212
return [
@@ -281,6 +284,9 @@ export default function useExtensions({
281284
History,
282285

283286
FileNode,
287+
FileDeletionTracker.configure({
288+
onFileDelete,
289+
}),
284290
];
285-
}, [placeholder]);
291+
}, [placeholder, onFileDelete]);
286292
}

src/shared/components/text-editor/types/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,5 @@ export interface ITextEditor {
3636
content?: Maybe<string>;
3737
setPendingFiles?: React.Dispatch<React.SetStateAction<PendingFile[]>>;
3838
source: FileSourceType;
39-
handleDeleteFile?: (content: string) => Promise<void>;
39+
handleDeleteFile?: (fileId: string) => Promise<void>;
4040
}

src/shared/features/send-comment/view/send-comment.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import { CommentEditor } from "shared/components/text-editor";
55
import { type RichTextEditorRef } from "shared/lib/mui-tiptap";
66
import SendButtons from "shared/components/send-buttons";
77
import { PendingFile } from "shared/components/text-editor/types";
8-
import { useHomeworkCommentFileUpload } from "shared/hooks";
8+
import {
9+
useHomeworkCommentFileDelete,
10+
useHomeworkCommentFileUpload,
11+
} from "shared/hooks";
912
import { createUrlWithParams } from "shared/utils";
1013

1114
import { ISendComment } from "./send-comment.types";
@@ -21,7 +24,9 @@ const SendComment: FC<ISendComment> = (props) => {
2124
} = props;
2225
const rteRef = useRef<RichTextEditorRef>(null);
2326
const [pendingFiles, setPendingFiles] = useState<PendingFile[]>([]);
27+
const [deletedFileIds, setDeletedFileIds] = useState<string[]>([]);
2428
const { uploadHomeworkCommentFile } = useHomeworkCommentFileUpload();
29+
const { deleteHomeworkCommentFile } = useHomeworkCommentFileDelete();
2530
const [error, setError] = useState("");
2631

2732
const handleSendComment = async () => {
@@ -78,6 +83,10 @@ const SendComment: FC<ISendComment> = (props) => {
7883
},
7984
});
8085

86+
for (const fileId of deletedFileIds) {
87+
await deleteHomeworkCommentFile(commentId, fileId);
88+
}
89+
8190
setPendingFiles([]);
8291
setError("");
8392
rteRef.current?.editor?.commands.clearContent();
@@ -88,13 +97,24 @@ const SendComment: FC<ISendComment> = (props) => {
8897
}
8998
};
9099

100+
const handleDeleteFile = async (fileId: string) => {
101+
if (fileId.startsWith("blob:")) {
102+
setPendingFiles((prev) =>
103+
prev.filter((pending) => pending.localUrl !== fileId)
104+
);
105+
} else {
106+
setDeletedFileIds((prev) => [...prev, fileId]);
107+
}
108+
};
109+
91110
return (
92111
<form>
93112
<StyledBox>
94113
<CommentEditor
95114
rteRef={rteRef}
96115
source="comment"
97116
setPendingFiles={setPendingFiles}
117+
handleDeleteFile={handleDeleteFile}
98118
/>
99119
{error && <StyledFormHelperText>{error}</StyledFormHelperText>}
100120
</StyledBox>

src/shared/features/send-homework/view/send-homework.tsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { HOMEWORK_FILE_GET_URI } from "config";
55
import { createUrlWithParams } from "shared/utils";
66
import { type RichTextEditorRef } from "shared/lib/mui-tiptap";
77
import { Editor } from "shared/components/text-editor";
8-
import { useHomeworkFileUpload } from "shared/hooks";
8+
import { useHomeworkFileDelete, useHomeworkFileUpload } from "shared/hooks";
99
import { PendingFile } from "shared/components/text-editor/types";
1010
import SendButtons from "shared/components/send-buttons";
1111

@@ -25,7 +25,9 @@ const SendHomework: FC<ISendHomeWork> = (props) => {
2525

2626
const rteRef = useRef<RichTextEditorRef>(null);
2727
const [pendingFiles, setPendingFiles] = useState<PendingFile[]>([]);
28+
const [deletedFileIds, setDeletedFileIds] = useState<string[]>([]);
2829
const { uploadHomeworkFile } = useHomeworkFileUpload();
30+
const { deleteHomeworkFile } = useHomeworkFileDelete();
2931
const [error, setError] = useState("");
3032

3133
const handleSendHomeWork = () => {
@@ -77,13 +79,18 @@ const SendHomework: FC<ISendHomeWork> = (props) => {
7779
},
7880
});
7981

82+
for (const id of deletedFileIds) {
83+
await deleteHomeworkFile(homeWorkId, id);
84+
}
85+
8086
await sendHomeWorkToCheck({
8187
variables: {
8288
homeWorkId,
8389
},
8490
});
8591

8692
setPendingFiles([]);
93+
setDeletedFileIds([]);
8794
setError("");
8895
rteRef.current?.editor?.commands.clearContent();
8996
},
@@ -93,12 +100,23 @@ const SendHomework: FC<ISendHomeWork> = (props) => {
93100
}
94101
};
95102

103+
const handleDeleteFile = async (fileId: string) => {
104+
if (fileId.startsWith("blob:")) {
105+
setPendingFiles((prev) =>
106+
prev.filter((pending) => pending.localUrl !== fileId)
107+
);
108+
} else {
109+
setDeletedFileIds((prev) => [...prev, fileId]);
110+
}
111+
};
112+
96113
return (
97114
<form>
98115
<StyledBox>
99116
<Editor
100117
rteRef={rteRef}
101118
setPendingFiles={setPendingFiles}
119+
handleDeleteFile={handleDeleteFile}
102120
source="studentHomework"
103121
/>
104122
{error && <StyledFormHelperText>{error}</StyledFormHelperText>}

0 commit comments

Comments
 (0)