Rebuild all thumbnails when toggling the thumbnail display on

This commit is contained in:
Igor Zinken
2026-03-28 15:07:14 +01:00
parent 075c9f7a0d
commit 40b66bb21f
4 changed files with 78 additions and 8 deletions

View File

@@ -106,6 +106,12 @@ export const getThumbnailForLayer = ( layerId: string ): string => {
return thumbnailCache.get( layerId )?.source ?? TRANSPARENT_IMAGE;
};
export const rebuildAllThumbnails = async ( activeDocument: Document ): Promise<void[]> => {
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 );

View File

@@ -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<PreferencesState, any> = {
window.localStorage?.setItem( STORAGE_KEY, JSON.stringify( state.preferences ));
dispatch( "handlePreferences" );
},
handlePreferences({ state }: ActionContext<PreferencesState, any> ): void {
handlePreferences({ state, getters }: ActionContext<PreferencesState, any> ): void {
const { preferences } = state;
const { activeDocument } = getters;
setEnabled( !!preferences.thumbnails );
if ( preferences.thumbnails && activeDocument ) {
rebuildAllThumbnails( activeDocument );
}
setWasmFilters( SUPPORT_WASM && !!preferences.wasmFilters );
},
}

View File

@@ -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 );
});
});

View File

@@ -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<PreferencesState, any>' 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<PreferencesState, any>' 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<PreferencesState, any>' 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<PreferencesState, any>' are callable
actions.handlePreferences({ state, getters });
expect( mockSetThumbnailCacheEnabled ).toHaveBeenCalledWith( true );
expect( mockRebuildAllThumbnails ).toHaveBeenCalledWith( getters.activeDocument );
});
});
});
});