diff --git a/src/entities/Font/model/services/fetchFontshareFonts.svelte.ts b/src/entities/Font/model/services/fetchFontshareFonts.svelte.ts new file mode 100644 index 0000000..77cd544 --- /dev/null +++ b/src/entities/Font/model/services/fetchFontshareFonts.svelte.ts @@ -0,0 +1,31 @@ +import { + type FontshareParams, + fetchFontshareFonts, +} from '../../api'; +import { normalizeFontshareFonts } from '../../lib'; +import type { UnifiedFont } from '../types'; + +/** + * Query function for fetching fonts from Fontshare. + * + * @param params - The parameters for fetching fonts from Fontshare (E.g. search query, page number, etc.). + * @returns A promise that resolves with an array of UnifiedFont objects representing the fonts found in Fontshare. + */ +export async function fetchFontshareFontsQuery(params: FontshareParams): Promise { + try { + const response = await fetchFontshareFonts(params); + return normalizeFontshareFonts(response.fonts); + } catch (error) { + if (error instanceof Error) { + if (error.message.includes('Failed to fetch')) { + throw new Error( + 'Unable to connect to Fontshare. Please check your internet connection.', + ); + } + if (error.message.includes('404')) { + throw new Error('Font not found in Fontshare catalog.'); + } + } + throw new Error('Failed to load fonts from Fontshare.'); + } +} diff --git a/src/features/FetchFonts/model/services/fetchGoogleFonts.ts b/src/entities/Font/model/services/fetchGoogleFonts.svelte.ts similarity index 65% rename from src/features/FetchFonts/model/services/fetchGoogleFonts.ts rename to src/entities/Font/model/services/fetchGoogleFonts.svelte.ts index 64051ba..db9818a 100644 --- a/src/features/FetchFonts/model/services/fetchGoogleFonts.ts +++ b/src/entities/Font/model/services/fetchGoogleFonts.svelte.ts @@ -1,30 +1,21 @@ /** * Service for fetching Google Fonts with Svelte 5 runes + TanStack Query */ -import { fetchGoogleFonts } from '$entities/Font'; -import { normalizeGoogleFonts } from '$entities/Font'; -import type { - FontCategory, - FontSubset, - GoogleFontsParams, -} from '$entities/Font'; -import type { UnifiedFont } from '$entities/Font'; import { type CreateQueryResult, createQuery, useQueryClient, } from '@tanstack/svelte-query'; - -/** - * Google Fonts query parameters - */ -// export interface GoogleFontsParams { -// category?: FontCategory; -// subset?: FontSubset; -// sort?: 'popularity' | 'alpha' | 'date'; -// search?: string; -// forceRefetch?: boolean; -// } +import { + type GoogleFontsParams, + fetchGoogleFonts, +} from '../../api'; +import { normalizeGoogleFonts } from '../../lib'; +import type { + FontCategory, + FontSubset, +} from '../types'; +import type { UnifiedFont } from '../types/normalize'; /** * Query key factory @@ -36,7 +27,7 @@ function getGoogleFontsQueryKey(params: GoogleFontsParams) { /** * Query function */ -async function fetchGoogleFontsQuery(params: GoogleFontsParams): Promise { +export async function fetchGoogleFontsQuery(params: GoogleFontsParams): Promise { try { const response = await fetchGoogleFonts({ category: params.category, @@ -281,109 +272,3 @@ export class GoogleFontsStore { export function createGoogleFontsStore(params: GoogleFontsParams = {}) { return new GoogleFontsStore(params); } - -/** - * Manager for multiple Google Fonts stores - */ -// export class GoogleFontsManager { -// stores = $state>(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(); -// } diff --git a/src/features/FetchFonts/index.ts b/src/features/FetchFonts/index.ts deleted file mode 100644 index 9e6df5a..0000000 --- a/src/features/FetchFonts/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Fetch fonts feature exports - * - * Exports service functions for fetching fonts from Google Fonts and Fontshare - */ - -export { createGoogleFontsStore } from './model/services/fetchGoogleFonts'; - -export { createFontshareStore } from './model/services/fetchFontshareFonts'; - -export { FontSearch } from './ui'; diff --git a/src/features/FetchFonts/model/services/fetchFontshareFonts.ts b/src/features/FetchFonts/model/services/fetchFontshareFonts.ts deleted file mode 100644 index 83c70b9..0000000 --- a/src/features/FetchFonts/model/services/fetchFontshareFonts.ts +++ /dev/null @@ -1,269 +0,0 @@ -import { - type FontshareParams, - type UnifiedFont, - fetchFontshareFonts, - normalizeFontshareFonts, -} from '$entities/Font'; -import { - type CreateQueryResult, - createQuery, - useQueryClient, -} from '@tanstack/svelte-query'; - -/** - * Query key factory - */ -function getFontshareQueryKey(params: FontshareParams) { - return ['fontshare', params] as const; -} - -/** - * Query function - */ -async function fetchFontshareFontsQuery(params: FontshareParams): Promise { - try { - const response = await fetchFontshareFonts(params); - return normalizeFontshareFonts(response.items); - } catch (error) { - if (error instanceof Error) { - if (error.message.includes('Failed to fetch')) { - throw new Error( - 'Unable to connect to Fontshare. Please check your internet connection.', - ); - } - if (error.message.includes('404')) { - throw new Error('Font not found in Fontshare catalog.'); - } - } - throw new Error('Failed to load fonts from Fontshare.'); - } -} - -/** - * Fontshare store wrapping TanStack Query with runes - */ -export class FontshareStore { - params = $state({}); - private query: CreateQueryResult; - private queryClient = useQueryClient(); - - constructor(initialParams: FontshareParams = {}) { - this.params = initialParams; - - // Create the query - it's already reactive - this.query = createQuery(() => ({ - queryKey: getFontshareQueryKey(this.params), - queryFn: () => fetchFontshareFontsQuery(this.params), - staleTime: 5 * 60 * 1000, - gcTime: 10 * 60 * 1000, - })); - } - - // Proxy TanStack Query's reactive state - get fonts() { - return this.query.data ?? []; - } - - get isLoading() { - return this.query.isLoading; - } - - get isFetching() { - return this.query.isFetching; - } - - get error() { - return this.query.error; - } - - get isError() { - return this.query.isError; - } - - get isSuccess() { - return this.query.isSuccess; - } - - // Derived helpers - get hasData() { - return this.fonts.length > 0; - } - - get isEmpty() { - return !this.isLoading && this.fonts.length === 0; - } - - /** - * Update parameters - TanStack Query will automatically refetch - */ - setParams(newParams: Partial) { - this.params = { ...this.params, ...newParams }; - } - - setCategories(categories: string[]) { - this.setParams({ categories }); - } - - setTags(tags: string[]) { - this.setParams({ tags }); - } - - setSearch(search: string) { - this.setParams({ search }); - } - - setPage(page: number) { - this.setParams({ page }); - } - - setLimit(limit: number) { - this.setParams({ limit }); - } - - /** - * Manually refetch - */ - async refetch() { - await this.query.refetch(); - } - - /** - * Invalidate cache and refetch - */ - invalidate() { - this.queryClient.invalidateQueries({ - queryKey: getFontshareQueryKey(this.params), - }); - } - - /** - * Invalidate all Fontshare queries - */ - invalidateAll() { - this.queryClient.invalidateQueries({ - queryKey: ['fontshare'], - }); - } - - /** - * Prefetch with different params (for hover states, pagination, etc.) - */ - async prefetch(params: FontshareParams) { - await this.queryClient.prefetchQuery({ - queryKey: getFontshareQueryKey(params), - queryFn: () => fetchFontshareFontsQuery(params), - staleTime: 5 * 60 * 1000, - }); - } - - /** - * Cancel ongoing queries - */ - cancel() { - this.queryClient.cancelQueries({ - queryKey: getFontshareQueryKey(this.params), - }); - } - - /** - * Clear cache for current params - */ - clearCache() { - this.queryClient.removeQueries({ - queryKey: getFontshareQueryKey(this.params), - }); - } - - /** - * Get cached data without triggering fetch - */ - getCachedData() { - return this.queryClient.getQueryData( - getFontshareQueryKey(this.params), - ); - } - - /** - * Set data manually (optimistic updates) - */ - setQueryData(updater: (old: UnifiedFont[] | undefined) => UnifiedFont[]) { - this.queryClient.setQueryData( - getFontshareQueryKey(this.params), - updater, - ); - } -} - -export function createFontshareStore(params: FontshareParams = {}) { - return new FontshareStore(params); -} - -/** - * Manager for multiple Fontshare stores - */ -// export class FontshareManager { -// stores = $state>(new Map()); -// private queryClient = useQueryClient(); - -// getStore(params: FontshareParams = {}): FontshareStore { -// const key = JSON.stringify(params); - -// if (!this.stores.has(key)) { -// this.stores.set(key, new FontshareStore(params)); -// } - -// return this.stores.get(key)!; -// } - -// /** -// * Invalidate ALL Fontshare queries across all stores -// */ -// invalidateAll() { -// this.queryClient.invalidateQueries({ -// queryKey: ['fontshare'], -// }); -// } - -// /** -// * Prefetch fonts in background -// */ -// async prefetch(params: FontshareParams) { -// await this.queryClient.prefetchQuery({ -// queryKey: getFontshareQueryKey(params), -// queryFn: () => fetchFontshareFontsQuery(params), -// staleTime: 5 * 60 * 1000, -// }); -// } - -// /** -// * Cancel all Fontshare queries -// */ -// cancelAll() { -// this.queryClient.cancelQueries({ -// queryKey: ['fontshare'], -// }); -// } - -// /** -// * Clear all Fontshare cache -// */ -// clearAllCache() { -// this.queryClient.removeQueries({ -// queryKey: ['fontshare'], -// }); -// } - -// /** -// * Get query state for debugging -// */ -// getQueryState(params: FontshareParams) { -// return this.queryClient.getQueryState( -// getFontshareQueryKey(params), -// ); -// } -// } - -// export function createFontshareManager() { -// return new FontshareManager(); -// } -// diff --git a/src/features/FetchFonts/model/types.ts b/src/features/FetchFonts/model/types.ts deleted file mode 100644 index a4f4ea0..0000000 --- a/src/features/FetchFonts/model/types.ts +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Fetch Fonts feature types - * - * Type definitions for font fetching feature - */ - -import type { - FontCategory, - FontProvider, - FontSubset, -} from '$entities/Font/model/types'; -import type { UnifiedFont } from '$entities/Font/model/types/normalize'; - -/** - * Combined query parameters for fetching from any provider - */ -export interface FetchFontsParams { - /** Font provider to fetch from */ - provider?: FontProvider; - /** Category filter */ - category?: FontCategory; - /** Subset filter */ - subset?: FontSubset; - /** Search query */ - search?: string; - /** Page number (for Fontshare) */ - page?: number; - /** Limit (for Fontshare) */ - limit?: number; - /** Force refetch even if cached */ - forceRefetch?: boolean; -} - -/** - * Font fetching result - */ -export interface FetchFontsResult { - /** Fetched fonts */ - fonts: UnifiedFont[]; - /** Total count (for pagination) */ - total?: number; - /** Whether more fonts are available */ - hasMore?: boolean; - /** Page number (for pagination) */ - page?: number; -} - -/** - * Font fetching error - */ -export interface FetchFontsError { - /** Error message */ - message: string; - /** Provider that failed */ - provider: FontProvider | 'all'; - /** HTTP status code (if applicable) */ - status?: number; - /** Original error */ - originalError?: unknown; -} - -/** - * Font fetching state - */ -export interface FetchFontsState { - /** Currently fetching */ - isFetching: boolean; - /** Currently loading initial data */ - isLoading: boolean; - /** Error state */ - error: FetchFontsError | null; - /** Cached fonts */ - fonts: UnifiedFont[]; - /** Last fetch timestamp */ - lastFetchedAt: number | null; -} diff --git a/src/features/FetchFonts/ui/FontSearch/FontSearch.svelte b/src/features/FetchFonts/ui/FontSearch/FontSearch.svelte deleted file mode 100644 index 32563e7..0000000 --- a/src/features/FetchFonts/ui/FontSearch/FontSearch.svelte +++ /dev/null @@ -1,17 +0,0 @@ - - - - - diff --git a/src/features/FetchFonts/ui/index.ts b/src/features/FetchFonts/ui/index.ts deleted file mode 100644 index e71451a..0000000 --- a/src/features/FetchFonts/ui/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import FontSearch from './FontSearch/FontSearch.svelte'; - -export { FontSearch }; diff --git a/src/features/FilterFonts/model/const/types/common.ts b/src/features/FilterFonts/model/const/types/common.ts deleted file mode 100644 index d953359..0000000 --- a/src/features/FilterFonts/model/const/types/common.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { Property } from '$shared/lib'; - -export interface FilterGroupConfig { - id: string; - label: string; - properties: Property[]; -} diff --git a/src/features/FilterFonts/model/state/manager.svelte.ts b/src/features/FilterFonts/model/state/manager.svelte.ts deleted file mode 100644 index 5b9e5a4..0000000 --- a/src/features/FilterFonts/model/state/manager.svelte.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { createFilterManager } from '../../lib/filterManager/filterManager.svelte'; -import { - FONT_CATEGORIES, - FONT_PROVIDERS, - FONT_SUBSETS, -} from '../const/const'; - -const filtersData = [ - { - id: 'providers', - label: 'Font provider', - properties: FONT_PROVIDERS, - }, - { - id: 'subsets', - label: 'Font subset', - properties: FONT_SUBSETS, - }, - { - id: 'categories', - label: 'Font category', - properties: FONT_CATEGORIES, - }, -]; - -export const filterManager = createFilterManager(filtersData); diff --git a/src/features/FilterFonts/index.ts b/src/features/GetFonts/index.ts similarity index 64% rename from src/features/FilterFonts/index.ts rename to src/features/GetFonts/index.ts index 18606e0..9f557d6 100644 --- a/src/features/FilterFonts/index.ts +++ b/src/features/GetFonts/index.ts @@ -1,11 +1,19 @@ export { createFilterManager, type FilterManager, -} from './lib/filterManager/filterManager.svelte'; + mapManagerToParams, +} from './lib'; + export { FONT_CATEGORIES, FONT_PROVIDERS, FONT_SUBSETS, } from './model/const/const'; -export type { FilterGroupConfig } from './model/const/types/common'; + export { filterManager } from './model/state/manager.svelte'; + +export { + FilterControls, + Filters, + FontSearch, +} from './ui'; diff --git a/src/features/GetFonts/lib/index.ts b/src/features/GetFonts/lib/index.ts new file mode 100644 index 0000000..e271659 --- /dev/null +++ b/src/features/GetFonts/lib/index.ts @@ -0,0 +1,6 @@ +export { + createFilterManager, + type FilterManager, +} from './filterManager/filterManager.svelte'; + +export { mapManagerToParams } from './mapper/mapManagerToParams'; diff --git a/src/features/FilterFonts/model/const/const.ts b/src/features/GetFonts/model/const/const.ts similarity index 100% rename from src/features/FilterFonts/model/const/const.ts rename to src/features/GetFonts/model/const/const.ts diff --git a/src/features/GetFonts/model/index.ts b/src/features/GetFonts/model/index.ts new file mode 100644 index 0000000..077b26e --- /dev/null +++ b/src/features/GetFonts/model/index.ts @@ -0,0 +1,6 @@ +export type { + FilterConfig, + FilterGroupConfig, +} from './types/filter'; + +export { filterManager } from './state/manager.svelte'; diff --git a/src/features/GetFonts/model/state/manager.svelte.ts b/src/features/GetFonts/model/state/manager.svelte.ts new file mode 100644 index 0000000..3ec8d8a --- /dev/null +++ b/src/features/GetFonts/model/state/manager.svelte.ts @@ -0,0 +1,29 @@ +import { createFilterManager } from '../../lib/filterManager/filterManager.svelte'; +import { + FONT_CATEGORIES, + FONT_PROVIDERS, + FONT_SUBSETS, +} from '../const/const'; + +const initialConfig = { + queryValue: '', + groups: [ + { + id: 'providers', + label: 'Font provider', + properties: FONT_PROVIDERS, + }, + { + id: 'subsets', + label: 'Font subset', + properties: FONT_SUBSETS, + }, + { + id: 'categories', + label: 'Font category', + properties: FONT_CATEGORIES, + }, + ], +}; + +export const filterManager = createFilterManager(initialConfig); diff --git a/src/features/GetFonts/ui/FontSearch/FontSearch.svelte b/src/features/GetFonts/ui/FontSearch/FontSearch.svelte new file mode 100644 index 0000000..78a0715 --- /dev/null +++ b/src/features/GetFonts/ui/FontSearch/FontSearch.svelte @@ -0,0 +1,38 @@ + + + + + diff --git a/src/features/GetFonts/ui/index.ts b/src/features/GetFonts/ui/index.ts new file mode 100644 index 0000000..c5bc080 --- /dev/null +++ b/src/features/GetFonts/ui/index.ts @@ -0,0 +1,9 @@ +import Filters from './Filters/Filters.svelte'; +import FilterControls from './FiltersControl/FilterControls.svelte'; +import FontSearch from './FontSearch/FontSearch.svelte'; + +export { + FilterControls, + Filters, + FontSearch, +};