mirror of https://github.com/usememos/memos.git
refactor: streamline MemoView component and related hooks, removing unused code and integrating user data
This commit is contained in:
parent
64ae13839a
commit
40585607f4
|
|
@ -1,32 +1,13 @@
|
|||
import { memo, useMemo, useRef, useState } from "react";
|
||||
import { useUser } from "@/hooks/useUserQueries";
|
||||
import { cn } from "@/lib/utils";
|
||||
import type { Memo } from "@/types/proto/api/v1/memo_service_pb";
|
||||
import MemoEditor from "../MemoEditor";
|
||||
import PreviewImageDialog from "../PreviewImageDialog";
|
||||
import { MemoBody, MemoHeader } from "./components";
|
||||
import { MEMO_CARD_BASE_CLASSES } from "./constants";
|
||||
import {
|
||||
useImagePreview,
|
||||
useKeyboardShortcuts,
|
||||
useMemoActions,
|
||||
useMemoCreator,
|
||||
useMemoEditor,
|
||||
useMemoHandlers,
|
||||
useMemoViewDerivedState,
|
||||
useNsfwContent,
|
||||
} from "./hooks";
|
||||
import { useImagePreview, useKeyboardShortcuts, useMemoActions, useMemoHandlers, useMemoViewDerivedState, useNsfwContent } from "./hooks";
|
||||
import { MemoViewContext } from "./MemoViewContext";
|
||||
|
||||
interface Props {
|
||||
memo: Memo;
|
||||
compact?: boolean;
|
||||
showCreator?: boolean;
|
||||
showVisibility?: boolean;
|
||||
showPinned?: boolean;
|
||||
showNsfwContent?: boolean;
|
||||
className?: string;
|
||||
parentPage?: string;
|
||||
}
|
||||
import type { MemoViewProps } from "./types";
|
||||
|
||||
/**
|
||||
* MemoView component displays a memo card with all its content, metadata, and interactive elements.
|
||||
|
|
@ -49,17 +30,22 @@ interface Props {
|
|||
* />
|
||||
* ```
|
||||
*/
|
||||
const MemoView: React.FC<Props> = (props: Props) => {
|
||||
const MemoView: React.FC<MemoViewProps> = (props: MemoViewProps) => {
|
||||
const { memo: memoData, className } = props;
|
||||
const cardRef = useRef<HTMLDivElement>(null);
|
||||
const [reactionSelectorOpen, setReactionSelectorOpen] = useState(false);
|
||||
const [showEditor, setShowEditor] = useState(false);
|
||||
|
||||
const creator = useMemoCreator(memoData.creator);
|
||||
const creator = useUser(memoData.creator).data;
|
||||
const { isArchived, readonly, parentPage } = useMemoViewDerivedState(memoData, props.parentPage);
|
||||
const { showNSFWContent, toggleNsfwVisibility } = useNsfwContent(memoData, props.showNsfwContent);
|
||||
const { nsfw, showNSFWContent, toggleNsfwVisibility } = useNsfwContent(memoData, props.showNsfwContent);
|
||||
const { previewState, openPreview, setPreviewOpen } = useImagePreview();
|
||||
const { showEditor, openEditor, handleEditorConfirm, handleEditorCancel } = useMemoEditor();
|
||||
const { archiveMemo, unpinMemo } = useMemoActions(memoData);
|
||||
const { archiveMemo, unpinMemo } = useMemoActions(memoData, isArchived);
|
||||
|
||||
const handleEditorConfirm = () => setShowEditor(false);
|
||||
const handleEditorCancel = () => setShowEditor(false);
|
||||
const openEditor = () => setShowEditor(true);
|
||||
|
||||
const { handleGotoMemoDetailPage, handleMemoContentClick, handleMemoContentDoubleClick } = useMemoHandlers({
|
||||
memoName: memoData.name,
|
||||
parentPage,
|
||||
|
|
@ -76,15 +62,15 @@ const MemoView: React.FC<Props> = (props: Props) => {
|
|||
onArchive: archiveMemo,
|
||||
});
|
||||
|
||||
// Minimal essential context - only non-derivable data
|
||||
const contextValue = useMemo(
|
||||
() => ({
|
||||
memo: memoData,
|
||||
creator,
|
||||
parentPage,
|
||||
showNSFWContent,
|
||||
nsfw,
|
||||
}),
|
||||
[memoData, creator, parentPage, showNSFWContent],
|
||||
[memoData, creator, parentPage, showNSFWContent, nsfw],
|
||||
);
|
||||
|
||||
if (showEditor) {
|
||||
|
|
|
|||
|
|
@ -9,12 +9,12 @@ import type { User } from "@/types/proto/api/v1/user_service_pb";
|
|||
import { isSuperUser } from "@/utils/user";
|
||||
import { RELATIVE_TIME_THRESHOLD_MS } from "./constants";
|
||||
|
||||
// Minimal essential context - only data that cannot be easily derived
|
||||
export interface MemoViewContextValue {
|
||||
memo: Memo;
|
||||
creator: User | undefined;
|
||||
parentPage: string;
|
||||
showNSFWContent: boolean;
|
||||
nsfw: boolean;
|
||||
}
|
||||
|
||||
export const MemoViewContext = createContext<MemoViewContextValue | null>(null);
|
||||
|
|
@ -27,7 +27,6 @@ export const useMemoViewContext = (): MemoViewContextValue => {
|
|||
return context;
|
||||
};
|
||||
|
||||
// Utility hooks to derive common values from context
|
||||
export const useMemoViewDerived = () => {
|
||||
const { memo } = useMemoViewContext();
|
||||
const location = useLocation();
|
||||
|
|
@ -45,14 +44,11 @@ export const useMemoViewDerived = () => {
|
|||
const relativeTimeFormat: "datetime" | "auto" =
|
||||
displayTime && Date.now() - displayTime.getTime() > RELATIVE_TIME_THRESHOLD_MS ? "datetime" : "auto";
|
||||
|
||||
const nsfw = memo.tags.some((tag) => tag.toLowerCase() === "nsfw");
|
||||
|
||||
return {
|
||||
isArchived,
|
||||
readonly,
|
||||
isInMemoDetailPage,
|
||||
commentAmount,
|
||||
relativeTimeFormat,
|
||||
nsfw,
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,21 +4,13 @@ import { useTranslate } from "@/utils/i18n";
|
|||
import MemoContent from "../../MemoContent";
|
||||
import { MemoReactionListView } from "../../MemoReactionListView";
|
||||
import { AttachmentList, LocationDisplay, RelationList } from "../../memo-metadata";
|
||||
import { useMemoViewContext, useMemoViewDerived } from "../MemoViewContext";
|
||||
import { useMemoViewContext } from "../MemoViewContext";
|
||||
import type { MemoBodyProps } from "../types";
|
||||
|
||||
interface Props {
|
||||
compact?: boolean;
|
||||
onContentClick: (e: React.MouseEvent) => void;
|
||||
onContentDoubleClick: (e: React.MouseEvent) => void;
|
||||
onToggleNsfwVisibility: () => void;
|
||||
}
|
||||
|
||||
const MemoBody: React.FC<Props> = ({ compact, onContentClick, onContentDoubleClick, onToggleNsfwVisibility }) => {
|
||||
const MemoBody: React.FC<MemoBodyProps> = ({ compact, onContentClick, onContentDoubleClick, onToggleNsfwVisibility }) => {
|
||||
const t = useTranslate();
|
||||
|
||||
// Get essential context and derive other values
|
||||
const { memo, parentPage, showNSFWContent } = useMemoViewContext();
|
||||
const { nsfw } = useMemoViewDerived();
|
||||
const { memo, parentPage, showNSFWContent, nsfw } = useMemoViewContext();
|
||||
|
||||
const referencedMemos = memo.relations.filter((relation) => relation.type === MemoRelation_Type.REFERENCE);
|
||||
|
||||
|
|
|
|||
|
|
@ -13,20 +13,9 @@ import { ReactionSelector } from "../../MemoReactionListView";
|
|||
import UserAvatar from "../../UserAvatar";
|
||||
import VisibilityIcon from "../../VisibilityIcon";
|
||||
import { useMemoViewContext, useMemoViewDerived } from "../MemoViewContext";
|
||||
import type { MemoHeaderProps } from "../types";
|
||||
|
||||
interface Props {
|
||||
showCreator?: boolean;
|
||||
showVisibility?: boolean;
|
||||
showPinned?: boolean;
|
||||
onEdit: () => void;
|
||||
onGotoDetail: () => void;
|
||||
onUnpin: () => void;
|
||||
onToggleNsfwVisibility?: () => void;
|
||||
reactionSelectorOpen: boolean;
|
||||
onReactionSelectorOpenChange: (open: boolean) => void;
|
||||
}
|
||||
|
||||
const MemoHeader: React.FC<Props> = ({
|
||||
const MemoHeader: React.FC<MemoHeaderProps> = ({
|
||||
showCreator,
|
||||
showVisibility,
|
||||
showPinned,
|
||||
|
|
@ -39,9 +28,8 @@ const MemoHeader: React.FC<Props> = ({
|
|||
}) => {
|
||||
const t = useTranslate();
|
||||
|
||||
// Get essential context and derive other values
|
||||
const { memo, creator, parentPage, showNSFWContent } = useMemoViewContext();
|
||||
const { isArchived, readonly, isInMemoDetailPage, commentAmount, relativeTimeFormat, nsfw } = useMemoViewDerived();
|
||||
const { memo, creator, parentPage, showNSFWContent, nsfw } = useMemoViewContext();
|
||||
const { isArchived, readonly, isInMemoDetailPage, commentAmount, relativeTimeFormat } = useMemoViewDerived();
|
||||
|
||||
const displayTime = isArchived ? (
|
||||
(memo.displayTime ? timestampDate(memo.displayTime) : undefined)?.toLocaleString(i18n.language)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
export { useImagePreview } from "./useImagePreview";
|
||||
export { useKeyboardShortcuts } from "./useKeyboardShortcuts";
|
||||
export { useMemoActions } from "./useMemoActions";
|
||||
export { useMemoCreator } from "./useMemoCreator";
|
||||
export { useMemoEditor } from "./useMemoEditor";
|
||||
export { useMemoHandlers } from "./useMemoHandlers";
|
||||
export { useMemoViewDerivedState } from "./useMemoViewDerivedState";
|
||||
export { useNsfwContent } from "./useNsfwContent";
|
||||
|
|
|
|||
|
|
@ -5,10 +5,9 @@ import { State } from "@/types/proto/api/v1/common_pb";
|
|||
import type { Memo } from "@/types/proto/api/v1/memo_service_pb";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
|
||||
export const useMemoActions = (memo: Memo) => {
|
||||
export const useMemoActions = (memo: Memo, isArchived: boolean) => {
|
||||
const t = useTranslate();
|
||||
const { mutateAsync: updateMemo } = useUpdateMemo();
|
||||
const isArchived = memo.state === State.ARCHIVED;
|
||||
|
||||
const archiveMemo = async () => {
|
||||
if (isArchived) return;
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
import { useUser } from "@/hooks/useUserQueries";
|
||||
|
||||
export const useMemoCreator = (creatorName: string) => {
|
||||
const { data: creator } = useUser(creatorName);
|
||||
return creator;
|
||||
};
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
import { useState } from "react";
|
||||
|
||||
export const useMemoEditor = () => {
|
||||
const [showEditor, setShowEditor] = useState(false);
|
||||
|
||||
return {
|
||||
showEditor,
|
||||
openEditor: () => setShowEditor(true),
|
||||
handleEditorConfirm: () => {
|
||||
setShowEditor(false);
|
||||
},
|
||||
handleEditorCancel: () => setShowEditor(false),
|
||||
};
|
||||
};
|
||||
|
|
@ -1,28 +1,16 @@
|
|||
import { timestampDate } from "@bufbuild/protobuf/wkt";
|
||||
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 { MemoRelation_Type } from "@/types/proto/api/v1/memo_service_pb";
|
||||
import { isSuperUser } from "@/utils/user";
|
||||
import { RELATIVE_TIME_THRESHOLD_MS } from "../constants";
|
||||
|
||||
export const useMemoViewDerivedState = (memo: Memo, parentPageProp?: string) => {
|
||||
const location = useLocation();
|
||||
const user = useCurrentUser();
|
||||
|
||||
const commentAmount = memo.relations.filter(
|
||||
(relation) => relation.type === MemoRelation_Type.COMMENT && relation.relatedMemo?.name === memo.name,
|
||||
).length;
|
||||
|
||||
const displayTime = memo.displayTime ? timestampDate(memo.displayTime) : undefined;
|
||||
const relativeTimeFormat: "datetime" | "auto" =
|
||||
displayTime && Date.now() - displayTime.getTime() > RELATIVE_TIME_THRESHOLD_MS ? "datetime" : "auto";
|
||||
|
||||
const isArchived = memo.state === State.ARCHIVED;
|
||||
const readonly = memo.creator !== user?.name && !isSuperUser(user);
|
||||
const isInMemoDetailPage = location.pathname.startsWith(`/${memo.name}`);
|
||||
const parentPage = parentPageProp || location.pathname;
|
||||
|
||||
return { commentAmount, relativeTimeFormat, isArchived, readonly, isInMemoDetailPage, parentPage };
|
||||
return { isArchived, readonly, parentPage };
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue