From 4873f7b9ff3f986cb2fbe07921560bca10fe7f84 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Sat, 31 Jan 2026 23:33:25 +0000 Subject: [PATCH] prefetch history --- .../page-history/components/history-item.tsx | 38 +++++++++++++---- .../page-history/components/history-list.tsx | 42 +++++++++++++++++-- 2 files changed, 68 insertions(+), 12 deletions(-) diff --git a/apps/client/src/features/page-history/components/history-item.tsx b/apps/client/src/features/page-history/components/history-item.tsx index eb348bd6..bd48e501 100644 --- a/apps/client/src/features/page-history/components/history-item.tsx +++ b/apps/client/src/features/page-history/components/history-item.tsx @@ -3,18 +3,40 @@ import { CustomAvatar } from "@/components/ui/custom-avatar.tsx"; import { formattedDate } from "@/lib/time"; import classes from "./history.module.css"; import clsx from "clsx"; +import { IPageHistory } from "@/features/page-history/types/page.types"; +import { memo, useCallback } from "react"; interface HistoryItemProps { - historyItem: any; - onSelect: (id: string) => void; + historyItem: IPageHistory; + index: number; + onSelect: (id: string, index: number) => void; + onHover?: (id: string) => void; + onHoverEnd?: () => void; isActive: boolean; } -function HistoryItem({ historyItem, onSelect, isActive }: HistoryItemProps) { +const HistoryItem = memo(function HistoryItem({ + historyItem, + index, + onSelect, + onHover, + onHoverEnd, + isActive, +}: HistoryItemProps) { + const handleClick = useCallback(() => { + onSelect(historyItem.id, index); + }, [onSelect, historyItem.id, index]); + + const handleMouseEnter = useCallback(() => { + onHover?.(historyItem.id); + }, [onHover, historyItem.id]); + return ( onSelect(historyItem.id)} + onClick={handleClick} + onMouseEnter={handleMouseEnter} + onMouseLeave={onHoverEnd} className={clsx(classes.history, { [classes.active]: isActive })} > @@ -27,11 +49,11 @@ function HistoryItem({ historyItem, onSelect, isActive }: HistoryItemProps) { - {historyItem.lastUpdatedBy.name} + {historyItem.lastUpdatedBy?.name} @@ -39,6 +61,6 @@ function HistoryItem({ historyItem, onSelect, isActive }: HistoryItemProps) { ); -} +}); export default HistoryItem; diff --git a/apps/client/src/features/page-history/components/history-list.tsx b/apps/client/src/features/page-history/components/history-list.tsx index b4a674c3..08f7c37f 100644 --- a/apps/client/src/features/page-history/components/history-list.tsx +++ b/apps/client/src/features/page-history/components/history-list.tsx @@ -2,6 +2,8 @@ import { usePageHistoryListQuery, usePageHistoryQuery, } from "@/features/page-history/queries/page-history-query"; +import { getPageHistoryById } from "@/features/page-history/services/page-history-service"; +import { queryClient } from "@/main"; import HistoryItem from "@/features/page-history/components/history-item"; import { activeHistoryIdAtom, @@ -34,6 +36,8 @@ import { SpaceCaslSubject, } from "@/features/space/permissions/permissions.type.ts"; +const PREFETCH_DELAY_MS = 300; + interface Props { pageId: string; } @@ -58,6 +62,7 @@ function HistoryList({ pageId }: Props) { ); const loadMoreRef = useRef(null); + const prefetchTimeoutRef = useRef | null>(null); const [mainEditor] = useAtom(pageEditorAtom); const [mainEditorTitle] = useAtom(titleEditorAtom); @@ -99,6 +104,35 @@ function HistoryList({ pageId }: Props) { } }, [activeHistoryData]); + const clearPrefetchTimeout = useCallback(() => { + if (prefetchTimeoutRef.current) { + clearTimeout(prefetchTimeoutRef.current); + prefetchTimeoutRef.current = null; + } + }, []); + + const handleHover = useCallback((historyId: string) => { + clearPrefetchTimeout(); + prefetchTimeoutRef.current = setTimeout(() => { + queryClient.prefetchQuery({ + queryKey: ["page-history", historyId], + queryFn: () => getPageHistoryById(historyId), + }); + }, PREFETCH_DELAY_MS); + }, [clearPrefetchTimeout]); + + useEffect(() => { + return clearPrefetchTimeout; + }, [clearPrefetchTimeout]); + + const handleSelect = useCallback( + (id: string, index: number) => { + setActiveHistoryId(id); + setActiveHistoryPrevId(historyItems[index + 1]?.id ?? ""); + }, + [historyItems, setActiveHistoryId, setActiveHistoryPrevId], + ); + useEffect(() => { if (historyItems.length > 0 && !activeHistoryId) { setActiveHistoryId(historyItems[0].id); @@ -142,10 +176,10 @@ function HistoryList({ pageId }: Props) { { - setActiveHistoryId(id); - setActiveHistoryPrevId(historyItems[index + 1]?.id ?? ""); - }} + index={index} + onSelect={handleSelect} + onHover={handleHover} + onHoverEnd={clearPrefetchTimeout} isActive={historyItem.id === activeHistoryId} /> ))}