From bfa99cde20a231834ea112d922084c3c1dddbb21 Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Wed, 15 Apr 2026 11:35:37 +0300 Subject: [PATCH] fix(comparisonStore): add missing batch request and effect for initial font loading --- .../model/stores/comparisonStore.svelte.ts | 66 ++++++++++++++++--- .../model/stores/comparisonStore.test.ts | 6 ++ 2 files changed, 64 insertions(+), 8 deletions(-) diff --git a/src/widgets/ComparisonView/model/stores/comparisonStore.svelte.ts b/src/widgets/ComparisonView/model/stores/comparisonStore.svelte.ts index f639d18..4792779 100644 --- a/src/widgets/ComparisonView/model/stores/comparisonStore.svelte.ts +++ b/src/widgets/ComparisonView/model/stores/comparisonStore.svelte.ts @@ -14,15 +14,19 @@ */ import { + type FontLoadRequestConfig, type UnifiedFont, + appliedFontsManager, fetchFontsByIds, fontStore, + getFontUrl, } from '$entities/Font'; import { DEFAULT_TYPOGRAPHY_CONTROLS_DATA, createTypographyControlManager, } from '$features/SetupFont'; import { createPersistentStore } from '$shared/lib'; +import { untrack } from 'svelte'; /** * Storage schema for comparison state @@ -70,8 +74,37 @@ export class ComparisonStore { constructor() { this.restoreFromStorage(); - // Reactively set defaults if we aren't restoring and have no selection + // Reactively handle font loading and default selection $effect.root(() => { + // Effect 1: Trigger font loading whenever selection or weight changes + $effect(() => { + const fa = this.#fontA; + const fb = this.#fontB; + const weight = this.#typography.weight; + + if (!fa || !fb) return; + + const configs: FontLoadRequestConfig[] = []; + [fa, fb].forEach(f => { + const url = getFontUrl(f, weight); + if (url) { + configs.push({ + id: f.id, + name: f.name, + weight, + url, + isVariable: f.features?.isVariable, + }); + } + }); + + if (configs.length > 0) { + appliedFontsManager.touch(configs); + this.#checkFontsLoaded(); + } + }); + + // Effect 2: Set defaults if we aren't restoring and have no selection $effect(() => { // Wait until we are done checking storage if (this.#isRestoring) { @@ -80,24 +113,41 @@ export class ComparisonStore { // If we already have a selection, do nothing if (this.#fontA && this.#fontB) { - this.#checkFontsLoaded(); return; } // Check if fonts are available to set as defaults const fonts = fontStore.fonts; if (fonts.length >= 2) { - // Only set if we really have nothing (fallback) - if (!this.#fontA) this.#fontA = fonts[0]; - if (!this.#fontB) this.#fontB = fonts[fonts.length - 1]; - - // Sync defaults to storage so they persist if the user leaves - this.updateStorage(); + // We need full objects with all URLs, so we trigger a batch fetch + // This is the "batch request" seen on initial load when storage is empty + untrack(() => { + this.restoreDefaults([fonts[0].id, fonts[fonts.length - 1].id]); + }); } }); }); } + /** + * Set default fonts by fetching full objects from the API + */ + private async restoreDefaults(ids: string[]) { + this.#isRestoring = true; + try { + const fullFonts = await fetchFontsByIds(ids); + if (fullFonts.length >= 2) { + this.#fontA = fullFonts[0]; + this.#fontB = fullFonts[1]; + this.updateStorage(); + } + } catch (error) { + console.warn('[ComparisonStore] Failed to set defaults:', error); + } finally { + this.#isRestoring = false; + } + } + /** * Checks if fonts are actually loaded in the browser at current weight * diff --git a/src/widgets/ComparisonView/model/stores/comparisonStore.test.ts b/src/widgets/ComparisonView/model/stores/comparisonStore.test.ts index 10d2316..9e73568 100644 --- a/src/widgets/ComparisonView/model/stores/comparisonStore.test.ts +++ b/src/widgets/ComparisonView/model/stores/comparisonStore.test.ts @@ -26,6 +26,12 @@ import { vi.mock('$entities/Font', () => ({ fetchFontsByIds: vi.fn(), fontStore: { fonts: [] }, + appliedFontsManager: { + touch: vi.fn(), + getFontStatus: vi.fn(), + ready: vi.fn(() => Promise.resolve()), + }, + getFontUrl: vi.fn(() => 'http://example.com/font.woff2'), })); vi.mock('$features/SetupFont', () => ({