use-prefetch-folder-threads.ts2.2 KBView on GitHub
import { useCallback, useRef } from 'react';
import { useQueryClient } from '@tanstack/react-query';
import { useTRPC } from '@/providers/query-provider';
import { getSystemFolderSplit } from '@/modules/threads/lib/system-folder-splits';

/**
 * Prefetches thread list data for a folder on hover, so navigating
 * to that folder feels instant. Uses a 100ms debounce to avoid
 * firing on casual mouse-overs.
 *
 * The query key and input shape intentionally match the pattern in
 * `useThreads` so the prefetched data is reused by the real query.
 */
export function usePrefetchFolderThreads() {
  const queryClient = useQueryClient();
  const trpc = useTRPC();
  const timerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);

  const prefetch = useCallback(
    (folderPath: string) => {
      if (timerRef.current) clearTimeout(timerRef.current);

      timerRef.current = setTimeout(() => {
        // Extract folder name from path like "/mail/inbox" -> "inbox"
        const folder = folderPath.split('/').pop() || 'inbox';
        const systemSplit = getSystemFolderSplit(folder);
        const usesSplitPath = folder === 'inbox' || !!systemSplit;

        queryClient.prefetchInfiniteQuery({
          ...trpc.mail.listThreads.infiniteQueryOptions(
            {
              q: usesSplitPath ? undefined : `label:${folder}`,
              inboxName: folder === 'inbox' ? 'Inbox' : systemSplit?.inboxName,
              compiledQuery:
                folder === 'inbox' ? 'label:INBOX' : systemSplit?.compiledQuery,
              queryHash:
                folder === 'inbox'
                  ? 'system-folder:inbox:label:INBOX'
                  : systemSplit?.queryHash,
              maxResults: 30,
            },
            {
              initialCursor: '',
              getNextPageParam: (lastPage) => lastPage?.nextPageToken ?? null,
              staleTime: 30_000,
            },
          ),
          // `pages` belongs to prefetchInfiniteQuery, not to the query options.
          pages: 1,
        });
      }, 100);
    },
    [queryClient, trpc],
  );

  const cancel = useCallback(() => {
    if (timerRef.current) clearTimeout(timerRef.current);
  }, []);

  return { prefetch, cancel };
}