From 1d7f857070651f676bbb5bd7e6d79c7fed56be5f Mon Sep 17 00:00:00 2001 From: Dominik Pschenitschni Date: Fri, 30 Sep 2022 12:52:21 +0200 Subject: [PATCH] feat: rework loading state of stores --- src/stores/auth.ts | 15 ++------------ src/stores/base.ts | 10 +-------- src/stores/helper.ts | 10 +++++++-- src/stores/kanban.ts | 12 +++++------ src/stores/labels.ts | 10 ++++----- src/stores/lists.ts | 8 ++++---- src/stores/namespaces.ts | 8 ++++---- src/stores/tasks.ts | 27 +++++++++++++++++-------- src/views/namespaces/ListNamespaces.vue | 4 +--- src/views/user/Login.vue | 15 +------------- src/views/user/OpenIdAuth.vue | 4 +--- src/views/user/Register.vue | 4 +--- src/views/user/settings/General.vue | 18 ++++++----------- 13 files changed, 59 insertions(+), 86 deletions(-) diff --git a/src/stores/auth.ts b/src/stores/auth.ts index f00559e3..61eda328 100644 --- a/src/stores/auth.ts +++ b/src/stores/auth.ts @@ -6,14 +6,13 @@ import {objectToSnakeCase} from '@/helpers/case' import UserModel, { getAvatarUrl } from '@/models/user' import UserSettingsService from '@/services/userSettings' import {getToken, refreshToken, removeToken, saveToken} from '@/helpers/auth' -import {setLoadingPinia} from '@/stores/helper' +import {setModuleLoading} from '@/stores/helper' import {success} from '@/message' import {redirectToProvider} from '@/helpers/redirectToProvider' import {AUTH_TYPES, type IUser} from '@/modelTypes/IUser' import type {IUserSettings} from '@/modelTypes/IUserSettings' import router from '@/router' import {useConfigStore} from '@/stores/config' -import {useBaseStore} from '@/stores/base' import UserSettingsModel from '@/models/userSettings' export interface AuthState { @@ -104,8 +103,6 @@ export const useAuthStore = defineStore('auth', { // Logs a user in with a set of credentials. async login(credentials) { const HTTP = HTTPFactory() - const baseStore = useBaseStore() - baseStore.setLoading(true) this.setIsLoading(true) // Delete an eventually preexisting old token @@ -129,7 +126,6 @@ export const useAuthStore = defineStore('auth', { throw e } finally { - baseStore.setLoading(false) this.setIsLoading(false) } }, @@ -138,8 +134,6 @@ export const useAuthStore = defineStore('auth', { // Not sure if this is the right place to put the logic in, maybe a seperate js component would be better suited. async register(credentials) { const HTTP = HTTPFactory() - const baseStore = useBaseStore() - baseStore.setLoading(true) this.setIsLoading(true) try { await HTTP.post('register', credentials) @@ -151,15 +145,12 @@ export const useAuthStore = defineStore('auth', { throw e } finally { - baseStore.setLoading(false) this.setIsLoading(false) } }, async openIdAuth({provider, code}) { const HTTP = HTTPFactory() - const baseStore = useBaseStore() - baseStore.setLoading(true) this.setIsLoading(true) const data = { @@ -176,7 +167,6 @@ export const useAuthStore = defineStore('auth', { // Tell others the user is autheticated this.checkAuth() } finally { - baseStore.setLoading(false) this.setIsLoading(false) } }, @@ -294,8 +284,7 @@ export const useAuthStore = defineStore('auth', { }) { const userSettingsService = new UserSettingsService() - // FIXME - const cancel = setLoadingPinia(this, this.setIsLoadingGeneralSettings) + const cancel = setModuleLoading(this, this.setIsLoadingGeneralSettings) try { saveLanguage(settings.language) await userSettingsService.update(settings) diff --git a/src/stores/base.ts b/src/stores/base.ts index 83f14305..9c07cdd8 100644 --- a/src/stores/base.ts +++ b/src/stores/base.ts @@ -11,7 +11,6 @@ import type {IList} from '@/modelTypes/IList' export interface RootStoreState { loading: boolean, - loadingModule: null, currentList: IList, background: string, @@ -27,7 +26,6 @@ export interface RootStoreState { export const useBaseStore = defineStore('base', { state: () : RootStoreState => ({ loading: false, - loadingModule: null, // This is used to highlight the current list in menu for all list related views currentList: new ListModel({ @@ -49,11 +47,6 @@ export const useBaseStore = defineStore('base', { this.loading = loading }, - setLoadingModule(module) { - this.loadingModule = module - }, - - // FIXME: same action as mutation name setCurrentList(currentList: IList) { // Server updates don't return the right. Therefore, the right is reset after updating the list which is // confusing because all the buttons will disappear in that case. To prevent this, we're keeping the right @@ -102,8 +95,7 @@ export const useBaseStore = defineStore('base', { this.logoVisible = visible }, - // FIXME: update all actions handleSetCurrentList - async handleSetCurrentList({list, forceUpdate = false}) { + async handleSetCurrentList({list, forceUpdate = false} : {list: IList, forceUpdate: boolean}) { if (list === null) { this.setCurrentList({}) this.setBackground('') diff --git a/src/stores/helper.ts b/src/stores/helper.ts index b6564394..d0746485 100644 --- a/src/stores/helper.ts +++ b/src/stores/helper.ts @@ -1,13 +1,19 @@ import type { StoreDefinition } from 'pinia' -export const setLoadingPinia = (store: StoreDefinition, loadFunc : ((isLoading: boolean) => void) | null = null) => { +export interface LoadingState { + isLoading: boolean +} + +const LOADING_TIMEOUT = 100 + +export const setModuleLoading = >(store: LoadingStore, loadFunc : ((isLoading: boolean) => void) | null = null) => { const timeout = setTimeout(() => { if (loadFunc === null) { store.isLoading = true } else { loadFunc(true) } - }, 100) + }, LOADING_TIMEOUT) return () => { clearTimeout(timeout) if (loadFunc === null) { diff --git a/src/stores/kanban.ts b/src/stores/kanban.ts index 01c7b645..5b93a1ab 100644 --- a/src/stores/kanban.ts +++ b/src/stores/kanban.ts @@ -8,7 +8,7 @@ import {success} from '@/message' import BucketService from '@/services/bucket' import TaskCollectionService from '@/services/taskCollection' -import {setLoadingPinia} from '@/stores/helper' +import {setModuleLoading} from '@/stores/helper' import type { ITask } from '@/modelTypes/ITask' import type { IList } from '@/modelTypes/IList' @@ -261,7 +261,7 @@ export const useKanbanStore = defineStore('kanban', { }, async loadBucketsForList({listId, params}: {listId: IList['id'], params}) { - const cancel = setLoadingPinia(this) + const cancel = setModuleLoading(this) // Clear everything to prevent having old buckets in the list if loading the buckets from this list takes a few moments this.setBuckets([]) @@ -295,7 +295,7 @@ export const useKanbanStore = defineStore('kanban', { return } - const cancel = setLoadingPinia(this) + const cancel = setModuleLoading(this) this.setBucketLoading({bucketId: bucketId, loading: true}) const params = JSON.parse(JSON.stringify(ps)) @@ -338,7 +338,7 @@ export const useKanbanStore = defineStore('kanban', { }, async createBucket(bucket: IBucket) { - const cancel = setLoadingPinia(this) + const cancel = setModuleLoading(this) const bucketService = new BucketService() try { @@ -351,7 +351,7 @@ export const useKanbanStore = defineStore('kanban', { }, async deleteBucket({bucket, params}: {bucket: IBucket, params}) { - const cancel = setLoadingPinia(this) + const cancel = setModuleLoading(this) const bucketService = new BucketService() try { @@ -366,7 +366,7 @@ export const useKanbanStore = defineStore('kanban', { }, async updateBucket(updatedBucketData: IBucket) { - const cancel = setLoadingPinia(this) + const cancel = setModuleLoading(this) const bucketIndex = findIndexById(this.buckets, updatedBucketData.id) const oldBucket = cloneDeep(this.buckets[bucketIndex]) diff --git a/src/stores/labels.ts b/src/stores/labels.ts index 2c77dbc1..6cdbdf9b 100644 --- a/src/stores/labels.ts +++ b/src/stores/labels.ts @@ -4,7 +4,7 @@ import LabelService from '@/services/label' import {success} from '@/message' import {i18n} from '@/i18n' import {createNewIndexer} from '@/indexes' -import {setLoadingPinia} from '@/stores/helper' +import {setModuleLoading} from '@/stores/helper' import type {ILabel} from '@/modelTypes/ILabel' const {add, remove, update, search} = createNewIndexer('labels', ['title', 'description']) @@ -85,7 +85,7 @@ export const useLabelStore = defineStore('label', { return } - const cancel = setLoadingPinia(this) + const cancel = setModuleLoading(this) try { const labels = await getAllLabels() @@ -97,7 +97,7 @@ export const useLabelStore = defineStore('label', { }, async deleteLabel(label: ILabel) { - const cancel = setLoadingPinia(this) + const cancel = setModuleLoading(this) const labelService = new LabelService() try { @@ -111,7 +111,7 @@ export const useLabelStore = defineStore('label', { }, async updateLabel(label: ILabel) { - const cancel = setLoadingPinia(this) + const cancel = setModuleLoading(this) const labelService = new LabelService() try { @@ -125,7 +125,7 @@ export const useLabelStore = defineStore('label', { }, async createLabel(label: ILabel) { - const cancel = setLoadingPinia(this) + const cancel = setModuleLoading(this) const labelService = new LabelService() try { diff --git a/src/stores/lists.ts b/src/stores/lists.ts index acb8c2cc..0e08517d 100644 --- a/src/stores/lists.ts +++ b/src/stores/lists.ts @@ -3,7 +3,7 @@ import {acceptHMRUpdate, defineStore} from 'pinia' import {useI18n} from 'vue-i18n' import ListService from '@/services/list' -import {setLoadingPinia} from '@/stores/helper' +import {setModuleLoading} from '@/stores/helper' import {removeListFromHistory} from '@/modules/listHistory' import {createNewIndexer} from '@/indexes' import {useNamespaceStore} from './namespaces' @@ -91,7 +91,7 @@ export const useListStore = defineStore('list', { }, async createList(list: IList) { - const cancel = setLoadingPinia(this) + const cancel = setModuleLoading(this) const listService = new ListService() try { @@ -107,7 +107,7 @@ export const useListStore = defineStore('list', { }, async updateList(list: IList) { - const cancel = setLoadingPinia(this) + const cancel = setModuleLoading(this) const listService = new ListService() try { @@ -143,7 +143,7 @@ export const useListStore = defineStore('list', { }, async deleteList(list: IList) { - const cancel = setLoadingPinia(this) + const cancel = setModuleLoading(this) const listService = new ListService() try { diff --git a/src/stores/namespaces.ts b/src/stores/namespaces.ts index 470c37b1..86a7f026 100644 --- a/src/stores/namespaces.ts +++ b/src/stores/namespaces.ts @@ -1,7 +1,7 @@ import {defineStore, acceptHMRUpdate} from 'pinia' import NamespaceService from '../services/namespace' -import {setLoadingPinia} from '@/stores/helper' +import {setModuleLoading} from '@/stores/helper' import {createNewIndexer} from '@/indexes' import type {INamespace} from '@/modelTypes/INamespace' import type {IList} from '@/modelTypes/IList' @@ -139,7 +139,7 @@ export const useNamespaceStore = defineStore('namespace', { }, async loadNamespaces() { - const cancel = setLoadingPinia(this) + const cancel = setModuleLoading(this) const namespaceService = new NamespaceService() try { @@ -174,7 +174,7 @@ export const useNamespaceStore = defineStore('namespace', { }, async deleteNamespace(namespace: INamespace) { - const cancel = setLoadingPinia(this) + const cancel = setModuleLoading(this) const namespaceService = new NamespaceService() try { @@ -187,7 +187,7 @@ export const useNamespaceStore = defineStore('namespace', { }, async createNamespace(namespace: INamespace) { - const cancel = setLoadingPinia(this) + const cancel = setModuleLoading(this) const namespaceService = new NamespaceService() try { diff --git a/src/stores/tasks.ts b/src/stores/tasks.ts index 990a05a8..2ed93259 100644 --- a/src/stores/tasks.ts +++ b/src/stores/tasks.ts @@ -23,8 +23,7 @@ import type {IUser} from '@/modelTypes/IUser' import type {IAttachment} from '@/modelTypes/IAttachment' import type {IList} from '@/modelTypes/IList' -import {setLoadingPinia} from '@/stores/helper' -import {useBaseStore} from '@/stores/base' +import {setModuleLoading} from '@/stores/helper' import {useLabelStore} from '@/stores/labels' import {useListStore} from '@/stores/lists' import {useAttachmentStore} from '@/stores/attachments' @@ -79,29 +78,41 @@ async function findAssignees(parsedTaskAssignees: string[]) { } export interface TaskState { + tasks: { [id: ITask['id']]: ITask } isLoading: boolean, } export const useTaskStore = defineStore('task', { state: () : TaskState => ({ + tasks: {}, isLoading: false, }), + getters: { + hasTasks(state) { + return Object.keys(state.tasks).length > 0 + }, + }, actions: { + setTasks(tasks: ITask[]) { + tasks.forEach(task => { + this.tasks[task.id] = task + }) + }, + async loadTasks(params) { const taskService = new TaskService() - const cancel = setLoadingPinia(this) + const cancel = setModuleLoading(this) try { - const tasks = await taskService.getAll({}, params) - useBaseStore().setHasTasks(tasks.length > 0) - return tasks + this.tasks = await taskService.getAll({}, params) + return this.tasks } finally { cancel() } }, async update(task: ITask) { - const cancel = setLoadingPinia(this) + const cancel = setModuleLoading(this) const taskService = new TaskService() try { @@ -358,7 +369,7 @@ export const useTaskStore = defineStore('task', { } : Partial, ) { - const cancel = setLoadingPinia(this) + const cancel = setModuleLoading(this) const parsedTask = parseTaskText(title, getQuickAddMagicMode()) const foundListId = await this.findListId({ diff --git a/src/views/namespaces/ListNamespaces.vue b/src/views/namespaces/ListNamespaces.vue index 805d20b7..046a9de4 100644 --- a/src/views/namespaces/ListNamespaces.vue +++ b/src/views/namespaces/ListNamespaces.vue @@ -79,17 +79,15 @@ import {getNamespaceTitle} from '@/helpers/getNamespaceTitle' import {useTitle} from '@/composables/useTitle' import {useStorage} from '@vueuse/core' -import {useBaseStore} from '@/stores/base' import {useNamespaceStore} from '@/stores/namespaces' const {t} = useI18n() -const baseStore = useBaseStore() const namespaceStore = useNamespaceStore() useTitle(() => t('namespace.title')) const showArchived = useStorage('showArchived', false) -const loading = computed(() => baseStore.loading) // FIXME: shouldn't this reference the namespace store? +const loading = computed(() => namespaceStore.isLoading) const namespaces = computed(() => { return namespaceStore.namespaces.filter(n => showArchived.value ? true : !n.isArchived) // return namespaceStore.namespaces.filter(n => showArchived.value ? true : !n.isArchived).map(n => { diff --git a/src/views/user/Login.vue b/src/views/user/Login.vue index ea0c8e32..e855c5f7 100644 --- a/src/views/user/Login.vue +++ b/src/views/user/Login.vue @@ -114,7 +114,6 @@ import {getLastVisited, clearLastVisited} from '../../helpers/saveLastVisited' import Password from '@/components/input/password.vue' import {setTitle} from '@/helpers/setTitle' -import {useBaseStore} from '@/stores/base' import {useConfigStore} from '@/stores/config' import {useAuthStore} from '@/stores/auth' @@ -172,13 +171,11 @@ export default defineComponent({ hasOpenIdProviders() { return this.openidConnect.enabled && this.openidConnect.providers?.length > 0 }, - ...mapState(useBaseStore, { - isLoading: state => state.loading, - }), ...mapState(useAuthStore, { needsTotpPasscode: state => state.needsTotpPasscode, authenticated: state => state.authenticated, + isLoading: state => state.isLoading, }), ...mapState(useConfigStore, { @@ -195,16 +192,6 @@ export default defineComponent({ }, }, methods: { - setLoading() { - const timeout = setTimeout(() => { - this.isLoading = true - }, 100) - return () => { - clearTimeout(timeout) - this.isLoading = false - } - }, - async submit() { this.errorMessage = '' // Some browsers prevent Vue bindings from working with autofilled values. diff --git a/src/views/user/OpenIdAuth.vue b/src/views/user/OpenIdAuth.vue index dfd1bc9e..a7a2a261 100644 --- a/src/views/user/OpenIdAuth.vue +++ b/src/views/user/OpenIdAuth.vue @@ -22,7 +22,6 @@ import {getErrorText} from '@/message' import Message from '@/components/misc/message.vue' import {clearLastVisited, getLastVisited} from '@/helpers/saveLastVisited' -import {useBaseStore} from '@/stores/base' import {useAuthStore} from '@/stores/auth' const {t} = useI18n({useScope: 'global'}) @@ -30,10 +29,9 @@ const {t} = useI18n({useScope: 'global'}) const router = useRouter() const route = useRoute() -const baseStore = useBaseStore() const authStore = useAuthStore() -const loading = computed(() => baseStore.loading) +const loading = computed(() => authStore.isLoading) const errorMessage = ref('') async function authenticateWithCode() { diff --git a/src/views/user/Register.vue b/src/views/user/Register.vue index e4c628c0..bdbf0f8d 100644 --- a/src/views/user/Register.vue +++ b/src/views/user/Register.vue @@ -77,10 +77,8 @@ import Message from '@/components/misc/message.vue' import {isEmail} from '@/helpers/isEmail' import Password from '@/components/input/password.vue' -import {useBaseStore} from '@/stores/base' import {useAuthStore} from '@/stores/auth' -const baseStore = useBaseStore() const authStore = useAuthStore() // FIXME: use the `beforeEnter` hook of vue-router @@ -97,7 +95,7 @@ const credentials = reactive({ password: '', }) -const isLoading = computed(() => baseStore.loading) +const isLoading = computed(() => authStore.isLoading) const errorMessage = ref('') const validatePasswordInitially = ref(false) diff --git a/src/views/user/settings/General.vue b/src/views/user/settings/General.vue index fc826041..834e270a 100644 --- a/src/views/user/settings/General.vue +++ b/src/views/user/settings/General.vue @@ -147,13 +147,7 @@