diff --git a/frontend/src/components/loading.tsx b/frontend/src/components/loading.tsx index 07f3430..716260c 100644 --- a/frontend/src/components/loading.tsx +++ b/frontend/src/components/loading.tsx @@ -4,7 +4,7 @@ export default function Loading() { const {t} = useTranslation(); return
- + {t("Loading")}
; } \ No newline at end of file diff --git a/frontend/src/components/navigator.tsx b/frontend/src/components/navigator.tsx index 73abf0d..c189fa8 100644 --- a/frontend/src/components/navigator.tsx +++ b/frontend/src/components/navigator.tsx @@ -5,6 +5,7 @@ import {useEffect, useState} from "react"; import {MdOutlinePerson, MdSearch, MdSettings} from "react-icons/md"; import { useTranslation } from "react-i18next"; import UploadingSideBar from "./uploading_side_bar.tsx"; +import {IoLogoGithub} from "react-icons/io"; export default function Navigator() { const outlet = useOutlet() @@ -29,6 +30,11 @@ export default function Navigator() { } + { app.isLoggedIn() ? : ); - } - if (page - 2 > 1) { - items.push(); - } - if (page-1 > 1) { - items.push(); - } - items.push(); - if (page+1 < totalPages) { - items.push(); - } - if (page+2 < totalPages) { - items.push(); - } - if (page < totalPages) { - items.push(); - } + if (page > 1) { + items.push(); + } + if (page - 2 > 1) { + items.push(); + } + if (page - 1 > 1) { + items.push(); + } + items.push(); + if (page + 1 < totalPages) { + items.push(); + } + if (page + 2 < totalPages) { + items.push(); + } + if (page < totalPages) { + items.push(); + } - return
- - {items} - -
+ return
+ + {items} + +
} \ No newline at end of file diff --git a/frontend/src/network/models.ts b/frontend/src/network/models.ts index 6da5f66..32ec4e5 100644 --- a/frontend/src/network/models.ts +++ b/frontend/src/network/models.ts @@ -89,3 +89,10 @@ export interface UploadingFile { storageId: number; resourceId: number; } + +export interface Comment { + id: number; + content: string; + created_at: string; + user: User; +} diff --git a/frontend/src/network/network.ts b/frontend/src/network/network.ts index eb4b9f3..54d3625 100644 --- a/frontend/src/network/network.ts +++ b/frontend/src/network/network.ts @@ -11,7 +11,8 @@ import { Tag, UploadingFile, User, - UserWithToken + UserWithToken, + Comment } from "./models.ts"; class Network { @@ -533,6 +534,28 @@ class Network { getFileDownloadLink(fileId: string): string { return `${this.apiBaseUrl}/files/download/${fileId}`; } + + async createComment(resourceID: number, content: string): Promise> { + try { + const response = await axios.postForm(`${this.apiBaseUrl}/comments/${resourceID}`, { content }); + return response.data; + } catch (e: any) { + console.error(e); + return { success: false, message: e.toString() }; + } + } + + async listComments(resourceID: number, page: number = 1): Promise> { + try { + const response = await axios.get(`${this.apiBaseUrl}/comments/${resourceID}`, { + params: { page } + }); + return response.data; + } catch (e: any) { + console.error(e); + return { success: false, message: e.toString() }; + } + } } export const network = new Network(); diff --git a/frontend/src/pages/resource_details_page.tsx b/frontend/src/pages/resource_details_page.tsx index a8e307c..c728176 100644 --- a/frontend/src/pages/resource_details_page.tsx +++ b/frontend/src/pages/resource_details_page.tsx @@ -1,6 +1,6 @@ import {useParams} from "react-router"; import {createContext, useCallback, useContext, useEffect, useRef, useState} from "react"; -import {ResourceDetails, RFile, Storage} from "../network/models.ts"; +import {ResourceDetails, RFile, Storage, Comment} from "../network/models.ts"; import {network} from "../network/network.ts"; import showToast from "../components/toast.ts"; import Markdown from "react-markdown"; @@ -10,11 +10,12 @@ import {MdAdd, MdOutlineArticle, MdOutlineComment, MdOutlineDataset, MdOutlineDo import {app} from "../app.ts"; import {uploadingManager} from "../network/uploading.ts"; import {ErrorAlert} from "../components/alert.tsx"; -import { useTranslation } from "react-i18next"; +import {useTranslation} from "react-i18next"; +import Pagination from "../components/pagination.tsx"; export default function ResourcePage() { const params = useParams() - const { t } = useTranslation(); + const {t} = useTranslation(); const idStr = params.id @@ -123,7 +124,9 @@ export default function ResourcePage() { {t("Comments")} -
{t("Comments")}
+
+ +
@@ -180,7 +183,7 @@ enum FileType { } function CreateFileDialog({resourceId}: { resourceId: number }) { - const { t } = useTranslation(); + const {t} = useTranslation(); const [isLoading, setLoading] = useState(false) const storages = useRef(null) const mounted = useRef(true) @@ -321,7 +324,8 @@ function CreateFileDialog({resourceId}: { resourceId: number }) { { fileType === FileType.upload && <> -

{t("Upload a file to server, then the file will be moved to the selected storage.")}

+

{t("Upload a file to server, then the file will be moved to the selected storage.")}

@@ -370,3 +375,113 @@ function CreateFileDialog({resourceId}: { resourceId: number }) { } +function Comments({resourceId}: { resourceId: number }) { + const [page, setPage] = useState(1); + + const [maxPage, setMaxPage] = useState(0); + + const [listKey, setListKey] = useState(0); + + const [commentContent, setCommentContent] = useState(""); + + const [isLoading, setLoading] = useState(false); + + const reload = useCallback(() => { + setPage(1); + setMaxPage(0); + setListKey(prev => prev + 1); + }, []) + + const sendComment = async () => { + if (isLoading) { + return; + } + if (commentContent === "") { + showToast({message: "Comment content cannot be empty", type: "error"}); + return; + } + setLoading(true); + const res = await network.createComment(resourceId, commentContent); + if (res.success) { + setCommentContent(""); + showToast({message: "Comment created successfully", type: "success"}); + reload(); + } else { + showToast({message: res.message, type: "error"}); + } + setLoading(false); + } + + return
+
+