use-predictive-prefetch.ts1.6 KBView on GitHub
import { useQueryClient } from '@tanstack/react-query';
import { useTRPC } from '@/providers/query-provider';
import { useCallback } from 'react';

/**
 * Schedule a callback during browser idle time.
 * Falls back to setTimeout(cb, 100) when requestIdleCallback is unavailable.
 */
function scheduleIdle(cb: () => void): void {
  if (typeof requestIdleCallback !== 'undefined') {
    requestIdleCallback(() => cb(), { timeout: 2000 });
  } else {
    setTimeout(cb, 100);
  }
}

/**
 * After common actions (archive, delete, send), prefetch the most likely next
 * view during browser idle time so navigation feels instant.
 *
 * Uses requestIdleCallback to avoid blocking the main thread during action
 * animations and optimistic UI updates.
 */
export function usePredictivePrefetch() {
  const queryClient = useQueryClient();
  const trpc = useTRPC();

  const prefetchInbox = useCallback(() => {
    scheduleIdle(() => {
      queryClient.prefetchInfiniteQuery({
        ...trpc.mail.listThreads.infiniteQueryOptions(
          { q: 'label:INBOX', maxResults: 30 },
          {
            initialCursor: '',
            getNextPageParam: (lastPage) => lastPage?.nextPageToken ?? null,
            staleTime: 10_000,
          },
        ),
        pages: 1,
      });
    });
  }, [queryClient, trpc]);

  /** After archiving or deleting, user likely returns to inbox */
  const afterArchiveOrDelete = useCallback(() => {
    prefetchInbox();
  }, [prefetchInbox]);

  /** After sending an email, user likely returns to inbox */
  const afterSend = useCallback(() => {
    prefetchInbox();
  }, [prefetchInbox]);

  return { afterArchiveOrDelete, afterSend, prefetchInbox };
}