fix(comments): list all memo comments via pagination
Memo detail only showed the first 10 comments: the frontend requested pageSize=0, which the backend normalizes to DefaultPageSize (10), and the returned nextPageToken was never followed. Add useInfiniteMemoComments (mirrors useInfiniteMemos) so the detail page paginates through every comment, with a "Load more" control in MemoCommentSection. Page size defaults to DEFAULT_LIST_MEMOS_PAGE_SIZE to match the memo list convention.
This commit is contained in:
parent
f5a60263b2
commit
f727ad217c
3 changed files with 53 additions and 6 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { MessageCircleIcon } from "lucide-react";
|
||||
import { LoaderCircleIcon, MessageCircleIcon } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import MemoEditor from "@/components/MemoEditor";
|
||||
import MemoView from "@/components/MemoView";
|
||||
|
|
@ -12,9 +12,12 @@ interface Props {
|
|||
memo: Memo;
|
||||
comments: Memo[];
|
||||
parentPage?: string;
|
||||
hasMoreComments?: boolean;
|
||||
isFetchingMoreComments?: boolean;
|
||||
onLoadMoreComments?: () => void;
|
||||
}
|
||||
|
||||
const MemoCommentSection = ({ memo, comments, parentPage }: Props) => {
|
||||
const MemoCommentSection = ({ memo, comments, parentPage, hasMoreComments, isFetchingMoreComments, onLoadMoreComments }: Props) => {
|
||||
const t = useTranslate();
|
||||
const currentUser = useCurrentUser();
|
||||
const [showEditor, setShowEditor] = useState(false);
|
||||
|
|
@ -71,6 +74,14 @@ const MemoCommentSection = ({ memo, comments, parentPage }: Props) => {
|
|||
<MemoView memo={comment} parentPage={parentPage} showCreator compact />
|
||||
</div>
|
||||
))}
|
||||
{hasMoreComments && (
|
||||
<div className="w-full mt-4 flex justify-center">
|
||||
<Button variant="outline" className="rounded-full px-4" onClick={onLoadMoreComments} disabled={isFetchingMoreComments}>
|
||||
{isFetchingMoreComments && <LoaderCircleIcon className="h-4 w-4 animate-spin" />}
|
||||
{t(isFetchingMoreComments ? "resource.fetching-data" : "memo.load-more")}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { FieldMaskSchema } from "@bufbuild/protobuf/wkt";
|
|||
import type { InfiniteData } from "@tanstack/react-query";
|
||||
import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { memoServiceClient } from "@/connect";
|
||||
import { DEFAULT_LIST_MEMOS_PAGE_SIZE } from "@/helpers/consts";
|
||||
import { userKeys } from "@/hooks/useUserQueries";
|
||||
import type { ListMemosRequest, ListMemosResponse, Memo } from "@/types/proto/api/v1/memo_service_pb";
|
||||
import { ListMemoCommentsRequestSchema, ListMemosRequestSchema, MemoSchema } from "@/types/proto/api/v1/memo_service_pb";
|
||||
|
|
@ -284,3 +285,27 @@ export function useMemoComments(name: string, options?: { enabled?: boolean; pag
|
|||
staleTime: 1000 * 60, // 1 minute
|
||||
});
|
||||
}
|
||||
|
||||
// useInfiniteMemoComments paginates through every comment via nextPageToken, instead of
|
||||
// stopping at the server's default page size (the cause of comments being truncated to 10).
|
||||
export function useInfiniteMemoComments(name: string, options?: { enabled?: boolean; pageSize?: number }) {
|
||||
const pageSize = options?.pageSize ?? DEFAULT_LIST_MEMOS_PAGE_SIZE;
|
||||
return useInfiniteQuery({
|
||||
queryKey: [...memoKeys.comments(name), "infinite", pageSize],
|
||||
queryFn: async ({ pageParam }) => {
|
||||
const response = await memoServiceClient.listMemoComments(
|
||||
create(ListMemoCommentsRequestSchema, {
|
||||
name,
|
||||
pageSize,
|
||||
pageToken: pageParam || "",
|
||||
}),
|
||||
);
|
||||
return response;
|
||||
},
|
||||
initialPageParam: "",
|
||||
getNextPageParam: (lastPage) => lastPage.nextPageToken || undefined,
|
||||
select: (data) => data.pages.flatMap((page) => page.memos),
|
||||
enabled: options?.enabled ?? true,
|
||||
staleTime: 1000 * 60, // 1 minute
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import MobileHeader from "@/components/MobileHeader";
|
|||
import { memoNamePrefix } from "@/helpers/resource-names";
|
||||
import useMediaQuery from "@/hooks/useMediaQuery";
|
||||
import useMemoDetailError from "@/hooks/useMemoDetailError";
|
||||
import { useMemo, useMemoComments } from "@/hooks/useMemoQueries";
|
||||
import { useInfiniteMemoComments, useMemo } from "@/hooks/useMemoQueries";
|
||||
import { useSharedMemo, withShareAttachmentLinks } from "@/hooks/useMemoShareQueries";
|
||||
import { cn } from "@/lib/utils";
|
||||
import type { Attachment } from "@/types/proto/api/v1/attachment_service_pb";
|
||||
|
|
@ -48,10 +48,14 @@ const MemoDetail = () => {
|
|||
enabled: !!memo?.parent,
|
||||
});
|
||||
|
||||
const { data: commentsResponse } = useMemoComments(memoName, {
|
||||
const {
|
||||
data: comments = [],
|
||||
fetchNextPage: fetchNextComments,
|
||||
hasNextPage: hasNextComments,
|
||||
isFetchingNextPage: isFetchingNextComments,
|
||||
} = useInfiniteMemoComments(memoName, {
|
||||
enabled: !!memo,
|
||||
});
|
||||
const comments = commentsResponse?.memos || [];
|
||||
|
||||
useEffect(() => {
|
||||
if (!hash || comments.length === 0) return;
|
||||
|
|
@ -110,7 +114,14 @@ const MemoDetail = () => {
|
|||
showPinned
|
||||
onShareImageDialogOpenChange={setShareImageDialogOpen}
|
||||
/>
|
||||
<MemoCommentSection memo={displayMemo} comments={comments} parentPage={locationState?.from} />
|
||||
<MemoCommentSection
|
||||
memo={displayMemo}
|
||||
comments={comments}
|
||||
parentPage={locationState?.from}
|
||||
hasMoreComments={hasNextComments}
|
||||
isFetchingMoreComments={isFetchingNextComments}
|
||||
onLoadMoreComments={fetchNextComments}
|
||||
/>
|
||||
</div>
|
||||
{md && (
|
||||
<div className="sticky top-0 left-0 shrink-0 -mt-6 w-56 h-full">
|
||||
|
|
|
|||
Loading…
Reference in a new issue