From 73768eebc92aeb1a37ded87cfd34fceecc4a6cdf Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Wed, 16 Apr 2025 10:46:20 -0600 Subject: [PATCH] prefetcher for version history panel --- .../api/buster_rest/metrics/queryRequests.ts | 31 +++++ .../versionHistory/VersionHistoryPanel.tsx | 41 +++++-- .../useListVersionHistories.tsx | 108 +++++++++++++----- 3 files changed, 145 insertions(+), 35 deletions(-) diff --git a/web/src/api/buster_rest/metrics/queryRequests.ts b/web/src/api/buster_rest/metrics/queryRequests.ts index 6fb7f5954..0cd6f1979 100644 --- a/web/src/api/buster_rest/metrics/queryRequests.ts +++ b/web/src/api/buster_rest/metrics/queryRequests.ts @@ -118,6 +118,30 @@ export const useGetMetric = ( }); }; +export const prefetchGetMetricClient = async ( + { id, versionNumber }: { id: string; versionNumber: number | undefined }, + queryClient: QueryClient +) => { + const options = metricsQueryKeys.metricsGetMetric(id, versionNumber); + const existingData = queryClient.getQueryData(options.queryKey); + if (!existingData) { + await queryClient.prefetchQuery({ + ...options, + queryFn: async () => { + const result = await getMetric({ id, version_number: versionNumber }); + return upgradeMetricToIMetric(result, null); + } + }); + } +}; + +export const usePrefetchGetMetricClient = () => { + const queryClient = useQueryClient(); + return useMemoizedFn(({ id, versionNumber }: { id: string; versionNumber: number | undefined }) => + prefetchGetMetricClient({ id, versionNumber }, queryClient) + ); +}; + export const useGetMetricsList = ( params: Omit[0], 'page_token' | 'page_size'> ) => { @@ -206,6 +230,13 @@ export const prefetchGetMetricDataClient = async ( } }; +export const usePrefetchGetMetricDataClient = () => { + const queryClient = useQueryClient(); + return useMemoizedFn(({ id, versionNumber }: { id: string; versionNumber: number | undefined }) => + prefetchGetMetricDataClient({ id, version_number: versionNumber }, queryClient) + ); +}; + /** * This is a mutation that saves a metric to the server. * It will simply use the params passed in and not do any special logic. diff --git a/web/src/components/features/versionHistory/VersionHistoryPanel.tsx b/web/src/components/features/versionHistory/VersionHistoryPanel.tsx index 0bc63ae05..0e9f4f5e4 100644 --- a/web/src/components/features/versionHistory/VersionHistoryPanel.tsx +++ b/web/src/components/features/versionHistory/VersionHistoryPanel.tsx @@ -8,20 +8,27 @@ import { cn } from '@/lib/classMerge'; import { timeFromNow, timeout } from '@/lib'; import { AppPageLayout } from '@/components/ui/layouts'; import { useListVersionHistories } from './useListVersionHistories'; -import { useMount } from '@/hooks'; +import { useMemoizedFn, useMount } from '@/hooks'; import { AppTooltip } from '@/components/ui/tooltip'; import Link from 'next/link'; import { useGetFileLink } from '@/context/Assets/useGetFileLink'; import { useChatLayoutContextSelector } from '@/layouts/ChatLayout'; +import { useRouter } from 'next/navigation'; +import { useCallback } from 'react'; export const VersionHistoryPanel = React.memo( ({ assetId, type }: { assetId: string; type: 'metric' | 'dashboard' }) => { const chatId = useChatLayoutContextSelector((x) => x.chatId); - const { listItems, currentVersionNumber, selectedQueryVersion, onClickRestoreVersion } = - useListVersionHistories({ - assetId, - type - }); + const { + listItems, + onPrefetchAsset, + currentVersionNumber, + selectedQueryVersion, + onClickRestoreVersion + } = useListVersionHistories({ + assetId, + type + }); const { getFileLink } = useGetFileLink(); const bodyRef = useRef(null); @@ -51,6 +58,7 @@ export const VersionHistoryPanel = React.memo( void; + onPrefetchAsset: (versionNumber: number, link: string) => Promise; link: string; }) => { + const routePrefetchTimeoutRef = useRef(); + + const onHoverLink = useMemoizedFn(() => { + // Prefetch route after 50ms + routePrefetchTimeoutRef.current = setTimeout(() => { + onPrefetchAsset(version_number, link); + }, 125); + }); + + const onHoverEnd = useCallback(() => { + if (routePrefetchTimeoutRef.current) { + clearTimeout(routePrefetchTimeoutRef.current); + } + }, []); + return (
{ + const router = useRouter(); const { onCloseVersionHistory } = useCloseVersionHistory(); const onChangePage = useAppLayoutContextSelector((x) => x.onChangePage); const { - dashboardVersions, + versions: dashboardVersions, selectedQueryVersion: dashboardSelectedQueryVersion, currentVersionNumber: dashboardCurrentVersionNumber, onRestoreVersion: onRestoreDashboardVersion, - isSavingDashboard + isSaving: isSavingDashboard, + onPrefetchAsset: onPrefetchDashboardAsset } = useListDashboardVersions({ assetId, type }); const { - metricVersions, + versions: metricVersions, selectedQueryVersion: metricSelectedQueryVersion, currentVersionNumber: metricCurrentVersionNumber, onRestoreVersion: onRestoreMetricVersion, - isSavingMetric + isSaving: isSavingMetric, + onPrefetchAsset: onPrefetchMetricAsset } = useListMetricVersions({ assetId, type @@ -82,13 +91,24 @@ export const useListVersionHistories = ({ } ); + const onPrefetchAsset = useMemoizedFn(async (versionNumber: number, link: string) => { + router.prefetch(link); + + if (type === 'metric') { + await onPrefetchMetricAsset(versionNumber); + } else { + await onPrefetchDashboardAsset(versionNumber); + } + }); + return useMemo(() => { return { listItems, currentVersionNumber, selectedQueryVersion, onClickRestoreVersion, - isRestoringVersion: isSavingDashboard || isSavingMetric + isRestoringVersion: isSavingDashboard || isSavingMetric, + onPrefetchAsset }; }, [ listItems, @@ -96,19 +116,34 @@ export const useListVersionHistories = ({ selectedQueryVersion, onClickRestoreVersion, isSavingDashboard, - isSavingMetric + isSavingMetric, + onPrefetchAsset ]); }; +type UseListVersionReturn = { + versions: + | { + version_number: number; + updated_at: string; + }[] + | undefined; + selectedQueryVersion: number | undefined; + onRestoreVersion: (versionNumber: number) => Promise; + currentVersionNumber: number | undefined; + isSaving: boolean; + onPrefetchAsset: (versionNumber: number) => Promise; +}; + const useListDashboardVersions = ({ assetId, type }: { assetId: string; type: 'metric' | 'dashboard'; -}) => { +}): UseListVersionReturn => { const dashboardVersionNumber = useChatLayoutContextSelector((x) => x.dashboardVersionNumber); - const { mutateAsync: updateDashboard, isPending: isSavingDashboard } = useUpdateDashboard({ + const { mutateAsync: updateDashboard, isPending: isSaving } = useUpdateDashboard({ saveToServer: true, updateVersion: true }); @@ -125,13 +160,13 @@ const useListDashboardVersions = ({ } ); - const dashboardVersions = dashboardData?.versions; + const versions = dashboardData?.versions; const currentVersionNumber = dashboardData?.version_number; const selectedQueryVersion = useMemo(() => { if (dashboardVersionNumber) return dashboardVersionNumber; - return last(dashboardVersions)?.version_number; - }, [dashboardVersions, dashboardVersionNumber]); + return last(versions)?.version_number; + }, [versions, dashboardVersionNumber]); const onRestoreVersion = useMemoizedFn(async (versionNumber: number) => { await updateDashboard({ @@ -140,20 +175,26 @@ const useListDashboardVersions = ({ }); }); + const onPrefetchAsset = useMemoizedFn(async (versionNumber: number) => { + // + }); + return useMemo(() => { return { - dashboardVersions, + versions, selectedQueryVersion, onRestoreVersion, currentVersionNumber, - isSavingDashboard + isSaving, + onPrefetchAsset }; }, [ - dashboardVersions, + versions, currentVersionNumber, onRestoreVersion, selectedQueryVersion, - isSavingDashboard + isSaving, + onPrefetchAsset ]); }; @@ -163,10 +204,12 @@ const useListMetricVersions = ({ }: { assetId: string; type: 'metric' | 'dashboard'; -}) => { - const { mutateAsync: updateMetric, isPending: isSavingMetric } = useSaveMetric({ +}): UseListVersionReturn => { + const { mutateAsync: updateMetric, isPending: isSaving } = useSaveMetric({ updateOnSave: true }); + const prefetchGetMetric = usePrefetchGetMetricClient(); + const prefetchGetMetricData = usePrefetchGetMetricDataClient(); const metricVersionNumber = useChatLayoutContextSelector((x) => x.metricVersionNumber); @@ -181,9 +224,14 @@ const useListMetricVersions = ({ }) } ); - const metricVersions = metric?.versions; + const versions = metric?.versions; const currentVersionNumber = metricVersionNumber || metric?.version_number; + const selectedQueryVersion = useMemo(() => { + if (metricVersionNumber) return metricVersionNumber; + return last(versions)?.version_number; + }, [versions, metricVersionNumber]); + const onRestoreVersion = useMemoizedFn(async (versionNumber: number) => { await updateMetric({ id: assetId, @@ -191,24 +239,28 @@ const useListMetricVersions = ({ }); }); - const selectedQueryVersion = useMemo(() => { - if (metricVersionNumber) return metricVersionNumber; - return last(metricVersions)?.version_number; - }, [metricVersions, metricVersionNumber]); + const onPrefetchAsset = useMemoizedFn(async (versionNumber: number) => { + await Promise.all([ + prefetchGetMetric({ id: assetId, versionNumber }), + prefetchGetMetricData({ id: assetId, versionNumber }) + ]); + }); return useMemo(() => { return { - metricVersions, + versions, selectedQueryVersion, onRestoreVersion, currentVersionNumber, - isSavingMetric + isSaving, + onPrefetchAsset }; }, [ - metricVersions, + versions, + currentVersionNumber, onRestoreVersion, selectedQueryVersion, - currentVersionNumber, - isSavingMetric + isSaving, + onPrefetchAsset ]); };