/** * Service for fetching Google Fonts * * Integrates with TanStack Query for caching, deduplication, * and automatic refetching. * * Uses reactive query args pattern for Svelte 5 compatibility. */ import type { FontCategory, FontSubset, } from '$entities/Font'; import { fetchGoogleFonts } from '$entities/Font/api/googleFonts'; import { normalizeGoogleFonts } from '$entities/Font/api/normalize'; import type { UnifiedFont } from '$entities/Font/api/normalize'; import type { QueryFunction } from '@tanstack/svelte-query'; import { createQuery, useQueryClient, } from '@tanstack/svelte-query'; /** * Google Fonts query parameters */ export interface GoogleFontsQueryParams { /** Font category filter */ category?: FontCategory; /** Character subset filter */ subset?: FontSubset; /** Sort order */ sort?: 'popularity' | 'alpha' | 'date'; /** Search query (for specific font) */ search?: string; /** Force refetch even if cached */ forceRefetch?: boolean; } /** * Query key factory for Google Fonts * Generates consistent query keys for cache management */ export function getGoogleFontsQueryKey( params: GoogleFontsQueryParams, ): readonly unknown[] { return ['googleFonts', params]; } /** * Query function for fetching Google Fonts * Handles caching, loading states, and errors */ export const fetchGoogleFontsQuery: QueryFunction< UnifiedFont[], readonly unknown[] > = async ({ queryKey }) => { const params = queryKey[1] as GoogleFontsQueryParams; try { const response = await fetchGoogleFonts({ category: params.category, subset: params.subset, sort: params.sort, }); const normalizedFonts = normalizeGoogleFonts(response.items); return normalizedFonts; } catch (error) { // User-friendly error messages if (error instanceof Error) { if (error.message.includes('Failed to fetch')) { throw new Error( 'Unable to connect to Google Fonts. Please check your internet connection and try again.', ); } if (error.message.includes('404')) { throw new Error('Font not found in Google Fonts catalog.'); } throw new Error( 'Failed to load fonts from Google Fonts. Please try again later.', ); } throw new Error('An unexpected error occurred while fetching fonts.'); } }; /** * Create a Google Fonts query hook * Use this in Svelte components to fetch Google Fonts with caching * * @param params - Query parameters * @returns Query result with data, loading state, and error * * @example * ```svelte * * * {#each fonts as font} * * {/each} * ``` */ export function useGoogleFontsQuery(params: GoogleFontsQueryParams = {}) { const queryClient = useQueryClient(); const query = createQuery(() => ({ queryKey: getGoogleFontsQueryKey(params), queryFn: fetchGoogleFontsQuery, staleTime: 5 * 60 * 1000, // 5 minutes gcTime: 10 * 60 * 1000, // 10 minutes })); return query; } /** * Prefetch Google Fonts * Fetch fonts in background without showing loading state * * @param params - Query parameters for prefetch * * @example * ```ts * // Prefetch fonts when user hovers over button * function onMouseEnter() { * prefetchGoogleFonts({ category: 'sans-serif' }); * } * ``` */ export async function prefetchGoogleFonts( params: GoogleFontsQueryParams = {}, ): Promise { const queryClient = useQueryClient(); await queryClient.prefetchQuery({ queryKey: getGoogleFontsQueryKey(params), queryFn: fetchGoogleFontsQuery, staleTime: 5 * 60 * 1000, }); } /** * Invalidate Google Fonts cache * Forces refetch on next query * * @param params - Query parameters to invalidate (all if not provided) * * @example * ```ts * // Invalidate all Google Fonts cache * invalidateGoogleFonts(); * * // Invalidate specific category cache * invalidateGoogleFonts({ category: 'sans-serif' }); * ``` */ export function invalidateGoogleFonts( params?: GoogleFontsQueryParams, ): void { const queryClient = useQueryClient(); if (params) { queryClient.invalidateQueries({ queryKey: getGoogleFontsQueryKey(params), }); } else { queryClient.invalidateQueries({ queryKey: ['googleFonts'], }); } } /** * Cancel Google Fonts queries * Abort in-flight requests * * @param params - Query parameters to cancel (all if not provided) * * @example * ```ts * // Cancel all Google Fonts queries * cancelGoogleFontsQueries(); * ``` */ export function cancelGoogleFontsQueries( params?: GoogleFontsQueryParams, ): void { const queryClient = useQueryClient(); if (params) { queryClient.cancelQueries({ queryKey: getGoogleFontsQueryKey(params), }); } else { queryClient.cancelQueries({ queryKey: ['googleFonts'], }); } }