feat: rework loading state of stores
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is failing Details

This commit is contained in:
Dominik Pschenitschni 2022-09-30 12:52:21 +02:00
parent 7f281fc5e9
commit 1d7f857070
Signed by: dpschen
GPG Key ID: B257AC0149F43A77
13 changed files with 59 additions and 86 deletions

View File

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

View File

@ -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('')

View File

@ -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 = <LoadingStore extends StoreDefinition<string, LoadingState>>(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) {

View File

@ -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])

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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<ITask>,
) {
const cancel = setLoadingPinia(this)
const cancel = setModuleLoading(this)
const parsedTask = parseTaskText(title, getQuickAddMagicMode())
const foundListId = await this.findListId({

View File

@ -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 => {

View File

@ -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.

View File

@ -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() {

View File

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

View File

@ -147,13 +147,7 @@
</template>
<script lang="ts">
import {defineComponent} from 'vue'
import { useListStore } from '@/stores/lists'
import { useAuthStore } from '@/stores/auth'
export default defineComponent({
name: 'user-settings-general',
})
export default {name: 'user-settings-general'}
</script>
<script setup lang="ts">
@ -168,14 +162,15 @@ import {availableLanguages} from '@/i18n'
import {playSoundWhenDoneKey, playPopSound} from '@/helpers/playPop'
import {getQuickAddMagicMode, setQuickAddMagicMode} from '@/helpers/quickAddMagicMode'
import {createRandomID} from '@/helpers/randomId'
import {objectIsEmpty} from '@/helpers/objectIsEmpty'
import {success} from '@/message'
import {AuthenticatedHTTPFactory} from '@/http-common'
import {useBaseStore} from '@/stores/base'
import {useColorScheme} from '@/composables/useColorScheme'
import {useTitle} from '@/composables/useTitle'
import {objectIsEmpty} from '@/helpers/objectIsEmpty'
import {useListStore} from '@/stores/lists'
import {useAuthStore} from '@/stores/auth'
const {t} = useI18n({useScope: 'global'})
useTitle(() => `${t('user.settings.general.title')} - ${t('user.settings.title')}`)
@ -228,7 +223,6 @@ function getPlaySoundWhenDoneSetting() {
const playSoundWhenDone = ref(getPlaySoundWhenDoneSetting())
const quickAddMagicMode = ref(getQuickAddMagicMode())
const baseStore = useBaseStore()
const authStore = useAuthStore()
const settings = ref({...authStore.settings})
const id = ref(createRandomID())
@ -257,7 +251,7 @@ const defaultList = computed({
settings.value.defaultListId = l ? l.id : DEFAULT_LIST_ID
},
})
const loading = computed(() => baseStore.loading && baseStore.loadingModule === 'general-settings')
const loading = computed(() => authStore.isLoadingGeneralSettings)
watch(
playSoundWhenDone,