import { Select, Tooltip, Option, IconButton } from "@mui/joy"; import copy from "copy-to-clipboard"; import { ClientError } from "nice-grpc-web"; import { useEffect, useState } from "react"; import { toast } from "react-hot-toast"; import { Link, useParams } from "react-router-dom"; import Icon from "@/components/Icon"; import MemoContent from "@/components/MemoContent"; import MemoEditor from "@/components/MemoEditor"; import showMemoEditorDialog from "@/components/MemoEditor/MemoEditorDialog"; import MemoRelationListView from "@/components/MemoRelationListView"; import MemoResourceListView from "@/components/MemoResourceListView"; import MemoView from "@/components/MemoView"; import MobileHeader from "@/components/MobileHeader"; import showShareMemoDialog from "@/components/ShareMemoDialog"; import UserAvatar from "@/components/UserAvatar"; import VisibilityIcon from "@/components/VisibilityIcon"; import { getDateTimeString } from "@/helpers/datetime"; import useCurrentUser from "@/hooks/useCurrentUser"; import useNavigateTo from "@/hooks/useNavigateTo"; import { useUserStore, useMemoStore, extractUsernameFromName } from "@/store/v1"; import { MemoRelation_Type } from "@/types/proto/api/v2/memo_relation_service"; import { Memo, Visibility } from "@/types/proto/api/v2/memo_service"; import { User } from "@/types/proto/api/v2/user_service"; import { useTranslate } from "@/utils/i18n"; import { convertVisibilityToString } from "@/utils/memo"; const MemoDetail = () => { const t = useTranslate(); const params = useParams(); const navigateTo = useNavigateTo(); const currentUser = useCurrentUser(); const memoStore = useMemoStore(); const userStore = useUserStore(); const [creator, setCreator] = useState(); const memoId = Number(params.memoId); const memo = memoStore.getMemoById(memoId); const [parentMemo, setParentMemo] = useState(undefined); const referenceRelations = memo?.relations.filter((relation) => relation.type === MemoRelation_Type.REFERENCE) || []; const commentRelations = memo?.relations.filter((relation) => relation.relatedMemoId === memo?.id && relation.type === MemoRelation_Type.COMMENT) || []; const comments = commentRelations.map((relation) => memoStore.getMemoById(relation.memoId)).filter((memo) => memo) as any as Memo[]; const readonly = memo?.creatorId !== currentUser?.id; // Prepare memo. useEffect(() => { if (memoId && !isNaN(memoId)) { memoStore .getOrFetchMemoById(memoId) .then(async (memo) => { const user = await userStore.getOrFetchUserByUsername(extractUsernameFromName(memo.creator)); setCreator(user); }) .catch((error: ClientError) => { toast.error(error.details); navigateTo("/403"); }); } else { navigateTo("/404"); } }, [memoId]); // Prepare memo comments. useEffect(() => { if (!memo) { return; } (async () => { if (memo.parentId) { memoStore.getOrFetchMemoById(memo.parentId).then((memo: Memo) => { setParentMemo(memo); }); } else { setParentMemo(undefined); } await Promise.all(commentRelations.map((relation) => memoStore.getOrFetchMemoById(relation.memoId))); })(); }, [memo]); if (!memo) { return null; } const handleMemoVisibilityOptionChanged = async (visibility: Visibility) => { await memoStore.updateMemo( { id: memo.id, visibility: visibility, }, ["visibility"] ); }; const handleEditMemoClick = () => { showMemoEditorDialog({ memoId: memo.id, cacheKey: `${memo.id}-${memo.updateTime}`, }); }; const handleCopyLinkBtnClick = () => { copy(`${window.location.origin}/m/${memo.id}`); toast.success(t("message.succeed-copy-link")); }; const handleCommentCreated = async (commentId: number) => { await memoStore.getOrFetchMemoById(commentId); await memoStore.getOrFetchMemoById(memo.id, { skipCache: true }); }; return (
{creator?.nickname} {getDateTimeString(memo.displayTime)}
{parentMemo && (
{parentMemo.content}
)}
{!readonly && ( )}
{!readonly && ( )} showShareMemoDialog(memo)}>
{comments.length === 0 ? (

{t("memo.comment.no-comment")}

) : ( <>
{t("memo.comment.self")} ({comments.length})
{comments.map((comment) => ( ))} )} {/* Only show comment editor when user login */} {currentUser && ( )}
); }; export default MemoDetail;