diff --git a/frontend/src/pages/resource_details_page.tsx b/frontend/src/pages/resource_details_page.tsx index 591c696..7c372af 100644 --- a/frontend/src/pages/resource_details_page.tsx +++ b/frontend/src/pages/resource_details_page.tsx @@ -1,7 +1,7 @@ -import { useNavigate, useParams } from "react-router"; -import { createContext, createRef, useCallback, useContext, useEffect, useRef, useState } from "react"; -import { ResourceDetails, RFile, Storage, Comment } from "../network/models.ts"; -import { network } from "../network/network.ts"; +import {useNavigate, useParams} from "react-router"; +import {createContext, createRef, useCallback, useContext, useEffect, useRef, useState} from "react"; +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"; import "../markdown.css"; @@ -14,21 +14,21 @@ import { MdOutlineDelete, MdOutlineDownload, MdOutlineEdit } from "react-icons/md"; -import { app } from "../app.ts"; -import { uploadingManager } from "../network/uploading.ts"; -import { ErrorAlert } from "../components/alert.tsx"; -import { useTranslation } from "react-i18next"; +import {app} from "../app.ts"; +import {uploadingManager} from "../network/uploading.ts"; +import {ErrorAlert} from "../components/alert.tsx"; +import {useTranslation} from "react-i18next"; import Pagination from "../components/pagination.tsx"; -import showPopup, { useClosePopup } from "../components/popup.tsx"; -import { Turnstile } from "@marsidev/react-turnstile"; +import showPopup, {useClosePopup} from "../components/popup.tsx"; +import {Turnstile} from "@marsidev/react-turnstile"; import Button from "../components/button.tsx"; -import Badge, { BadgeAccent } from "../components/badge.tsx"; +import Badge, {BadgeAccent} from "../components/badge.tsx"; import Input from "../components/input.tsx"; import {FaSteam} from "react-icons/fa"; export default function ResourcePage() { const params = useParams() - const { t } = useTranslation(); + const {t} = useTranslation(); const idStr = params.id @@ -45,7 +45,7 @@ export default function ResourcePage() { if (res.success) { setResource(res.data!) } else { - showToast({ message: res.message, type: "error" }) + showToast({message: res.message, type: "error"}) } } }, [id]) @@ -62,7 +62,7 @@ export default function ResourcePage() { setResource(res.data!) document.title = res.data!.title } else { - showToast({ message: res.message, type: "error" }) + showToast({message: res.message, type: "error"}) } }) } @@ -79,7 +79,7 @@ export default function ResourcePage() { } if (!resource) { - return + return } return @@ -98,7 +98,7 @@ export default function ResourcePage() {
- {"avatar"} + {"avatar"}/
@@ -118,63 +118,64 @@ export default function ResourcePage() {
-
+
- +
- +
{ - app.isAdmin() || app.user?.id === resource.author.id ? : null + app.isAdmin() || app.user?.id === resource.author.id ? + : null } - +
} -function DeleteResourceDialog({ resourceId, uploaderId }: { resourceId: number, uploaderId?: number }) { +function DeleteResourceDialog({resourceId, uploaderId}: { resourceId: number, uploaderId?: number }) { const [isLoading, setLoading] = useState(false) const navigate = useNavigate() - const { t } = useTranslation() + const {t} = useTranslation() const handleDelete = async () => { if (isLoading) { @@ -185,10 +186,10 @@ function DeleteResourceDialog({ resourceId, uploaderId }: { resourceId: number, const dialog = document.getElementById("delete_resource_dialog") as HTMLDialogElement dialog.close() if (res.success) { - showToast({ message: t("Resource deleted successfully"), type: "success" }) - navigate("/", { replace: true }) + showToast({message: t("Resource deleted successfully"), type: "success"}) + navigate("/", {replace: true}) } else { - showToast({ message: res.message, type: "error" }) + showToast({message: res.message, type: "error"}) } setLoading(false) } @@ -202,7 +203,7 @@ function DeleteResourceDialog({ resourceId, uploaderId }: { resourceId: number, const dialog = document.getElementById("delete_resource_dialog") as HTMLDialogElement dialog.showModal() }}> - +
@@ -223,76 +224,48 @@ function DeleteResourceDialog({ resourceId, uploaderId }: { resourceId: number, const context = createContext<() => void>(() => { }) -function Article({ resource }: { resource: ResourceDetails }) { - const articleRef = useRef(null) - +function Article({resource}: { resource: ResourceDetails }) { const navigate = useNavigate() - useEffect(() => { - if (articleRef.current) { - if (!resource.related) { - return; - } - for (const child of articleRef.current.children) { - if (child.tagName === "P" && child.children.length === 1 && child.children[0].tagName === "A") { - const href = (child.children[0] as HTMLAnchorElement).href as string - if (href.startsWith(window.location.origin) || href.startsWith("/")) { - let path = href - if (path.startsWith(window.location.origin)) { - path = path.substring(window.location.origin.length) - } - const content = child.children[0].innerHTML - if (path.startsWith("/resources/")) { - const id = path.substring("/resources/".length) - for (const r of resource.related) { - if (r.id.toString() === id) { - child.children[0].classList.add("hidden") - const div = document.createElement("div") - div.innerHTML = ` - ${child.innerHTML} -
- ${r.image ? ` -
- Cover -
- ` : ""} -
-

${r.title}

-

${content}

-
-
- ` - child.appendChild(div); - - (child as HTMLParagraphElement).onclick = (e) => { - e.stopPropagation() - e.preventDefault() - navigate(`/resources/${r.id}`) - div.remove(); - } - } - } - } - } - } - } - } - }, [navigate, resource]) - - return
+ return
{ + "a": ({node, ...props}) => { const href = props.href as string if (href.startsWith("https://store.steampowered.com/app/")) { return -
+
{props.children}
+ } else if (href.startsWith(window.location.origin) || href.startsWith("/")) { + let path = href + if (path.startsWith(window.location.origin)) { + path = path.substring(window.location.origin.length) + } + const content = props.children?.toString() + if (path.startsWith("/resources/")) { + const id = path.substring("/resources/".length) + for (const r of resource.related) { + if (r.id.toString() === id) { + return
{ + navigate(`/resources/${r.id}`) + }}> + {r.image ?
+ Cover +
: null} +
+

{r.title}

+

{content}

+
+
+ } + } + } } return {props.children} } @@ -312,10 +285,10 @@ function fileSizeToString(size: number) { } } -function FileTile({ file }: { file: RFile }) { +function FileTile({file}: { file: RFile }) { const buttonRef = createRef() - const { t } = useTranslation() + const {t} = useTranslation() return
@@ -332,19 +305,19 @@ function FileTile({ file }: { file: RFile }) { const link = network.getFileDownloadLink(file.id, ""); window.open(link, "_blank"); } else { - showPopup(, buttonRef.current!) + showPopup(, buttonRef.current!) } }}> - + - - + +
} -function CloudflarePopup({ file }: { file: RFile }) { +function CloudflarePopup({file}: { file: RFile }) { const closePopup = useClosePopup() const [isLoading, setLoading] = useState(true) @@ -365,7 +338,7 @@ function CloudflarePopup({ file }: { file: RFile }) {
} -function Files({ files, resourceID }: { files: RFile[], resourceID: number }) { +function Files({files, resourceID}: { files: RFile[], resourceID: number }) { return
{ files.map((file) => { @@ -386,8 +359,8 @@ enum FileType { upload = "upload", } -function CreateFileDialog({ resourceId }: { resourceId: number }) { - const { t } = useTranslation(); +function CreateFileDialog({resourceId}: { resourceId: number }) { + const {t} = useTranslation(); const [isLoading, setLoading] = useState(false) const storages = useRef(null) const mounted = useRef(true) @@ -433,7 +406,7 @@ function CreateFileDialog({ resourceId }: { resourceId: number }) { setSubmitting(false) const dialog = document.getElementById("upload_dialog") as HTMLDialogElement dialog.close() - showToast({ message: t("File created successfully"), type: "success" }) + showToast({message: t("File created successfully"), type: "success"}) reload() } else { setError(res.message) @@ -454,7 +427,7 @@ function CreateFileDialog({ resourceId }: { resourceId: number }) { setSubmitting(false) const dialog = document.getElementById("upload_dialog") as HTMLDialogElement dialog.close() - showToast({ message: t("Successfully create uploading task."), type: "success" }) + showToast({message: t("Successfully create uploading task."), type: "success"}) } else { setError(res.message) setSubmitting(false) @@ -474,7 +447,7 @@ function CreateFileDialog({ resourceId }: { resourceId: number }) { return; } if (!res.success) { - showToast({ message: res.message, type: "error" }) + showToast({message: res.message, type: "error"}) } else { storages.current = res.data! setLoading(false) @@ -488,7 +461,7 @@ function CreateFileDialog({ resourceId }: { resourceId: number }) { dialog.showModal() }}> { - isLoading ? : + isLoading ? : } {t("Upload")} @@ -502,13 +475,13 @@ function CreateFileDialog({ resourceId }: { resourceId: number }) {
{ setFileType(null); - }} /> + }}/> { setFileType(FileType.redirect); - }} /> + }}/> { setFileType(FileType.upload); - }} /> + }}/>
{ @@ -516,13 +489,13 @@ function CreateFileDialog({ resourceId }: { resourceId: number }) {

{t("User who click the file will be redirected to the URL")}

{ setFilename(e.target.value) - }} /> + }}/> { setRedirectUrl(e.target.value) - }} /> + }}/> { setDescription(e.target.value) - }} /> + }}/> } @@ -545,25 +518,25 @@ function CreateFileDialog({ resourceId }: { resourceId: number }) { { storages.current?.map((s) => { return + value={s.id}>{s.name}({(s.currentSize / 1024 / 1024).toFixed(2)}/{s.maxSize / 1024 / 1024}MB) }) } { - if (e.target.files) { - setFile(e.target.files[0]) - } - }} /> + if (e.target.files) { + setFile(e.target.files[0]) + } + }}/> { setDescription(e.target.value) - }} /> + }}/> } - {error && } + {error && }
@@ -579,14 +552,14 @@ function CreateFileDialog({ resourceId }: { resourceId: number }) { } -function UpdateFileInfoDialog({ file }: { file: RFile }) { +function UpdateFileInfoDialog({file}: { file: RFile }) { const [isLoading, setLoading] = useState(false) const [filename, setFilename] = useState(file.filename) const [description, setDescription] = useState(file.description) - const { t } = useTranslation() + const {t} = useTranslation() const reload = useContext(context) @@ -599,10 +572,10 @@ function UpdateFileInfoDialog({ file }: { file: RFile }) { const dialog = document.getElementById(`update_file_info_dialog_${file.id}`) as HTMLDialogElement dialog.close() if (res.success) { - showToast({ message: t("File info updated successfully"), type: "success" }) + showToast({message: t("File info updated successfully"), type: "success"}) reload() } else { - showToast({ message: res.message, type: "error" }) + showToast({message: res.message, type: "error"}) } setLoading(false) } @@ -616,13 +589,14 @@ function UpdateFileInfoDialog({ file }: { file: RFile }) { const dialog = document.getElementById(`update_file_info_dialog_${file.id}`) as HTMLDialogElement dialog.showModal() }}> - +

{t("Update File Info")}

- setFilename(e.target.value)} /> - setDescription(e.target.value)} /> + setFilename(e.target.value)}/> + setDescription(e.target.value)}/>
@@ -634,7 +608,7 @@ function UpdateFileInfoDialog({ file }: { file: RFile }) { } -function Comments({ resourceId }: { resourceId: number }) { +function Comments({resourceId}: { resourceId: number }) { const [page, setPage] = useState(1); const [maxPage, setMaxPage] = useState(0); @@ -645,7 +619,7 @@ function Comments({ resourceId }: { resourceId: number }) { const [isLoading, setLoading] = useState(false); - const { t } = useTranslation(); + const {t} = useTranslation(); const reload = useCallback(() => { setPage(1); @@ -658,17 +632,17 @@ function Comments({ resourceId }: { resourceId: number }) { return; } if (commentContent === "") { - showToast({ message: t("Comment content cannot be empty"), type: "error" }); + showToast({message: t("Comment content cannot be empty"), type: "error"}); return; } setLoading(true); const res = await network.createComment(resourceId, commentContent); if (res.success) { setCommentContent(""); - showToast({ message: t("Comment created successfully"), type: "success" }); + showToast({message: t("Comment created successfully"), type: "success"}); reload(); } else { - showToast({ message: res.message, type: "error" }); + showToast({message: res.message, type: "error"}); } setLoading(false); } @@ -676,23 +650,23 @@ function Comments({ resourceId }: { resourceId: number }) { return