diff --git a/src/rendering/cache/thumbnail-cache.ts b/src/rendering/cache/thumbnail-cache.ts index 609fc1b..37370f1 100644 --- a/src/rendering/cache/thumbnail-cache.ts +++ b/src/rendering/cache/thumbnail-cache.ts @@ -106,6 +106,12 @@ export const getThumbnailForLayer = ( layerId: string ): string => { return thumbnailCache.get( layerId )?.source ?? TRANSPARENT_IMAGE; }; +export const rebuildAllThumbnails = async ( activeDocument: Document ): Promise => { + return Promise.all( + activeDocument.layers.map( layer => createLayerThumbnail( layer, activeDocument )), + ); +}; + export const flushThumbnailForLayer = ( layer: Layer ): void => { // console.info( "flushing thumbnail for layer " + layer.id ); thumbnailCache.delete( layer.id ); diff --git a/src/store/modules/preferences-module.ts b/src/store/modules/preferences-module.ts index 3c1ceb8..b037f8f 100644 --- a/src/store/modules/preferences-module.ts +++ b/src/store/modules/preferences-module.ts @@ -22,7 +22,7 @@ */ import type { ActionContext, Module } from "vuex"; import { isMobile } from "@/utils/environment-util"; -import { setEnabled } from "@/rendering/cache/thumbnail-cache"; +import { setEnabled, rebuildAllThumbnails } from "@/rendering/cache/thumbnail-cache"; import { setWasmFilters } from "@/services/render-service"; // @ts-expect-error 'import.meta' property not allowed (not an issue, Vite takes care of it) @@ -94,10 +94,14 @@ const PreferencesModule: Module = { window.localStorage?.setItem( STORAGE_KEY, JSON.stringify( state.preferences )); dispatch( "handlePreferences" ); }, - handlePreferences({ state }: ActionContext ): void { + handlePreferences({ state, getters }: ActionContext ): void { const { preferences } = state; + const { activeDocument } = getters; setEnabled( !!preferences.thumbnails ); + if ( preferences.thumbnails && activeDocument ) { + rebuildAllThumbnails( activeDocument ); + } setWasmFilters( SUPPORT_WASM && !!preferences.wasmFilters ); }, } diff --git a/tests/unit/rendering/cache/thumbnail-cache.spec.ts b/tests/unit/rendering/cache/thumbnail-cache.spec.ts index 4133332..31b96f2 100644 --- a/tests/unit/rendering/cache/thumbnail-cache.spec.ts +++ b/tests/unit/rendering/cache/thumbnail-cache.spec.ts @@ -12,6 +12,7 @@ import { flushThumbnailCache, getThumbnailForLayer, isEnabled, + rebuildAllThumbnails, setEnabled, subscribe, TRANSPARENT_IMAGE, @@ -217,7 +218,7 @@ describe( "Thumbnail cache", () => { }); }); - describe( "when caching and retrieving a layers thumbnail", () => { + describe( "when caching and retrieving a Layers thumbnail", () => { it( "should by default retrieve a transparent image when the thumbnail isn't cached yet", () => { expect( getThumbnailForLayer( layer.id )).toEqual( TRANSPARENT_IMAGE ); }); @@ -236,4 +237,22 @@ describe( "Thumbnail cache", () => { expect( thumb ).toEqual( "data:image/png;base64," ); }); }); + + it( "should be able to rebuild all thumbnails for a Document", async () => { + setEnabled( true ); + + const layer1 = LayerFactory.create(); + const layer2 = LayerFactory.create(); + const layer3 = LayerFactory.create(); + + const doc = DocumentFactory.create({ + layers: [ layer1, layer2, layer3 ], + }); + + await rebuildAllThumbnails( doc ); + + expect( hasThumbnail( layer1.id )).toBe( true ); + expect( hasThumbnail( layer2.id )).toBe( true ); + expect( hasThumbnail( layer3.id )).toBe( true ); + }); }); \ No newline at end of file diff --git a/tests/unit/store/modules/preferences-module.spec.ts b/tests/unit/store/modules/preferences-module.spec.ts index cf317fb..d499641 100644 --- a/tests/unit/store/modules/preferences-module.spec.ts +++ b/tests/unit/store/modules/preferences-module.spec.ts @@ -1,5 +1,6 @@ -import { it, describe, expect, vi } from "vitest"; +import { it, afterEach, describe, expect, vi } from "vitest"; import { mockZCanvas } from "../../mocks"; +import DocumentFactory from "@/factories/document-factory"; import storeModule, { createPreferencesState, type Preferences } from "@/store/modules/preferences-module"; const { getters, mutations, actions } = storeModule; @@ -10,8 +11,10 @@ Storage.prototype.getItem = vi.fn(() => JSON.stringify( mockStorageData )); mockZCanvas(); const mockSetThumbnailCacheEnabled = vi.fn(); +const mockRebuildAllThumbnails = vi.fn(); vi.mock( "@/rendering/cache/thumbnail-cache", () => ({ setEnabled: vi.fn(( ...args ) => mockSetThumbnailCacheEnabled( ...args )), + rebuildAllThumbnails: vi.fn(( ...args ) => mockRebuildAllThumbnails( ...args )), })); const mockSetWasmFilters = vi.fn(); @@ -20,6 +23,10 @@ vi.mock( "@/services/render-service", () => ({ })); describe( "Vuex preferences module", () => { + afterEach(() => { + vi.resetAllMocks(); + }); + describe( "getters", () => { const state = createPreferencesState({ lowMemory : false, @@ -99,15 +106,49 @@ describe( "Vuex preferences module", () => { wasmFilters : true, antiAlias : true, autoAlias : true, - }) + }); + const getters = { activeDocument: DocumentFactory.create() }; // @ts-expect-error Not all constituents of type 'Action' are callable - actions.handlePreferences({ state }); - - expect( mockSetThumbnailCacheEnabled ).toHaveBeenCalledWith( true ); + actions.handlePreferences({ state, getters }); // WASM support is currently disabled expect( mockSetWasmFilters ).toHaveBeenCalledWith( false ); }); + + describe( "when handling changes to thumbnail display", () => { + it( "should disable the thumbnail cache when disabling", () => { + const state = createPreferencesState({ thumbnails: false }); + const getters = { activeDocument: DocumentFactory.create() }; + + // @ts-expect-error Not all constituents of type 'Action' are callable + actions.handlePreferences({ state, getters }); + + expect( mockSetThumbnailCacheEnabled ).toHaveBeenCalledWith( false ); + expect( mockRebuildAllThumbnails ).not.toHaveBeenCalled(); + }); + + it( "should enable the thumbnail cache when enabling", () => { + const state = createPreferencesState({ thumbnails: true }); + const getters = {}; + + // @ts-expect-error Not all constituents of type 'Action' are callable + actions.handlePreferences({ state, getters }); + + expect( mockSetThumbnailCacheEnabled ).toHaveBeenCalledWith( true ); + expect( mockRebuildAllThumbnails ).not.toHaveBeenCalled(); + }); + + it( "should rebuild the thumbnail cache when there is an active document when enabling", () => { + const state = createPreferencesState({ thumbnails: true }); + const getters = { activeDocument: DocumentFactory.create() }; + + // @ts-expect-error Not all constituents of type 'Action' are callable + actions.handlePreferences({ state, getters }); + + expect( mockSetThumbnailCacheEnabled ).toHaveBeenCalledWith( true ); + expect( mockRebuildAllThumbnails ).toHaveBeenCalledWith( getters.activeDocument ); + }); + }); }); });