diff --git a/src/entities/Font/api/proxy/proxyFonts.ts b/src/entities/Font/api/proxy/proxyFonts.ts index 7852008..96f8a45 100644 --- a/src/entities/Font/api/proxy/proxyFonts.ts +++ b/src/entities/Font/api/proxy/proxyFonts.ts @@ -7,8 +7,6 @@ * Proxy API normalizes font data from Google Fonts and Fontshare into a single * unified format, eliminating the need for client-side normalization. * - * Fallback: If proxy API fails, falls back to Fontshare API for development. - * * @see https://api.glyphdiff.com/api/v1/fonts */ @@ -26,40 +24,37 @@ import type { */ const PROXY_API_URL = 'https://api.glyphdiff.com/api/v1/fonts' as const; -/** - * Whether to use proxy API (true) or fallback (false) - * - * Set to true when your proxy API is ready: - * const USE_PROXY_API = true; - * - * Set to false to use Fontshare API as fallback during development: - * const USE_PROXY_API = false; - * - * The app will automatically fall back to Fontshare API if the proxy fails. - */ -const USE_PROXY_API = true; - /** * Proxy API parameters * * Maps directly to the proxy API query parameters + * + * UPDATED: Now supports array values for filters */ export interface ProxyFontsParams extends QueryParams { /** - * Font provider filter ("google" or "fontshare") - * Omit to fetch from both providers + * Font provider filter + * + * NEW: Supports array of providers (e.g., ["google", "fontshare"]) + * Backward compatible: Single value still works */ - provider?: 'google' | 'fontshare'; + providers?: string[] | string; /** * Font category filter + * + * NEW: Supports array of categories (e.g., ["serif", "sans-serif"]) + * Backward compatible: Single value still works */ - category?: FontCategory; + categories?: string[] | string; /** * Character subset filter + * + * NEW: Supports array of subsets (e.g., ["latin", "cyrillic"]) + * Backward compatible: Single value still works */ - subset?: FontSubset; + subsets?: string[] | string; /** * Search query (e.g., "roboto", "satoshi") @@ -108,8 +103,6 @@ export interface ProxyFontsResponse { /** * Fetch fonts from proxy API * - * If proxy API fails or is unavailable, falls back to Fontshare API for development. - * * @param params - Query parameters for filtering and pagination * @returns Promise resolving to proxy API response * @throws ApiError when request fails @@ -138,84 +131,16 @@ export interface ProxyFontsResponse { export async function fetchProxyFonts( params: ProxyFontsParams = {}, ): Promise { - // Try proxy API first if enabled - if (USE_PROXY_API) { - try { - const queryString = buildQueryString(params); - const url = `${PROXY_API_URL}${queryString}`; + const queryString = buildQueryString(params); + const url = `${PROXY_API_URL}${queryString}`; - console.log('[fetchProxyFonts] Fetching from proxy API', { params, url }); + const response = await api.get(url); - const response = await api.get(url); - - // Validate response has fonts array - if (!response.data || !Array.isArray(response.data.fonts)) { - console.error('[fetchProxyFonts] Invalid response from proxy API', response.data); - throw new Error('Proxy API returned invalid response'); - } - - console.log('[fetchProxyFonts] Proxy API success', { - count: response.data.fonts.length, - }); - return response.data; - } catch (error) { - console.warn('[fetchProxyFonts] Proxy API failed, using fallback', error); - - // Check if it's a network error or proxy not available - const isNetworkError = error instanceof Error - && (error.message.includes('Failed to fetch') - || error.message.includes('Network') - || error.message.includes('404') - || error.message.includes('500')); - - if (isNetworkError) { - // Fall back to Fontshare API - console.log('[fetchProxyFonts] Using Fontshare API as fallback'); - return await fetchFontshareFallback(params); - } - - // Re-throw other errors - if (error instanceof Error) { - throw error; - } - throw new Error(`Failed to fetch fonts from proxy API: ${String(error)}`); - } + if (!response.data || !Array.isArray(response.data.fonts)) { + throw new Error('Proxy API returned invalid response'); } - // Use Fontshare API directly - console.log('[fetchProxyFonts] Using Fontshare API (proxy disabled)'); - return await fetchFontshareFallback(params); -} - -/** - * Fallback to Fontshare API when proxy is unavailable - * - * Maps proxy API params to Fontshare API params and normalizes response - */ -async function fetchFontshareFallback( - params: ProxyFontsParams, -): Promise { - // Import dynamically to avoid circular dependency - const { fetchFontshareFonts } = await import('$entities/Font/api/fontshare/fontshare'); - const { normalizeFontshareFonts } = await import('$entities/Font/lib/normalize/normalize'); - - // Map proxy params to Fontshare params - const fontshareParams = { - q: params.q, - categories: params.category ? [params.category] : undefined, - page: params.offset ? Math.floor(params.offset / (params.limit || 50)) + 1 : undefined, - limit: params.limit, - }; - - const response = await fetchFontshareFonts(fontshareParams); - const normalizedFonts = normalizeFontshareFonts(response.fonts); - - return { - fonts: normalizedFonts, - total: response.count_total, - limit: params.limit || response.count, - offset: params.offset || 0, - }; + return response.data; } /** @@ -256,24 +181,9 @@ export async function fetchProxyFontById( export async function fetchFontsByIds(ids: string[]): Promise { if (ids.length === 0) return []; - // Use proxy API if enabled - if (USE_PROXY_API) { - const queryString = ids.join(','); - const url = `${PROXY_API_URL}/batch?ids=${queryString}`; + const queryString = ids.join(','); + const url = `${PROXY_API_URL}/batch?ids=${queryString}`; - try { - const response = await api.get(url); - return response.data ?? []; - } catch (error) { - console.warn('[fetchFontsByIds] Proxy API batch fetch failed, falling back', error); - // Fallthrough to fallback - } - } - - // Fallback: Fetch individually (not efficient but functional for fallback) - const results = await Promise.all( - ids.map(id => fetchProxyFontById(id)), - ); - - return results.filter((f): f is UnifiedFont => !!f); + const response = await api.get(url); + return response.data ?? []; } diff --git a/src/features/GetFonts/lib/mapper/mapManagerToParams.ts b/src/features/GetFonts/lib/mapper/mapManagerToParams.ts index 8375a10..c016683 100644 --- a/src/features/GetFonts/lib/mapper/mapManagerToParams.ts +++ b/src/features/GetFonts/lib/mapper/mapManagerToParams.ts @@ -4,8 +4,7 @@ import type { FilterManager } from '../filterManager/filterManager.svelte'; /** * Maps filter manager to proxy API parameters. * - * Transforms UI filter state into proxy API query parameters. - * Handles conversion from filter groups to API-specific parameters. + * Updated to support multiple filter values (arrays) * * @param manager - Filter manager instance with reactive state * @returns - Partial proxy API parameters ready for API call @@ -15,13 +14,18 @@ import type { FilterManager } from '../filterManager/filterManager.svelte'; * // Example filter manager state: * // { * // queryValue: 'roboto', - * // providers: ['google'], - * // categories: ['sans-serif'], + * // providers: ['google', 'fontshare'], + * // categories: ['sans-serif', 'serif'], * // subsets: ['latin'] * // } * * const params = mapManagerToParams(manager); - * // Returns: { provider: 'google', category: 'sans-serif', subset: 'latin', q: 'roboto' } + * // Returns: { + * // providers: ['google', 'fontshare'], + * // categories: ['sans-serif', 'serif'], + * // subsets: ['latin'], + * // q: 'roboto' + * // } * ``` */ export function mapManagerToParams(manager: FilterManager): Partial { @@ -33,22 +37,17 @@ export function mapManagerToParams(manager: FilterManager): Partial 0 + ? providers as string[] : undefined, - // Category filter (single value - proxy API doesn't support array) - // Use first category if multiple selected, or undefined if none/all selected - category: categories && categories.length === 1 - ? (categories[0] as ProxyFontsParams['category']) + categories: categories && categories.length > 0 + ? categories as string[] : undefined, - // Subset filter (single value - proxy API doesn't support array) - // Use first subset if multiple selected, or undefined if none/all selected - subset: subsets && subsets.length === 1 - ? (subsets[0] as ProxyFontsParams['subset']) + subsets: subsets && subsets.length > 0 + ? subsets as string[] : undefined, }; } diff --git a/src/features/GetFonts/model/state/manager.svelte.ts b/src/features/GetFonts/model/state/manager.svelte.ts index 3ec8d8a..f17057d 100644 --- a/src/features/GetFonts/model/state/manager.svelte.ts +++ b/src/features/GetFonts/model/state/manager.svelte.ts @@ -4,26 +4,58 @@ import { FONT_PROVIDERS, FONT_SUBSETS, } from '../const/const'; +import { filtersStore } from './filters.svelte'; -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, - }, - ], -}; +/** + * Creates initial filter config + * + * Uses dynamic filters from backend if available, + * otherwise falls back to hard-coded constants + */ +function createInitialConfig() { + const dynamicFilters = filtersStore.filters; + + // If filters are loaded, use them + if (dynamicFilters.length > 0) { + return { + queryValue: '', + groups: dynamicFilters.map(filter => ({ + id: filter.id, + label: filter.name, + properties: filter.options.map(opt => ({ + id: opt.id, + name: opt.name, + value: opt.value, + count: opt.count, + selected: false, + })), + })), + }; + } + + // Fallback to hard-coded constants (backward compatibility) + return { + 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, + }, + ], + }; +} + +const initialConfig = createInitialConfig(); export const filterManager = createFilterManager(initialConfig);