From 921e3c013373cc7ec24230b8d31c6afaaebdbfcd Mon Sep 17 00:00:00 2001 From: Igor Zinken Date: Sun, 3 Jan 2021 19:54:53 +0100 Subject: [PATCH] Implemented loading state for potentially long running operations --- README.md | 1 - src/bitmappery.vue | 18 +- .../dropbox-file-selector.vue | 6 +- .../file-menu/export-image/export-image.vue | 6 +- .../save-dropbox-document.vue | 6 +- src/components/loader/loader.vue | 189 ++++++++++++++++++ src/store/index.js | 15 +- tests/unit/store/store.spec.js | 25 ++- 8 files changed, 254 insertions(+), 12 deletions(-) create mode 100644 src/components/loader/loader.vue diff --git a/README.md b/README.md index b79f4c7..4381e5c 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,6 @@ npm run lint * Implement iframe based rendering (as more compatible alternative to OffscreenCanvas) for effects * Implement action queue when drawing, only execute drawing on update() hook -* Implement loaders on document load/save, image export and dropbox import * Maintain cache for transformations and filters, rendered at the display destination size (invalidate on window resize) * Drawing masks on a rotated layer that is panned (or mirrored) is broken * Dragging of masks on rotated/mirror content is kinda broken diff --git a/src/bitmappery.vue b/src/bitmappery.vue index 70834a4..b440774 100644 --- a/src/bitmappery.vue +++ b/src/bitmappery.vue @@ -1,7 +1,7 @@ /** * The MIT License (MIT) * - * Igor Zinken 2020 - https://www.igorski.nl + * Igor Zinken 2020-2021 - https://www.igorski.nl * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -56,13 +56,14 @@ @close="closeModal()" /> + + + diff --git a/src/store/index.js b/src/store/index.js index e539427..abb25a7 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,7 +1,7 @@ /** * The MIT License (MIT) * - * Igor Zinken 2020 - https://www.igorski.nl + * Igor Zinken 2020-2021 - https://www.igorski.nl * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -56,6 +56,7 @@ export default { panMode: false, // whether drag interactions with the document will pan its viewport dialog: null, // currently opened dialog modal: null, // currently opened modal + loadingStates: [], // wether one or more long running operations are running notifications: [], // notification message queue dropboxConnected: false, windowSize: { @@ -66,6 +67,7 @@ export default { getters: { // eslint-disable-next-line no-unused-vars t: state => ( key, optArgs ) => translate( key, optArgs ), + isLoading: state => state.loadingStates.length > 0, }, mutations: { setMenuOpened( state, value ) { @@ -86,6 +88,17 @@ export default { setPanMode( state, value ) { state.panMode = value; }, + setLoading( state, key ) { + if ( !state.loadingStates.includes( key )) { + state.loadingStates.push( key ); + } + }, + unsetLoading( state, key ) { + const idx = state.loadingStates.indexOf( key ); + if ( idx > -1 ) { + state.loadingStates.splice( idx, 1 ); + } + }, /** * open a dialog window showing given title and message. * types can be info, error or confirm. When type is confirm, optional diff --git a/tests/unit/store/store.spec.js b/tests/unit/store/store.spec.js index 93b96ea..d1dd1ed 100644 --- a/tests/unit/store/store.spec.js +++ b/tests/unit/store/store.spec.js @@ -1,7 +1,7 @@ import store, { PROJECT_FILE_EXTENSION } from "@/store"; import { LAYER_IMAGE } from "@/definitions/layer-types"; -const { mutations, actions } = store; +const { getters, mutations, actions } = store; let mockUpdateFn; jest.mock( "@/services/keyboard-service", () => ({ @@ -19,6 +19,15 @@ jest.mock( "@/utils/file-util", () => ({ })) describe( "Vuex store", () => { + describe( "getters", () => { + it( "should know when there is currently a loading state active", () => { + const state = { loadingStates: [] }; + expect( getters.isLoading( state )).toBe( false ); + state.loadingStates.push( "foo" ); + expect( getters.isLoading( state )).toBe( true ); + }); + }); + describe( "mutations", () => { it( "should be able to toggle the opened state of the menu", () => { const state = { menuOpened: false }; @@ -57,6 +66,20 @@ describe( "Vuex store", () => { expect( state.panMode ).toBe( true ); }); + describe( "when toggling loading states", () => { + it( "should be able to register a new loading state", () => { + const state = { loadingStates: [ "foo" ] }; + mutations.setLoading( state, "bar" ); + expect( state.loadingStates ).toEqual([ "foo", "bar" ]); + }); + + it( "should be able to unregister an existing loading state", () => { + const state = { loadingStates: [ "foo", "bar" ] }; + mutations.unsetLoading( state, "foo" ); + expect( state.loadingStates ).toEqual([ "bar" ]); + }); + }); + describe( "when toggling dialog windows", () => { it( "should be able to open a dialog window and apply its request parameters", () => { const state = { dialog: null };