Skip to content

Commit 9539a52

Browse files
authored
Merge pull request #369 from qa-guru/QAGDEV-681
Подключение проекта к S3
2 parents f458eec + 69b8028 commit 9539a52

26 files changed

+705
-98
lines changed

.env.development

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ VITE_HOMEWORK_FILE_DELETE_URI=/student/homework/:homeWorkId/file/:fileId
1212
VITE_LECTURE_FILE_UPLOAD_URI=/lecture/:lectureId/file
1313
VITE_LECTURE_FILE_GET_URI=/lecture/:lectureId/file/:fileId
1414
VITE_LECTURE_FILE_DELETE_URI=/lecture/:lectureId/file/:fileId
15-
VITE_LECTURE_HOMEWORK_FILE_UPLOAD_URI=/lecture/homework/:lectureId/file
16-
VITE_LECTURE_HOMEWORK_FILE_GET_URI=/lecture/homework/:lectureId/file/:fileId
17-
VITE_LECTURE_HOMEWORK_FILE_DELETE_URI=/lecture/homework/:lectureId/file/:fileId
15+
VITE_LECTURE_HOMEWORK_FILE_UPLOAD_URI=/lecture/:lectureId/homework/file
16+
VITE_LECTURE_HOMEWORK_FILE_GET_URI=/lecture/:lectureId/homework/file/:fileId
17+
VITE_LECTURE_HOMEWORK_FILE_DELETE_URI=/lecture/:lectureId/homework/file/:fileId
18+
VITE_HOMEWORK_COMMENT_FILE_UPLOAD_URI=/homework/comment/:commentId/file
19+
VITE_HOMEWORK_COMMENT_FILE_GET_URI=/homework/comment/:commentId/file/:fileId
20+
VITE_HOMEWORK_COMMENT_FILE_DELETE_URI=/homework/comment/:commentId/file/:fileId
1821
VITE_APP_ENDPOINT="http://app-stage.qa.guru:8080"

.env.production

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ VITE_HOMEWORK_FILE_DELETE_URI=/api/student/homework/:homeWorkId/file/:fileId
1212
VITE_LECTURE_FILE_UPLOAD_URI=/api/lecture/:lectureId/file
1313
VITE_LECTURE_FILE_GET_URI=/api/lecture/:lectureId/file/:fileId
1414
VITE_LECTURE_FILE_DELETE_URI=/api/lecture/:lectureId/file/:fileId
15-
VITE_LECTURE_HOMEWORK_FILE_UPLOAD_URI=/api/lecture/homework/:lectureId/file
16-
VITE_LECTURE_HOMEWORK_FILE_GET_URI=/api/lecture/homework/:lectureId/file/:fileId
17-
VITE_LECTURE_HOMEWORK_FILE_DELETE_URI=/api/lecture/homework/:lectureId/file/:fileId
15+
VITE_LECTURE_HOMEWORK_FILE_UPLOAD_URI=/api/lecture/:lectureId/homework/file
16+
VITE_LECTURE_HOMEWORK_FILE_GET_URI=/api/lecture/:lectureId/homework/file/:fileId
17+
VITE_LECTURE_HOMEWORK_FILE_DELETE_URI=/api/lecture/:lectureId/homework/file/:fileId
18+
VITE_HOMEWORK_COMMENT_FILE_UPLOAD_URI=/api/homework/comment/:commentId/file
19+
VITE_HOMEWORK_COMMENT_FILE_GET_URI=/api/homework/comment/:commentId/file/:fileId
20+
VITE_HOMEWORK_COMMENT_FILE_DELETE_URI=/api/homework/comment/:commentId/file/:fileId

src/api/rest/auth-service.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import axios, { type AxiosResponse } from "axios";
2+
import { LOGIN_URI, LOGOUT_URI, REFRESH_TOKEN_URI } from "config";
23
import qs from "qs";
34

4-
import { LOGIN_URI, LOGOUT_URI, REFRESH_TOKEN_URI } from "../../config";
5-
65
export interface LoginResponse {
76
username: string;
87
password: string;

src/api/rest/avatar-upload-service.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import axios, { type AxiosResponse } from "axios";
2-
3-
import { AVATAR_UPLOAD_URI, AVATAR_DELETE_URI } from "../../config";
2+
import { AVATAR_DELETE_URI, AVATAR_UPLOAD_URI } from "config";
43

54
export interface AvatarUploadResponse {
65
file: string | File;
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import axios, { type AxiosResponse } from "axios";
2+
import {
3+
HOMEWORK_COMMENT_FILE_UPLOAD_URI,
4+
HOMEWORK_COMMENT_FILE_GET_URI,
5+
HOMEWORK_COMMENT_FILE_DELETE_URI,
6+
} from "config";
7+
8+
import { createUrlWithParams } from "shared/utils";
9+
10+
export interface HomeworkFileResponse {
11+
id: string;
12+
fileName: string;
13+
contentType: string;
14+
size: number;
15+
creationDate: string;
16+
}
17+
18+
export default class HomeworkCommentFileService {
19+
static uploadFile(
20+
commentId: string,
21+
file: File
22+
): Promise<AxiosResponse<HomeworkFileResponse>> {
23+
const formData = new FormData();
24+
formData.append("file", file);
25+
26+
const uploadFileUrl = createUrlWithParams(
27+
HOMEWORK_COMMENT_FILE_UPLOAD_URI,
28+
{
29+
commentId,
30+
}
31+
);
32+
33+
return axios({
34+
method: "POST",
35+
url: uploadFileUrl,
36+
headers: { "Content-Type": "multipart/form-data" },
37+
data: formData,
38+
});
39+
}
40+
41+
static getFile(
42+
commentId: string,
43+
fileId: string
44+
): Promise<AxiosResponse<Blob>> {
45+
const getFileUrl = createUrlWithParams(HOMEWORK_COMMENT_FILE_GET_URI, {
46+
commentId,
47+
fileId,
48+
});
49+
50+
return axios({
51+
method: "GET",
52+
url: getFileUrl,
53+
responseType: "blob",
54+
});
55+
}
56+
57+
static deleteFile(
58+
commentId: string,
59+
fileId: string
60+
): Promise<AxiosResponse<void>> {
61+
const deleteUrl = createUrlWithParams(HOMEWORK_COMMENT_FILE_DELETE_URI, {
62+
commentId,
63+
fileId,
64+
});
65+
66+
return axios({
67+
method: "DELETE",
68+
url: deleteUrl,
69+
});
70+
}
71+
}

src/api/rest/training-upload-service.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import axios, { type AxiosResponse } from "axios";
2+
import { TRAINING_DELETE_URI, TRAINING_UPLOAD_URI } from "config";
23

34
import { createUrlWithParams } from "shared/utils";
45

5-
import { TRAINING_DELETE_URI, TRAINING_UPLOAD_URI } from "../../config";
6-
76
export interface TrainingUploadResponse {
87
file: string | File;
98
}

src/config.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,9 @@ export const LECTURE_HOMEWORK_FILE_GET_URI = import.meta.env
2222
.VITE_LECTURE_HOMEWORK_FILE_GET_URI;
2323
export const LECTURE_HOMEWORK_FILE_DELETE_URI = import.meta.env
2424
.VITE_LECTURE_HOMEWORK_FILE_DELETE_URI;
25+
export const HOMEWORK_COMMENT_FILE_UPLOAD_URI = import.meta.env
26+
.VITE_HOMEWORK_COMMENT_FILE_UPLOAD_URI;
27+
export const HOMEWORK_COMMENT_FILE_GET_URI = import.meta.env
28+
.VITE_HOMEWORK_COMMENT_FILE_GET_URI;
29+
export const HOMEWORK_COMMENT_FILE_DELETE_URI = import.meta.env
30+
.VITE_HOMEWORK_COMMENT_FILE_DELETE_URI;

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

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { FC, useRef, useState } from "react";
44
import { SubmitHandler, useForm } from "react-hook-form";
55
import { Clear, Save } from "@mui/icons-material";
66
import { useNavigate } from "react-router-dom";
7-
import { LECTURE_FILE_GET_URI } from "config";
7+
import { LECTURE_FILE_GET_URI, LECTURE_HOMEWORK_FILE_GET_URI } from "config";
88

99
import { InputText } from "shared/components/form";
1010
import { Editor } from "shared/components/text-editor";
@@ -13,9 +13,12 @@ import { UserRole } from "api/graphql/generated/graphql";
1313
import { PendingFile } from "shared/components/text-editor/types";
1414
import { createUrlWithParams } from "shared/utils";
1515
import {
16+
useLectureFileDelete,
1617
useLectureFileUpload,
18+
useLectureHomeworkFileDelete,
1719
useLectureHomeworkFileUpload,
1820
} from "shared/hooks";
21+
import { extractFileId } from "shared/helpers";
1922

2023
import { SelectLectors } from "../../containers";
2124
import {
@@ -42,6 +45,8 @@ const EditLecture: FC<IEditLecture> = ({
4245
const [pendingFiles, setPendingFiles] = useState<PendingFile[]>([]);
4346
const { uploadLectureFile } = useLectureFileUpload();
4447
const { uploadLectureHomeworkFile } = useLectureHomeworkFileUpload();
48+
const { deleteLectureFile } = useLectureFileDelete();
49+
const { deleteLectureHomeworkFile } = useLectureHomeworkFileDelete();
4550

4651
const [description, setDescription] = useState(
4752
dataLecture?.lecture?.description!
@@ -92,7 +97,7 @@ const EditLecture: FC<IEditLecture> = ({
9297
const uploadedFile = await uploadLectureHomeworkFile(file, lectureId);
9398
return {
9499
localUrl,
95-
realUrl: createUrlWithParams(LECTURE_FILE_GET_URI, {
100+
realUrl: createUrlWithParams(LECTURE_HOMEWORK_FILE_GET_URI, {
96101
lectureId,
97102
fileId: uploadedFile?.id!,
98103
}),
@@ -153,6 +158,26 @@ const EditLecture: FC<IEditLecture> = ({
153158
})();
154159
};
155160

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+
}
169+
};
170+
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+
}
179+
};
180+
156181
return (
157182
<Container>
158183
<form onSubmit={handleSubmit(onSubmit)}>
@@ -190,6 +215,7 @@ const EditLecture: FC<IEditLecture> = ({
190215
rteRef={rteRefContent}
191216
setPendingFiles={setPendingFiles}
192217
source="lecture"
218+
handleDeleteFile={handleDeleteLectureFiles}
193219
/>
194220
</StyledInfoStack>
195221
</StyledPaper>
@@ -201,6 +227,7 @@ const EditLecture: FC<IEditLecture> = ({
201227
rteRef={rteRefContentHomeWork}
202228
setPendingFiles={setPendingFiles}
203229
source="lectureHomework"
230+
handleDeleteFile={handleDeleteHomeworkFiles}
204231
/>
205232
</StyledInfoStack>
206233
</StyledPaper>

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

Lines changed: 99 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,30 @@
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";
45
import { FC, useCallback, useState } from "react";
56

6-
import { LinkBubbleMenu, RichTextEditor } from "shared/lib/mui-tiptap";
7+
import {
8+
insertFiles,
9+
insertImages,
10+
LinkBubbleMenu,
11+
RichTextEditor,
12+
} from "shared/lib/mui-tiptap";
713
import { TableBubbleMenu, MenuButton } from "shared/lib/mui-tiptap/controls";
14+
import { extractFileId } from "shared/helpers";
815

916
import { EditorMenuControls } from "./ui";
1017
import { fileListToImageFiles } from "../utils/file-list-to-image-files";
1118
import useExtensions from "../hooks/use-extensions";
1219
import { ITextEditor } from "../types";
1320

14-
const CommentEditor: FC<ITextEditor> = ({ rteRef, content }) => {
21+
const CommentEditor: FC<ITextEditor> = ({
22+
rteRef,
23+
content,
24+
setPendingFiles,
25+
source,
26+
handleDeleteFile,
27+
}) => {
1528
const extensions = useExtensions({
1629
placeholder: "Введите текст...",
1730
});
@@ -43,6 +56,81 @@ const CommentEditor: FC<ITextEditor> = ({ rteRef, content }) => {
4356
return pastedImageFiles.length > 0;
4457
}, []);
4558

59+
const handleNewImageFiles = useCallback(
60+
(files: File[], insertPosition?: number): void => {
61+
if (!rteRef.current?.editor) return;
62+
63+
const filesWithUrl = files.map((file) => ({
64+
file,
65+
localUrl: URL.createObjectURL(file),
66+
source,
67+
}));
68+
69+
setPendingFiles?.((prev) => [...prev, ...filesWithUrl]);
70+
71+
const attributesForImageFiles = filesWithUrl.map(
72+
({ file, localUrl }) => ({
73+
src: localUrl,
74+
alt: file.name,
75+
})
76+
);
77+
78+
insertImages({
79+
images: attributesForImageFiles,
80+
editor: rteRef.current.editor,
81+
position: insertPosition,
82+
});
83+
},
84+
[rteRef, setPendingFiles]
85+
);
86+
87+
const handleNewFiles = useCallback(
88+
(files: File[], insertPosition?: number): void => {
89+
if (!rteRef.current?.editor) {
90+
return;
91+
}
92+
93+
const filesWithUrl = files.map((file) => ({
94+
file,
95+
localUrl: URL.createObjectURL(file),
96+
source,
97+
}));
98+
99+
setPendingFiles?.((prev) => [...prev, ...filesWithUrl]);
100+
101+
const attributesForFiles = filesWithUrl.map(({ localUrl, file }) => ({
102+
href: localUrl,
103+
fileName: file.name,
104+
}));
105+
106+
insertFiles({
107+
files: attributesForFiles,
108+
editor: rteRef.current.editor,
109+
position: insertPosition,
110+
});
111+
},
112+
[rteRef, setPendingFiles]
113+
);
114+
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+
46134
return (
47135
<>
48136
<Box
@@ -62,8 +150,16 @@ const CommentEditor: FC<ITextEditor> = ({ rteRef, content }) => {
62150
editorProps={{
63151
handleDrop,
64152
handlePaste,
153+
handleDOMEvents: {
154+
keydown: handleKeyDown,
155+
},
65156
}}
66-
renderControls={() => <EditorMenuControls />}
157+
renderControls={() => (
158+
<EditorMenuControls
159+
onUploadImageFiles={handleNewImageFiles}
160+
onUploadFiles={handleNewFiles}
161+
/>
162+
)}
67163
RichTextFieldProps={{
68164
variant: "outlined",
69165
MenuBarProps: {

src/shared/components/text-editor/comment-editor/ui/editor-menu-controls.tsx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,27 @@ import {
22
MenuButtonCodeBlock,
33
MenuButtonEditLink,
44
MenuButtonEmoji,
5+
MenuButtonFileUpload,
6+
MenuButtonImageUpload,
57
MenuButtonRedo,
68
MenuButtonUndo,
79
MenuButtonYoutube,
810
MenuControlsContainer,
911
MenuDivider,
1012
} from "shared/lib/mui-tiptap/controls";
1113
import { useResponsive } from "shared/hooks";
14+
import { Maybe } from "api/graphql/generated/graphql";
1215

13-
export default function EditorMenuControls() {
16+
interface EditorMenuControlsProps {
17+
homeWorkId?: Maybe<string>;
18+
onUploadImageFiles: (files: File[]) => any;
19+
onUploadFiles: (files: File[]) => any;
20+
}
21+
22+
export default function EditorMenuControls({
23+
onUploadImageFiles,
24+
onUploadFiles,
25+
}: EditorMenuControlsProps) {
1426
const { isDesktop } = useResponsive();
1527

1628
return (
@@ -29,6 +41,15 @@ export default function EditorMenuControls() {
2941

3042
<MenuDivider />
3143

44+
<MenuButtonImageUpload
45+
onUploadFiles={onUploadImageFiles}
46+
tooltipLabel="Upload images"
47+
/>
48+
<MenuButtonFileUpload
49+
onUploadFiles={onUploadFiles}
50+
tooltipLabel="Upload files"
51+
/>
52+
3253
<MenuButtonEmoji />
3354

3455
{isDesktop && (

0 commit comments

Comments
 (0)