diff --git a/frontend/src/components/comment_tile.tsx b/frontend/src/components/comment_tile.tsx index 8c912c1..1c1cffb 100644 --- a/frontend/src/components/comment_tile.tsx +++ b/frontend/src/components/comment_tile.tsx @@ -1,6 +1,5 @@ import { useTranslation } from "../utils/i18n"; import { useNavigate } from "react-router"; -import { MdOutlineComment } from "react-icons/md"; import { Comment } from "../network/models"; import { network } from "../network/network"; import Badge from "./badge"; @@ -55,29 +54,66 @@ export function CommentTile({ {new Date(comment.created_at).toLocaleDateString()} -
+
-
- {comment.content_truncated && ( - {t("Click to view more")} - )} - - {comment.reply_count > 0 && ( - - - {comment.reply_count} + {comment.content_truncated ? ( +
+ + {t("Click to view more")} - )} -
+
+ ) : ( +
+ )} + ); } +function CommentReplies({ comment }: { comment: Comment }) { + const { t } = useTranslation(); + + if (!comment.replies) { + return null; + } + + return ( +
+ {comment.replies.map((e) => { + return ( +

+ {e.user.username}: + {CommentToPlainText(e.content)} +

+ ); + })} + {comment.reply_count > comment.replies.length ? ( +

+ {t("View {count} more replies").replace( + "{count}", + (comment.reply_count - comment.replies.length).toString(), + )} +

+ ) : null} +
+ ); +} + +function CommentToPlainText(content: string) { + // Remove Markdown syntax to convert to plain text + return content + .replace(/!\[.*?]\(.*?\)/g, "") // Remove images + .replace(/\[([^\]]+)]\((.*?)\)/g, "$1") // Convert links to just the text + .replace(/[#>*_`~-]/g, "") // Remove other Markdown characters + .replace(/\n+/g, " ") // Replace newlines with spaces + .trim(); +} + export function CommentContent({ content }: { content: string }) { const lines = content.split("\n"); for (let i = 0; i < lines.length; i++) { - let line = lines[i]; + const 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 + " "; diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts index b98a846..34d2df6 100644 --- a/frontend/src/i18n.ts +++ b/frontend/src/i18n.ts @@ -254,6 +254,7 @@ export const i18nData = { "You do not have permission to upload files, please contact the administrator.": "您没有上传文件的权限,请联系管理员。", "Private": "私有", + "View {count} more replies": "查看另外 {count} 条回复", }, }, "zh-TW": { @@ -511,6 +512,7 @@ export const i18nData = { "You do not have permission to upload files, please contact the administrator.": "您沒有上傳檔案的權限,請聯繫管理員。", "Private": "私有", + "View {count} more replies": "查看另外 {count} 條回覆", }, }, }; diff --git a/frontend/src/network/models.ts b/frontend/src/network/models.ts index 57ff7e1..af79b4b 100644 --- a/frontend/src/network/models.ts +++ b/frontend/src/network/models.ts @@ -126,6 +126,7 @@ export interface Comment { images: Image[]; content_truncated: boolean; reply_count: number; + replies: Comment[]; } export interface CommentWithResource { diff --git a/frontend/src/utils/i18n.ts b/frontend/src/utils/i18n.ts index cb28e5c..13e8f1e 100644 --- a/frontend/src/utils/i18n.ts +++ b/frontend/src/utils/i18n.ts @@ -1,7 +1,7 @@ import { createContext, useContext, useMemo } from "react"; function t(data: any, language: string) { - return (key: string) => { + return (key: string): string => { return data[language]?.["translation"]?.[key] || key; }; }