import { useCallback, useEffect, useState } from "react"; import { network } from "../network/network"; import showToast from "../components/toast"; import { useNavigate, useParams } from "react-router"; import { useTranslation } from "../utils/i18n"; import { CommentWithRef, Resource } from "../network/models"; import Loading from "../components/loading"; import Markdown from "react-markdown"; import Badge from "../components/badge"; import { CommentInput } from "../components/comment_input"; import { CommentTile } from "../components/comment_tile"; import { Comment } from "../network/models"; import Pagination from "../components/pagination"; import { MdOutlineDelete, MdOutlineEdit } from "react-icons/md"; import { TextArea } from "../components/input"; import { app } from "../app"; import { useNavigator } from "../components/navigator"; export default function CommentPage() { const params = useParams(); const commentId = params.id; const [comment, setComment] = useState(null); const { t } = useTranslation(); const navigate = useNavigate(); useEffect(() => { setComment(null); const id = parseInt(commentId || "0"); if (isNaN(id) || id <= 0) { showToast({ message: t("Invalid comment ID"), type: "error", }); return; } const preFetchData = app.getPreFetchData(); if (preFetchData?.comment?.id === id) { setComment(preFetchData.comment); return; } network.getComment(id).then((res) => { if (res.success) { setComment(res.data!); } else { showToast({ message: res.message, type: "error", }); } }); }, [commentId]); const onUpdated = useCallback(() => { setComment(null); const id = parseInt(commentId || "0"); if (isNaN(id) || id <= 0) { showToast({ message: t("Invalid comment ID"), type: "error", }); return; } network.getComment(id).then((res) => { if (res.success) { setComment(res.data!); } else { showToast({ message: res.message, type: "error", }); } }); }, []); const onDeleted = useCallback(() => { // check history length if (window.history.length > 1) { // go back to the previous page navigate(-1); } else { // if there is no previous page, go to the home page navigate("/"); } }, [navigate]); useEffect(() => { document.title = t("Comment Details"); }, [t]); const navigator = useNavigator(); useEffect(() => { if (comment?.resource && comment.resource.image) { navigator.setBackground( network.getResampledImageUrl(comment.resource.image.id), ); } else if (comment?.images?.length) { // comment images are not resampled navigator.setBackground(network.getImageUrl(comment.images[0].id)); } }, [comment]); if (!comment) { return ; } return (
{comment.resource && } {comment.reply_to && }
{t("Commented on")} {new Date(comment.created_at).toLocaleDateString()}
{app.user?.id === comment.user.id && (
)}
); } function CommentContent({ content }: { content: string }) { const lines = content.split("\n"); for (let i = 0; i < lines.length; i++) { let line = lines[i]; if (!line.endsWith(" ")) { // Ensure that each line ends with two spaces for Markdown to recognize it as a line break lines[i] = line + " "; } } content = lines.join("\n"); return {content}; } function ResourceCard({ resource }: { resource: Resource }) { const navigate = useNavigate(); let tags = resource.tags; if (tags.length > 10) { tags = tags.slice(0, 10); } const link = `/resources/${resource.id}`; return ( { e.preventDefault(); navigate(link); }} > {resource.image != null && ( cover )}

{resource.title}

{tags.map((tag) => { return ( {tag.name} ); })}

{resource.author.username}
); } function CommentReply({ comment }: { comment: CommentWithRef }) { const { t } = useTranslation(); const [page, setPage] = useState(1); const [maxPage, setMaxPage] = useState(0); const [listKey, setListKey] = useState(0); const reload = useCallback(() => { setPage(1); setMaxPage(0); setListKey((prev) => prev + 1); }, []); return ( <>

{t("Replies")}

{ setMaxPage(maxPage); }} key={listKey} reload={reload} /> {maxPage ? (
) : null} ); } function CommentsList({ commentId, page, maxPageCallback, reload, }: { commentId: number; page: number; maxPageCallback: (maxPage: number) => void; reload: () => void; }) { const [comments, setComments] = useState(null); useEffect(() => { network.listCommentReplies(commentId, page).then((res) => { if (res.success) { setComments(res.data!); maxPageCallback(res.totalPages || 1); } else { showToast({ message: res.message, type: "error", }); } }); }, [maxPageCallback, page, commentId]); if (comments == null) { return (
); } return ( <> {comments.map((comment) => { return ( ); })} ); } function EditCommentDialog({ comment, onUpdated, }: { comment: CommentWithRef; onUpdated?: () => void; }) { const [isLoading, setLoading] = useState(false); const [content, setContent] = useState(comment.content); const { t } = useTranslation(); const handleUpdate = async () => { if (isLoading) { return; } setLoading(true); const res = await network.updateComment(comment.id, content); const dialog = document.getElementById( `edit_comment_dialog_${comment.id}`, ) as HTMLDialogElement; dialog.close(); if (res.success) { showToast({ message: t("Comment updated successfully"), type: "success", }); if (onUpdated) { onUpdated(); } } else { showToast({ message: res.message, type: "error", parent: document.getElementById(`dialog_box`), }); } setLoading(false); }; return ( <> { e.preventDefault(); e.stopPropagation(); }} >

{t("Edit Comment")}