diff --git a/web/src/components/MemoView/MemoView.tsx b/web/src/components/MemoView/MemoView.tsx index deea52554..9d617b7f8 100644 --- a/web/src/components/MemoView/MemoView.tsx +++ b/web/src/components/MemoView/MemoView.tsx @@ -1,22 +1,28 @@ import { memo, useMemo, useRef, useState } from "react"; +import useCurrentUser from "@/hooks/useCurrentUser"; import { useUser } from "@/hooks/useUserQueries"; import { cn } from "@/lib/utils"; +import { State } from "@/types/proto/api/v1/common_pb"; +import { isSuperUser } from "@/utils/user"; import MemoEditor from "../MemoEditor"; import PreviewImageDialog from "../PreviewImageDialog"; import { MemoBody, MemoHeader } from "./components"; import { MEMO_CARD_BASE_CLASSES } from "./constants"; -import { useImagePreview, useMemoActions, useMemoHandlers, useMemoViewDerivedState, useNsfwContent } from "./hooks"; +import { useImagePreview, useMemoActions, useMemoHandlers, useNsfwContent } from "./hooks"; import { MemoViewContext } from "./MemoViewContext"; import type { MemoViewProps } from "./types"; const MemoView: React.FC = (props: MemoViewProps) => { - const { memo: memoData, className } = props; + const { memo: memoData, className, parentPage: parentPageProp } = props; const cardRef = useRef(null); - const [reactionSelectorOpen, setReactionSelectorOpen] = useState(false); const [showEditor, setShowEditor] = useState(false); + const currentUser = useCurrentUser(); const creator = useUser(memoData.creator).data; - const { isArchived, readonly, parentPage } = useMemoViewDerivedState(memoData, props.parentPage); + const isArchived = memoData.state === State.ARCHIVED; + const readonly = memoData.creator !== currentUser?.name && !isSuperUser(currentUser); + const parentPage = parentPageProp || "/"; + const { nsfw, showNSFWContent, toggleNsfwVisibility } = useNsfwContent(memoData, props.showNsfwContent); const { previewState, openPreview, setPreviewOpen } = useImagePreview(); const { unpinMemo } = useMemoActions(memoData, isArchived); @@ -37,11 +43,14 @@ const MemoView: React.FC = (props: MemoViewProps) => { () => ({ memo: memoData, creator, + currentUser, parentPage, + isArchived, + readonly, showNSFWContent, nsfw, }), - [memoData, creator, parentPage, showNSFWContent, nsfw], + [memoData, creator, currentUser, parentPage, isArchived, readonly, showNSFWContent, nsfw], ); if (showEditor) { @@ -68,8 +77,6 @@ const MemoView: React.FC = (props: MemoViewProps) => { onGotoDetail={handleGotoMemoDetailPage} onUnpin={unpinMemo} onToggleNsfwVisibility={toggleNsfwVisibility} - reactionSelectorOpen={reactionSelectorOpen} - onReactionSelectorOpenChange={setReactionSelectorOpen} /> { }; export const useMemoViewDerived = () => { - const { memo } = useMemoViewContext(); + const { memo, isArchived, readonly } = useMemoViewContext(); const location = useLocation(); - const currentUser = useCurrentUser(); - const isArchived = memo.state === State.ARCHIVED; - const readonly = memo.creator !== currentUser?.name && !isSuperUser(currentUser); const isInMemoDetailPage = location.pathname.startsWith(`/${memo.name}`); const commentAmount = memo.relations.filter( diff --git a/web/src/components/MemoView/components/MemoHeader.tsx b/web/src/components/MemoView/components/MemoHeader.tsx index be0077e90..3196c9973 100644 --- a/web/src/components/MemoView/components/MemoHeader.tsx +++ b/web/src/components/MemoView/components/MemoHeader.tsx @@ -1,5 +1,6 @@ import { timestampDate } from "@bufbuild/protobuf/wkt"; import { BookmarkIcon, EyeOffIcon, MessageCircleMoreIcon } from "lucide-react"; +import { useState } from "react"; import { Link } from "react-router-dom"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import i18n from "@/i18n"; @@ -23,13 +24,12 @@ const MemoHeader: React.FC = ({ onGotoDetail, onUnpin, onToggleNsfwVisibility, - reactionSelectorOpen, - onReactionSelectorOpenChange, }) => { const t = useTranslate(); + const [reactionSelectorOpen, setReactionSelectorOpen] = useState(false); - const { memo, creator, parentPage, showNSFWContent, nsfw } = useMemoViewContext(); - const { isArchived, readonly, isInMemoDetailPage, commentAmount, relativeTimeFormat } = useMemoViewDerived(); + const { memo, creator, currentUser, parentPage, isArchived, readonly, showNSFWContent, nsfw } = useMemoViewContext(); + const { isInMemoDetailPage, commentAmount, relativeTimeFormat } = useMemoViewDerived(); const displayTime = isArchived ? ( (memo.displayTime ? timestampDate(memo.displayTime) : undefined)?.toLocaleString(i18n.language) @@ -43,7 +43,6 @@ const MemoHeader: React.FC = ({ return (
- {/* Left section: Creator info or time */}
{showCreator && creator ? ( @@ -52,18 +51,15 @@ const MemoHeader: React.FC = ({ )}
- {/* Right section: Actions */}
- {/* Reaction selector */} - {!isArchived && ( + {currentUser && !isArchived && ( )} - {/* Comment count link */} {!isInMemoDetailPage && ( = ({ )} - {/* Visibility icon */} {showVisibility && memo.visibility !== Visibility.PRIVATE && ( @@ -93,7 +88,6 @@ const MemoHeader: React.FC = ({ )} - {/* Pinned indicator */} {showPinned && memo.pinned && ( @@ -109,14 +103,12 @@ const MemoHeader: React.FC = ({ )} - {/* NSFW hide button */} {nsfw && showNSFWContent && onToggleNsfwVisibility && ( )} - {/* Action menu */}
diff --git a/web/src/components/MemoView/hooks/index.ts b/web/src/components/MemoView/hooks/index.ts index 8d6c583db..65ca60b1b 100644 --- a/web/src/components/MemoView/hooks/index.ts +++ b/web/src/components/MemoView/hooks/index.ts @@ -1,5 +1,4 @@ export { useImagePreview } from "./useImagePreview"; export { useMemoActions } from "./useMemoActions"; export { useMemoHandlers } from "./useMemoHandlers"; -export { useMemoViewDerivedState } from "./useMemoViewDerivedState"; export { useNsfwContent } from "./useNsfwContent"; diff --git a/web/src/components/MemoView/hooks/useMemoViewDerivedState.ts b/web/src/components/MemoView/hooks/useMemoViewDerivedState.ts deleted file mode 100644 index e5f77a1de..000000000 --- a/web/src/components/MemoView/hooks/useMemoViewDerivedState.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { useLocation } from "react-router-dom"; -import useCurrentUser from "@/hooks/useCurrentUser"; -import { State } from "@/types/proto/api/v1/common_pb"; -import type { Memo } from "@/types/proto/api/v1/memo_service_pb"; -import { isSuperUser } from "@/utils/user"; - -export const useMemoViewDerivedState = (memo: Memo, parentPageProp?: string) => { - const location = useLocation(); - const user = useCurrentUser(); - - const isArchived = memo.state === State.ARCHIVED; - const readonly = memo.creator !== user?.name && !isSuperUser(user); - const parentPage = parentPageProp || location.pathname; - - return { isArchived, readonly, parentPage }; -}; diff --git a/web/src/components/MemoView/types.ts b/web/src/components/MemoView/types.ts index 613ab2590..527d0f057 100644 --- a/web/src/components/MemoView/types.ts +++ b/web/src/components/MemoView/types.ts @@ -1,56 +1,28 @@ import type { Memo } from "@/types/proto/api/v1/memo_service_pb"; -/** - * Props for the MemoView component. - * MemoView is the main component for displaying a memo card with all its metadata, - * content, and interactive elements. - */ export interface MemoViewProps { - /** The memo object to display */ memo: Memo; - /** Whether to show compact view (hides some metadata) */ compact?: boolean; - /** Whether to show the creator's profile information */ showCreator?: boolean; - /** Whether to show the visibility indicator */ showVisibility?: boolean; - /** Whether to show the pinned indicator */ showPinned?: boolean; - /** Whether to show NSFW content by default */ showNsfwContent?: boolean; - /** Additional CSS classes to apply to the root element */ className?: string; - /** The parent page URL for navigation context */ parentPage?: string; } -/** - * Props for the MemoHeader component. - * Displays memo metadata like creator, timestamp, and action buttons. - */ export interface MemoHeaderProps { - // Display options showCreator?: boolean; showVisibility?: boolean; showPinned?: boolean; - // Callbacks onEdit: () => void; onGotoDetail: () => void; onUnpin: () => void; onToggleNsfwVisibility?: () => void; - // Reaction state - reactionSelectorOpen: boolean; - onReactionSelectorOpenChange: (open: boolean) => void; } -/** - * Props for the MemoBody component. - * Displays memo content, attachments, and relations. - */ export interface MemoBodyProps { - // Display options compact?: boolean; - // Callbacks onContentClick: (e: React.MouseEvent) => void; onContentDoubleClick: (e: React.MouseEvent) => void; onToggleNsfwVisibility: () => void;