refactor: remove dead code and deduplicate comment amount logic
This commit is contained in:
parent
3a5d3c8ff9
commit
a69e405c95
6 changed files with 39 additions and 52 deletions
|
|
@ -4,18 +4,17 @@ 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 { MemoRelation_Type } from "@/types/proto/api/v1/memo_service_pb";
|
||||
import { isSuperUser } from "@/utils/user";
|
||||
import MemoEditor from "../MemoEditor";
|
||||
import PreviewImageDialog from "../PreviewImageDialog";
|
||||
import { MemoBody, MemoCommentListView, MemoHeader } from "./components";
|
||||
import { MEMO_CARD_BASE_CLASSES } from "./constants";
|
||||
import { useImagePreview, useMemoActions, useMemoHandlers } from "./hooks";
|
||||
import { MemoViewContext } from "./MemoViewContext";
|
||||
import { computeCommentAmount, MemoViewContext } from "./MemoViewContext";
|
||||
import type { MemoViewProps } from "./types";
|
||||
|
||||
const MemoView: React.FC<MemoViewProps> = (props: MemoViewProps) => {
|
||||
const { memo: memoData, className, parentPage: parentPageProp } = props;
|
||||
const { memo: memoData, className, parentPage: parentPageProp, compact, showCreator, showVisibility, showPinned } = props;
|
||||
const cardRef = useRef<HTMLDivElement>(null);
|
||||
const [showEditor, setShowEditor] = useState(false);
|
||||
|
||||
|
|
@ -31,7 +30,7 @@ const MemoView: React.FC<MemoViewProps> = (props: MemoViewProps) => {
|
|||
const toggleNsfwVisibility = () => setShowNSFWContent((prev) => !prev);
|
||||
|
||||
const { previewState, openPreview, setPreviewOpen } = useImagePreview();
|
||||
const { unpinMemo } = useMemoActions(memoData, isArchived);
|
||||
const { unpinMemo } = useMemoActions(memoData);
|
||||
|
||||
const closeEditor = () => setShowEditor(false);
|
||||
const openEditor = () => setShowEditor(true);
|
||||
|
|
@ -46,10 +45,7 @@ const MemoView: React.FC<MemoViewProps> = (props: MemoViewProps) => {
|
|||
|
||||
const location = useLocation();
|
||||
const isInMemoDetailPage = location.pathname.startsWith(`/${memoData.name}`);
|
||||
const commentAmount = memoData.relations.filter(
|
||||
(r) => r.type === MemoRelation_Type.COMMENT && r.relatedMemo?.name === memoData.name,
|
||||
).length;
|
||||
const showCommentPreview = !isInMemoDetailPage && commentAmount > 0;
|
||||
const showCommentPreview = !isInMemoDetailPage && computeCommentAmount(memoData) > 0;
|
||||
|
||||
const contextValue = useMemo(
|
||||
() => ({
|
||||
|
|
@ -85,16 +81,16 @@ const MemoView: React.FC<MemoViewProps> = (props: MemoViewProps) => {
|
|||
tabIndex={readonly ? -1 : 0}
|
||||
>
|
||||
<MemoHeader
|
||||
showCreator={props.showCreator}
|
||||
showVisibility={props.showVisibility}
|
||||
showPinned={props.showPinned}
|
||||
showCreator={showCreator}
|
||||
showVisibility={showVisibility}
|
||||
showPinned={showPinned}
|
||||
onEdit={openEditor}
|
||||
onGotoDetail={handleGotoMemoDetailPage}
|
||||
onUnpin={unpinMemo}
|
||||
/>
|
||||
|
||||
<MemoBody
|
||||
compact={props.compact}
|
||||
compact={compact}
|
||||
onContentClick={handleMemoContentClick}
|
||||
onContentDoubleClick={handleMemoContentDoubleClick}
|
||||
onToggleNsfwVisibility={toggleNsfwVisibility}
|
||||
|
|
|
|||
|
|
@ -27,15 +27,15 @@ export const useMemoViewContext = (): MemoViewContextValue => {
|
|||
return context;
|
||||
};
|
||||
|
||||
export const computeCommentAmount = (memo: Memo): number =>
|
||||
memo.relations.filter((r) => r.type === MemoRelation_Type.COMMENT && r.relatedMemo?.name === memo.name).length;
|
||||
|
||||
export const useMemoViewDerived = () => {
|
||||
const { memo, isArchived, readonly } = useMemoViewContext();
|
||||
const location = useLocation();
|
||||
|
||||
const isInMemoDetailPage = location.pathname.startsWith(`/${memo.name}`);
|
||||
|
||||
const commentAmount = memo.relations.filter(
|
||||
(relation) => relation.type === MemoRelation_Type.COMMENT && relation.relatedMemo?.name === memo.name,
|
||||
).length;
|
||||
const commentAmount = computeCommentAmount(memo);
|
||||
|
||||
const displayTime = memo.displayTime ? timestampDate(memo.displayTime) : undefined;
|
||||
const relativeTimeFormat: "datetime" | "auto" =
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { ArrowUpRightIcon } from "lucide-react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { extractMemoIdFromName } from "@/helpers/resource-names";
|
||||
import { useMemoComments } from "@/hooks/useMemoQueries";
|
||||
import { useMemoViewContext, useMemoViewDerived } from "../MemoViewContext";
|
||||
|
||||
|
|
@ -28,11 +29,18 @@ const MemoCommentListView: React.FC = () => {
|
|||
<ArrowUpRightIcon className="w-3 h-3" />
|
||||
</Link>
|
||||
</div>
|
||||
{displayedComments.map((comment) => (
|
||||
<div key={comment.name} className="bg-muted/60 rounded-md px-2 py-1 text-xs text-muted-foreground truncate leading-relaxed">
|
||||
{comment.content}
|
||||
</div>
|
||||
))}
|
||||
{displayedComments.map((comment) => {
|
||||
const uid = extractMemoIdFromName(comment.name);
|
||||
return (
|
||||
<Link
|
||||
key={comment.name}
|
||||
to={`/${memo.name}#${uid}`}
|
||||
className="bg-muted/60 rounded-md px-2 py-1 text-xs text-muted-foreground truncate leading-relaxed hover:bg-muted transition-colors block"
|
||||
>
|
||||
{comment.content}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ export interface ImagePreviewState {
|
|||
export interface UseImagePreviewReturn {
|
||||
previewState: ImagePreviewState;
|
||||
openPreview: (url: string) => void;
|
||||
closePreview: () => void;
|
||||
setPreviewOpen: (open: boolean) => void;
|
||||
}
|
||||
|
||||
|
|
@ -19,7 +18,6 @@ export const useImagePreview = (): UseImagePreviewReturn => {
|
|||
return {
|
||||
previewState,
|
||||
openPreview: (url: string) => setPreviewState({ open: true, urls: [url], index: 0 }),
|
||||
closePreview: () => setPreviewState({ open: false, urls: [], index: 0 }),
|
||||
setPreviewOpen: (open: boolean) => setPreviewState((prev) => ({ ...prev, open })),
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,31 +1,13 @@
|
|||
import toast from "react-hot-toast";
|
||||
import { useUpdateMemo } from "@/hooks/useMemoQueries";
|
||||
import { handleError } from "@/lib/error";
|
||||
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, isArchived: boolean) => {
|
||||
const t = useTranslate();
|
||||
export const useMemoActions = (memo: Memo) => {
|
||||
const { mutateAsync: updateMemo } = useUpdateMemo();
|
||||
|
||||
const archiveMemo = async () => {
|
||||
if (isArchived) return;
|
||||
try {
|
||||
await updateMemo({ update: { name: memo.name, state: State.ARCHIVED }, updateMask: ["state"] });
|
||||
toast.success(t("message.archived-successfully"));
|
||||
} catch (error: unknown) {
|
||||
handleError(error, toast.error, {
|
||||
context: "Archive memo",
|
||||
fallbackMessage: "Failed to archive memo",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const unpinMemo = async () => {
|
||||
if (!memo.pinned) return;
|
||||
await updateMemo({ update: { name: memo.name, pinned: false }, updateMask: ["pinned"] });
|
||||
};
|
||||
|
||||
return { archiveMemo, unpinMemo };
|
||||
return { unpinMemo };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { ConnectError } from "@connectrpc/connect";
|
||||
import { ArrowUpLeftFromCircleIcon, MessageCircleIcon } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { Link, useLocation, useParams } from "react-router-dom";
|
||||
import { MemoDetailSidebar, MemoDetailSidebarDrawer } from "@/components/MemoDetailSidebar";
|
||||
|
|
@ -8,7 +8,7 @@ import MemoEditor from "@/components/MemoEditor";
|
|||
import MemoView from "@/components/MemoView";
|
||||
import MobileHeader from "@/components/MobileHeader";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { memoNamePrefix } from "@/helpers/resource-names";
|
||||
import { extractMemoIdFromName, memoNamePrefix } from "@/helpers/resource-names";
|
||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||
import useMediaQuery from "@/hooks/useMediaQuery";
|
||||
import { useMemo, useMemoComments } from "@/hooks/useMemoQueries";
|
||||
|
|
@ -47,6 +47,13 @@ const MemoDetail = () => {
|
|||
});
|
||||
const comments = commentsResponse?.memos || [];
|
||||
|
||||
const { hash } = useLocation();
|
||||
useEffect(() => {
|
||||
if (!hash || comments.length === 0) return;
|
||||
const el = document.getElementById(hash.slice(1));
|
||||
el?.scrollIntoView({ behavior: "smooth", block: "center" });
|
||||
}, [hash, comments]);
|
||||
|
||||
const showCreateCommentButton = currentUser && !showCommentEditor;
|
||||
|
||||
if (isLoading || !memo) {
|
||||
|
|
@ -135,13 +142,9 @@ const MemoDetail = () => {
|
|||
</div>
|
||||
)}
|
||||
{comments.map((comment) => (
|
||||
<MemoView
|
||||
key={`${comment.name}-${comment.displayTime}`}
|
||||
memo={comment}
|
||||
parentPage={locationState?.from}
|
||||
showCreator
|
||||
compact
|
||||
/>
|
||||
<div key={`${comment.name}-${comment.displayTime}`} id={extractMemoIdFromName(comment.name)}>
|
||||
<MemoView memo={comment} parentPage={locationState?.from} showCreator compact />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue