import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useLocation } from "react-router-dom"; import { useInstance } from "@/contexts/InstanceContext"; import useCurrentUser from "@/hooks/useCurrentUser"; import { useUser } from "@/hooks/useUserQueries"; import { findTagMetadata } from "@/lib/tag"; import { cn } from "@/lib/utils"; import { State } from "@/types/proto/api/v1/common_pb"; import { isSuperUser } from "@/utils/user"; import MemoShareImageDialog from "../MemoActionMenu/MemoShareImageDialog"; import MemoEditor from "../MemoEditor"; import PreviewImageDialog from "../PreviewImageDialog"; import { MemoBody, MemoCommentListView, MemoHeader } from "./components"; import { MEMO_CARD_BASE_CLASSES } from "./constants"; import { useImagePreview } from "./hooks"; import { computeCommentAmount, MemoViewContext } from "./MemoViewContext"; import type { MemoViewProps } from "./types"; const MemoView: React.FC = (props: MemoViewProps) => { const { memo: memoData, className, parentPage: parentPageProp, compact, showCreator, showVisibility, showPinned } = props; const cardRef = useRef(null); const [showEditor, setShowEditor] = useState(false); const [cardWidth, setCardWidth] = useState(0); const currentUser = useCurrentUser(); const { tagsSetting } = useInstance(); const creator = useUser(memoData.creator).data; const isArchived = memoData.state === State.ARCHIVED; const readonly = memoData.creator !== currentUser?.name && !isSuperUser(currentUser); const location = useLocation(); const isInMemoDetailPage = location.pathname.startsWith(`/${memoData.name}`) || location.pathname.startsWith("/memos/shares/"); const currentPage = `${location.pathname}${location.search}${location.hash}`; const parentPage = parentPageProp || (isInMemoDetailPage ? "/" : currentPage); // Blur content when any tag has blur_content enabled in the instance tag settings. const [showBlurredContent, setShowBlurredContent] = useState(false); const blurred = memoData.tags?.some((tag) => findTagMetadata(tag, tagsSetting)?.blurContent) ?? false; const toggleBlurVisibility = useCallback(() => setShowBlurredContent((prev) => !prev), []); const { previewState, openPreview, setPreviewOpen } = useImagePreview(); const openEditor = useCallback(() => setShowEditor(true), []); const closeEditor = useCallback(() => setShowEditor(false), []); const showCommentPreview = !isInMemoDetailPage && computeCommentAmount(memoData) > 0; useEffect(() => { const card = cardRef.current; if (!card) { return; } const updateWidth = (nextWidth?: number) => { const width = Math.round(nextWidth ?? card.getBoundingClientRect().width); setCardWidth((prev) => (prev === width ? prev : width)); }; updateWidth(); if (typeof ResizeObserver === "undefined") { const handleResize = () => updateWidth(); window.addEventListener("resize", handleResize); return () => window.removeEventListener("resize", handleResize); } const resizeObserver = new ResizeObserver((entries) => { updateWidth(entries[0]?.contentRect.width); }); resizeObserver.observe(card); return () => resizeObserver.disconnect(); }, []); const contextValue = useMemo( () => ({ memo: memoData, creator, currentUser, parentPage, cardWidth, isArchived, readonly, showBlurredContent, blurred, openEditor, toggleBlurVisibility, openPreview, }), [ memoData, creator, currentUser, parentPage, cardWidth, isArchived, readonly, showBlurredContent, blurred, openEditor, toggleBlurVisibility, openPreview, ], ); if (showEditor) { return ( ); } const article = (
{props.onShareImageDialogOpenChange && ( )}
); return ( {showCommentPreview ? (
{article}
) : ( article )}
); }; export default memo(MemoView);