import {useNavigate, useParams} from "react-router"; import {createContext, 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"; import Loading from "../components/loading.tsx"; import {MdAdd, MdOutlineArticle, MdOutlineComment, MdOutlineDataset, MdOutlineDownload} 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 Pagination from "../components/pagination.tsx"; export default function ResourcePage() { const params = useParams() const {t} = useTranslation(); const idStr = params.id const id = idStr ? parseInt(idStr) : NaN const [resource, setResource] = useState(null) const [page, setPage] = useState(0) const reload = useCallback(async () => { if (!isNaN(id)) { setResource(null) const res = await network.getResourceDetails(id) if (res.success) { setResource(res.data!) } else { showToast({message: res.message, type: "error"}) } } }, [id]) useEffect(() => { if (!isNaN(id)) { network.getResourceDetails(id).then((res) => { if (res.success) { setResource(res.data!) } else { showToast({message: res.message, type: "error"}) } }) } }, [id]) const navigate = useNavigate() if (isNaN(id)) { return
{t("Resource ID is required")}
} if (!resource) { return } return

{resource.title}

{ resource.alternativeTitles.map((e, i) => { return

{e}

}) }

{ resource.tags.map((e) => { return { navigate(`/tag/${e.name}`); }}>{e.name} }) }

} const context = createContext<() => void>(() => { }) function Article({article}: { article: string }) { return
{article}
} function FileTile({file}: { file: RFile }) { return

{file.filename}

{file.description}

} function Files({files, resourceID}: { files: RFile[], resourceID: number }) { return
{ files.map((file) => { return }) }
{ app.isAdmin() &&
}
} enum FileType { redirect = "redirect", upload = "upload", } function CreateFileDialog({resourceId}: { resourceId: number }) { const {t} = useTranslation(); const [isLoading, setLoading] = useState(false) const storages = useRef(null) const mounted = useRef(true) const [fileType, setFileType] = useState(null) const [filename, setFilename] = useState("") const [redirectUrl, setRedirectUrl] = useState("") const [storage, setStorage] = useState(null) const [file, setFile] = useState(null) const [description, setDescription] = useState("") const reload = useContext(context) const [isSubmitting, setSubmitting] = useState(false) const [error, setError] = useState(null) useEffect(() => { mounted.current = true return () => { mounted.current = false } }, []); const submit = async () => { if (isSubmitting) { return } if (!fileType) { setError(t("Please select a file type")) return } setSubmitting(true) if (fileType === FileType.redirect) { if (!redirectUrl || !filename || !description) { setError(t("Please fill in all fields")); setSubmitting(false); return; } const res = await network.createRedirectFile(filename, description, resourceId, redirectUrl); if (res.success) { setSubmitting(false) const dialog = document.getElementById("upload_dialog") as HTMLDialogElement dialog.close() showToast({message: t("File created successfully"), type: "success"}) reload() } else { setError(res.message) setSubmitting(false) } } else { if (!file || !storage) { setError(t("Please select a file and storage")) setSubmitting(false) return } const res = await uploadingManager.addTask(file, resourceId, storage.id, description, () => { if (mounted.current) { reload(); } }); if (res.success) { setSubmitting(false) const dialog = document.getElementById("upload_dialog") as HTMLDialogElement dialog.close() showToast({message: t("Successfully create uploading task."), type: "success"}) } else { setError(res.message) setSubmitting(false) } } } return <>

{t("Create File")}

{t("Type")}

{ setFileType(null); }}/> { setFileType(FileType.redirect); }}/> { setFileType(FileType.upload); }}/>
{ fileType === FileType.redirect && <>

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

{ setFilename(e.target.value) }}/> { setRedirectUrl(e.target.value) }}/> { setDescription(e.target.value) }}/> } { fileType === FileType.upload && <>

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

{ if (e.target.files) { setFile(e.target.files[0]) } }}/> { setDescription(e.target.value) }}/> } {error && }
} 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