fix(filters): use proxy fetch function
This commit is contained in:
83
src/entities/Font/api/proxy/filters.ts
Normal file
83
src/entities/Font/api/proxy/filters.ts
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
/**
|
||||||
|
* Proxy API filters
|
||||||
|
*
|
||||||
|
* Fetches filter metadata from GlyphDiff proxy API.
|
||||||
|
* Provides type-safe response handling.
|
||||||
|
*
|
||||||
|
* @see https://api.glyphdiff.com/api/v1/filters
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { api } from '$shared/api/api';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter metadata type from backend
|
||||||
|
*/
|
||||||
|
export interface FilterMetadata {
|
||||||
|
/** Filter ID (e.g., "providers", "categories", "subsets") */
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
/** Display name (e.g., "Font Providers", "Categories", "Character Subsets") */
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
/** Filter description */
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
/** Filter type */
|
||||||
|
type: 'enum' | 'string' | 'array';
|
||||||
|
|
||||||
|
/** Available filter options */
|
||||||
|
options: FilterOption[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter option type
|
||||||
|
*/
|
||||||
|
export interface FilterOption {
|
||||||
|
/** Option ID (e.g., "google", "serif", "latin") */
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
/** Display name (e.g., "Google Fonts", "Serif", "Latin") */
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
/** Option value (e.g., "google", "serif", "latin") */
|
||||||
|
value: string;
|
||||||
|
|
||||||
|
/** Number of fonts with this value */
|
||||||
|
count: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy filters API response
|
||||||
|
*/
|
||||||
|
export interface ProxyFiltersResponse {
|
||||||
|
/** Array of filter metadata */
|
||||||
|
filters: FilterMetadata[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch filters from proxy API
|
||||||
|
*
|
||||||
|
* @returns Promise resolving to array of filter metadata
|
||||||
|
* @throws ApiError when request fails
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* // Fetch all filters
|
||||||
|
* const filters = await fetchProxyFilters();
|
||||||
|
*
|
||||||
|
* console.log(filters); // [
|
||||||
|
* // { id: "providers", name: "Font Providers", options: [...] },
|
||||||
|
* // { id: "categories", name: "Categories", options: [...] },
|
||||||
|
* // { id: "subsets", name: "Character Subsets", options: [...] }
|
||||||
|
* // ]
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export async function fetchProxyFilters(): Promise<FilterMetadata[]> {
|
||||||
|
const response = await api.get<FilterMetadata[]>('/api/v1/filters');
|
||||||
|
|
||||||
|
if (!response.data || !Array.isArray(response.data)) {
|
||||||
|
throw new Error('Proxy API returned invalid response');
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
135
src/features/GetFonts/model/state/filters.svelte.ts
Normal file
135
src/features/GetFonts/model/state/filters.svelte.ts
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
/**
|
||||||
|
* Filters store for dynamic filter metadata
|
||||||
|
*
|
||||||
|
* Fetches and caches filter metadata from /api/v1/filters endpoint.
|
||||||
|
* Provides reactive access to filter data for providers, categories, and subsets.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* import { filtersStore } from '$features/GetFonts';
|
||||||
|
*
|
||||||
|
* // Access filters (reactive)
|
||||||
|
* $: filters = filtersStore.filters;
|
||||||
|
* $: isLoading = filtersStore.isLoading;
|
||||||
|
* $: error = filtersStore.error;
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { fetchProxyFilters } from '$entities/Font/api/proxy/filters';
|
||||||
|
import type {
|
||||||
|
FilterMetadata,
|
||||||
|
FilterOption,
|
||||||
|
} from '$entities/Font/api/proxy/filters';
|
||||||
|
import { queryClient } from '$shared/api/queryClient';
|
||||||
|
import {
|
||||||
|
type QueryKey,
|
||||||
|
QueryObserver,
|
||||||
|
type QueryObserverOptions,
|
||||||
|
type QueryObserverResult,
|
||||||
|
} from '@tanstack/query-core';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters store wrapping TanStack Query
|
||||||
|
*
|
||||||
|
* Fetches and caches filter metadata using fetchProxyFilters()
|
||||||
|
* Provides reactive access to filter data
|
||||||
|
*/
|
||||||
|
class FiltersStore {
|
||||||
|
/** Cleanup function for effects */
|
||||||
|
cleanup: () => void;
|
||||||
|
|
||||||
|
/** TanStack Query result state */
|
||||||
|
protected result = $state<QueryObserverResult<FilterMetadata[], Error>>({} as any);
|
||||||
|
|
||||||
|
/** TanStack Query observer instance */
|
||||||
|
protected observer: QueryObserver<FilterMetadata[], Error>;
|
||||||
|
|
||||||
|
/** Shared query client */
|
||||||
|
protected qc = queryClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new filters store
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
this.observer = new QueryObserver(this.qc, this.getOptions());
|
||||||
|
|
||||||
|
// Sync TanStack Query state -> Svelte state
|
||||||
|
this.observer.subscribe(r => {
|
||||||
|
this.result = r;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sync Svelte state changes -> TanStack Query options
|
||||||
|
this.cleanup = $effect.root(() => {
|
||||||
|
$effect(() => {
|
||||||
|
this.observer.setOptions(this.getOptions());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query key for TanStack Query caching
|
||||||
|
*/
|
||||||
|
protected getQueryKey(): QueryKey {
|
||||||
|
return ['filters'] as const;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch function for filter metadata
|
||||||
|
* Uses fetchProxyFilters() from proxy API
|
||||||
|
*/
|
||||||
|
protected async fetchFn(): Promise<FilterMetadata[]> {
|
||||||
|
return await fetchProxyFilters();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TanStack Query options
|
||||||
|
*/
|
||||||
|
protected getOptions(): QueryObserverOptions<FilterMetadata[], Error> {
|
||||||
|
return {
|
||||||
|
queryKey: this.getQueryKey(),
|
||||||
|
queryFn: () => this.fetchFn(),
|
||||||
|
staleTime: 5 * 60 * 1000, // 5 minutes
|
||||||
|
gcTime: 10 * 60 * 1000, // 10 minutes
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all filters
|
||||||
|
*/
|
||||||
|
get filters(): FilterMetadata[] {
|
||||||
|
return this.result.data ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get loading state
|
||||||
|
*/
|
||||||
|
get isLoading(): boolean {
|
||||||
|
return this.result.isLoading;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get error state
|
||||||
|
*/
|
||||||
|
get isError(): boolean {
|
||||||
|
return this.result.isError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get error message
|
||||||
|
*/
|
||||||
|
get error(): string | null {
|
||||||
|
return this.result.error?.message ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean up effects and observers
|
||||||
|
*/
|
||||||
|
destroy() {
|
||||||
|
this.cleanup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Singleton instance
|
||||||
|
*/
|
||||||
|
export const filtersStore = new FiltersStore();
|
||||||
Reference in New Issue
Block a user