Files
frontend-svelte/src/features/FetchFonts/model/services/fetchGoogleFonts.ts
T

390 lines
9.6 KiB
TypeScript
Raw Normal View History

/**
2026-01-09 16:09:56 +03:00
* Service for fetching Google Fonts with Svelte 5 runes + TanStack Query
*/
2026-01-09 16:09:56 +03:00
import { fetchGoogleFonts } from '$entities/Font';
import { normalizeGoogleFonts } from '$entities/Font';
import type {
FontCategory,
FontSubset,
2026-01-09 16:09:56 +03:00
GoogleFontsParams,
} from '$entities/Font';
import type { UnifiedFont } from '$entities/Font';
import {
2026-01-09 16:09:56 +03:00
type CreateQueryResult,
createQuery,
useQueryClient,
} from '@tanstack/svelte-query';
/**
* Google Fonts query parameters
*/
2026-01-09 16:09:56 +03:00
// export interface GoogleFontsParams {
// category?: FontCategory;
// subset?: FontSubset;
// sort?: 'popularity' | 'alpha' | 'date';
// search?: string;
// forceRefetch?: boolean;
// }
/**
2026-01-09 16:09:56 +03:00
* Query key factory
*/
2026-01-09 16:09:56 +03:00
function getGoogleFontsQueryKey(params: GoogleFontsParams) {
return ['googleFonts', params] as const;
}
/**
2026-01-09 16:09:56 +03:00
* Query function
*/
2026-01-09 16:09:56 +03:00
async function fetchGoogleFontsQuery(params: GoogleFontsParams): Promise<UnifiedFont[]> {
try {
const response = await fetchGoogleFonts({
category: params.category,
subset: params.subset,
sort: params.sort,
});
2026-01-09 16:09:56 +03:00
return normalizeGoogleFonts(response.items);
} catch (error) {
if (error instanceof Error) {
if (error.message.includes('Failed to fetch')) {
throw new Error(
2026-01-09 16:09:56 +03:00
'Unable to connect to Google Fonts. Please check your internet connection.',
);
}
if (error.message.includes('404')) {
throw new Error('Font not found in Google Fonts catalog.');
}
}
2026-01-09 16:09:56 +03:00
throw new Error('Failed to load fonts from Google Fonts.');
}
2026-01-09 16:09:56 +03:00
}
/**
2026-01-09 16:09:56 +03:00
* Google Fonts store wrapping TanStack Query with runes
*/
2026-01-09 16:09:56 +03:00
export class GoogleFontsStore {
params = $state<GoogleFontsParams>({});
private query: CreateQueryResult<UnifiedFont[], Error>;
private queryClient = useQueryClient();
2026-01-09 16:09:56 +03:00
constructor(initialParams: GoogleFontsParams = {}) {
this.params = initialParams;
2026-01-09 16:09:56 +03:00
// Create the query - automatically reactive
this.query = createQuery(() => ({
queryKey: getGoogleFontsQueryKey(this.params),
queryFn: () => fetchGoogleFontsQuery(this.params),
staleTime: 5 * 60 * 1000, // 5 minutes
gcTime: 10 * 60 * 1000, // 10 minutes
}));
}
2026-01-09 16:09:56 +03:00
// Proxy TanStack Query's reactive state
get fonts() {
return this.query.data ?? [];
}
2026-01-09 16:09:56 +03:00
get isLoading() {
return this.query.isLoading;
}
2026-01-09 16:09:56 +03:00
get isFetching() {
return this.query.isFetching;
}
get isRefetching() {
return this.query.isRefetching;
}
get error() {
return this.query.error;
}
get isError() {
return this.query.isError;
}
get isSuccess() {
return this.query.isSuccess;
}
get status() {
return this.query.status;
}
// Derived helpers
get hasData() {
return this.fonts.length > 0;
}
get isEmpty() {
return !this.isLoading && this.fonts.length === 0;
}
get fontCount() {
return this.fonts.length;
}
// Filtered fonts by category (if you need additional client-side filtering)
get sansSerifFonts() {
return this.fonts.filter(f => f.category === 'sans-serif');
}
get serifFonts() {
return this.fonts.filter(f => f.category === 'serif');
}
get displayFonts() {
return this.fonts.filter(f => f.category === 'display');
}
get handwritingFonts() {
return this.fonts.filter(f => f.category === 'handwriting');
}
get monospaceFonts() {
return this.fonts.filter(f => f.category === 'monospace');
}
/**
* Update parameters - TanStack Query will automatically refetch
*/
setParams(newParams: Partial<GoogleFontsParams>) {
this.params = { ...this.params, ...newParams };
}
setCategory(category: FontCategory | undefined) {
this.setParams({ category });
}
setSubset(subset: FontSubset | undefined) {
this.setParams({ subset });
}
setSort(sort: 'popularity' | 'alpha' | 'date' | undefined) {
this.setParams({ sort });
}
setSearch(search: string) {
this.setParams({ search });
}
clearSearch() {
this.setParams({ search: undefined });
}
clearFilters() {
this.params = {};
}
/**
* Manually refetch
*/
async refetch() {
await this.query.refetch();
}
/**
* Invalidate cache and refetch
*/
invalidate() {
this.queryClient.invalidateQueries({
queryKey: getGoogleFontsQueryKey(this.params),
});
2026-01-09 16:09:56 +03:00
}
/**
* Invalidate all Google Fonts queries
*/
invalidateAll() {
this.queryClient.invalidateQueries({
queryKey: ['googleFonts'],
});
}
2026-01-09 16:09:56 +03:00
/**
* Prefetch with different params (for hover states, pagination, etc.)
*/
async prefetch(params: GoogleFontsParams) {
await this.queryClient.prefetchQuery({
queryKey: getGoogleFontsQueryKey(params),
2026-01-09 16:09:56 +03:00
queryFn: () => fetchGoogleFontsQuery(params),
staleTime: 5 * 60 * 1000,
});
2026-01-09 16:09:56 +03:00
}
/**
* Prefetch next category (useful for tab switching)
*/
async prefetchCategory(category: FontCategory) {
await this.prefetch({ ...this.params, category });
}
/**
* Cancel ongoing queries
*/
cancel() {
this.queryClient.cancelQueries({
queryKey: getGoogleFontsQueryKey(this.params),
});
}
2026-01-09 16:09:56 +03:00
/**
* Clear cache for current params
*/
clearCache() {
this.queryClient.removeQueries({
queryKey: getGoogleFontsQueryKey(this.params),
});
}
/**
* Get cached data without triggering fetch
*/
getCachedData() {
return this.queryClient.getQueryData<UnifiedFont[]>(
getGoogleFontsQueryKey(this.params),
);
}
/**
* Check if data exists in cache
*/
hasCache(params?: GoogleFontsParams) {
const key = params ? getGoogleFontsQueryKey(params) : getGoogleFontsQueryKey(this.params);
return this.queryClient.getQueryData(key) !== undefined;
}
/**
* Set data manually (optimistic updates)
*/
setQueryData(updater: (old: UnifiedFont[] | undefined) => UnifiedFont[]) {
this.queryClient.setQueryData(
getGoogleFontsQueryKey(this.params),
updater,
);
}
/**
* Get query state for debugging
*/
getQueryState() {
return this.queryClient.getQueryState(
getGoogleFontsQueryKey(this.params),
);
}
}
2026-01-09 16:09:56 +03:00
/**
* Factory function to create Google Fonts store
*/
export function createGoogleFontsStore(params: GoogleFontsParams = {}) {
return new GoogleFontsStore(params);
}
/**
* Manager for multiple Google Fonts stores
*/
// export class GoogleFontsManager {
// stores = $state<Map<string, GoogleFontsStore>>(new Map());
// private queryClient = useQueryClient();
// /**
// * Get or create a store with specific parameters
// */
// getStore(params: GoogleFontsParams = {}): GoogleFontsStore {
// const key = JSON.stringify(params);
// if (!this.stores.has(key)) {
// this.stores.set(key, new GoogleFontsStore(params));
// }
// return this.stores.get(key)!;
// }
// /**
// * Get store by category (convenience method)
// */
// getStoreByCategory(category: FontCategory): GoogleFontsStore {
// return this.getStore({ category });
// }
// /**
// * Invalidate ALL Google Fonts queries across all stores
// */
// invalidateAll() {
// this.queryClient.invalidateQueries({
// queryKey: ['googleFonts'],
// });
// }
// /**
// * Prefetch fonts in background
// */
// async prefetch(params: GoogleFontsParams) {
// await this.queryClient.prefetchQuery({
// queryKey: getGoogleFontsQueryKey(params),
// queryFn: () => fetchGoogleFontsQuery(params),
// staleTime: 5 * 60 * 1000,
// });
// }
// /**
// * Prefetch all categories (useful on app init)
// */
// async prefetchAllCategories() {
// const categories: FontCategory[] = ['sans-serif', 'serif', 'display', 'handwriting', 'monospace'];
// await Promise.all(
// categories.map(category => this.prefetch({ category }))
// );
// }
// /**
// * Cancel all Google Fonts queries
// */
// cancelAll() {
// this.queryClient.cancelQueries({
// queryKey: ['googleFonts'],
// });
// }
// /**
// * Clear all Google Fonts cache
// */
// clearAllCache() {
// this.queryClient.removeQueries({
// queryKey: ['googleFonts'],
// });
// }
// /**
// * Get total fonts count across all stores
// */
// get totalFontsCount() {
// return Array.from(this.stores.values()).reduce(
// (sum, store) => sum + store.fontCount,
// 0
// );
// }
// /**
// * Check if any store is loading
// */
// get isAnyLoading() {
// return Array.from(this.stores.values()).some(store => store.isLoading);
// }
// /**
// * Get all errors from all stores
// */
// get allErrors() {
// return Array.from(this.stores.values())
// .map(store => store.error)
// .filter((error): error is Error => error !== null);
// }
// }
// export function createGoogleFontsManager() {
// return new GoogleFontsManager();
// }