From 3b940cb56c2e29f83b5b329dea45f6059be7ca48 Mon Sep 17 00:00:00 2001 From: Dominik Pschenitschni Date: Sat, 9 Oct 2021 16:04:19 +0200 Subject: [PATCH 01/15] feat: don't rethrow same error and handle errors globally --- src/App.vue | 1 - src/components/home/contentAuth.vue | 3 -- src/components/home/navigation.vue | 4 -- src/components/list/partials/filters.vue | 4 -- src/components/list/partials/list-card.vue | 4 -- src/components/migrator/migration.vue | 12 ------ src/components/misc/subscription.vue | 6 --- .../notifications/notifications.vue | 4 -- .../quick-actions/quick-actions.vue | 12 ------ src/components/sharing/linkSharing.vue | 9 ---- src/components/sharing/userTeam.vue | 15 ------- src/components/tasks/add-task.vue | 6 +-- src/components/tasks/edit-task.vue | 3 -- src/components/tasks/gantt-component.vue | 12 ------ src/components/tasks/mixins/taskList.js | 3 -- src/components/tasks/partials/attachments.vue | 3 -- src/components/tasks/partials/comments.vue | 12 ------ src/components/tasks/partials/defer-task.vue | 3 -- src/components/tasks/partials/description.vue | 3 -- .../tasks/partials/editAssignees.vue | 9 ---- src/components/tasks/partials/editLabels.vue | 9 ---- src/components/tasks/partials/heading.vue | 3 -- src/components/tasks/partials/kanban-card.vue | 3 -- src/components/tasks/partials/listSearch.vue | 3 -- .../tasks/partials/relatedTasks.vue | 19 +------- .../tasks/partials/singleTaskInList.vue | 6 --- src/components/user/avatar-settings.vue | 11 ++--- src/components/user/settings/data-export.vue | 1 - src/components/user/settings/deletion.vue | 2 - src/helpers/auth.ts | 4 +- src/helpers/migrator.ts | 2 +- src/main.ts | 22 +++++++++- src/services/abstractService.js | 43 +++---------------- src/services/backgroundUnsplash.js | 3 -- src/services/list.js | 10 +---- src/services/passwordReset.js | 6 --- src/store/modules/auth.js | 9 +--- src/store/modules/config.js | 1 - src/store/modules/kanban.js | 32 +++----------- src/store/modules/labels.js | 4 -- src/store/modules/lists.js | 6 +-- src/store/modules/namespaces.js | 3 -- src/store/modules/tasks.js | 37 +++------------- src/views/filters/CreateSavedFilter.vue | 1 - src/views/filters/settings/delete.vue | 1 - src/views/filters/settings/edit.vue | 2 - src/views/labels/ListLabels.vue | 9 ---- src/views/labels/NewLabel.vue | 3 -- src/views/list/NewList.vue | 3 -- src/views/list/ShowList.vue | 3 -- src/views/list/settings/archive.vue | 3 -- src/views/list/settings/background.vue | 12 ------ src/views/list/settings/delete.vue | 3 -- src/views/list/settings/duplicate.vue | 3 -- src/views/list/settings/edit.vue | 6 --- src/views/list/settings/share.vue | 3 -- src/views/list/views/Kanban.vue | 1 - src/views/list/views/List.vue | 3 -- src/views/namespaces/NewNamespace.vue | 3 -- src/views/namespaces/settings/archive.vue | 3 -- src/views/namespaces/settings/delete.vue | 3 -- src/views/namespaces/settings/edit.vue | 6 --- src/views/namespaces/settings/share.vue | 3 -- src/views/tasks/ShowTasks.vue | 3 -- src/views/tasks/TaskDetailView.vue | 39 ++++++----------- src/views/teams/EditTeam.vue | 21 --------- src/views/teams/ListTeams.vue | 3 -- src/views/teams/NewTeam.vue | 3 -- src/views/user/DataExportDownload.vue | 1 - src/views/user/Register.vue | 1 - src/views/user/Settings.vue | 8 +--- 71 files changed, 69 insertions(+), 451 deletions(-) diff --git a/src/App.vue b/src/App.vue index 90231eb0..79c9f433 100644 --- a/src/App.vue +++ b/src/App.vue @@ -109,7 +109,6 @@ export default defineComponent({ this.$message.success({message: this.$t('user.deletion.confirmSuccess')}) this.$store.dispatch('auth/refreshUserInfo') }) - .catch(e => this.$message.error(e)) } }, }, diff --git a/src/components/home/contentAuth.vue b/src/components/home/contentAuth.vue index ff055830..b4b6c527 100644 --- a/src/components/home/contentAuth.vue +++ b/src/components/home/contentAuth.vue @@ -128,9 +128,6 @@ export default { }, loadLabels() { this.$store.dispatch('labels/loadAllLabels') - .catch(e => { - this.$message.error(e) - }) }, }, } diff --git a/src/components/home/navigation.vue b/src/components/home/navigation.vue index 44a3c9a0..4e874f2d 100644 --- a/src/components/home/navigation.vue +++ b/src/components/home/navigation.vue @@ -218,7 +218,6 @@ export default { return } this.$store.dispatch('lists/toggleListFavorite', list) - .catch(e => this.$message.error(e)) }, resize() { // Hide the menu by default on mobile @@ -263,9 +262,6 @@ export default { ...list, position, }) - .catch(e => { - this.$message.error(e) - }) .finally(() => { this.listUpdating[list.id] = false }) diff --git a/src/components/list/partials/filters.vue b/src/components/list/partials/filters.vue index e27cee12..a9175e98 100644 --- a/src/components/list/partials/filters.vue +++ b/src/components/list/partials/filters.vue @@ -483,7 +483,6 @@ export default { .then(r => { this[kind] = r }) - .catch(e => this.$message.error(e)) } }, setDoneFilter() { @@ -535,9 +534,6 @@ export default { // Filter users from the results who are already assigned this[`found${kind}`] = response.filter(({id}) => !includesById(this[kind], id)) }) - .catch(e => { - this.$message.error(e) - }) }, add(kind, filterName) { this.$nextTick(() => { diff --git a/src/components/list/partials/list-card.vue b/src/components/list/partials/list-card.vue index 09be012a..c351fb2a 100644 --- a/src/components/list/partials/list-card.vue +++ b/src/components/list/partials/list-card.vue @@ -68,9 +68,6 @@ export default { .then(b => { this.background = b }) - .catch(e => { - this.$message.error(e) - }) .finally(() => this.backgroundLoading = false) }, toggleFavoriteList(list) { @@ -80,7 +77,6 @@ export default { return } this.$store.dispatch('lists/toggleListFavorite', list) - .catch(e => this.$message.error(e)) }, }, } diff --git a/src/components/migrator/migration.vue b/src/components/migrator/migration.vue index 9846f7ef..57a3cab5 100644 --- a/src/components/migrator/migration.vue +++ b/src/components/migrator/migration.vue @@ -138,9 +138,6 @@ export default { } this.migrate() }) - .catch(e => { - this.$message.error(e) - }) } }, methods: { @@ -149,9 +146,6 @@ export default { .then(r => { this.authUrl = r.url }) - .catch(e => { - this.$message.error(e) - }) }, migrate() { this.isMigrating = true @@ -167,9 +161,6 @@ export default { this.message = r.message this.$store.dispatch('namespaces/loadNamespaces') }) - .catch(e => { - this.$message.error(e) - }) .finally(() => { this.isMigrating = false }) @@ -184,9 +175,6 @@ export default { this.message = r.message this.$store.dispatch('namespaces/loadNamespaces') }) - .catch(e => { - this.$message.error(e) - }) .finally(() => { this.isMigrating = false }) diff --git a/src/components/misc/subscription.vue b/src/components/misc/subscription.vue index f29091c6..adbc8826 100644 --- a/src/components/misc/subscription.vue +++ b/src/components/misc/subscription.vue @@ -99,9 +99,6 @@ export default { this.$emit('change', subscription) this.$message.success({message: this.$t('task.subscription.subscribeSuccess', {entity: this.entity})}) }) - .catch(e => { - this.$message.error(e) - }) }, unsubscribe() { const subscription = new SubscriptionModel({ @@ -113,9 +110,6 @@ export default { this.$emit('change', null) this.$message.success({message: this.$t('task.subscription.unsubscribeSuccess', {entity: this.entity})}) }) - .catch(e => { - this.$message.error(e) - }) }, }, } diff --git a/src/components/notifications/notifications.vue b/src/components/notifications/notifications.vue index 0fb3d4c6..a99e0779 100644 --- a/src/components/notifications/notifications.vue +++ b/src/components/notifications/notifications.vue @@ -98,9 +98,6 @@ export default { .then(r => { this.allNotifications = r }) - .catch(e => { - this.$message.error(e) - }) }, to(n, index) { const to = { @@ -137,7 +134,6 @@ export default { .then(r => { this.allNotifications[index] = r }) - .catch(e => this.$message.error(e)) } }, }, diff --git a/src/components/quick-actions/quick-actions.vue b/src/components/quick-actions/quick-actions.vue index 5ed2fa1b..22572a6c 100644 --- a/src/components/quick-actions/quick-actions.vue +++ b/src/components/quick-actions/quick-actions.vue @@ -392,9 +392,6 @@ export default { this.$router.push({name: 'task.detail', params: {id: r.id}}) this.closeQuickActions() }) - .catch((e) => { - this.$message.error(e) - }) }, newList() { if (this.currentList === null) { @@ -411,9 +408,6 @@ export default { this.$router.push({name: 'list.index', params: {listId: r.id}}) this.closeQuickActions() }) - .catch((e) => { - this.$message.error(e) - }) }, newNamespace() { const newNamespace = new NamespaceModel({title: this.query}) @@ -423,9 +417,6 @@ export default { this.$message.success({message: this.$t('namespace.create.success')}) this.closeQuickActions() }) - .catch((e) => { - this.$message.error(e) - }) }, newTeam() { const newTeam = new TeamModel({name: this.query}) @@ -438,9 +429,6 @@ export default { this.$message.success({message: this.$t('team.create.success')}) this.closeQuickActions() }) - .catch((e) => { - this.$message.error(e) - }) }, select(parentIndex, index) { diff --git a/src/components/sharing/linkSharing.vue b/src/components/sharing/linkSharing.vue index cbebea28..78113156 100644 --- a/src/components/sharing/linkSharing.vue +++ b/src/components/sharing/linkSharing.vue @@ -226,9 +226,6 @@ export default { .then((r) => { this.linkShares = r }) - .catch((e) => { - this.$message.error(e) - }) }, add(listId) { const newLinkShare = new LinkShareModel({ @@ -247,9 +244,6 @@ export default { this.$message.success({message: this.$t('list.share.links.createSuccess')}) this.load(listId) }) - .catch((e) => { - this.$message.error(e) - }) }, remove(listId) { const linkshare = new LinkShareModel({ @@ -262,9 +256,6 @@ export default { this.$message.success({message: this.$t('list.share.links.deleteSuccess')}) this.load(listId) }) - .catch((e) => { - this.$message.error(e) - }) .finally(() => { this.showDeleteModal = false }) diff --git a/src/components/sharing/userTeam.vue b/src/components/sharing/userTeam.vue index 66bd524f..91d7ca8f 100644 --- a/src/components/sharing/userTeam.vue +++ b/src/components/sharing/userTeam.vue @@ -281,9 +281,6 @@ export default { this.selectedRight[s.id] = s.right, ) }) - .catch((e) => { - this.$message.error(e) - }) }, deleteSharable() { if (this.shareType === 'user') { @@ -306,9 +303,6 @@ export default { } this.$message.success({message: this.$t('list.share.userTeam.removeSuccess', {type: this.shareTypeName, sharable: this.sharableName})}) }) - .catch((e) => { - this.$message.error(e) - }) }, add(admin) { if (admin === null) { @@ -331,9 +325,6 @@ export default { this.$message.success({message: this.$t('list.share.userTeam.addedSuccess', {type: this.shareTypeName})}) this.load() }) - .catch((e) => { - this.$message.error(e) - }) }, toggleType(sharable) { if ( @@ -367,9 +358,6 @@ export default { } this.$message.success({message: this.$t('list.share.userTeam.updatedSuccess', {type: this.shareTypeName})}) }) - .catch((e) => { - this.$message.error(e) - }) }, find(query) { if (query === '') { @@ -382,9 +370,6 @@ export default { .then((response) => { this.found = response }) - .catch((e) => { - this.$message.error(e) - }) }, clearAll() { this.found = [] diff --git a/src/components/tasks/add-task.vue b/src/components/tasks/add-task.vue index ade0ea76..99d1a950 100644 --- a/src/components/tasks/add-task.vue +++ b/src/components/tasks/add-task.vue @@ -113,16 +113,16 @@ export default { ) }) - Promise.all(newTasks) + return Promise.all(newTasks) .then(() => { this.newTaskTitle = '' }) .catch(e => { - if (e === 'NO_LIST') { + if (e.message === 'NO_LIST') { this.errorMessage = this.$t('list.create.addListRequired') return } - this.$message.error(e) + throw e }) }, handleEnter(e) { diff --git a/src/components/tasks/edit-task.vue b/src/components/tasks/edit-task.vue index dc92cfba..61926133 100644 --- a/src/components/tasks/edit-task.vue +++ b/src/components/tasks/edit-task.vue @@ -142,9 +142,6 @@ export default { this.initTaskFields() this.$message.success({message: this.$t('task.detail.updateSuccess')}) }) - .catch((e) => { - this.$message.error(e) - }) }, }, } diff --git a/src/components/tasks/gantt-component.vue b/src/components/tasks/gantt-component.vue index 2ea3591f..1e3825ba 100644 --- a/src/components/tasks/gantt-component.vue +++ b/src/components/tasks/gantt-component.vue @@ -317,9 +317,6 @@ export default { return tasks } }) - .catch((e) => { - return Promise.reject(e) - }) } getAllTasks() @@ -343,9 +340,6 @@ export default { return 0 }) }) - .catch((e) => { - this.$message.error(e) - }) }, addGantAttributes(t) { if (typeof t.durationDays !== 'undefined' && typeof t.offsetDays !== 'undefined') { @@ -423,9 +417,6 @@ export default { } } }) - .catch((e) => { - this.$message.error(e) - }) }, editTask(task) { this.taskToEdit = task @@ -460,9 +451,6 @@ export default { this.newTaskTitle = '' this.hideCrateNewTask() }) - .catch((e) => { - this.$message.error(e) - }) }, formatYear(date) { return this.format(date, 'MMMM, yyyy') diff --git a/src/components/tasks/mixins/taskList.js b/src/components/tasks/mixins/taskList.js index 130e9390..da120b19 100644 --- a/src/components/tasks/mixins/taskList.js +++ b/src/components/tasks/mixins/taskList.js @@ -84,9 +84,6 @@ export default { this.loadedList = JSON.parse(JSON.stringify(currentList)) }) - .catch(e => { - this.$message.error(e) - }) }, loadTasksForPage(e) { diff --git a/src/components/tasks/partials/attachments.vue b/src/components/tasks/partials/attachments.vue index a641a819..de3c8c6a 100644 --- a/src/components/tasks/partials/attachments.vue +++ b/src/components/tasks/partials/attachments.vue @@ -228,9 +228,6 @@ export default { ) this.$message.success(r) }) - .catch((e) => { - this.$message.error(e) - }) .finally(() => { this.showDeleteModal = false }) diff --git a/src/components/tasks/partials/comments.vue b/src/components/tasks/partials/comments.vue index b217ea10..136832e4 100644 --- a/src/components/tasks/partials/comments.vue +++ b/src/components/tasks/partials/comments.vue @@ -227,9 +227,6 @@ export default { this.comments = r this.makeActions() }) - .catch((e) => { - this.$message.error(e) - }) }, addComment() { if (this.newComment.comment === '') { @@ -253,9 +250,6 @@ export default { this.$message.success({message: this.$t('task.comment.addedSuccess')}) this.makeActions() }) - .catch((e) => { - this.$message.error(e) - }) .finally(() => { this.creating = false }) @@ -289,9 +283,6 @@ export default { this.saved = null }, 2000) }) - .catch((e) => { - this.$message.error(e) - }) .finally(() => { this.isCommentEdit = false this.saving = null @@ -307,9 +298,6 @@ export default { } } }) - .catch((e) => { - this.$message.error(e) - }) .finally(() => { this.showDeleteModal = false }) diff --git a/src/components/tasks/partials/defer-task.vue b/src/components/tasks/partials/defer-task.vue index 2e66b501..549c3367 100644 --- a/src/components/tasks/partials/defer-task.vue +++ b/src/components/tasks/partials/defer-task.vue @@ -129,9 +129,6 @@ export default { this.task = r this.$emit('update:modelValue', r) }) - .catch((e) => { - this.$message.error(e) - }) }, }, } diff --git a/src/components/tasks/partials/description.vue b/src/components/tasks/partials/description.vue index fe721158..18ccbfe0 100644 --- a/src/components/tasks/partials/description.vue +++ b/src/components/tasks/partials/description.vue @@ -83,9 +83,6 @@ export default { this.saved = false }, 2000) }) - .catch(e => { - this.$message.error(e) - }) .finally(() => { this.saving = false }) diff --git a/src/components/tasks/partials/editAssignees.vue b/src/components/tasks/partials/editAssignees.vue index 17ecc0db..563735ab 100644 --- a/src/components/tasks/partials/editAssignees.vue +++ b/src/components/tasks/partials/editAssignees.vue @@ -84,9 +84,6 @@ export default { this.$emit('update:modelValue', this.assignees) this.$message.success({message: this.$t('task.assignee.assignSuccess')}) }) - .catch(e => { - this.$message.error(e) - }) }, removeAssignee(user) { this.$store.dispatch('tasks/removeAssignee', {user: user, taskId: this.taskId}) @@ -99,9 +96,6 @@ export default { } this.$message.success({message: this.$t('task.assignee.unassignSuccess')}) }) - .catch(e => { - this.$message.error(e) - }) }, findUser(query) { if (query === '') { @@ -114,9 +108,6 @@ export default { // Filter the results to not include users who are already assigned this.foundUsers = response.filter(({id}) => !includesById(this.assignees, id)) }) - .catch(e => { - this.$message.error(e) - }) }, clearAllFoundUsers() { this.foundUsers = [] diff --git a/src/components/tasks/partials/editLabels.vue b/src/components/tasks/partials/editLabels.vue index 41cf01c0..dd2e10da 100644 --- a/src/components/tasks/partials/editLabels.vue +++ b/src/components/tasks/partials/editLabels.vue @@ -111,9 +111,6 @@ export default { this.$message.success({message: this.$t('task.label.addSuccess')}) } }) - .catch(e => { - this.$message.error(e) - }) }, removeLabel(label) { const removeFromState = () => { @@ -136,9 +133,6 @@ export default { removeFromState() this.$message.success({message: this.$t('task.label.removeSuccess')}) }) - .catch(e => { - this.$message.error(e) - }) }, createAndAddLabel(title) { if (this.taskId === 0) { @@ -152,9 +146,6 @@ export default { this.labels.push(r) this.$message.success({message: this.$t('task.label.addCreateSuccess')}) }) - .catch(e => { - this.$message.error(e) - }) }, }, diff --git a/src/components/tasks/partials/heading.vue b/src/components/tasks/partials/heading.vue index 55f86f58..e775fb8c 100644 --- a/src/components/tasks/partials/heading.vue +++ b/src/components/tasks/partials/heading.vue @@ -82,9 +82,6 @@ export default { this.showSavedMessage = false }, 2000) }) - .catch(e => { - this.$message.error(e) - }) .finally(() => { this.saving = false }) diff --git a/src/components/tasks/partials/kanban-card.vue b/src/components/tasks/partials/kanban-card.vue index e7e30694..0d9141da 100644 --- a/src/components/tasks/partials/kanban-card.vue +++ b/src/components/tasks/partials/kanban-card.vue @@ -104,9 +104,6 @@ export default { playPop() } }) - .catch(e => { - this.$message.error(e) - }) .finally(() => { this.loadingInternal = false }) diff --git a/src/components/tasks/partials/listSearch.vue b/src/components/tasks/partials/listSearch.vue index 14f61b64..fe7a7437 100644 --- a/src/components/tasks/partials/listSearch.vue +++ b/src/components/tasks/partials/listSearch.vue @@ -60,9 +60,6 @@ export default { .then(response => { this.foundLists = response }) - .catch(e => { - this.$message.error(e) - }) }, clearAll() { this.foundLists = [] diff --git a/src/components/tasks/partials/relatedTasks.vue b/src/components/tasks/partials/relatedTasks.vue index dad4072e..5744ef5f 100644 --- a/src/components/tasks/partials/relatedTasks.vue +++ b/src/components/tasks/partials/relatedTasks.vue @@ -185,14 +185,8 @@ export default { }, }, methods: { - findTasks(query) { - this.taskService.getAll({}, {s: query}) - .then(response => { - this.foundTasks = response - }) - .catch(e => { - this.$message.error(e) - }) + async findTasks(query) { + this.foundTasks = await this.taskService.getAll({}, {s: query}) }, addTaskRelation() { let rel = new TaskRelationModel({ @@ -213,9 +207,6 @@ export default { this.saved = false }, 2000) }) - .catch(e => { - this.$message.error(e) - }) }, removeTaskRelation() { const rel = new TaskRelationModel({ @@ -237,9 +228,6 @@ export default { this.saved = false }, 2000) }) - .catch(e => { - this.$message.error(e) - }) .finally(() => { this.showDeleteModal = false }) @@ -251,9 +239,6 @@ export default { this.newTaskRelationTask = r this.addTaskRelation() }) - .catch(e => { - this.$message.error(e) - }) }, relationKindTitle(kind, length) { return this.$tc(`task.relation.kinds.${kind}`, length) diff --git a/src/components/tasks/partials/singleTaskInList.vue b/src/components/tasks/partials/singleTaskInList.vue index d2d21129..5cd7c614 100644 --- a/src/components/tasks/partials/singleTaskInList.vue +++ b/src/components/tasks/partials/singleTaskInList.vue @@ -187,9 +187,6 @@ export default { }, }]) }) - .catch(e => { - this.$message.error(e) - }) } if (checked) { @@ -206,9 +203,6 @@ export default { this.$emit('task-updated', t) this.$store.dispatch('namespaces/loadNamespacesIfFavoritesDontExist') }) - .catch(e => { - this.$message.error(e) - }) }, hideDeferDueDatePopup(e) { if (this.showDefer) { diff --git a/src/components/user/avatar-settings.vue b/src/components/user/avatar-settings.vue index 8fb03565..cdc8a83a 100644 --- a/src/components/user/avatar-settings.vue +++ b/src/components/user/avatar-settings.vue @@ -87,12 +87,9 @@ export default { Cropper, }, methods: { - avatarStatus() { - this.avatarService.get({}) - .then(r => { - this.avatarProvider = r.avatarProvider - }) - .catch(e => this.$message.error(e)) + async avatarStatus() { + const { avatarProvider } = await this.avatarService.get({}) + this.avatarProvider = avatarProvider }, updateAvatarStatus() { const avatarStatus = new AvatarModel({avatarProvider: this.avatarProvider}) @@ -101,7 +98,6 @@ export default { this.$message.success({message: this.$t('user.settings.avatar.statusUpdateSuccess')}) this.$store.commit('auth/reloadAvatar') }) - .catch(e => this.$message.error(e)) }, uploadAvatar() { this.loading = true @@ -114,7 +110,6 @@ export default { this.$message.success({message: this.$t('user.settings.avatar.setSuccess')}) this.$store.commit('auth/reloadAvatar') }) - .catch(e => this.$message.error(e)) .finally(() => { this.loading = false this.isCropAvatar = false diff --git a/src/components/user/settings/data-export.vue b/src/components/user/settings/data-export.vue index 3060d038..e395e6ac 100644 --- a/src/components/user/settings/data-export.vue +++ b/src/components/user/settings/data-export.vue @@ -64,7 +64,6 @@ export default { this.$message.success({message: this.$t('user.export.success')}) this.password = '' }) - .catch(e => this.$message.error(e)) }, }, } diff --git a/src/components/user/settings/deletion.vue b/src/components/user/settings/deletion.vue index a769d737..40dd272b 100644 --- a/src/components/user/settings/deletion.vue +++ b/src/components/user/settings/deletion.vue @@ -113,7 +113,6 @@ export default { this.$message.success({message: this.$t('user.deletion.requestSuccess')}) this.password = '' }) - .catch(e => this.$message.error(e)) }, cancelDeletion() { if (this.password === '') { @@ -128,7 +127,6 @@ export default { this.$store.dispatch('auth/refreshUserInfo') this.password = '' }) - .catch(e => this.$message.error(e)) }, }, } diff --git a/src/helpers/auth.ts b/src/helpers/auth.ts index 53444ff1..60a5f70b 100644 --- a/src/helpers/auth.ts +++ b/src/helpers/auth.ts @@ -53,9 +53,7 @@ export const refreshToken = (persist: boolean): Promise => { return Promise.resolve(r) }) .catch(e => { - // eslint-disable-next-line - console.log('Error renewing token: ', e) - return Promise.reject(e) + throw new Error('Error renewing token: ', { cause: e }) }) } diff --git a/src/helpers/migrator.ts b/src/helpers/migrator.ts index 7949ab69..92106b76 100644 --- a/src/helpers/migrator.ts +++ b/src/helpers/migrator.ts @@ -33,7 +33,7 @@ export const getMigratorFromSlug = (slug: string): Migrator => { isFileMigrator: true, } default: - throw Error('Unknown migrator slug ' + slug) + throw new Error('Unknown migrator slug ' + slug) } } diff --git a/src/main.ts b/src/main.ts index 179446a0..d92234ca 100644 --- a/src/main.ts +++ b/src/main.ts @@ -96,10 +96,30 @@ app.config.errorHandler = (err, vm, info) => { // if (import.meta.env.PROD) { // error(err) // } else { - console.error(err, vm, info) + // console.error(err, vm, info) + error(err) // } } +if (import.meta.env.DEV) { + app.config.warnHandler = (msg, vm, info) => { + error(msg) + } +} + + +// https://stackoverflow.com/a/52076738/15522256 +window.addEventListener('error', (err) => { + error(err) +}) + + +window.addEventListener('unhandledrejection', (err) => { + // event.promise contains the promise object + // event.reason contains the reason for the rejection + error(err) +}) + app.config.globalProperties.$message = { error, success, diff --git a/src/services/abstractService.js b/src/services/abstractService.js index 0aef3c37..f61a337d 100644 --- a/src/services/abstractService.js +++ b/src/services/abstractService.js @@ -105,19 +105,6 @@ export default class AbstractService { return true } - ///////////////////// - // Global error handler - /////////////////// - - /** - * Handles the error and rejects the promise. - * @param error - * @returns {Promise} - */ - errorHandler(error) { - return Promise.reject(error) - } - ///////////////// // Helper functions /////////////// @@ -284,7 +271,7 @@ export default class AbstractService { */ get(model, params = {}) { if (this.paths.get === '') { - return Promise.reject({message: 'This model is not able to get data.'}) + throw new Error('This model is not able to get data.') } return this.getM(this.paths.get, model, params) @@ -304,10 +291,7 @@ export default class AbstractService { model = this.beforeGet(model) const finalUrl = this.getReplacedRoute(url, model) - return this.http.get(finalUrl, {params: params}) - .catch(error => { - return this.errorHandler(error) - }) + return this.http.get(finalUrl, {params}) .then(response => { const result = this.modelGetFactory(response.data) result.maxRight = Number(response.headers['x-max-right']) @@ -339,7 +323,7 @@ export default class AbstractService { */ getAll(model = {}, params = {}, page = 1) { if (this.paths.getAll === '') { - return Promise.reject({message: 'This model is not able to get data.'}) + throw new Error('This model is not able to get data.') } params.page = page @@ -349,9 +333,6 @@ export default class AbstractService { const finalUrl = this.getReplacedRoute(this.paths.getAll, model) return this.http.get(finalUrl, {params: params}) - .catch(error => { - return this.errorHandler(error) - }) .then(response => { this.resultCount = Number(response.headers['x-pagination-result-count']) this.totalPages = Number(response.headers['x-pagination-total-pages']) @@ -378,16 +359,13 @@ export default class AbstractService { */ create(model) { if (this.paths.create === '') { - return Promise.reject({message: 'This model is not able to create data.'}) + throw new Error('This model is not able to create data.') } const cancel = this.setLoading() const finalUrl = this.getReplacedRoute(this.paths.create, model) return this.http.put(finalUrl, model) - .catch(error => { - return this.errorHandler(error) - }) .then(response => { const result = this.modelCreateFactory(response.data) if (typeof model.maxRight !== 'undefined') { @@ -411,9 +389,6 @@ export default class AbstractService { const cancel = this.setLoading() return this.http.post(url, model) - .catch(error => { - return this.errorHandler(error) - }) .then(response => { const result = this.modelUpdateFactory(response.data) if (typeof model.maxRight !== 'undefined') { @@ -433,7 +408,7 @@ export default class AbstractService { */ update(model) { if (this.paths.update === '') { - return Promise.reject({message: 'This model is not able to update data.'}) + throw new Error('This model is not able to update data.') } const finalUrl = this.getReplacedRoute(this.paths.update, model) @@ -447,16 +422,13 @@ export default class AbstractService { */ delete(model) { if (this.paths.delete === '') { - return Promise.reject({message: 'This model is not able to delete data.'}) + throw new Error('This model is not able to delete data.') } const cancel = this.setLoading() const finalUrl = this.getReplacedRoute(this.paths.delete, model) return this.http.delete(finalUrl, model) - .catch(error => { - return this.errorHandler(error) - }) .then(response => { return Promise.resolve(response.data) }) @@ -513,9 +485,6 @@ export default class AbstractService { }, }, ) - .catch(error => { - return this.errorHandler(error) - }) .then(response => { return Promise.resolve(this.modelCreateFactory(response.data)) }) diff --git a/src/services/backgroundUnsplash.js b/src/services/backgroundUnsplash.js index f0d29645..40161fe2 100644 --- a/src/services/backgroundUnsplash.js +++ b/src/services/backgroundUnsplash.js @@ -27,8 +27,5 @@ export default class BackgroundUnsplashService extends AbstractService { .then(response => { return window.URL.createObjectURL(new Blob([response.data])) }) - .catch(e => { - return e - }) } } \ No newline at end of file diff --git a/src/services/list.js b/src/services/list.js index 5fd2fc3d..b720df9f 100644 --- a/src/services/list.js +++ b/src/services/list.js @@ -57,21 +57,13 @@ export default class ListService extends AbstractService { .then(response => { return window.URL.createObjectURL(new Blob([response.data])) }) - .catch(e => { - return e - }) } removeBackground(list) { const cancel = this.setLoading() return this.http.delete(`/lists/${list.id}/background`, list) - .then(response => { - return Promise.resolve(response.data) - }) - .catch(error => { - return this.errorHandler(error) - }) + .then(response => response.data) .finally(() => { cancel() }) diff --git a/src/services/passwordReset.js b/src/services/passwordReset.js index 6f972504..95b2c94d 100644 --- a/src/services/passwordReset.js +++ b/src/services/passwordReset.js @@ -18,9 +18,6 @@ export default class PasswordResetService extends AbstractService { resetPassword(model) { const cancel = this.setLoading() return this.http.post(this.paths.reset, model) - .catch(error => { - return this.errorHandler(error) - }) .then(response => { return Promise.resolve(this.modelFactory(response.data)) }) @@ -32,9 +29,6 @@ export default class PasswordResetService extends AbstractService { requestResetPassword(model) { const cancel = this.setLoading() return this.http.post(this.paths.requestReset, model) - .catch(error => { - return this.errorHandler(error) - }) .then(response => { return Promise.resolve(this.modelFactory(response.data)) }) diff --git a/src/store/modules/auth.js b/src/store/modules/auth.js index f9e5b159..12795faf 100644 --- a/src/store/modules/auth.js +++ b/src/store/modules/auth.js @@ -159,9 +159,6 @@ export default { ctx.dispatch('checkAuth') return Promise.resolve() }) - .catch(e => { - return Promise.reject(e) - }) .finally(() => { ctx.commit(LOADING, false, {root: true}) }) @@ -174,9 +171,7 @@ export default { .then(r => { saveToken(r.data.token, false) ctx.dispatch('checkAuth') - return Promise.resolve(r.data) - }).catch(e => { - return Promise.reject(e) + return r.data }) }, // Populates user information from jwt token saved in local storage in store @@ -238,7 +233,7 @@ export default { ctx.commit('lastUserRefresh') }) .catch(e => { - console.error('Error while refreshing user info:', e) + throw new Error('Error while refreshing user info:', { cause: e }) }) }, // Renews the api token and saves it to local storage diff --git a/src/store/modules/config.js b/src/store/modules/config.js index 5019d901..7fe8f57b 100644 --- a/src/store/modules/config.js +++ b/src/store/modules/config.js @@ -68,7 +68,6 @@ export default { ctx.commit(CONFIG, r.data) return Promise.resolve(r) }) - .catch(e => Promise.reject(e)) }, redirectToProviderIfNothingElseIsEnabled(ctx) { if (ctx.state.auth.local.enabled === false && diff --git a/src/store/modules/kanban.js b/src/store/modules/kanban.js index cf7413d3..87349c0c 100644 --- a/src/store/modules/kanban.js +++ b/src/store/modules/kanban.js @@ -222,14 +222,9 @@ export default { .then(r => { ctx.commit('setBuckets', r) ctx.commit('setListId', listId) - return Promise.resolve(r) - }) - .catch(e => { - return Promise.reject(e) - }) - .finally(() => { - cancel() + return r }) + .finally(() => cancel()) }, loadNextTasksForBucket(ctx, {listId, ps = {}, bucketId}) { @@ -282,10 +277,7 @@ export default { if (taskService.totalPages <= page) { ctx.commit('setAllTasksLoadedForBucket', bucketId) } - return Promise.resolve(r) - }) - .catch(e => { - return Promise.reject(e) + return r }) .finally(() => { cancel() @@ -300,14 +292,9 @@ export default { return bucketService.create(bucket) .then(r => { ctx.commit('addBucket', r) - return Promise.resolve(r) - }) - .catch(e => { - return Promise.reject(e) - }) - .finally(() => { - cancel() + return r }) + .finally(() => cancel()) }, deleteBucket(ctx, {bucket, params}) { @@ -319,14 +306,9 @@ export default { ctx.commit('removeBucket', bucket) // We reload all buckets because tasks are being moved from the deleted bucket ctx.dispatch('loadBucketsForList', {listId: bucket.listId, params: params}) - return Promise.resolve(r) - }) - .catch(e => { - return Promise.reject(e) - }) - .finally(() => { - cancel() + return r }) + .finally(() => cancel()) }, updateBucket(ctx, updatedBucketData) { diff --git a/src/store/modules/labels.js b/src/store/modules/labels.js index 948636bc..3d113560 100644 --- a/src/store/modules/labels.js +++ b/src/store/modules/labels.js @@ -91,7 +91,6 @@ export default { ctx.commit('setLoaded', true) return Promise.resolve(r) }) - .catch(e => Promise.reject(e)) .finally(() => cancel()) }, deleteLabel(ctx, label) { @@ -103,7 +102,6 @@ export default { ctx.commit('removeLabelById', label) return Promise.resolve(r) }) - .catch(e => Promise.reject(e)) .finally(() => cancel()) }, updateLabel(ctx, label) { @@ -115,7 +113,6 @@ export default { ctx.commit('setLabel', r) return Promise.resolve(r) }) - .catch(e => Promise.reject(e)) .finally(() => cancel()) }, createLabel(ctx, label) { @@ -127,7 +124,6 @@ export default { ctx.commit('setLabel', r) return Promise.resolve(r) }) - .catch(e => Promise.reject(e)) .finally(() => cancel()) }, }, diff --git a/src/store/modules/lists.js b/src/store/modules/lists.js index d67610de..aae097a6 100644 --- a/src/store/modules/lists.js +++ b/src/store/modules/lists.js @@ -53,7 +53,6 @@ export default { ctx.commit('setList', r) return Promise.resolve(r) }) - .catch(e => Promise.reject(e)) .finally(() => cancel()) }, updateList(ctx, list) { @@ -99,10 +98,7 @@ export default { ctx.commit('removeListById', list) ctx.commit('namespaces/removeListFromNamespaceById', list, {root: true}) removeListFromHistory({id: list.id}) - return Promise.resolve(r) - }) - .catch(e => { - return Promise.reject(e) + return r }) .finally(() => cancel()) }, diff --git a/src/store/modules/namespaces.js b/src/store/modules/namespaces.js index 01e37ed2..2bbe8dcd 100644 --- a/src/store/modules/namespaces.js +++ b/src/store/modules/namespaces.js @@ -115,7 +115,6 @@ export default { return Promise.resolve(r) }) - .catch(e => Promise.reject(e)) .finally(() => { cancel() }) @@ -141,7 +140,6 @@ export default { ctx.commit('removeNamespaceById', namespace.id) return Promise.resolve(r) }) - .catch(e => Promise.reject(e)) .finally(() => cancel()) }, createNamespace(ctx, namespace) { @@ -153,7 +151,6 @@ export default { ctx.commit('addNamespace', r) return Promise.resolve(r) }) - .catch(e => Promise.reject(e)) .finally(() => cancel()) }, }, diff --git a/src/store/modules/tasks.js b/src/store/modules/tasks.js index cb5c6633..ddc628af 100644 --- a/src/store/modules/tasks.js +++ b/src/store/modules/tasks.js @@ -45,7 +45,6 @@ function addLabelToTask(task, label) { task.labels.push(label) return Promise.resolve(result) }) - .catch(e => Promise.reject(e)) } function findAssignees(parsedTaskAssignees) { @@ -77,9 +76,6 @@ export default { ctx.commit(HAS_TASKS, r.length > 0, {root: true}) return r }) - .catch(e => { - return Promise.reject(e) - }) .finally(() => { cancel() }) @@ -92,10 +88,7 @@ export default { return taskService.update(task) .then(t => { ctx.commit('kanban/setTaskInBucket', t, {root: true}) - return Promise.resolve(t) - }) - .catch(e => { - return Promise.reject(e) + return t }) .finally(() => { cancel() @@ -106,10 +99,7 @@ export default { return taskService.delete(task) .then(t => { ctx.commit('kanban/removeTaskInBucket', task, {root: true}) - return Promise.resolve(t) - }) - .catch(e => { - return Promise.reject(e) + return t }) }, // Adds a task attachment in store. @@ -151,10 +141,7 @@ export default { // FIXME: direct store manipulation (task) t.task.assignees.push(user) ctx.commit('kanban/setTaskInBucketByIndex', t, {root: true}) - return Promise.resolve(r) - }) - .catch(e => { - return Promise.reject(e) + return r }) }, removeAssignee(ctx, {user, taskId}) { @@ -182,10 +169,7 @@ export default { } ctx.commit('kanban/setTaskInBucketByIndex', t, {root: true}) - return Promise.resolve(r) - }) - .catch(e => { - return Promise.reject(e) + return r }) }, @@ -208,10 +192,7 @@ export default { t.task.labels.push(label) ctx.commit('kanban/setTaskInBucketByIndex', t, {root: true}) - return Promise.resolve(r) - }) - .catch(e => { - return Promise.reject(e) + return r }) }, @@ -241,10 +222,7 @@ export default { ctx.commit('kanban/setTaskInBucketByIndex', t, {root: true}) - return Promise.resolve(r) - }) - .catch(e => { - return Promise.reject(e) + return r }) }, @@ -296,7 +274,7 @@ export default { // 4. If none of the above worked, reject the promise with an error. if (typeof foundListId === 'undefined' || listId === null) { - return Promise.reject('NO_LIST') + throw new Error('NO_LIST') } return foundListId @@ -336,7 +314,6 @@ export default { task, parsedLabels:parsedTask.labels, })) - .catch(e => Promise.reject(e)) }, }, } \ No newline at end of file diff --git a/src/views/filters/CreateSavedFilter.vue b/src/views/filters/CreateSavedFilter.vue index fe45f1cd..4b7c30d8 100644 --- a/src/views/filters/CreateSavedFilter.vue +++ b/src/views/filters/CreateSavedFilter.vue @@ -100,7 +100,6 @@ export default { this.$store.dispatch('namespaces/loadNamespaces') this.$router.push({name: 'list.index', params: {listId: r.getListId()}}) }) - .catch(e => this.$message.error(e)) }, }, } diff --git a/src/views/filters/settings/delete.vue b/src/views/filters/settings/delete.vue index a95ba565..f581890d 100644 --- a/src/views/filters/settings/delete.vue +++ b/src/views/filters/settings/delete.vue @@ -35,7 +35,6 @@ export default { this.$message.success({message: this.$t('filters.delete.success')}) this.$router.push({name: 'namespaces.index'}) }) - .catch(e => this.$message.error(e)) }, }, } diff --git a/src/views/filters/settings/edit.vue b/src/views/filters/settings/edit.vue index 2834adf9..bb3ef8fb 100644 --- a/src/views/filters/settings/edit.vue +++ b/src/views/filters/settings/edit.vue @@ -105,7 +105,6 @@ export default { this.filter = r this.filters = objectToSnakeCase(this.filter.filters) }) - .catch(e => this.$message.error(e)) }, save() { this.filter.filters = this.filters @@ -117,7 +116,6 @@ export default { this.filters = objectToSnakeCase(this.filter.filters) this.$router.back() }) - .catch(e => this.$message.error(e)) }, }, } diff --git a/src/views/labels/ListLabels.vue b/src/views/labels/ListLabels.vue index a7a2b9d4..f49baafe 100644 --- a/src/views/labels/ListLabels.vue +++ b/src/views/labels/ListLabels.vue @@ -133,27 +133,18 @@ export default { methods: { loadLabels() { this.$store.dispatch('labels/loadAllLabels') - .catch(e => { - this.$message.error(e) - }) }, deleteLabel(label) { this.$store.dispatch('labels/deleteLabel', label) .then(() => { this.$message.success({message: this.$t('label.deleteSuccess')}) }) - .catch(e => { - this.$message.error(e) - }) }, editLabelSubmit() { this.$store.dispatch('labels/updateLabel', this.labelEditLabel) .then(() => { this.$message.success({message: this.$t('label.edit.success')}) }) - .catch(e => { - this.$message.error(e) - }) }, editLabel(label) { if (label.createdBy.id !== this.userInfo.id) { diff --git a/src/views/labels/NewLabel.vue b/src/views/labels/NewLabel.vue index 87506bba..30560cba 100644 --- a/src/views/labels/NewLabel.vue +++ b/src/views/labels/NewLabel.vue @@ -75,9 +75,6 @@ export default { }) this.$message.success({message: this.$t('label.create.success')}) }) - .catch((e) => { - this.$message.error(e) - }) }, }, } diff --git a/src/views/list/NewList.vue b/src/views/list/NewList.vue index 3bb96771..326422f4 100644 --- a/src/views/list/NewList.vue +++ b/src/views/list/NewList.vue @@ -71,9 +71,6 @@ export default { params: { listId: r.id }, }) }) - .catch((e) => { - this.$message.error(e) - }) }, }, } diff --git a/src/views/list/ShowList.vue b/src/views/list/ShowList.vue index 8c41d2dd..9e761345 100644 --- a/src/views/list/ShowList.vue +++ b/src/views/list/ShowList.vue @@ -144,9 +144,6 @@ export default { this.$store.dispatch(CURRENT_LIST, r) this.setTitle(this.getListTitle(r)) }) - .catch(e => { - this.$message.error(e) - }) .finally(() => { this.listLoaded = this.$route.params.listId }) diff --git a/src/views/list/settings/archive.vue b/src/views/list/settings/archive.vue index 86f595bf..c5afb790 100644 --- a/src/views/list/settings/archive.vue +++ b/src/views/list/settings/archive.vue @@ -42,9 +42,6 @@ export default { this.$store.commit('namespaces/setListInNamespaceById', r) this.$message.success({message: this.$t('list.archive.success')}) }) - .catch(e => { - this.$message.error(e) - }) .finally(() => { this.$router.back() }) diff --git a/src/views/list/settings/background.vue b/src/views/list/settings/background.vue index f2c8f913..6ae39094 100644 --- a/src/views/list/settings/background.vue +++ b/src/views/list/settings/background.vue @@ -128,9 +128,6 @@ export default { }) }) }) - .catch(e => { - this.$message.error(e) - }) }, 300) }, setBackground(backgroundId) { @@ -145,9 +142,6 @@ export default { this.$store.commit('namespaces/setListInNamespaceById', l) this.$message.success({message: this.$t('list.background.success')}) }) - .catch(e => { - this.$message.error(e) - }) }, uploadBackground() { if (this.$refs.backgroundUploadInput.files.length === 0) { @@ -160,9 +154,6 @@ export default { this.$store.commit('namespaces/setListInNamespaceById', l) this.$message.success({message: this.$t('list.background.success')}) }) - .catch(e => { - this.$message.error(e) - }) }, removeBackground() { this.listService.removeBackground(this.currentList) @@ -172,9 +163,6 @@ export default { this.$message.success({message: this.$t('list.background.removeSuccess')}) this.$router.back() }) - .catch(e => { - this.$message.error(e) - }) }, }, } diff --git a/src/views/list/settings/delete.vue b/src/views/list/settings/delete.vue index 2f614dba..3e6e30f5 100644 --- a/src/views/list/settings/delete.vue +++ b/src/views/list/settings/delete.vue @@ -30,9 +30,6 @@ export default { this.$message.success({message: this.$t('list.delete.success')}) this.$router.push({name: 'home'}) }) - .catch(e => { - this.$message.error(e) - }) }, }, } diff --git a/src/views/list/settings/duplicate.vue b/src/views/list/settings/duplicate.vue index 184e57a7..7ac07cd5 100644 --- a/src/views/list/settings/duplicate.vue +++ b/src/views/list/settings/duplicate.vue @@ -50,9 +50,6 @@ export default { this.$message.success({message: this.$t('list.duplicate.success')}) this.$router.push({name: 'list.index', params: {listId: r.list.id}}) }) - .catch(e => { - this.$message.error(e) - }) }, }, } diff --git a/src/views/list/settings/edit.vue b/src/views/list/settings/edit.vue index 5d39644a..aafb0503 100644 --- a/src/views/list/settings/edit.vue +++ b/src/views/list/settings/edit.vue @@ -101,9 +101,6 @@ export default { .then(r => { this.list = { ...r } }) - .catch(e => { - this.$message.error(e) - }) }, save() { this.$store.dispatch('lists/updateList', this.list) @@ -113,9 +110,6 @@ export default { this.$message.success({message: this.$t('list.edit.success')}) this.$router.back() }) - .catch(e => { - this.$message.error(e) - }) }, }, } diff --git a/src/views/list/settings/share.vue b/src/views/list/settings/share.vue index e60f3139..257df938 100644 --- a/src/views/list/settings/share.vue +++ b/src/views/list/settings/share.vue @@ -68,9 +68,6 @@ export default { this.manageUsersComponent = 'userTeam' this.setTitle(this.$t('list.share.title', {list: this.list.title})) }) - .catch(e => { - this.$message.error(e) - }) }, }, } diff --git a/src/views/list/views/Kanban.vue b/src/views/list/views/Kanban.vue index e91c1a2d..e918dd96 100644 --- a/src/views/list/views/Kanban.vue +++ b/src/views/list/views/Kanban.vue @@ -565,7 +565,6 @@ export default { } this.$store.dispatch('kanban/updateBucket', newBucket) .then(() => this.$message.success({message: this.$t('list.kanban.doneBucketSavedSuccess')})) - .catch(e => this.$message.error(e)) }, collapseBucket(bucket) { this.collapsedBuckets[bucket.id] = true diff --git a/src/views/list/views/List.vue b/src/views/list/views/List.vue index a41fea56..c853440c 100644 --- a/src/views/list/views/List.vue +++ b/src/views/list/views/List.vue @@ -307,9 +307,6 @@ export default { .then(r => { this.tasks[e.newIndex] = r }) - .catch(e => { - this.$message.error(e) - }) }, }, } diff --git a/src/views/namespaces/NewNamespace.vue b/src/views/namespaces/NewNamespace.vue index e4c55670..e5746859 100644 --- a/src/views/namespaces/NewNamespace.vue +++ b/src/views/namespaces/NewNamespace.vue @@ -77,9 +77,6 @@ export default { this.$message.success({message: this.$t('namespace.create.success') }) this.$router.back() }) - .catch((e) => { - this.$message.error(e) - }) }, }, } diff --git a/src/views/namespaces/settings/archive.vue b/src/views/namespaces/settings/archive.vue index 0a7d4a1e..7d98aabf 100644 --- a/src/views/namespaces/settings/archive.vue +++ b/src/views/namespaces/settings/archive.vue @@ -40,9 +40,6 @@ export default { this.$store.commit('namespaces/setNamespaceById', r) this.$message.success({message: this.$t('namespace.archive.success')}) }) - .catch(e => { - this.$message.error(e) - }) .finally(() => { this.$router.back() }) diff --git a/src/views/namespaces/settings/delete.vue b/src/views/namespaces/settings/delete.vue index 07f026b4..60fe51ff 100644 --- a/src/views/namespaces/settings/delete.vue +++ b/src/views/namespaces/settings/delete.vue @@ -41,9 +41,6 @@ export default { this.$message.success({message: this.$t('namespace.delete.success')}) this.$router.push({name: 'home'}) }) - .catch(e => { - this.$message.error(e) - }) }, }, } diff --git a/src/views/namespaces/settings/edit.vue b/src/views/namespaces/settings/edit.vue index 6e4e01ce..459c8ccf 100644 --- a/src/views/namespaces/settings/edit.vue +++ b/src/views/namespaces/settings/edit.vue @@ -112,9 +112,6 @@ export default { this.title = this.$t('namespace.edit.title', {namespace: r.title}) this.setTitle(this.title) }) - .catch(e => { - this.$message.error(e) - }) }, save() { this.namespaceService.update(this.namespace) @@ -124,9 +121,6 @@ export default { this.$message.success({message: this.$t('namespace.edit.success')}) this.$router.back() }) - .catch(e => { - this.$message.error(e) - }) }, }, } diff --git a/src/views/namespaces/settings/share.vue b/src/views/namespaces/settings/share.vue index 55f89781..e218f52e 100644 --- a/src/views/namespaces/settings/share.vue +++ b/src/views/namespaces/settings/share.vue @@ -68,9 +68,6 @@ export default { this.title = this.$t('namespace.share.title', { namespace: this.namespace.title }) this.setTitle(this.title) }) - .catch(e => { - this.$message.error(e) - }) }, }, } diff --git a/src/views/tasks/ShowTasks.vue b/src/views/tasks/ShowTasks.vue index 0e24b769..8cac15c3 100644 --- a/src/views/tasks/ShowTasks.vue +++ b/src/views/tasks/ShowTasks.vue @@ -211,9 +211,6 @@ export default { this.tasks = tasks }) - .catch(e => { - this.$message.error(e) - }) }, updateTasks(updatedTask) { for (const t in this.tasks) { diff --git a/src/views/tasks/TaskDetailView.vue b/src/views/tasks/TaskDetailView.vue index 4ada14bf..d4f43dde 100644 --- a/src/views/tasks/TaskDetailView.vue +++ b/src/views/tasks/TaskDetailView.vue @@ -574,9 +574,6 @@ export default { this.setActiveFields() this.setTitle(this.task.title) }) - .catch(e => { - this.$message.error(e) - }) .finally(() => { this.$nextTick(() => this.visible = true) this.scrollToHeading() @@ -620,25 +617,21 @@ export default { this.task.endDate = this.task.dueDate } - try { - this.task = await this.$store.dispatch('tasks/update', this.task) - this.setActiveFields() + this.task = await this.$store.dispatch('tasks/update', this.task) + this.setActiveFields() - if (!showNotification) { - return - } - - let actions = [] - if (undoCallback !== null) { - actions = [{ - title: 'Undo', - callback: undoCallback, - }] - } - this.$message.success({message: this.$t('task.detail.updateSuccess')}, actions) - } catch(e) { - this.$message.error(e) + if (!showNotification) { + return } + + let actions = [] + if (undoCallback !== null) { + actions = [{ + title: 'Undo', + callback: undoCallback, + }] + } + this.$message.success({message: this.$t('task.detail.updateSuccess')}, actions) }, setFieldActive(fieldName) { this.activeFields[fieldName] = true @@ -664,9 +657,6 @@ export default { this.$message.success({message: this.$t('task.detail.deleteSuccess')}) this.$router.push({name: 'list.index', params: {listId: this.task.listId}}) }) - .catch(e => { - this.$message.error(e) - }) }, toggleTaskDone() { this.task.done = !this.task.done @@ -705,9 +695,6 @@ export default { this.task = t this.$store.dispatch('namespaces/loadNamespacesIfFavoritesDontExist') }) - .catch(e => { - this.$message.error(e) - }) }, }, } diff --git a/src/views/teams/EditTeam.vue b/src/views/teams/EditTeam.vue index d2e9e272..25c89b94 100644 --- a/src/views/teams/EditTeam.vue +++ b/src/views/teams/EditTeam.vue @@ -229,9 +229,6 @@ export default { this.title = this.$t('team.edit.title', {team: this.team.name}) this.setTitle(this.title) }) - .catch((e) => { - this.$message.error(e) - }) }, save() { if (this.team.name === '') { @@ -246,9 +243,6 @@ export default { this.team = response this.$message.success({message: this.$t('team.edit.success')}) }) - .catch((e) => { - this.$message.error(e) - }) }, deleteTeam() { this.teamService @@ -257,9 +251,6 @@ export default { this.$message.success({message: this.$t('team.edit.delete.success')}) this.$router.push({name: 'teams.index'}) }) - .catch((e) => { - this.$message.error(e) - }) }, deleteUser() { this.teamMemberService @@ -268,9 +259,6 @@ export default { this.$message.success({message: this.$t('team.edit.deleteUser.success')}) this.loadTeam() }) - .catch((e) => { - this.$message.error(e) - }) .finally(() => { this.showUserDeleteModal = false }) @@ -286,9 +274,6 @@ export default { this.loadTeam() this.$message.success({message: this.$t('team.edit.userAddedSuccess')}) }) - .catch((e) => { - this.$message.error(e) - }) }, toggleUserType(member) { member.admin = !member.admin @@ -308,9 +293,6 @@ export default { this.$t('team.edit.madeMember'), }) }) - .catch((e) => { - this.$message.error(e) - }) }, findUser(query) { if (query === '') { @@ -323,9 +305,6 @@ export default { .then((response) => { this.foundUsers = response }) - .catch((e) => { - this.$message.error(e) - }) }, clearAll() { this.foundUsers = [] diff --git a/src/views/teams/ListTeams.vue b/src/views/teams/ListTeams.vue index 66ab034a..8d028449 100644 --- a/src/views/teams/ListTeams.vue +++ b/src/views/teams/ListTeams.vue @@ -48,9 +48,6 @@ export default { .then(response => { this.teams = response }) - .catch(e => { - this.$message.error(e) - }) }, }, } diff --git a/src/views/teams/NewTeam.vue b/src/views/teams/NewTeam.vue index a92f50b3..d13e37c5 100644 --- a/src/views/teams/NewTeam.vue +++ b/src/views/teams/NewTeam.vue @@ -65,9 +65,6 @@ export default { }) this.$message.success({message: this.$t('team.create.success') }) }) - .catch((e) => { - this.$message.error(e) - }) }, }, } diff --git a/src/views/user/DataExportDownload.vue b/src/views/user/DataExportDownload.vue index 71507c52..c8db6541 100644 --- a/src/views/user/DataExportDownload.vue +++ b/src/views/user/DataExportDownload.vue @@ -57,7 +57,6 @@ export default { } this.dataExportService.download(this.password) - .catch(e => this.$message.error(e)) }, }, } diff --git a/src/views/user/Register.vue b/src/views/user/Register.vue index 7d3b24ec..08792d15 100644 --- a/src/views/user/Register.vue +++ b/src/views/user/Register.vue @@ -147,7 +147,6 @@ export default { } this.$store.dispatch('auth/register', credentials) - .catch(() => {}) }, }, } diff --git a/src/views/user/Settings.vue b/src/views/user/Settings.vue index e2f4a4ed..9ede261d 100644 --- a/src/views/user/Settings.vue +++ b/src/views/user/Settings.vue @@ -395,14 +395,12 @@ export default { .then(() => { this.$message.success({message: this.$t('user.settings.passwordUpdateSuccess')}) }) - .catch(e => this.$message.error(e)) }, updateEmail() { this.emailUpdateService.update(this.emailUpdate) .then(() => { this.$message.success({message: this.$t('user.settings.updateEmailSuccess')}) }) - .catch(e => this.$message.error(e)) }, totpStatus() { if (!this.totpEnabled) { @@ -420,7 +418,7 @@ export default { return } - this.$message.error(e) + throw e }) }, totpSetQrCode() { @@ -437,7 +435,6 @@ export default { this.totp = r this.totpSetQrCode() }) - .catch(e => this.$message.error(e)) }, totpConfirm() { this.totpService.enable({passcode: this.totpConfirmPasscode}) @@ -445,7 +442,6 @@ export default { this.totp.enabled = true this.$message.success({message: this.$t('user.settings.totp.confirmSuccess')}) }) - .catch(e => this.$message.error(e)) }, totpDisable() { this.totpService.disable({password: this.totpDisablePassword}) @@ -454,7 +450,6 @@ export default { this.totp = new TotpModel() this.$message.success({message: this.$t('user.settings.totp.disableSuccess')}) }) - .catch(e => this.$message.error(e)) }, updateSettings() { localStorage.setItem(playSoundWhenDoneKey, this.playSoundWhenDone) @@ -467,7 +462,6 @@ export default { this.$store.commit('auth/setUserSettings', this.settings) this.$message.success({message: this.$t('user.settings.general.savedSuccess')}) }) - .catch(e => this.$message.error(e)) }, anchorHashCheck() { if (window.location.hash === this.$route.hash) { From a776e1d2f30fc4889430d8e8d8dbe376a76e28f8 Mon Sep 17 00:00:00 2001 From: Dominik Pschenitschni Date: Sat, 9 Oct 2021 16:34:57 +0200 Subject: [PATCH 02/15] chore: don't resolve when returning from promise & improve list store module --- src/components/misc/api-config.vue | 14 ++--- src/helpers/auth.ts | 2 +- src/i18n/index.js | 15 +++-- src/services/abstractService.js | 33 +++++----- src/services/attachment.js | 6 +- src/services/list.js | 2 +- src/services/passwordReset.js | 4 +- src/services/totp.js | 7 +-- src/store/modules/auth.js | 24 ++++---- src/store/modules/config.js | 2 +- src/store/modules/kanban.js | 8 +-- src/store/modules/labels.js | 96 +++++++++++++++--------------- src/store/modules/lists.js | 6 +- src/store/modules/namespaces.js | 7 +-- src/store/modules/tasks.js | 39 ++++++------ src/views/labels/ListLabels.vue | 15 +---- 16 files changed, 130 insertions(+), 150 deletions(-) diff --git a/src/components/misc/api-config.vue b/src/components/misc/api-config.vue index 06312ca3..acf36df8 100644 --- a/src/components/misc/api-config.vue +++ b/src/components/misc/api-config.vue @@ -106,7 +106,7 @@ export default { window.API_URL = urlToCheck.toString() return this.$store.dispatch('config/update') } - return Promise.reject(e) + throw e }) .catch((e) => { // Check if it has a port and if not check if it is reachable at https @@ -115,7 +115,7 @@ export default { window.API_URL = urlToCheck.toString() return this.$store.dispatch('config/update') } - return Promise.reject(e) + throw e }) .catch((e) => { // Check if it is reachable at /api/v1 and https @@ -128,7 +128,7 @@ export default { window.API_URL = urlToCheck.toString() return this.$store.dispatch('config/update') } - return Promise.reject(e) + throw e }) .catch((e) => { // Check if it is reachable at port API_DEFAULT_PORT and https @@ -138,7 +138,7 @@ export default { window.API_URL = urlToCheck.toString() return this.$store.dispatch('config/update') } - return Promise.reject(e) + throw e }) .catch((e) => { // Check if it is reachable at :API_DEFAULT_PORT and /api/v1 and https @@ -151,7 +151,7 @@ export default { window.API_URL = urlToCheck.toString() return this.$store.dispatch('config/update') } - return Promise.reject(e) + throw e }) .catch((e) => { // Check if it is reachable at port API_DEFAULT_PORT and http @@ -161,7 +161,7 @@ export default { window.API_URL = urlToCheck.toString() return this.$store.dispatch('config/update') } - return Promise.reject(e) + throw e }) .catch((e) => { // Check if it is reachable at :API_DEFAULT_PORT and /api/v1 and http @@ -174,7 +174,7 @@ export default { window.API_URL = urlToCheck.toString() return this.$store.dispatch('config/update') } - return Promise.reject(e) + throw e }) .catch(() => { // Still not found, url is still invalid diff --git a/src/helpers/auth.ts b/src/helpers/auth.ts index 60a5f70b..b3297bde 100644 --- a/src/helpers/auth.ts +++ b/src/helpers/auth.ts @@ -50,7 +50,7 @@ export const refreshToken = (persist: boolean): Promise => { }) .then(r => { saveToken(r.data.token, persist) - return Promise.resolve(r) + return r }) .catch(e => { throw new Error('Error renewing token: ', { cause: e }) diff --git a/src/i18n/index.js b/src/i18n/index.js index 6b21f2a9..00623402 100644 --- a/src/i18n/index.js +++ b/src/i18n/index.js @@ -27,14 +27,13 @@ const setI18nLanguage = lang => { } export const loadLanguageAsync = lang => { - // If the same language - if (i18n.locale === lang) { - return Promise.resolve(setI18nLanguage(lang)) - } - - // If the language was already loaded - if (loadedLanguages.includes(lang)) { - return Promise.resolve(setI18nLanguage(lang)) + if ( + // If the same language + i18n.locale === lang || + // If the language was already loaded + loadedLanguages.includes(lang) + ) { + return setI18nLanguage(lang) } // If the language hasn't been loaded yet diff --git a/src/services/abstractService.js b/src/services/abstractService.js index f61a337d..2bb58be0 100644 --- a/src/services/abstractService.js +++ b/src/services/abstractService.js @@ -295,7 +295,7 @@ export default class AbstractService { .then(response => { const result = this.modelGetFactory(response.data) result.maxRight = Number(response.headers['x-max-right']) - return Promise.resolve(result) + return result }) .finally(() => { cancel() @@ -338,14 +338,12 @@ export default class AbstractService { this.totalPages = Number(response.headers['x-pagination-total-pages']) if (Array.isArray(response.data)) { - return Promise.resolve(response.data.map(entry => { - return this.modelGetAllFactory(entry) - })) + return response.data.map(entry => this.modelGetAllFactory(entry)) } if (response.data === null) { - return Promise.resolve([]) + return [] } - return Promise.resolve(this.modelGetAllFactory(response.data)) + return this.modelGetAllFactory(response.data) }) .finally(() => { cancel() @@ -371,7 +369,7 @@ export default class AbstractService { if (typeof model.maxRight !== 'undefined') { result.maxRight = model.maxRight } - return Promise.resolve(result) + return result }) .finally(() => { cancel() @@ -394,7 +392,7 @@ export default class AbstractService { if (typeof model.maxRight !== 'undefined') { result.maxRight = model.maxRight } - return Promise.resolve(result) + return result }) .finally(() => { cancel() @@ -420,7 +418,7 @@ export default class AbstractService { * @param model * @returns {Q.Promise} */ - delete(model) { + async delete(model) { if (this.paths.delete === '') { throw new Error('This model is not able to delete data.') } @@ -428,13 +426,12 @@ export default class AbstractService { const cancel = this.setLoading() const finalUrl = this.getReplacedRoute(this.paths.delete, model) - return this.http.delete(finalUrl, model) - .then(response => { - return Promise.resolve(response.data) - }) - .finally(() => { - cancel() - }) + try { + const {data} = await this.http.delete(finalUrl, model) + return data + } finally { + cancel() + } } /** @@ -485,9 +482,7 @@ export default class AbstractService { }, }, ) - .then(response => { - return Promise.resolve(this.modelCreateFactory(response.data)) - }) + .then(response => this.modelCreateFactory(response.data)) .finally(() => { this.uploadProgress = 0 cancel() diff --git a/src/services/attachment.js b/src/services/attachment.js index afd28cdc..098d5ef6 100644 --- a/src/services/attachment.js +++ b/src/services/attachment.js @@ -37,9 +37,9 @@ export default class AttachmentService extends AbstractService { return AbstractService.prototype.getBlobUrl.call(this, '/tasks/' + model.taskId + '/attachments/' + model.id) } - download(model) { - this.getBlobUrl(model) - .then(url => downloadBlob(url, model.file.name)) + async download(model) { + const url = await this.getBlobUrl(model) + return downloadBlob(url, model.file.name) } /** diff --git a/src/services/list.js b/src/services/list.js index b720df9f..925773dd 100644 --- a/src/services/list.js +++ b/src/services/list.js @@ -46,7 +46,7 @@ export default class ListService extends AbstractService { background(list) { if (list.background === null) { - return Promise.resolve('') + return '' } return this.http({ diff --git a/src/services/passwordReset.js b/src/services/passwordReset.js index 95b2c94d..023f7c75 100644 --- a/src/services/passwordReset.js +++ b/src/services/passwordReset.js @@ -19,7 +19,7 @@ export default class PasswordResetService extends AbstractService { const cancel = this.setLoading() return this.http.post(this.paths.reset, model) .then(response => { - return Promise.resolve(this.modelFactory(response.data)) + return this.modelFactory(response.data) }) .finally(() => { cancel() @@ -30,7 +30,7 @@ export default class PasswordResetService extends AbstractService { const cancel = this.setLoading() return this.http.post(this.paths.requestReset, model) .then(response => { - return Promise.resolve(this.modelFactory(response.data)) + return this.modelFactory(response.data) }) .finally(() => { cancel() diff --git a/src/services/totp.js b/src/services/totp.js index 021d68e0..93367ab3 100644 --- a/src/services/totp.js +++ b/src/services/totp.js @@ -26,13 +26,12 @@ export default class TotpService extends AbstractService { return this.post(`${this.urlPrefix}/disable`, model) } - qrcode() { - return this.http({ + async qrcode() { + const response = await this.http({ url: `${this.urlPrefix}/qrcode`, method: 'GET', responseType: 'blob', - }).then(response => { - return Promise.resolve(new Blob([response.data])) }) + return new Blob([response.data]) } } \ No newline at end of file diff --git a/src/store/modules/auth.js b/src/store/modules/auth.js index 12795faf..b7b9b652 100644 --- a/src/store/modules/auth.js +++ b/src/store/modules/auth.js @@ -101,17 +101,17 @@ export default { // Tell others the user is autheticated ctx.dispatch('checkAuth') - return Promise.resolve() }) .catch(e => { - if (e.response) { - if (e.response.data.code === 1017 && !credentials.totpPasscode) { - ctx.commit('needsTotpPasscode', true) - return Promise.reject(e) - } + if ( + e.response && + e.response.data.code === 1017 && + !credentials.totpPasscode + ) { + ctx.commit('needsTotpPasscode', true) } - return Promise.reject(e) + throw e }) .finally(() => { ctx.commit(LOADING, false, {root: true}) @@ -134,7 +134,7 @@ export default { ctx.commit(ERROR_MESSAGE, e.response.data.message, {root: true}) } - return Promise.reject(e) + throw e }) .finally(() => { ctx.commit(LOADING, false, {root: true}) @@ -157,7 +157,6 @@ export default { // Tell others the user is autheticated ctx.dispatch('checkAuth') - return Promise.resolve() }) .finally(() => { ctx.commit(LOADING, false, {root: true}) @@ -180,7 +179,7 @@ export default { // This function can be called from multiple places at the same time and shortly after one another. // To prevent hitting the api too frequently or race conditions, we check at most once per minute. if (ctx.state.lastUserInfoRefresh !== null && ctx.state.lastUserInfoRefresh > (new Date()).setMinutes((new Date()).getMinutes() + 1)) { - return Promise.resolve() + return } const jwt = getToken() @@ -190,14 +189,13 @@ export default { .split('.')[1] .replace('-', '+') .replace('_', '/') - const info = new UserModel(JSON.parse(window.atob(base64))) + const info = new UserModel(JSON.parse(atob(base64))) const ts = Math.round((new Date()).getTime() / 1000) authenticated = info.exp >= ts ctx.commit('info', info) if (authenticated) { ctx.dispatch('refreshUserInfo') - ctx.commit('authenticated', authenticated) } } @@ -206,8 +204,6 @@ export default { ctx.commit('info', null) ctx.dispatch('config/redirectToProviderIfNothingElseIsEnabled', null, {root: true}) } - - return Promise.resolve() }, refreshUserInfo(ctx) { const jwt = getToken() diff --git a/src/store/modules/config.js b/src/store/modules/config.js index 7fe8f57b..2e929711 100644 --- a/src/store/modules/config.js +++ b/src/store/modules/config.js @@ -66,7 +66,7 @@ export default { return HTTP.get('info') .then(r => { ctx.commit(CONFIG, r.data) - return Promise.resolve(r) + return r }) }, redirectToProviderIfNothingElseIsEnabled(ctx) { diff --git a/src/store/modules/kanban.js b/src/store/modules/kanban.js index 87349c0c..e01a83fa 100644 --- a/src/store/modules/kanban.js +++ b/src/store/modules/kanban.js @@ -227,19 +227,19 @@ export default { .finally(() => cancel()) }, - loadNextTasksForBucket(ctx, {listId, ps = {}, bucketId}) { + async loadNextTasksForBucket(ctx, {listId, ps = {}, bucketId}) { const bucketIndex = findIndexById(ctx.state.buckets, bucketId) const isLoading = ctx.state.bucketLoading[bucketIndex] ?? false if (isLoading) { - return Promise.resolve() + return } const page = (ctx.state.taskPagesPerBucket[bucketIndex] ?? 1) + 1 const alreadyLoaded = ctx.state.allTasksLoadedForBucket[bucketIndex] ?? false if (alreadyLoaded) { - return Promise.resolve() + return } const cancel = setLoading(ctx, 'kanban') @@ -334,7 +334,7 @@ export default { // restore original state ctx.commit('setBucketByIndex', {bucketIndex, bucket: oldBucket}) - return Promise.reject(e) + throw e }) .finally(() => cancel()) }, diff --git a/src/store/modules/labels.js b/src/store/modules/labels.js index 3d113560..104734f3 100644 --- a/src/store/modules/labels.js +++ b/src/store/modules/labels.js @@ -1,5 +1,7 @@ import LabelService from '@/services/label' import {setLoading} from '@/store/helper' +import { success } from '@/message' +import {i18n} from '@/i18n' /** * Returns the labels by id if found @@ -28,6 +30,18 @@ function getLabelsByIds(state, ids) { }) } +const labelService = new LabelService() + +const getAllLabels = async (page = 1) => { + const labels = await labelService.getAll({}, {}, page) + if (page < labelService.totalPages) { + const nextLabels = await getAllLabels(page + 1) + return labels.concat(nextLabels) + } else { + return labels + } +} + export default { namespaced: true, state: () => ({ @@ -60,71 +74,59 @@ export default { }, }, actions: { - loadAllLabels(ctx, {forceLoad} = {}) { + async loadAllLabels(ctx, {forceLoad} = {}) { if (ctx.state.loaded && !forceLoad) { - return Promise.resolve() + return } const cancel = setLoading(ctx, 'labels') - const labelService = new LabelService() - const getAllLabels = (page = 1) => { - return labelService.getAll({}, {}, page) - .then(labels => { - if (page < labelService.totalPages) { - return getAllLabels(page + 1) - .then(nextLabels => { - return labels.concat(nextLabels) - }) - } else { - return labels - } - }) - .catch(e => { - return Promise.reject(e) - }) + try { + const labels = await getAllLabels() + ctx.commit('setLabels', labels) + ctx.commit('setLoaded', true) + return labels + } finally { + cancel() } - - return getAllLabels() - .then(r => { - ctx.commit('setLabels', r) - ctx.commit('setLoaded', true) - return Promise.resolve(r) - }) - .finally(() => cancel()) }, - deleteLabel(ctx, label) { + async deleteLabel(ctx, label) { const cancel = setLoading(ctx, 'labels') const labelService = new LabelService() - return labelService.delete(label) - .then(r => { - ctx.commit('removeLabelById', label) - return Promise.resolve(r) - }) - .finally(() => cancel()) + try { + const result = await labelService.delete(label) + ctx.commit('removeLabelById', label) + success({message: i18n.global.t('label.deleteSuccess')}) + return result + } finally { + cancel() + } }, - updateLabel(ctx, label) { + async updateLabel(ctx, label) { const cancel = setLoading(ctx, 'labels') const labelService = new LabelService() - return labelService.update(label) - .then(r => { - ctx.commit('setLabel', r) - return Promise.resolve(r) - }) - .finally(() => cancel()) + try { + const newLabel = await labelService.update(label) + ctx.commit('setLabel', newLabel) + success({message: i18n.global.t('label.edit.success')}) + return newLabel + } finally { + cancel() + } }, - createLabel(ctx, label) { + async createLabel(ctx, label) { const cancel = setLoading(ctx, 'labels') const labelService = new LabelService() - return labelService.create(label) - .then(r => { - ctx.commit('setLabel', r) - return Promise.resolve(r) - }) - .finally(() => cancel()) + try { + const newLabel = await labelService.create(label) + ctx.commit('setLabel', newLabel) + return newLabel + } finally { + cancel() + } }, }, } diff --git a/src/store/modules/lists.js b/src/store/modules/lists.js index aae097a6..e7058835 100644 --- a/src/store/modules/lists.js +++ b/src/store/modules/lists.js @@ -51,7 +51,7 @@ export default { r.namespaceId = list.namespaceId ctx.commit('namespaces/addListToNamespace', r, {root: true}) ctx.commit('setList', r) - return Promise.resolve(r) + return r }) .finally(() => cancel()) }, @@ -77,7 +77,7 @@ export default { } ctx.dispatch('namespaces/loadNamespacesIfFavoritesDontExist', null, {root: true}) ctx.dispatch('namespaces/removeFavoritesNamespaceIfEmpty', null, {root: true}) - return Promise.resolve(newList) + return newList }) .catch(e => { // Reset the list state to the initial one to avoid confusion for the user @@ -85,7 +85,7 @@ export default { ...list, isFavorite: !list.isFavorite, }) - return Promise.reject(e) + throw e }) .finally(() => cancel()) }, diff --git a/src/store/modules/namespaces.js b/src/store/modules/namespaces.js index 2bbe8dcd..b27225d6 100644 --- a/src/store/modules/namespaces.js +++ b/src/store/modules/namespaces.js @@ -113,7 +113,7 @@ export default { ctx.commit('lists/setLists', lists, {root: true}) - return Promise.resolve(r) + return r }) .finally(() => { cancel() @@ -128,7 +128,6 @@ export default { removeFavoritesNamespaceIfEmpty(ctx) { if (ctx.state.namespaces[0].id === -2 && ctx.state.namespaces[0].lists.length === 0) { ctx.state.namespaces.splice(0, 1) - return Promise.resolve() } }, deleteNamespace(ctx, namespace) { @@ -138,7 +137,7 @@ export default { return namespaceService.delete(namespace) .then(r => { ctx.commit('removeNamespaceById', namespace.id) - return Promise.resolve(r) + return r }) .finally(() => cancel()) }, @@ -149,7 +148,7 @@ export default { return namespaceService.create(namespace) .then(r => { ctx.commit('addNamespace', r) - return Promise.resolve(r) + return r }) .finally(() => cancel()) }, diff --git a/src/store/modules/tasks.js b/src/store/modules/tasks.js index ddc628af..29b1271f 100644 --- a/src/store/modules/tasks.js +++ b/src/store/modules/tasks.js @@ -43,23 +43,23 @@ function addLabelToTask(task, label) { return labelTaskService.create(labelTask) .then(result => { task.labels.push(label) - return Promise.resolve(result) + return result }) } -function findAssignees(parsedTaskAssignees) { +async function findAssignees(parsedTaskAssignees) { if (parsedTaskAssignees.length <= 0) { - return Promise.resolve([]) + return [] } const userService = new UserService() - const assignees = parsedTaskAssignees.map(a => - userService.getAll({}, {s: a}) - .then(users => validateUsername(users, a)), - - ) + const assignees = parsedTaskAssignees.map(async a => { + const users = await userService.getAll({}, {s: a}) + return validateUsername(users, a) + }) - return Promise.all(assignees).filter((item) => Boolean(item)) + const validatedUsers = await Promise.all(assignees) + return validatedUsers.filter((item) => Boolean(item)) } @@ -136,7 +136,7 @@ export default { // Usually this means the kanban board hasn't been accessed until now. // Vuex seems to have its difficulties with that, so we just log the error and fail silently. console.debug('Could not add assignee to task in kanban, task not found', t) - return Promise.resolve(r) + return r } // FIXME: direct store manipulation (task) t.task.assignees.push(user) @@ -157,7 +157,7 @@ export default { // Usually this means the kanban board hasn't been accessed until now. // Vuex seems to have its difficulties with that, so we just log the error and fail silently. console.debug('Could not remove assignee from task in kanban, task not found', t) - return Promise.resolve(r) + return r } for (const a in t.task.assignees) { @@ -186,7 +186,7 @@ export default { // Usually this means the kanban board hasn't been accessed until now. // Vuex seems to have its difficulties with that, so we just log the error and fail silently. console.debug('Could not add label to task in kanban, task not found', t) - return Promise.resolve(r) + return r } // FIXME: direct store manipulation (task) t.task.labels.push(label) @@ -208,7 +208,7 @@ export default { // Usually this means the kanban board hasn't been accessed until now. // Vuex seems to have its difficulties with that, so we just log the error and fail silently. console.debug('Could not remove label from task in kanban, task not found', t) - return Promise.resolve(r) + return r } // Remove the label from the list @@ -234,22 +234,21 @@ export default { const {labels} = rootState.labels - const labelAddsToWaitFor = parsedLabels.map(labelTitle => new Promise((resolve) => { + const labelAddsToWaitFor = parsedLabels.map(async labelTitle => { let label = validateLabel(labels, labelTitle) if (typeof label !== 'undefined') { - return resolve(label) + return label } // label not found, create it const labelModel = new LabelModel({title: labelTitle}) - return dispatch('labels/createLabel', labelModel).then(() => resolve(label)) + await dispatch('labels/createLabel', labelModel) + addLabelToTask(task, label) }) - .then((label) => addLabelToTask(task, label)) - .catch(e => Promise.reject(e)), - ) // This waits until all labels are created and added to the task - return Promise.all(labelAddsToWaitFor).then(() => task) + await Promise.all(labelAddsToWaitFor) + return task }, findListId({ rootGetters }, { list, listId }) { diff --git a/src/views/labels/ListLabels.vue b/src/views/labels/ListLabels.vue index f49baafe..e7ce816a 100644 --- a/src/views/labels/ListLabels.vue +++ b/src/views/labels/ListLabels.vue @@ -119,7 +119,7 @@ export default { } }, created() { - this.loadLabels() + this.$store.dispatch('labels/loadAllLabels') }, mounted() { this.setTitle(this.$t('label.title')) @@ -131,20 +131,11 @@ export default { loading: state => state[LOADING] && state[LOADING_MODULE] === 'labels', }), methods: { - loadLabels() { - this.$store.dispatch('labels/loadAllLabels') - }, deleteLabel(label) { - this.$store.dispatch('labels/deleteLabel', label) - .then(() => { - this.$message.success({message: this.$t('label.deleteSuccess')}) - }) + return this.$store.dispatch('labels/deleteLabel', label) }, editLabelSubmit() { - this.$store.dispatch('labels/updateLabel', this.labelEditLabel) - .then(() => { - this.$message.success({message: this.$t('label.edit.success')}) - }) + return this.$store.dispatch('labels/updateLabel', this.labelEditLabel) }, editLabel(label) { if (label.createdBy.id !== this.userInfo.id) { From bb94c1ba3afbed2b9acf7d8595e3c7aaf620f608 Mon Sep 17 00:00:00 2001 From: Dominik Pschenitschni Date: Mon, 11 Oct 2021 19:37:20 +0200 Subject: [PATCH 03/15] feat: use async / await where it makes sense --- src/App.vue | 35 +-- src/components/home/navigation.vue | 19 +- src/components/input/editor.vue | 10 +- src/components/list/partials/filters.vue | 22 +- src/components/list/partials/list-card.vue | 12 +- src/components/migrator/migration.vue | 45 ++-- src/components/misc/subscription.vue | 20 +- .../notifications/notifications.vue | 14 +- .../quick-actions/quick-actions.vue | 90 +++---- src/components/sharing/linkSharing.vue | 45 ++-- src/components/sharing/userTeam.vue | 96 ++++---- src/components/tasks/add-task.vue | 44 ++-- src/components/tasks/edit-task.vue | 12 +- src/components/tasks/gantt-component.vue | 117 +++++----- src/components/tasks/mixins/taskList.js | 13 +- src/components/tasks/partials/attachments.vue | 30 ++- src/components/tasks/partials/comments.vue | 145 ++++++------ src/components/tasks/partials/defer-task.vue | 14 +- src/components/tasks/partials/description.vue | 24 +- .../tasks/partials/editAssignees.vue | 46 ++-- src/components/tasks/partials/editLabels.vue | 39 ++-- src/components/tasks/partials/heading.vue | 24 +- src/components/tasks/partials/kanban-card.vue | 27 ++- src/components/tasks/partials/listSearch.vue | 10 +- .../tasks/partials/relatedTasks.vue | 77 +++--- .../tasks/partials/singleTaskInList.vue | 63 +++-- src/components/user/avatar-settings.vue | 40 ++-- src/components/user/settings/data-export.vue | 15 +- src/components/user/settings/deletion.vue | 23 +- src/helpers/attachments.ts | 30 ++- src/helpers/auth.ts | 24 +- src/models/task.js | 53 ++--- src/services/abstractService.js | 139 ++++++----- src/services/backgroundUnsplash.js | 8 +- src/services/dataExport.js | 11 +- src/services/list.js | 21 +- src/services/passwordReset.js | 30 ++- src/store/modules/auth.js | 175 +++++++------- src/store/modules/config.js | 11 +- src/store/modules/kanban.js | 110 ++++----- src/store/modules/lists.js | 99 ++++---- src/store/modules/namespaces.js | 70 +++--- src/store/modules/tasks.js | 221 +++++++++--------- src/views/Home.vue | 2 +- src/views/filters/CreateSavedFilter.vue | 10 +- src/views/filters/settings/delete.vue | 12 +- src/views/filters/settings/edit.vue | 25 +- src/views/labels/NewLabel.vue | 16 +- src/views/list/NewList.vue | 17 +- src/views/list/ShowList.vue | 18 +- src/views/list/settings/archive.vue | 19 +- src/views/list/settings/background.vue | 70 +++--- src/views/list/settings/delete.vue | 10 +- src/views/list/settings/duplicate.vue | 15 +- src/views/list/settings/edit.vue | 23 +- src/views/list/settings/share.vue | 17 +- src/views/list/views/Kanban.vue | 87 ++++--- src/views/list/views/List.vue | 9 +- src/views/namespaces/NewNamespace.vue | 13 +- src/views/namespaces/settings/archive.vue | 20 +- src/views/namespaces/settings/delete.vue | 10 +- src/views/namespaces/settings/edit.vue | 34 ++- src/views/namespaces/settings/share.vue | 17 +- src/views/sharing/LinkSharingAuth.vue | 45 ++-- src/views/tasks/ShowTasks.vue | 34 +-- src/views/tasks/TaskDetailView.vue | 62 +++-- src/views/teams/EditTeam.vue | 108 ++++----- src/views/teams/ListTeams.vue | 7 +- src/views/teams/NewTeam.vue | 17 +- src/views/user/Login.vue | 33 +-- src/views/user/OpenIdAuth.vue | 52 ++--- src/views/user/PasswordReset.vue | 19 +- src/views/user/RequestPasswordReset.vue | 15 +- src/views/user/Settings.vue | 111 ++++----- 74 files changed, 1458 insertions(+), 1662 deletions(-) diff --git a/src/App.vue b/src/App.vue index 79c9f433..d2690c26 100644 --- a/src/App.vue +++ b/src/App.vue @@ -54,6 +54,7 @@ export default defineComponent({ this.setupAccountDeletionVerification() }, beforeCreate() { + // FIXME: async action in beforeCreate, might be not finished when component mounts this.$store.dispatch('config/update') .then(() => { this.$store.dispatch('auth/checkAuth') @@ -88,28 +89,30 @@ export default defineComponent({ window.addEventListener('offline', () => this.$store.commit(ONLINE, navigator.onLine)) }, setupPasswortResetRedirect() { - if (typeof this.$route.query.userPasswordReset !== 'undefined') { - localStorage.removeItem('passwordResetToken') // Delete an eventually preexisting old token - localStorage.setItem('passwordResetToken', this.$route.query.userPasswordReset) - this.$router.push({name: 'user.password-reset.reset'}) + if (typeof this.$route.query.userPasswordReset === 'undefined') { + return } + + localStorage.setItem('passwordResetToken', this.$route.query.userPasswordReset) + this.$router.push({name: 'user.password-reset.reset'}) }, setupEmailVerificationRedirect() { - if (typeof this.$route.query.userEmailConfirm !== 'undefined') { - localStorage.removeItem('emailConfirmToken') // Delete an eventually preexisting old token - localStorage.setItem('emailConfirmToken', this.$route.query.userEmailConfirm) - this.$router.push({name: 'user.login'}) + if (typeof this.$route.query.userEmailConfirm === 'undefined') { + return } + + localStorage.setItem('emailConfirmToken', this.$route.query.userEmailConfirm) + this.$router.push({name: 'user.login'}) }, - setupAccountDeletionVerification() { - if (typeof this.$route.query.accountDeletionConfirm !== 'undefined') { - const accountDeletionService = new AccountDeleteService() - accountDeletionService.confirm(this.$route.query.accountDeletionConfirm) - .then(() => { - this.$message.success({message: this.$t('user.deletion.confirmSuccess')}) - this.$store.dispatch('auth/refreshUserInfo') - }) + async setupAccountDeletionVerification() { + if (typeof this.$route.query.accountDeletionConfirm === 'undefined') { + return } + + const accountDeletionService = new AccountDeleteService() + await accountDeletionService.confirm(this.$route.query.accountDeletionConfirm) + this.$message.success({message: this.$t('user.deletion.confirmSuccess')}) + this.$store.dispatch('auth/refreshUserInfo') }, }, }) diff --git a/src/components/home/navigation.vue b/src/components/home/navigation.vue index 4e874f2d..2cc7a114 100644 --- a/src/components/home/navigation.vue +++ b/src/components/home/navigation.vue @@ -195,6 +195,7 @@ export default { }, }, beforeCreate() { + // FIXME: async action in beforeCreate, might be unfinished when component mounts this.$store.dispatch('namespaces/loadNamespaces') .then(namespaces => { namespaces.forEach(n => { @@ -248,7 +249,8 @@ export default { this.$store.commit('namespaces/setNamespaceById', newNamespace) }, - saveListPosition(e, namespaceIndex) { + + async saveListPosition(e, namespaceIndex) { const listsActive = this.activeLists[namespaceIndex] const list = listsActive[e.newIndex] const listBefore = listsActive[e.newIndex - 1] ?? null @@ -257,14 +259,15 @@ export default { const position = calculateItemPosition(listBefore !== null ? listBefore.position : null, listAfter !== null ? listAfter.position : null) - // create a copy of the list in order to not violate vuex mutations - this.$store.dispatch('lists/updateList', { - ...list, - position, - }) - .finally(() => { - this.listUpdating[list.id] = false + try { + // create a copy of the list in order to not violate vuex mutations + await this.$store.dispatch('lists/updateList', { + ...list, + position, }) + } finally { + this.listUpdating[list.id] = false + } }, }, } diff --git a/src/components/input/editor.vue b/src/components/input/editor.vue index d51ed19f..a2042db0 100644 --- a/src/components/input/editor.vue +++ b/src/components/input/editor.vue @@ -233,7 +233,7 @@ export default { // dom tree. If we're calling this right after setting this.preview it could be the images were // not already made available. // Some docs at https://stackoverflow.com/q/62865160/10924593 - this.$nextTick(() => { + this.$nextTick(async () => { const attachmentImage = document.getElementsByClassName('attachment-image') if (attachmentImage) { for (const img of attachmentImage) { @@ -254,11 +254,9 @@ export default { this.attachmentService = new AttachmentService() } - this.attachmentService.getBlobUrl(attachment) - .then(url => { - img.src = url - this.loadedAttachments[cacheKey] = url - }) + const url = await this.attachmentService.getBlobUrl(attachment) + img.src = url + this.loadedAttachments[cacheKey] = url } } diff --git a/src/components/list/partials/filters.vue b/src/components/list/partials/filters.vue index a9175e98..7360e616 100644 --- a/src/components/list/partials/filters.vue +++ b/src/components/list/partials/filters.vue @@ -468,7 +468,7 @@ export default { this.filters.done = true } }, - prepareRelatedObjectFilter(kind, filterName = null, servicePrefix = null) { + async prepareRelatedObjectFilter(kind, filterName = null, servicePrefix = null) { if (filterName === null) { filterName = kind } @@ -478,12 +478,11 @@ export default { } this.prepareSingleValue(filterName) - if (typeof this.filters[filterName] !== 'undefined' && this.filters[filterName] !== '') { - this[`${servicePrefix}Service`].getAll({}, {s: this.filters[filterName]}) - .then(r => { - this[kind] = r - }) + if (typeof this.filters[filterName] === 'undefined' || this.filters[filterName] === '') { + return } + + this[kind] = await this[`${servicePrefix}Service`].getAll({}, {s: this.filters[filterName]}) }, setDoneFilter() { if (this.filters.done) { @@ -523,17 +522,16 @@ export default { clear(kind) { this[`found${kind}`] = [] }, - find(kind, query) { + async find(kind, query) { if (query === '') { this.clear(kind) } - this[`${kind}Service`].getAll({}, {s: query}) - .then(response => { - // Filter users from the results who are already assigned - this[`found${kind}`] = response.filter(({id}) => !includesById(this[kind], id)) - }) + const response = await this[`${kind}Service`].getAll({}, {s: query}) + + // Filter users from the results who are already assigned + this[`found${kind}`] = response.filter(({id}) => !includesById(this[kind], id)) }, add(kind, filterName) { this.$nextTick(() => { diff --git a/src/components/list/partials/list-card.vue b/src/components/list/partials/list-card.vue index c351fb2a..f54f1117 100644 --- a/src/components/list/partials/list-card.vue +++ b/src/components/list/partials/list-card.vue @@ -56,7 +56,7 @@ export default { }, }, methods: { - loadBackground() { + async loadBackground() { if (this.list === null || !this.list.backgroundInformation || this.backgroundLoading) { return } @@ -64,11 +64,11 @@ export default { this.backgroundLoading = true const listService = new ListService() - listService.background(this.list) - .then(b => { - this.background = b - }) - .finally(() => this.backgroundLoading = false) + try { + this.background = await listService.background(this.list) + } finally { + this.backgroundLoading = false + } }, toggleFavoriteList(list) { // The favorites pseudo list is always favorite diff --git a/src/components/migrator/migration.vue b/src/components/migrator/migration.vue index 57a3cab5..cd52e4a0 100644 --- a/src/components/migrator/migration.vue +++ b/src/components/migrator/migration.vue @@ -141,43 +141,32 @@ export default { } }, methods: { - getAuthUrl() { - this.migrationService.getAuthUrl() - .then(r => { - this.authUrl = r.url - }) + async getAuthUrl() { + const { url } = this.migrationService.getAuthUrl() + this.authUrl = url }, - migrate() { + + async migrate() { this.isMigrating = true this.lastMigrationDate = null this.message = '' + let migrationConfig = { code: this.migratorAuthCode } + if (this.isFileMigrator) { - return this.migrateFile() + if (this.$refs.uploadInput.files.length === 0) { + return + } + migrationConfig = this.$refs.uploadInput.files[0] } - this.migrationService.migrate({code: this.migratorAuthCode}) - .then(r => { - this.message = r.message - this.$store.dispatch('namespaces/loadNamespaces') - }) - .finally(() => { - this.isMigrating = false - }) - }, - migrateFile() { - if (this.$refs.uploadInput.files.length === 0) { - return + try { + const { message } = await this.migrationService.migrate(migrationConfig) + this.message = message + return this.$store.dispatch('namespaces/loadNamespaces') + } finally { + this.isMigrating = false } - - this.migrationService.migrate(this.$refs.uploadInput.files[0]) - .then(r => { - this.message = r.message - this.$store.dispatch('namespaces/loadNamespaces') - }) - .finally(() => { - this.isMigrating = false - }) }, }, } diff --git a/src/components/misc/subscription.vue b/src/components/misc/subscription.vue index adbc8826..8865c43f 100644 --- a/src/components/misc/subscription.vue +++ b/src/components/misc/subscription.vue @@ -89,27 +89,23 @@ export default { this.unsubscribe() } }, - subscribe() { + async subscribe() { const subscription = new SubscriptionModel({ entity: this.entity, entityId: this.entityId, }) - this.subscriptionService.create(subscription) - .then(() => { - this.$emit('change', subscription) - this.$message.success({message: this.$t('task.subscription.subscribeSuccess', {entity: this.entity})}) - }) + await this.subscriptionService.create(subscription) + this.$emit('change', subscription) + this.$message.success({message: this.$t('task.subscription.subscribeSuccess', {entity: this.entity})}) }, - unsubscribe() { + async unsubscribe() { const subscription = new SubscriptionModel({ entity: this.entity, entityId: this.entityId, }) - this.subscriptionService.delete(subscription) - .then(() => { - this.$emit('change', null) - this.$message.success({message: this.$t('task.subscription.unsubscribeSuccess', {entity: this.entity})}) - }) + await this.subscriptionService.delete(subscription) + this.$emit('change', null) + this.$message.success({message: this.$t('task.subscription.unsubscribeSuccess', {entity: this.entity})}) }, }, } diff --git a/src/components/notifications/notifications.vue b/src/components/notifications/notifications.vue index a99e0779..bd185511 100644 --- a/src/components/notifications/notifications.vue +++ b/src/components/notifications/notifications.vue @@ -93,11 +93,8 @@ export default { closeWhenClickedOutside(e, this.$refs.popup, () => this.showNotifications = false) } }, - loadNotifications() { - this.notificationService.getAll() - .then(r => { - this.allNotifications = r - }) + async loadNotifications() { + this.allNotifications = await this.notificationService.getAll() }, to(n, index) { const to = { @@ -124,16 +121,13 @@ export default { break } - return () => { + return async () => { if (to.name !== '') { this.$router.push(to) } n.read = true - this.notificationService.update(n) - .then(r => { - this.allNotifications[index] = r - }) + this.allNotifications[index] = await this.notificationService.update(n) } }, }, diff --git a/src/components/quick-actions/quick-actions.vue b/src/components/quick-actions/quick-actions.vue index 22572a6c..51fc8daa 100644 --- a/src/components/quick-actions/quick-actions.vue +++ b/src/components/quick-actions/quick-actions.vue @@ -282,20 +282,17 @@ export default { this.taskSearchTimeout = null } - this.taskSearchTimeout = setTimeout(() => { - this.taskService.getAll({}, {s: query}) - .then(r => { - r = r.map(t => { - t.type = TYPE_TASK - const list = this.$store.getters['lists/getListById'](t.listId) === null ? null : this.$store.getters['lists/getListById'](t.listId) - if (list !== null) { - t.title = `${t.title} (${list.title})` - } + this.taskSearchTimeout = setTimeout(async () => { + const r = await this.taskService.getAll({}, {s: query}) + this.foundTasks = r.map(t => { + t.type = TYPE_TASK + const list = this.$store.getters['lists/getListById'](t.listId) + if (list !== null) { + t.title = `${t.title} (${list.title})` + } - return t - }) - this.foundTasks = r - }) + return t + }) }, 150) }, searchTeams() { @@ -318,15 +315,12 @@ export default { this.teamSearchTimeout = null } - this.teamSearchTimeout = setTimeout(() => { - this.teamService.getAll({}, {s: query}) - .then(r => { - r = r.map(t => { - t.title = t.name - return t - }) - this.foundTeams = r - }) + this.teamSearchTimeout = setTimeout(async () => { + const r = await this.teamService.getAll({}, {s: query}) + this.foundTeams = r.map(t => { + t.title = t.name + return t + }) }, 150) }, closeQuickActions() { @@ -378,22 +372,20 @@ export default { break } }, - newTask() { + async newTask() { if (this.currentList === null) { return } - this.$store.dispatch('tasks/createNewTask', { + const task = await this.$store.dispatch('tasks/createNewTask', { title: this.query, listId: this.currentList.id, }) - .then(r => { - this.$message.success({message: this.$t('task.createSuccess')}) - this.$router.push({name: 'task.detail', params: {id: r.id}}) - this.closeQuickActions() - }) + this.$message.success({message: this.$t('task.createSuccess')}) + this.$router.push({name: 'task.detail', params: {id: task.id}}) + this.closeQuickActions() }, - newList() { + async newList() { if (this.currentList === null) { return } @@ -402,33 +394,27 @@ export default { title: this.query, namespaceId: this.currentList.namespaceId, }) - this.$store.dispatch('lists/createList', newList) - .then(r => { - this.$message.success({message: this.$t('list.create.createdSuccess')}) - this.$router.push({name: 'list.index', params: {listId: r.id}}) - this.closeQuickActions() - }) + const list = await this.$store.dispatch('lists/createList', newList) + this.$message.success({message: this.$t('list.create.createdSuccess')}) + this.$router.push({name: 'list.index', params: {listId: list.id}}) + this.closeQuickActions() }, - newNamespace() { + async newNamespace() { const newNamespace = new NamespaceModel({title: this.query}) - this.$store.dispatch('namespaces/createNamespace', newNamespace) - .then(() => { - this.$message.success({message: this.$t('namespace.create.success')}) - this.closeQuickActions() - }) + await this.$store.dispatch('namespaces/createNamespace', newNamespace) + this.$message.success({message: this.$t('namespace.create.success')}) + this.closeQuickActions() }, - newTeam() { + async newTeam() { const newTeam = new TeamModel({name: this.query}) - this.teamService.create(newTeam) - .then(r => { - this.$router.push({ - name: 'teams.edit', - params: {id: r.id}, - }) - this.$message.success({message: this.$t('team.create.success')}) - this.closeQuickActions() - }) + const team = await this.teamService.create(newTeam) + this.$router.push({ + name: 'teams.edit', + params: {id: team.id}, + }) + this.$message.success({message: this.$t('team.create.success')}) + this.closeQuickActions() }, select(parentIndex, index) { diff --git a/src/components/sharing/linkSharing.vue b/src/components/sharing/linkSharing.vue index 78113156..6b0dee34 100644 --- a/src/components/sharing/linkSharing.vue +++ b/src/components/sharing/linkSharing.vue @@ -215,50 +215,41 @@ export default { frontendUrl: (state) => state.config.frontendUrl, }), methods: { - load(listId) { + async load(listId) { // If listId == 0 the list on the calling component wasn't already loaded, so we just bail out here if (listId === 0) { return } - this.linkShareService - .getAll({listId}) - .then((r) => { - this.linkShares = r - }) + this.linkShares = await this.linkShareService.getAll({listId}) }, - add(listId) { + async add(listId) { const newLinkShare = new LinkShareModel({ right: this.selectedRight, listId, name: this.name, password: this.password, }) - this.linkShareService - .create(newLinkShare) - .then(() => { - this.selectedRight = rights.READ - this.name = '' - this.password = '' - this.showNewForm = false - this.$message.success({message: this.$t('list.share.links.createSuccess')}) - this.load(listId) - }) + await this.linkShareService.create(newLinkShare) + this.selectedRight = rights.READ + this.name = '' + this.password = '' + this.showNewForm = false + this.$message.success({message: this.$t('list.share.links.createSuccess')}) + await this.load(listId) }, - remove(listId) { + async remove(listId) { const linkshare = new LinkShareModel({ id: this.linkIdToDelete, listId, }) - this.linkShareService - .delete(linkshare) - .then(() => { - this.$message.success({message: this.$t('list.share.links.deleteSuccess')}) - this.load(listId) - }) - .finally(() => { - this.showDeleteModal = false - }) + try { + await this.linkShareService.delete(linkshare) + this.$message.success({message: this.$t('list.share.links.deleteSuccess')}) + await this.load(listId) + } finally { + this.showDeleteModal = false + } }, copy, getShareLink(hash) { diff --git a/src/components/sharing/userTeam.vue b/src/components/sharing/userTeam.vue index 91d7ca8f..f6b7cb13 100644 --- a/src/components/sharing/userTeam.vue +++ b/src/components/sharing/userTeam.vue @@ -272,39 +272,34 @@ export default { this.load() }, methods: { - load() { - this.stuffService - .getAll(this.stuffModel) - .then((r) => { - this.sharables = r - r.forEach((s) => - this.selectedRight[s.id] = s.right, - ) - }) + async load() { + this.sharables = await this.stuffService.getAll(this.stuffModel) + this.sharables.forEach((s) => + this.selectedRight[s.id] = s.right, + ) }, - deleteSharable() { + + async deleteSharable() { if (this.shareType === 'user') { this.stuffModel.userId = this.sharable.username } else if (this.shareType === 'team') { this.stuffModel.teamId = this.sharable.id } - this.stuffService - .delete(this.stuffModel) - .then(() => { - this.showDeleteModal = false - for (const i in this.sharables) { - if ( - (this.sharables[i].username === this.stuffModel.userId && this.shareType === 'user') || - (this.sharables[i].id === this.stuffModel.teamId && this.shareType === 'team') - ) { - this.sharables.splice(i, 1) - } - } - this.$message.success({message: this.$t('list.share.userTeam.removeSuccess', {type: this.shareTypeName, sharable: this.sharableName})}) - }) + await this.stuffService.delete(this.stuffModel) + this.showDeleteModal = false + for (const i in this.sharables) { + if ( + (this.sharables[i].username === this.stuffModel.userId && this.shareType === 'user') || + (this.sharables[i].id === this.stuffModel.teamId && this.shareType === 'team') + ) { + this.sharables.splice(i, 1) + } + } + this.$message.success({message: this.$t('list.share.userTeam.removeSuccess', {type: this.shareTypeName, sharable: this.sharableName})}) }, - add(admin) { + + async add(admin) { if (admin === null) { admin = false } @@ -319,14 +314,12 @@ export default { this.stuffModel.teamId = this.sharable.id } - this.stuffService - .create(this.stuffModel) - .then(() => { - this.$message.success({message: this.$t('list.share.userTeam.addedSuccess', {type: this.shareTypeName})}) - this.load() - }) + await this.stuffService.create(this.stuffModel) + this.$message.success({message: this.$t('list.share.userTeam.addedSuccess', {type: this.shareTypeName})}) + await this.load() }, - toggleType(sharable) { + + async toggleType(sharable) { if ( this.selectedRight[sharable.id] !== rights.ADMIN && this.selectedRight[sharable.id] !== rights.READ && @@ -342,35 +335,30 @@ export default { this.stuffModel.teamId = sharable.id } - this.stuffService - .update(this.stuffModel) - .then((r) => { - for (const i in this.sharables) { - if ( - (this.sharables[i].username === - this.stuffModel.userId && - this.shareType === 'user') || - (this.sharables[i].id === this.stuffModel.teamId && - this.shareType === 'team') - ) { - this.sharables[i].right = r.right - } - } - this.$message.success({message: this.$t('list.share.userTeam.updatedSuccess', {type: this.shareTypeName})}) - }) + const r = await this.stuffService.update(this.stuffModel) + for (const i in this.sharables) { + if ( + (this.sharables[i].username === + this.stuffModel.userId && + this.shareType === 'user') || + (this.sharables[i].id === this.stuffModel.teamId && + this.shareType === 'team') + ) { + this.sharables[i].right = r.right + } + } + this.$message.success({message: this.$t('list.share.userTeam.updatedSuccess', {type: this.shareTypeName})}) }, - find(query) { + + async find(query) { if (query === '') { this.clearAll() return } - this.searchService - .getAll({}, {s: query}) - .then((response) => { - this.found = response - }) + this.found = await this.searchService.getAll({}, {s: query}) }, + clearAll() { this.found = [] }, diff --git a/src/components/tasks/add-task.vue b/src/components/tasks/add-task.vue index 99d1a950..3ae8d4f8 100644 --- a/src/components/tasks/add-task.vue +++ b/src/components/tasks/add-task.vue @@ -82,7 +82,7 @@ export default { this.initialTextAreaHeight = this.$refs.newTaskInput.scrollHeight + INPUT_BORDER_PX }, methods: { - addTask() { + async addTask() { if (this.newTaskTitle === '') { this.errorMessage = this.$t('list.create.addTitleRequired') return @@ -93,37 +93,31 @@ export default { return } - const newTasks = [] - this.newTaskTitle.split(/[\r\n]+/).forEach(t => { + const newTasks = this.newTaskTitle.split(/[\r\n]+/).map(async t => { const title = cleanupTitle(t) if (title === '') { return } - newTasks.push( - this.$store.dispatch('tasks/createNewTask', { - title: this.newTaskTitle, - listId: this.$store.state.auth.settings.defaultListId, - position: this.defaultPosition, - }) - .then(task => { - this.$emit('taskAdded', task) - return task - }), - ) + const task = await this.$store.dispatch('tasks/createNewTask', { + title: this.newTaskTitle, + listId: this.$store.state.auth.settings.defaultListId, + position: this.defaultPosition, + }) + this.$emit('taskAdded', task) + return task }) - return Promise.all(newTasks) - .then(() => { - this.newTaskTitle = '' - }) - .catch(e => { - if (e.message === 'NO_LIST') { - this.errorMessage = this.$t('list.create.addListRequired') - return - } - throw e - }) + try { + await Promise.all(newTasks) + this.newTaskTitle = '' + } catch(e) { + if (e.message === 'NO_LIST') { + this.errorMessage = this.$t('list.create.addListRequired') + return + } + throw e + } }, handleEnter(e) { // when pressing shift + enter we want to continue as we normally would. Otherwise, we want to create diff --git a/src/components/tasks/edit-task.vue b/src/components/tasks/edit-task.vue index 61926133..53459736 100644 --- a/src/components/tasks/edit-task.vue +++ b/src/components/tasks/edit-task.vue @@ -134,14 +134,10 @@ export default { this.editorActive = false this.$nextTick(() => (this.editorActive = true)) }, - editTaskSubmit() { - this.taskService - .update(this.taskEditTask) - .then((r) => { - this.taskEditTask = r - this.initTaskFields() - this.$message.success({message: this.$t('task.detail.updateSuccess')}) - }) + async editTaskSubmit() { + this.taskEditTask = await this.taskService.update(this.taskEditTask) + this.initTaskFields() + this.$message.success({message: this.$t('task.detail.updateSuccess')}) }, }, } diff --git a/src/components/tasks/gantt-component.vue b/src/components/tasks/gantt-component.vue index 1e3825ba..8981ac10 100644 --- a/src/components/tasks/gantt-component.vue +++ b/src/components/tasks/gantt-component.vue @@ -297,48 +297,41 @@ export default { console.debug('prepareGanttDays; years:', years) this.days = years }, + parseTasks() { this.setDates() this.loadTasks() }, - loadTasks() { + + async loadTasks() { this.theTasks = [] this.tasksWithoutDates = [] - const getAllTasks = (page = 1) => { - return this.taskCollectionService - .getAll({listId: this.listId}, this.params, page) - .then((tasks) => { - if (page < this.taskCollectionService.totalPages) { - return getAllTasks(page + 1).then((nextTasks) => { - return tasks.concat(nextTasks) - }) - } else { - return tasks - } - }) + const getAllTasks = async (page = 1) => { + const tasks = await this.taskCollectionService.getAll({listId: this.listId}, this.params, page) + if (page < this.taskCollectionService.totalPages) { + const nextTasks = await getAllTasks(page + 1) + return tasks.concat(nextTasks) + } + return tasks } - getAllTasks() - .then((tasks) => { - this.theTasks = tasks - .filter((t) => { - if (t.startDate === null && !t.done) { - this.tasksWithoutDates.push(t) - } - return ( - t.startDate >= this.startDate && - t.endDate <= this.endDate - ) - }) - .map((t) => { - return this.addGantAttributes(t) - }) - .sort(function (a, b) { - if (a.startDate < b.startDate) return -1 - if (a.startDate > b.startDate) return 1 - return 0 - }) + const tasks = await getAllTasks() + this.theTasks = tasks + .filter((t) => { + if (t.startDate === null && !t.done) { + this.tasksWithoutDates.push(t) + } + return ( + t.startDate >= this.startDate && + t.endDate <= this.endDate + ) + }) + .map((t) => this.addGantAttributes(t)) + .sort(function (a, b) { + if (a.startDate < b.startDate) return -1 + if (a.startDate > b.startDate) return 1 + return 0 }) }, addGantAttributes(t) { @@ -351,7 +344,7 @@ export default { t.offsetDays = Math.floor((t.startDate - this.startDate) / 1000 / 60 / 60 / 24) return t }, - resizeTask(taskDragged, newRect) { + async resizeTask(taskDragged, newRect) { if (this.isTaskEdit) { return } @@ -392,31 +385,28 @@ export default { offsetDays: newTask.offsetDays, } - this.taskService - .update(newTask) - .then(r => { - r.endDate = ganttData.endDate - r.durationDays = ganttData.durationDays - r.offsetDays = ganttData.offsetDays + const r = await this.taskService.update(newTask) + r.endDate = ganttData.endDate + r.durationDays = ganttData.durationDays + r.offsetDays = ganttData.offsetDays - // If the task didn't have dates before, we'll update the list - if (didntHaveDates) { - for (const t in this.tasksWithoutDates) { - if (this.tasksWithoutDates[t].id === r.id) { - this.tasksWithoutDates.splice(t, 1) - break - } - } - this.theTasks.push(this.addGantAttributes(r)) - } else { - for (const tt in this.theTasks) { - if (this.theTasks[tt].id === r.id) { - this.theTasks[tt] = this.addGantAttributes(r) - break - } - } + // If the task didn't have dates before, we'll update the list + if (didntHaveDates) { + for (const t in this.tasksWithoutDates) { + if (this.tasksWithoutDates[t].id === r.id) { + this.tasksWithoutDates.splice(t, 1) + break } - }) + } + this.theTasks.push(this.addGantAttributes(r)) + } else { + for (const tt in this.theTasks) { + if (this.theTasks[tt].id === r.id) { + this.theTasks[tt] = this.addGantAttributes(r) + break + } + } + } }, editTask(task) { this.taskToEdit = task @@ -436,7 +426,7 @@ export default { this.$nextTick(() => (this.newTaskFieldActive = false)) } }, - addNewTask() { + async addNewTask() { if (!this.newTaskFieldActive) { return } @@ -444,13 +434,10 @@ export default { title: this.newTaskTitle, listId: this.listId, }) - this.taskService - .create(task) - .then((r) => { - this.tasksWithoutDates.push(this.addGantAttributes(r)) - this.newTaskTitle = '' - this.hideCrateNewTask() - }) + const r = await this.taskService.create(task) + this.tasksWithoutDates.push(this.addGantAttributes(r)) + this.newTaskTitle = '' + this.hideCrateNewTask() }, formatYear(date) { return this.format(date, 'MMMM, yyyy') diff --git a/src/components/tasks/mixins/taskList.js b/src/components/tasks/mixins/taskList.js index da120b19..d411e78f 100644 --- a/src/components/tasks/mixins/taskList.js +++ b/src/components/tasks/mixins/taskList.js @@ -38,7 +38,7 @@ export default { '$route.path': 'loadTasksOnSavedFilter', }, methods: { - loadTasks( + async loadTasks( page, search = '', params = null, @@ -76,14 +76,9 @@ export default { } this.tasks = [] - - this.taskCollectionService.getAll(list, params, page) - .then(r => { - this.tasks = r - this.currentPage = page - - this.loadedList = JSON.parse(JSON.stringify(currentList)) - }) + this.tasks = await this.taskCollectionService.getAll(list, params, page) + this.currentPage = page + this.loadedList = JSON.parse(JSON.stringify(currentList)) }, loadTasksForPage(e) { diff --git a/src/components/tasks/partials/attachments.vue b/src/components/tasks/partials/attachments.vue index de3c8c6a..ba03e7b2 100644 --- a/src/components/tasks/partials/attachments.vue +++ b/src/components/tasks/partials/attachments.vue @@ -218,21 +218,19 @@ export default { uploadFiles(files) { uploadFiles(this.attachmentService, this.taskId, files) }, - deleteAttachment() { - this.attachmentService - .delete(this.attachmentToDelete) - .then((r) => { - this.$store.commit( - 'attachments/removeById', - this.attachmentToDelete.id, - ) - this.$message.success(r) - }) - .finally(() => { - this.showDeleteModal = false - }) + async deleteAttachment() { + try { + const r = await this.attachmentService.delete(this.attachmentToDelete) + this.$store.commit( + 'attachments/removeById', + this.attachmentToDelete.id, + ) + this.$message.success(r) + } finally{ + this.showDeleteModal = false + } }, - viewOrDownload(attachment) { + async viewOrDownload(attachment) { if ( attachment.file.name.endsWith('.jpg') || attachment.file.name.endsWith('.png') || @@ -240,9 +238,7 @@ export default { attachment.file.name.endsWith('.gif') ) { this.showImageModal = true - this.attachmentService.getBlobUrl(attachment).then((url) => { - this.attachmentImageBlobUrl = url - }) + this.attachmentImageBlobUrl = await this.attachmentService.getBlobUrl(attachment) } else { this.downloadAttachment(attachment) } diff --git a/src/components/tasks/partials/comments.vue b/src/components/tasks/partials/comments.vue index 136832e4..73accf97 100644 --- a/src/components/tasks/partials/comments.vue +++ b/src/components/tasks/partials/comments.vue @@ -134,9 +134,9 @@ @@ -186,7 +186,6 @@ export default { taskCommentService: new TaskCommentService(), newComment: new TaskCommentModel(), editorActive: true, - actions: {}, saved: null, saving: null, @@ -195,40 +194,46 @@ export default { }, watch: { taskId: { - handler(taskId) { - if (!this.enabled) { - return - } - - this.loadComments() - this.newComment.taskId = taskId - this.commentEdit.taskId = taskId - this.commentToDelete.taskId = taskId - }, + handler: 'loadComments', immediate: true, }, - canWrite() { - this.makeActions() + }, + computed: { + ...mapState({ + userAvatar: state => state.auth.info.getAvatarUrl(48), + enabled: state => state.config.taskCommentsEnabled, + }), + actions() { + if (!this.canWrite) { + return {} + } + return Object.fromEntries(this.comments.map((c) => ([ + c.id, + [{ + action: () => this.toggleDelete(c.id), + title: this.$t('misc.delete'), + }], + ]))) }, }, - computed: mapState({ - userAvatar: state => state.auth.info.getAvatarUrl(48), - enabled: state => state.config.taskCommentsEnabled, - }), + methods: { attachmentUpload(...args) { return uploadFile(this.taskId, ...args) }, - loadComments() { - this.taskCommentService - .getAll({taskId: this.taskId}) - .then(r => { - this.comments = r - this.makeActions() - }) + async loadComments(taskId) { + if (!this.enabled) { + return + } + + this.newComment.taskId = taskId + this.commentEdit.taskId = taskId + this.commentToDelete.taskId = taskId + this.comments = await this.taskCommentService.getAll({taskId}) }, - addComment() { + + async addComment() { if (this.newComment.comment === '') { return } @@ -242,27 +247,27 @@ export default { this.$nextTick(() => (this.editorActive = true)) this.creating = true - this.taskCommentService - .create(this.newComment) - .then((r) => { - this.comments.push(r) - this.newComment.comment = '' - this.$message.success({message: this.$t('task.comment.addedSuccess')}) - this.makeActions() - }) - .finally(() => { - this.creating = false - }) + try { + const comment = await this.taskCommentService.create(this.newComment) + this.comments.push(comment) + this.newComment.comment = '' + this.$message.success({message: this.$t('task.comment.addedSuccess')}) + } finally { + this.creating = false + } }, + toggleEdit(comment) { this.isCommentEdit = !this.isCommentEdit this.commentEdit = comment }, + toggleDelete(commentId) { this.showDeleteModal = !this.showDeleteModal this.commentToDelete.id = commentId }, - editComment() { + + async editComment() { if (this.commentEdit.comment === '') { return } @@ -270,48 +275,30 @@ export default { this.saving = this.commentEdit.id this.commentEdit.taskId = this.taskId - this.taskCommentService - .update(this.commentEdit) - .then((r) => { - for (const c in this.comments) { - if (this.comments[c].id === this.commentEdit.id) { - this.comments[c] = r - } + try { + const comment = this.taskCommentService.update(this.commentEdit) + for (const c in this.comments) { + if (this.comments[c].id === this.commentEdit.id) { + this.comments[c] = comment } - this.saved = this.commentEdit.id - setTimeout(() => { - this.saved = null - }, 2000) - }) - .finally(() => { - this.isCommentEdit = false - this.saving = null - }) + } + this.saved = this.commentEdit.id + setTimeout(() => { + this.saved = null + }, 2000) + } finally { + this.isCommentEdit = false + this.saving = null + } }, - deleteComment() { - this.taskCommentService - .delete(this.commentToDelete) - .then(() => { - for (const a in this.comments) { - if (this.comments[a].id === this.commentToDelete.id) { - this.comments.splice(a, 1) - } - } - }) - .finally(() => { - this.showDeleteModal = false - }) - }, - makeActions() { - if (this.canWrite) { - this.comments.forEach((c) => { - this.actions[c.id] = [ - { - action: () => this.toggleDelete(c.id), - title: this.$t('misc.delete'), - }, - ] - }) + + async deleteComment(commentToDelete) { + try { + await this.taskCommentService.delete(commentToDelete) + const index = this.comments.findIndex(({id}) => id === commentToDelete.id) + this.comments.splice(index, 1) + } finally { + this.showDeleteModal = false } }, }, diff --git a/src/components/tasks/partials/defer-task.vue b/src/components/tasks/partials/defer-task.vue index 549c3367..c5ef510b 100644 --- a/src/components/tasks/partials/defer-task.vue +++ b/src/components/tasks/partials/defer-task.vue @@ -112,7 +112,8 @@ export default { this.dueDate = this.dueDate.setDate(this.dueDate.getDate() + days) this.updateDueDate() }, - updateDueDate() { + + async updateDueDate() { if (!this.dueDate) { return } @@ -122,13 +123,10 @@ export default { } this.task.dueDate = new Date(this.dueDate) - this.taskService - .update(this.task) - .then((r) => { - this.lastValue = r.dueDate - this.task = r - this.$emit('update:modelValue', r) - }) + const task = await this.taskService.update(this.task) + this.lastValue = task.dueDate + this.task = task + this.$emit('update:modelValue', task) }, }, } diff --git a/src/components/tasks/partials/description.vue b/src/components/tasks/partials/description.vue index 18ccbfe0..28fd5a88 100644 --- a/src/components/tasks/partials/description.vue +++ b/src/components/tasks/partials/description.vue @@ -71,21 +71,19 @@ export default { }, }, methods: { - save() { + async save() { this.saving = true - this.$store.dispatch('tasks/update', this.task) - .then(t => { - this.task = t - this.$emit('update:modelValue', t) - this.saved = true - setTimeout(() => { - this.saved = false - }, 2000) - }) - .finally(() => { - this.saving = false - }) + try { + this.task = await this.$store.dispatch('tasks/update', this.task) + this.$emit('update:modelValue', this.task) + this.saved = true + setTimeout(() => { + this.saved = false + }, 2000) + } finally { + this.saving = false + } }, }, } diff --git a/src/components/tasks/partials/editAssignees.vue b/src/components/tasks/partials/editAssignees.vue index 563735ab..adac63c7 100644 --- a/src/components/tasks/partials/editAssignees.vue +++ b/src/components/tasks/partials/editAssignees.vue @@ -78,40 +78,40 @@ export default { }, }, methods: { - addAssignee(user) { - this.$store.dispatch('tasks/addAssignee', {user: user, taskId: this.taskId}) - .then(() => { - this.$emit('update:modelValue', this.assignees) - this.$message.success({message: this.$t('task.assignee.assignSuccess')}) - }) + async addAssignee(user) { + await this.$store.dispatch('tasks/addAssignee', {user: user, taskId: this.taskId}) + this.$emit('update:modelValue', this.assignees) + this.$message.success({message: this.$t('task.assignee.assignSuccess')}) }, - removeAssignee(user) { - this.$store.dispatch('tasks/removeAssignee', {user: user, taskId: this.taskId}) - .then(() => { - // Remove the assignee from the list - for (const a in this.assignees) { - if (this.assignees[a].id === user.id) { - this.assignees.splice(a, 1) - } - } - this.$message.success({message: this.$t('task.assignee.unassignSuccess')}) - }) + + async removeAssignee(user) { + await this.$store.dispatch('tasks/removeAssignee', {user: user, taskId: this.taskId}) + + // Remove the assignee from the list + for (const a in this.assignees) { + if (this.assignees[a].id === user.id) { + this.assignees.splice(a, 1) + } + } + this.$message.success({message: this.$t('task.assignee.unassignSuccess')}) }, - findUser(query) { + + async findUser(query) { if (query === '') { this.clearAllFoundUsers() return } - this.listUserService.getAll({listId: this.listId}, {s: query}) - .then(response => { - // Filter the results to not include users who are already assigned - this.foundUsers = response.filter(({id}) => !includesById(this.assignees, id)) - }) + const response = await this.listUserService.getAll({listId: this.listId}, {s: query}) + + // Filter the results to not include users who are already assigned + this.foundUsers = response.filter(({id}) => !includesById(this.assignees, id)) }, + clearAllFoundUsers() { this.foundUsers = [] }, + focus() { this.$refs.multiselect.focus() }, diff --git a/src/components/tasks/partials/editLabels.vue b/src/components/tasks/partials/editLabels.vue index dd2e10da..25ee20b6 100644 --- a/src/components/tasks/partials/editLabels.vue +++ b/src/components/tasks/partials/editLabels.vue @@ -93,7 +93,8 @@ export default { findLabel(query) { this.query = query }, - addLabel(label, showNotification = true) { + + async addLabel(label, showNotification = true) { const bubble = () => { this.$emit('update:modelValue', this.labels) this.$emit('change', this.labels) @@ -104,15 +105,14 @@ export default { return } - this.$store.dispatch('tasks/addLabel', {label: label, taskId: this.taskId}) - .then(() => { - bubble() - if (showNotification) { - this.$message.success({message: this.$t('task.label.addSuccess')}) - } - }) + await this.$store.dispatch('tasks/addLabel', {label: label, taskId: this.taskId}) + bubble() + if (showNotification) { + this.$message.success({message: this.$t('task.label.addSuccess')}) + } }, - removeLabel(label) { + + async removeLabel(label) { const removeFromState = () => { for (const l in this.labels) { if (this.labels[l].id === label.id) { @@ -128,24 +128,21 @@ export default { return } - this.$store.dispatch('tasks/removeLabel', {label: label, taskId: this.taskId}) - .then(() => { - removeFromState() - this.$message.success({message: this.$t('task.label.removeSuccess')}) - }) + await this.$store.dispatch('tasks/removeLabel', {label: label, taskId: this.taskId}) + removeFromState() + this.$message.success({message: this.$t('task.label.removeSuccess')}) }, - createAndAddLabel(title) { + + async createAndAddLabel(title) { if (this.taskId === 0) { return } const newLabel = new LabelModel({title: title}) - this.$store.dispatch('labels/createLabel', newLabel) - .then(r => { - this.addLabel(r, false) - this.labels.push(r) - this.$message.success({message: this.$t('task.label.addCreateSuccess')}) - }) + const label = await this.$store.dispatch('labels/createLabel', newLabel) + this.addLabel(label, false) + this.labels.push(label) + this.$message.success({message: this.$t('task.label.addCreateSuccess')}) }, }, diff --git a/src/components/tasks/partials/heading.vue b/src/components/tasks/partials/heading.vue index e775fb8c..7ba4d1d8 100644 --- a/src/components/tasks/partials/heading.vue +++ b/src/components/tasks/partials/heading.vue @@ -58,7 +58,7 @@ export default { emits: ['update:modelValue'], methods: { - save(title) { + async save(title) { // We only want to save if the title was actually changed. // Because the contenteditable does not have a change event // we're building it ourselves and only continue @@ -74,17 +74,17 @@ export default { title, } - this.$store.dispatch('tasks/update', newTask) - .then((task) => { - this.$emit('update:modelValue', task) - this.showSavedMessage = true - setTimeout(() => { - this.showSavedMessage = false - }, 2000) - }) - .finally(() => { - this.saving = false - }) + try { + const task = await this.$store.dispatch('tasks/update', newTask) + this.$emit('update:modelValue', task) + this.showSavedMessage = true + setTimeout(() => { + this.showSavedMessage = false + }, 2000) + } + finally { + this.saving = false + } }, }, } diff --git a/src/components/tasks/partials/kanban-card.vue b/src/components/tasks/partials/kanban-card.vue index 0d9141da..6bc60297 100644 --- a/src/components/tasks/partials/kanban-card.vue +++ b/src/components/tasks/partials/kanban-card.vue @@ -6,9 +6,9 @@ 'has-light-text': !colorIsDark(task.hexColor) && task.hexColor !== `#${task.defaultColor}` && task.hexColor !== task.defaultColor, }" :style="{'background-color': task.hexColor !== '#' && task.hexColor !== `#${task.defaultColor}` ? task.hexColor : false}" - @click.ctrl="() => markTaskAsDone(task)" + @click.ctrl="() => toggleTaskDone(task)" @click.exact="() => $router.push({ name: 'task.kanban.detail', params: { id: task.id } })" - @click.meta="() => markTaskAsDone(task)" + @click.meta="() => toggleTaskDone(task)" class="task loader-container draggable" > @@ -93,20 +93,19 @@ export default { }, }, methods: { - markTaskAsDone(task) { + async toggleTaskDone(task) { this.loadingInternal = true - this.$store.dispatch('tasks/update', { - ...task, - done: !task.done, - }) - .then(() => { - if (task.done) { - playPop() - } - }) - .finally(() => { - this.loadingInternal = false + try { + await this.$store.dispatch('tasks/update', { + ...task, + done: !task.done, }) + if (task.done) { + playPop() + } + } finally { + this.loadingInternal = false + } }, }, } diff --git a/src/components/tasks/partials/listSearch.vue b/src/components/tasks/partials/listSearch.vue index fe7a7437..252424f4 100644 --- a/src/components/tasks/partials/listSearch.vue +++ b/src/components/tasks/partials/listSearch.vue @@ -50,25 +50,25 @@ export default { }, }, methods: { - findLists(query) { + async findLists(query) { if (query === '') { this.clearAll() return } - this.listSerivce.getAll({}, {s: query}) - .then(response => { - this.foundLists = response - }) + this.foundLists = await this.listSerivce.getAll({}, {s: query}) }, + clearAll() { this.foundLists = [] }, + select(list) { this.list = list this.$emit('selected', list) this.$emit('update:modelValue', list) }, + namespace(namespaceId) { const namespace = this.$store.getters['namespaces/getNamespaceById'](namespaceId) if (namespace !== null) { diff --git a/src/components/tasks/partials/relatedTasks.vue b/src/components/tasks/partials/relatedTasks.vue index 5744ef5f..9279e147 100644 --- a/src/components/tasks/partials/relatedTasks.vue +++ b/src/components/tasks/partials/relatedTasks.vue @@ -188,58 +188,59 @@ export default { async findTasks(query) { this.foundTasks = await this.taskService.getAll({}, {s: query}) }, - addTaskRelation() { - let rel = new TaskRelationModel({ + + async addTaskRelation() { + const rel = new TaskRelationModel({ taskId: this.taskId, otherTaskId: this.newTaskRelationTask.id, relationKind: this.newTaskRelationKind, }) - this.taskRelationService.create(rel) - .then(() => { - if (!this.relatedTasks[this.newTaskRelationKind]) { - this.relatedTasks[this.newTaskRelationKind] = [] - } - this.relatedTasks[this.newTaskRelationKind].push(this.newTaskRelationTask) - this.newTaskRelationTask = null - this.saved = true - this.showNewRelationForm = false - setTimeout(() => { - this.saved = false - }, 2000) - }) + await this.taskRelationService.create(rel) + if (!this.relatedTasks[this.newTaskRelationKind]) { + this.relatedTasks[this.newTaskRelationKind] = [] + } + this.relatedTasks[this.newTaskRelationKind].push(this.newTaskRelationTask) + this.newTaskRelationTask = null + this.saved = true + this.showNewRelationForm = false + setTimeout(() => { + this.saved = false + }, 2000) }, - removeTaskRelation() { + + async removeTaskRelation() { const rel = new TaskRelationModel({ relationKind: this.relationToDelete.relationKind, taskId: this.taskId, otherTaskId: this.relationToDelete.otherTaskId, }) - this.taskRelationService.delete(rel) - .then(() => { - Object.keys(this.relatedTasks).forEach(relationKind => { - for (const t in this.relatedTasks[relationKind]) { - if (this.relatedTasks[relationKind][t].id === this.relationToDelete.otherTaskId && relationKind === this.relationToDelete.relationKind) { - this.relatedTasks[relationKind].splice(t, 1) - } - } - }) - this.saved = true - setTimeout(() => { - this.saved = false - }, 2000) - }) - .finally(() => { - this.showDeleteModal = false + try { + await this.taskRelationService.delete(rel) + + Object.entries(this.relatedTasks).some(([relationKind, t]) => { + const found = this.relatedTasks[relationKind][t].id === this.relationToDelete.otherTaskId && + relationKind === this.relationToDelete.relationKind + if (!found) return false + + this.relatedTasks[relationKind].splice(t, 1) + return true }) + + this.saved = true + setTimeout(() => { + this.saved = false + }, 2000) + } finally { + this.showDeleteModal = false + } }, - createAndRelateTask(title) { + + async createAndRelateTask(title) { const newTask = new TaskModel({title: title, listId: this.listId}) - this.taskService.create(newTask) - .then(r => { - this.newTaskRelationTask = r - this.addTaskRelation() - }) + this.newTaskRelationTask = await this.taskService.create(newTask) + await this.addTaskRelation() }, + relationKindTitle(kind, length) { return this.$tc(`task.relation.kinds.${kind}`, length) }, diff --git a/src/components/tasks/partials/singleTaskInList.vue b/src/components/tasks/partials/singleTaskInList.vue index 5cd7c614..697207cf 100644 --- a/src/components/tasks/partials/singleTaskInList.vue +++ b/src/components/tasks/partials/singleTaskInList.vue @@ -166,50 +166,47 @@ export default { }, }, methods: { - markAsDone(checked) { - const updateFunc = () => { - this.taskService.update(this.task) - .then(t => { - if (this.task.done) { - playPop() - } - this.task = t - this.$emit('task-updated', t) - this.$message.success({ - message: this.task.done ? - this.$t('task.doneSuccess') : - this.$t('task.undoneSuccess'), - }, [{ - title: 'Undo', - callback: () => { - this.task.done = !this.task.done - this.markAsDone(!checked) - }, - }]) - }) + async markAsDone(checked) { + const updateFunc = async () => { + const task = await this.taskService.update(this.task) + if (this.task.done) { + playPop() + } + this.task = task + this.$emit('task-updated', task) + this.$message.success({ + message: this.task.done ? + this.$t('task.doneSuccess') : + this.$t('task.undoneSuccess'), + }, [{ + title: 'Undo', + callback() { + this.task.done = !this.task.done + this.markAsDone(!checked) + }, + }]) } if (checked) { setTimeout(updateFunc, 300) // Delay it to show the animation when marking a task as done } else { - updateFunc() // Don't delay it when un-marking it as it doesn't have an animation the other way around + await updateFunc() // Don't delay it when un-marking it as it doesn't have an animation the other way around } }, - toggleFavorite() { + + async toggleFavorite() { this.task.isFavorite = !this.task.isFavorite - this.taskService.update(this.task) - .then(t => { - this.task = t - this.$emit('task-updated', t) - this.$store.dispatch('namespaces/loadNamespacesIfFavoritesDontExist') - }) + this.task = await this.taskService.update(this.task) + this.$emit('task-updated', this.task) + this.$store.dispatch('namespaces/loadNamespacesIfFavoritesDontExist') }, hideDeferDueDatePopup(e) { - if (this.showDefer) { - closeWhenClickedOutside(e, this.$refs.deferDueDate.$el, () => { - this.showDefer = false - }) + if (!this.showDefer) { + return } + closeWhenClickedOutside(e, this.$refs.deferDueDate.$el, () => { + this.showDefer = false + }) }, }, } diff --git a/src/components/user/avatar-settings.vue b/src/components/user/avatar-settings.vue index cdc8a83a..6d42f17d 100644 --- a/src/components/user/avatar-settings.vue +++ b/src/components/user/avatar-settings.vue @@ -91,34 +91,34 @@ export default { const { avatarProvider } = await this.avatarService.get({}) this.avatarProvider = avatarProvider }, - updateAvatarStatus() { + + async updateAvatarStatus() { const avatarStatus = new AvatarModel({avatarProvider: this.avatarProvider}) - this.avatarService.update(avatarStatus) - .then(() => { - this.$message.success({message: this.$t('user.settings.avatar.statusUpdateSuccess')}) - this.$store.commit('auth/reloadAvatar') - }) + await this.avatarService.update(avatarStatus) + this.$message.success({message: this.$t('user.settings.avatar.statusUpdateSuccess')}) + this.$store.commit('auth/reloadAvatar') }, - uploadAvatar() { + + async uploadAvatar() { this.loading = true const {canvas} = this.$refs.cropper.getResult() - if (canvas) { - canvas.toBlob(blob => { - this.avatarService.create(blob) - .then(() => { - this.$message.success({message: this.$t('user.settings.avatar.setSuccess')}) - this.$store.commit('auth/reloadAvatar') - }) - .finally(() => { - this.loading = false - this.isCropAvatar = false - }) - }) - } else { + if (!canvas) { this.loading = false + return + } + + try { + const blob = await new Promise(resolve => canvas.toBlob(blob => resolve(blob))) + await this.avatarService.create(blob) + this.$message.success({message: this.$t('user.settings.avatar.setSuccess')}) + this.$store.commit('auth/reloadAvatar') + } finally { + this.loading = false + this.isCropAvatar = false } }, + cropAvatar() { const avatar = this.$refs.avatarUploadInput.files diff --git a/src/components/user/settings/data-export.vue b/src/components/user/settings/data-export.vue index e395e6ac..f092444f 100644 --- a/src/components/user/settings/data-export.vue +++ b/src/components/user/settings/data-export.vue @@ -43,27 +43,22 @@ export default { name: 'data-export', data() { return { - dataExportService: DataExportService, + dataExportService: new DataExportService(), password: '', errPasswordRequired: false, } }, - created() { - this.dataExportService = new DataExportService() - }, methods: { - requestDataExport() { + async requestDataExport() { if (this.password === '') { this.errPasswordRequired = true this.$refs.passwordInput.focus() return } - this.dataExportService.request(this.password) - .then(() => { - this.$message.success({message: this.$t('user.export.success')}) - this.password = '' - }) + await this.dataExportService.request(this.password) + this.$message.success({message: this.$t('user.export.success')}) + this.password = '' }, }, } diff --git a/src/components/user/settings/deletion.vue b/src/components/user/settings/deletion.vue index 40dd272b..f51c7909 100644 --- a/src/components/user/settings/deletion.vue +++ b/src/components/user/settings/deletion.vue @@ -101,32 +101,29 @@ export default { deletionScheduledAt: state => parseDateOrNull(state.auth.info.deletionScheduledAt), }), methods: { - deleteAccount() { + async deleteAccount() { if (this.password === '') { this.errPasswordRequired = true this.$refs.passwordInput.focus() return } - this.accountDeleteService.request(this.password) - .then(() => { - this.$message.success({message: this.$t('user.deletion.requestSuccess')}) - this.password = '' - }) + await this.accountDeleteService.request(this.password) + this.$message.success({message: this.$t('user.deletion.requestSuccess')}) + this.password = '' }, - cancelDeletion() { + + async cancelDeletion() { if (this.password === '') { this.errPasswordRequired = true this.$refs.passwordInput.focus() return } - this.accountDeleteService.cancel(this.password) - .then(() => { - this.$message.success({message: this.$t('user.deletion.scheduledCancelSuccess')}) - this.$store.dispatch('auth/refreshUserInfo') - this.password = '' - }) + await this.accountDeleteService.cancel(this.password) + this.$message.success({message: this.$t('user.deletion.scheduledCancelSuccess')}) + this.$store.dispatch('auth/refreshUserInfo') + this.password = '' }, }, } diff --git a/src/helpers/attachments.ts b/src/helpers/attachments.ts index 09ec77f9..4532bee1 100644 --- a/src/helpers/attachments.ts +++ b/src/helpers/attachments.ts @@ -11,24 +11,22 @@ export function uploadFile(taskId: number, file: FileModel, onSuccess: () => Fun return uploadFiles(attachmentService, taskId, files, onSuccess) } -export function uploadFiles(attachmentService: AttachmentService, taskId: number, files: FileModel[], onSuccess : Function = () => {}) { +export async function uploadFiles(attachmentService: AttachmentService, taskId: number, files: FileModel[], onSuccess : Function = () => {}) { const attachmentModel = new AttachmentModel({taskId}) - attachmentService.create(attachmentModel, files) - .then(r => { - console.debug(`Uploaded attachments for task ${taskId}, response was`, r) - if (r.success !== null) { - r.success.forEach((attachment: AttachmentModel) => { - store.dispatch('tasks/addTaskAttachment', { - taskId, - attachment, - }) - onSuccess(generateAttachmentUrl(taskId, attachment.id)) - }) - } - if (r.errors !== null) { - throw Error(r.errors) - } + const response = await attachmentService.create(attachmentModel, files) + console.debug(`Uploaded attachments for task ${taskId}, response was`, response) + + response.success?.map((attachment: AttachmentModel) => { + store.dispatch('tasks/addTaskAttachment', { + taskId, + attachment, }) + onSuccess(generateAttachmentUrl(taskId, attachment.id)) + }) + + if (response.errors !== null) { + throw Error(response.errors) + } } export function generateAttachmentUrl(taskId: number, attachmentId: number) : any { diff --git a/src/helpers/auth.ts b/src/helpers/auth.ts index b3297bde..b073a8ca 100644 --- a/src/helpers/auth.ts +++ b/src/helpers/auth.ts @@ -41,19 +41,19 @@ export const removeToken = () => { * Refreshes an auth token while ensuring it is updated everywhere. * @returns {Promise>} */ -export const refreshToken = (persist: boolean): Promise => { +export async function refreshToken(persist: boolean): Promise { const HTTP = HTTPFactory() - return HTTP.post('user/token', null, { - headers: { - Authorization: `Bearer ${getToken()}`, - }, - }) - .then(r => { - saveToken(r.data.token, persist) - return r - }) - .catch(e => { - throw new Error('Error renewing token: ', { cause: e }) + try { + const response = await HTTP.post('user/token', null, { + headers: { + Authorization: `Bearer ${getToken()}`, + }, }) + saveToken(response.data.token, persist) + return response + + } catch(e) { + throw new Error('Error renewing token: ', { cause: e }) + } } diff --git a/src/models/task.js b/src/models/task.js index 79c0a8e0..2b3a9f7c 100644 --- a/src/models/task.js +++ b/src/models/task.js @@ -24,16 +24,12 @@ export default class TaskModel extends AbstractModel { this.endDate = parseDateOrNull(this.endDate) this.doneAt = parseDateOrNull(this.doneAt) + this.reminderDates = this.reminderDates.map(d => new Date(d)) // Cancel all scheduled notifications for this task to be sure to only have available notifications - this.cancelScheduledNotifications() - .then(() => { - this.reminderDates = this.reminderDates.map(d => { - d = new Date(d) - // Every time we see a reminder, we schedule a notification for it - this.scheduleNotification(d) - return d - }) - }) + this.cancelScheduledNotifications().then(() => { + // Every time we see a reminder, we schedule a notification for it + this.reminderDates.forEach(d => this.scheduleNotification(d)) + }) // Parse the repeat after into something usable this.parseRepeatAfter() @@ -218,27 +214,26 @@ export default class TaskModel extends AbstractModel { } // Register the actual notification - registration.showNotification('Vikunja Reminder', { - tag: `vikunja-task-${this.id}`, // Group notifications by task id so we're only showing one notification per task - body: this.title, - // eslint-disable-next-line no-undef - showTrigger: new TimestampTrigger(date), - badge: '/images/icons/badge-monochrome.png', - icon: '/images/icons/android-chrome-512x512.png', - data: {taskId: this.id}, - actions: [ - { - action: 'show-task', - title: 'Show task', - }, - ], - }) - .then(() => { - console.debug('Notification scheduled for ' + date) - }) - .catch(e => { - console.debug('Error scheduling notification', e) + try { + registration.showNotification('Vikunja Reminder', { + tag: `vikunja-task-${this.id}`, // Group notifications by task id so we're only showing one notification per task + body: this.title, + // eslint-disable-next-line no-undef + showTrigger: new TimestampTrigger(date), + badge: '/images/icons/badge-monochrome.png', + icon: '/images/icons/android-chrome-512x512.png', + data: {taskId: this.id}, + actions: [ + { + action: 'show-task', + title: 'Show task', + }, + ], }) + console.debug('Notification scheduled for ' + date) + } catch(e) { + throw new Error('Error scheduling notification', e) + } } } diff --git a/src/services/abstractService.js b/src/services/abstractService.js index 2bb58be0..aa8e7b45 100644 --- a/src/services/abstractService.js +++ b/src/services/abstractService.js @@ -285,32 +285,30 @@ export default class AbstractService { * @param params * @returns {Q.Promise} */ - getM(url, model = {}, params = {}) { + async getM(url, model = {}, params = {}) { const cancel = this.setLoading() model = this.beforeGet(model) const finalUrl = this.getReplacedRoute(url, model) - return this.http.get(finalUrl, {params}) - .then(response => { - const result = this.modelGetFactory(response.data) - result.maxRight = Number(response.headers['x-max-right']) - return result - }) - .finally(() => { - cancel() - }) + try { + const response = await this.http.get(finalUrl, {params}) + const result = this.modelGetFactory(response.data) + result.maxRight = Number(response.headers['x-max-right']) + return result + } finally { + cancel() + } } - getBlobUrl(url, method = 'GET', data = {}) { - return this.http({ + async getBlobUrl(url, method = 'GET', data = {}) { + const response = await this.http({ url: url, method: method, responseType: 'blob', data: data, - }).then(response => { - return window.URL.createObjectURL(new Blob([response.data])) }) + return window.URL.createObjectURL(new Blob([response.data])) } /** @@ -321,7 +319,7 @@ export default class AbstractService { * @param page The page to get * @returns {Q.Promise} */ - getAll(model = {}, params = {}, page = 1) { + async getAll(model = {}, params = {}, page = 1) { if (this.paths.getAll === '') { throw new Error('This model is not able to get data.') } @@ -332,22 +330,22 @@ export default class AbstractService { model = this.beforeGet(model) const finalUrl = this.getReplacedRoute(this.paths.getAll, model) - return this.http.get(finalUrl, {params: params}) - .then(response => { - this.resultCount = Number(response.headers['x-pagination-result-count']) - this.totalPages = Number(response.headers['x-pagination-total-pages']) + try { + const response = await this.http.get(finalUrl, {params: params}) + this.resultCount = Number(response.headers['x-pagination-result-count']) + this.totalPages = Number(response.headers['x-pagination-total-pages']) + + if (response.data === null) { + return [] + } - if (Array.isArray(response.data)) { - return response.data.map(entry => this.modelGetAllFactory(entry)) - } - if (response.data === null) { - return [] - } - return this.modelGetAllFactory(response.data) - }) - .finally(() => { - cancel() - }) + if (Array.isArray(response.data)) { + return response.data.map(entry => this.modelGetAllFactory(entry)) + } + return this.modelGetAllFactory(response.data) + } finally { + cancel() + } } /** @@ -355,7 +353,7 @@ export default class AbstractService { * @param model * @returns {Promise} */ - create(model) { + async create(model) { if (this.paths.create === '') { throw new Error('This model is not able to create data.') } @@ -363,17 +361,16 @@ export default class AbstractService { const cancel = this.setLoading() const finalUrl = this.getReplacedRoute(this.paths.create, model) - return this.http.put(finalUrl, model) - .then(response => { - const result = this.modelCreateFactory(response.data) - if (typeof model.maxRight !== 'undefined') { - result.maxRight = model.maxRight - } - return result - }) - .finally(() => { - cancel() - }) + try { + const response = await this.http.put(finalUrl, model) + const result = this.modelCreateFactory(response.data) + if (typeof model.maxRight !== 'undefined') { + result.maxRight = model.maxRight + } + return result + } finally { + cancel() + } } /** @@ -383,20 +380,19 @@ export default class AbstractService { * @param model * @returns {Q.Promise} */ - post(url, model) { + async post(url, model) { const cancel = this.setLoading() - return this.http.post(url, model) - .then(response => { - const result = this.modelUpdateFactory(response.data) - if (typeof model.maxRight !== 'undefined') { - result.maxRight = model.maxRight - } - return result - }) - .finally(() => { - cancel() - }) + try { + const response = await this.http.post(url, model) + const result = this.modelUpdateFactory(response.data) + if (typeof model.maxRight !== 'undefined') { + result.maxRight = model.maxRight + } + return result + } finally { + cancel() + } } /** @@ -465,27 +461,28 @@ export default class AbstractService { * @param formData * @returns {Q.Promise} */ - uploadFormData(url, formData) { + async uploadFormData(url, formData) { console.log(formData, formData._boundary) const cancel = this.setLoading() - return this.http.put( - url, - formData, - { - headers: { - 'Content-Type': + try { + const response = await this.http.put( + url, + formData, + { + headers: { + 'Content-Type': 'multipart/form-data; boundary=' + formData._boundary, + }, + onUploadProgress: progressEvent => { + this.uploadProgress = Math.round((progressEvent.loaded * 100) / progressEvent.total) + }, }, - onUploadProgress: progressEvent => { - this.uploadProgress = Math.round((progressEvent.loaded * 100) / progressEvent.total) - }, - }, - ) - .then(response => this.modelCreateFactory(response.data)) - .finally(() => { - this.uploadProgress = 0 - cancel() - }) + ) + this.modelCreateFactory(response.data) + } finally { + this.uploadProgress = 0 + cancel() + } } } \ No newline at end of file diff --git a/src/services/backgroundUnsplash.js b/src/services/backgroundUnsplash.js index 40161fe2..09b748ac 100644 --- a/src/services/backgroundUnsplash.js +++ b/src/services/backgroundUnsplash.js @@ -18,14 +18,12 @@ export default class BackgroundUnsplashService extends AbstractService { return new ListModel(data) } - thumb(model) { - return this.http({ + async thumb(model) { + const response = await this.http({ url: `/backgrounds/unsplash/images/${model.id}/thumb`, method: 'GET', responseType: 'blob', }) - .then(response => { - return window.URL.createObjectURL(new Blob([response.data])) - }) + return window.URL.createObjectURL(new Blob([response.data])) } } \ No newline at end of file diff --git a/src/services/dataExport.js b/src/services/dataExport.js index edadf756..9f8720bf 100644 --- a/src/services/dataExport.js +++ b/src/services/dataExport.js @@ -6,10 +6,13 @@ export default class DataExportService extends AbstractService { return this.post('/user/export/request', {password: password}) } - download(password) { + async download(password) { const clear = this.setLoading() - return this.getBlobUrl('/user/export/download', 'POST', {password}) - .then(url => downloadBlob(url, 'vikunja-export.zip')) - .finally(() => clear()) + try { + const url = await this.getBlobUrl('/user/export/download', 'POST', {password}) + downloadBlob(url, 'vikunja-export.zip') + } finally { + clear() + } } } \ No newline at end of file diff --git a/src/services/list.js b/src/services/list.js index 925773dd..3d61218f 100644 --- a/src/services/list.js +++ b/src/services/list.js @@ -44,28 +44,27 @@ export default class ListService extends AbstractService { return super.update(newModel) } - background(list) { + async background(list) { if (list.background === null) { return '' } - return this.http({ + const response = await this.http({ url: `/lists/${list.id}/background`, method: 'GET', responseType: 'blob', }) - .then(response => { - return window.URL.createObjectURL(new Blob([response.data])) - }) + return window.URL.createObjectURL(new Blob([response.data])) } - removeBackground(list) { + async removeBackground(list) { const cancel = this.setLoading() - return this.http.delete(`/lists/${list.id}/background`, list) - .then(response => response.data) - .finally(() => { - cancel() - }) + try { + const response = await this.http.delete(`/lists/${list.id}/background`, list) + return response.data + } finally { + cancel() + } } } \ No newline at end of file diff --git a/src/services/passwordReset.js b/src/services/passwordReset.js index 023f7c75..9d3dc56b 100644 --- a/src/services/passwordReset.js +++ b/src/services/passwordReset.js @@ -15,25 +15,23 @@ export default class PasswordResetService extends AbstractService { return new PasswordResetModel(data) } - resetPassword(model) { + async resetPassword(model) { const cancel = this.setLoading() - return this.http.post(this.paths.reset, model) - .then(response => { - return this.modelFactory(response.data) - }) - .finally(() => { - cancel() - }) + try { + const response = await this.http.post(this.paths.reset, model) + return this.modelFactory(response.data) + } finally { + cancel() + } } - requestResetPassword(model) { + async requestResetPassword(model) { const cancel = this.setLoading() - return this.http.post(this.paths.requestReset, model) - .then(response => { - return this.modelFactory(response.data) - }) - .finally(() => { - cancel() - }) + try { + const response = await this.http.post(this.paths.requestReset, model) + return this.modelFactory(response.data) + } finally { + cancel() + } } } \ No newline at end of file diff --git a/src/store/modules/auth.js b/src/store/modules/auth.js index b7b9b652..2d5bbec8 100644 --- a/src/store/modules/auth.js +++ b/src/store/modules/auth.js @@ -78,7 +78,7 @@ export default { }, actions: { // Logs a user in with a set of credentials. - login(ctx, credentials) { + async login(ctx, credentials) { const HTTP = HTTPFactory() ctx.commit(LOADING, true, {root: true}) @@ -94,53 +94,51 @@ export default { data.totp_passcode = credentials.totpPasscode } - return HTTP.post('login', data) - .then(response => { - // Save the token to local storage for later use - saveToken(response.data.token, true) + try { + const response = await HTTP.post('login', data) + // Save the token to local storage for later use + saveToken(response.data.token, true) + + // Tell others the user is autheticated + ctx.dispatch('checkAuth') + } catch(e) { + if ( + e.response && + e.response.data.code === 1017 && + !credentials.totpPasscode + ) { + ctx.commit('needsTotpPasscode', true) + } - // Tell others the user is autheticated - ctx.dispatch('checkAuth') - }) - .catch(e => { - if ( - e.response && - e.response.data.code === 1017 && - !credentials.totpPasscode - ) { - ctx.commit('needsTotpPasscode', true) - } - - throw e - }) - .finally(() => { - ctx.commit(LOADING, false, {root: true}) - }) + throw e + } finally { + ctx.commit(LOADING, false, {root: true}) + } }, + // Registers a new user and logs them in. // Not sure if this is the right place to put the logic in, maybe a seperate js component would be better suited. - register(ctx, credentials) { + async register(ctx, credentials) { const HTTP = HTTPFactory() - return HTTP.post('register', { - username: credentials.username, - email: credentials.email, - password: credentials.password, - }) - .then(() => { - return ctx.dispatch('login', credentials) + try { + await HTTP.post('register', { + username: credentials.username, + email: credentials.email, + password: credentials.password, }) - .catch(e => { - if (e.response && e.response.data && e.response.data.message) { - ctx.commit(ERROR_MESSAGE, e.response.data.message, {root: true}) - } + return ctx.dispatch('login', credentials) + } catch(e) { + if (e.response && e.response.data && e.response.data.message) { + ctx.commit(ERROR_MESSAGE, e.response.data.message, {root: true}) + } - throw e - }) - .finally(() => { - ctx.commit(LOADING, false, {root: true}) - }) + throw e + } finally { + ctx.commit(LOADING, false, {root: true}) + } }, - openIdAuth(ctx, {provider, code}) { + + async openIdAuth(ctx, {provider, code}) { const HTTP = HTTPFactory() ctx.commit(LOADING, true, {root: true}) @@ -150,29 +148,28 @@ export default { // Delete an eventually preexisting old token removeToken() - return HTTP.post(`/auth/openid/${provider}/callback`, data) - .then(response => { - // Save the token to local storage for later use - saveToken(response.data.token, true) - - // Tell others the user is autheticated - ctx.dispatch('checkAuth') - }) - .finally(() => { - ctx.commit(LOADING, false, {root: true}) - }) + try { + const response = await HTTP.post(`/auth/openid/${provider}/callback`, data) + // Save the token to local storage for later use + saveToken(response.data.token, true) + + // Tell others the user is autheticated + ctx.dispatch('checkAuth') + } finally { + ctx.commit(LOADING, false, {root: true}) + } }, - linkShareAuth(ctx, {hash, password}) { + + async linkShareAuth(ctx, {hash, password}) { const HTTP = HTTPFactory() - return HTTP.post('/shares/' + hash + '/auth', { + const response = await HTTP.post('/shares/' + hash + '/auth', { password: password, }) - .then(r => { - saveToken(r.data.token, false) - ctx.dispatch('checkAuth') - return r.data - }) + saveToken(response.data.token, false) + ctx.dispatch('checkAuth') + return response.data }, + // Populates user information from jwt token saved in local storage in store checkAuth(ctx) { @@ -205,54 +202,54 @@ export default { ctx.dispatch('config/redirectToProviderIfNothingElseIsEnabled', null, {root: true}) } }, - refreshUserInfo(ctx) { + + async refreshUserInfo(ctx) { const jwt = getToken() if (!jwt) { return } const HTTP = HTTPFactory() - // We're not returning the promise here to prevent blocking the initial ui render if the user is - // accessing the site with a token in local storage - HTTP.get('user', { - headers: { - Authorization: `Bearer ${jwt}`, - }, - }) - .then(r => { - const info = new UserModel(r.data) - info.type = ctx.state.info.type - info.email = ctx.state.info.email - info.exp = ctx.state.info.exp + try { - ctx.commit('info', info) - ctx.commit('lastUserRefresh') - }) - .catch(e => { - throw new Error('Error while refreshing user info:', { cause: e }) + const response = await HTTP.get('user', { + headers: { + Authorization: `Bearer ${jwt}`, + }, }) + const info = new UserModel(response.data) + info.type = ctx.state.info.type + info.email = ctx.state.info.email + info.exp = ctx.state.info.exp + + ctx.commit('info', info) + ctx.commit('lastUserRefresh') + return info + } catch(e) { + throw new Error('Error while refreshing user info:', { cause: e }) + } }, + // Renews the api token and saves it to local storage renewToken(ctx) { - // Timeout to avoid race conditions when authenticated as a user (=auth token in localStorage) and as a + // FIXME: Timeout to avoid race conditions when authenticated as a user (=auth token in localStorage) and as a // link share in another tab. Without the timeout both the token renew and link share auth are executed at // the same time and one might win over the other. - setTimeout(() => { + setTimeout(async () => { if (!ctx.state.authenticated) { return } - refreshToken(!ctx.state.isLinkShareAuth) - .then(() => { - ctx.dispatch('checkAuth') - }) - .catch(e => { - // Don't logout on network errors as the user would then get logged out if they don't have - // internet for a short period of time - such as when the laptop is still reconnecting - if (e.request.status) { - ctx.dispatch('logout') - } - }) + try { + await refreshToken(!ctx.state.isLinkShareAuth) + ctx.dispatch('checkAuth') + } catch(e) { + // Don't logout on network errors as the user would then get logged out if they don't have + // internet for a short period of time - such as when the laptop is still reconnecting + if (e.request.status) { + ctx.dispatch('logout') + } + } }, 5000) }, logout(ctx) { diff --git a/src/store/modules/config.js b/src/store/modules/config.js index 2e929711..e28a6b46 100644 --- a/src/store/modules/config.js +++ b/src/store/modules/config.js @@ -60,15 +60,14 @@ export default { }, }, actions: { - update(ctx) { + async update(ctx) { const HTTP = HTTPFactory() - return HTTP.get('info') - .then(r => { - ctx.commit(CONFIG, r.data) - return r - }) + const { data: info } = await HTTP.get('info') + ctx.commit(CONFIG, info) + return info }, + redirectToProviderIfNothingElseIsEnabled(ctx) { if (ctx.state.auth.local.enabled === false && ctx.state.auth.openidConnect.enabled && diff --git a/src/store/modules/kanban.js b/src/store/modules/kanban.js index e01a83fa..acd6824e 100644 --- a/src/store/modules/kanban.js +++ b/src/store/modules/kanban.js @@ -209,7 +209,7 @@ export default { }, actions: { - loadBucketsForList(ctx, {listId, params}) { + async loadBucketsForList(ctx, {listId, params}) { const cancel = setLoading(ctx, 'kanban') // Clear everything to prevent having old buckets in the list if loading the buckets from this list takes a few moments @@ -218,13 +218,14 @@ export default { params.per_page = TASKS_PER_BUCKET const bucketService = new BucketService() - return bucketService.getAll({listId: listId}, params) - .then(r => { - ctx.commit('setBuckets', r) - ctx.commit('setListId', listId) - return r - }) - .finally(() => cancel()) + try { + const response = await bucketService.getAll({listId: listId}, params) + ctx.commit('setBuckets', response) + ctx.commit('setListId', listId) + return response + } finally { + cancel() + } }, async loadNextTasksForBucket(ctx, {listId, ps = {}, bucketId}) { @@ -270,48 +271,50 @@ export default { params.per_page = TASKS_PER_BUCKET const taskService = new TaskCollectionService() - return taskService.getAll({listId: listId}, params, page) - .then(r => { - ctx.commit('addTasksToBucket', {tasks: r, bucketId: bucketId}) - ctx.commit('setTasksLoadedForBucketPage', {bucketId, page}) - if (taskService.totalPages <= page) { - ctx.commit('setAllTasksLoadedForBucket', bucketId) - } - return r - }) - .finally(() => { - cancel() - ctx.commit('setBucketLoading', {bucketId: bucketId, loading: false}) - }) + try { + + const tasks = await taskService.getAll({listId: listId}, params, page) + ctx.commit('addTasksToBucket', {tasks, bucketId: bucketId}) + ctx.commit('setTasksLoadedForBucketPage', {bucketId, page}) + if (taskService.totalPages <= page) { + ctx.commit('setAllTasksLoadedForBucket', bucketId) + } + return tasks + } finally { + cancel() + ctx.commit('setBucketLoading', {bucketId: bucketId, loading: false}) + } }, - createBucket(ctx, bucket) { + async createBucket(ctx, bucket) { const cancel = setLoading(ctx, 'kanban') const bucketService = new BucketService() - return bucketService.create(bucket) - .then(r => { - ctx.commit('addBucket', r) - return r - }) - .finally(() => cancel()) + try { + const createdBucket = await bucketService.create(bucket) + ctx.commit('addBucket', createdBucket) + return createdBucket + } finally { + cancel() + } }, - deleteBucket(ctx, {bucket, params}) { + async deleteBucket(ctx, {bucket, params}) { const cancel = setLoading(ctx, 'kanban') const bucketService = new BucketService() - return bucketService.delete(bucket) - .then(r => { - ctx.commit('removeBucket', bucket) - // We reload all buckets because tasks are being moved from the deleted bucket - ctx.dispatch('loadBucketsForList', {listId: bucket.listId, params: params}) - return r - }) - .finally(() => cancel()) + try { + const response = await bucketService.delete(bucket) + ctx.commit('removeBucket', bucket) + // We reload all buckets because tasks are being moved from the deleted bucket + ctx.dispatch('loadBucketsForList', {listId: bucket.listId, params: params}) + return response + } finally { + cancel() + } }, - updateBucket(ctx, updatedBucketData) { + async updateBucket(ctx, updatedBucketData) { const cancel = setLoading(ctx, 'kanban') const bucketIndex = findIndexById(ctx.state.buckets, updatedBucketData.id) @@ -324,22 +327,22 @@ export default { ctx.commit('setBucketByIndex', {bucketIndex, bucket: updatedBucket}) - const bucketService = new BucketService() - return bucketService.update(updatedBucket) - .then(r => { - ctx.commit('setBucketByIndex', {bucketIndex, bucket: r}) - Promise.resolve(r) - }) - .catch(e => { - // restore original state - ctx.commit('setBucketByIndex', {bucketIndex, bucket: oldBucket}) + const bucketService = new BucketService() + try { + const returnedBucket = await bucketService.update(updatedBucket) + ctx.commit('setBucketByIndex', {bucketIndex, bucket: returnedBucket}) + return returnedBucket + } catch(e) { + // restore original state + ctx.commit('setBucketByIndex', {bucketIndex, bucket: oldBucket}) - throw e - }) - .finally(() => cancel()) + throw e + } finally { + cancel() + } }, - updateBucketTitle(ctx, { id, title }) { + async updateBucketTitle(ctx, { id, title }) { const bucket = findById(ctx.state.buckets, id) if (bucket.title === title) { @@ -352,9 +355,8 @@ export default { title, } - ctx.dispatch('updateBucket', updatedBucketData).then(() => { - success({message: i18n.global.t('list.kanban.bucketTitleSavedSuccess')}) - }) + await ctx.dispatch('updateBucket', updatedBucketData) + success({message: i18n.global.t('list.kanban.bucketTitleSavedSuccess')}) }, }, } \ No newline at end of file diff --git a/src/store/modules/lists.js b/src/store/modules/lists.js index e7058835..f5aa37b7 100644 --- a/src/store/modules/lists.js +++ b/src/store/modules/lists.js @@ -42,65 +42,70 @@ export default { isFavorite: !list.isFavorite, }) }, - createList(ctx, list) { + + async createList(ctx, list) { const cancel = setLoading(ctx, 'lists') const listService = new ListService() - return listService.create(list) - .then(r => { - r.namespaceId = list.namespaceId - ctx.commit('namespaces/addListToNamespace', r, {root: true}) - ctx.commit('setList', r) - return r - }) - .finally(() => cancel()) + try { + const createdList = await listService.create(list) + createdList.namespaceId = list.namespaceId + ctx.commit('namespaces/addListToNamespace', createdList, {root: true}) + ctx.commit('setList', createdList) + return createdList + } finally { + cancel() + } }, - updateList(ctx, list) { + + async updateList(ctx, list) { const cancel = setLoading(ctx, 'lists') const listService = new ListService() - return listService.update(list) - .then(() => { - ctx.commit('setList', list) - ctx.commit('namespaces/setListInNamespaceById', list, {root: true}) - - // the returned list from listService.update is the same! - // in order to not validate vuex mutations we have to create a new copy - const newList = { - ...list, - namespaceId: FavoriteListsNamespace, - } - if (list.isFavorite) { - ctx.commit('namespaces/addListToNamespace', newList, {root: true}) - } else { - ctx.commit('namespaces/removeListFromNamespaceById', newList, {root: true}) - } - ctx.dispatch('namespaces/loadNamespacesIfFavoritesDontExist', null, {root: true}) - ctx.dispatch('namespaces/removeFavoritesNamespaceIfEmpty', null, {root: true}) - return newList + try { + await listService.update(list) + ctx.commit('setList', list) + ctx.commit('namespaces/setListInNamespaceById', list, {root: true}) + + // the returned list from listService.update is the same! + // in order to not validate vuex mutations we have to create a new copy + const newList = { + ...list, + namespaceId: FavoriteListsNamespace, + } + if (list.isFavorite) { + ctx.commit('namespaces/addListToNamespace', newList, {root: true}) + } else { + ctx.commit('namespaces/removeListFromNamespaceById', newList, {root: true}) + } + ctx.dispatch('namespaces/loadNamespacesIfFavoritesDontExist', null, {root: true}) + ctx.dispatch('namespaces/removeFavoritesNamespaceIfEmpty', null, {root: true}) + return newList + } catch(e) { + // Reset the list state to the initial one to avoid confusion for the user + ctx.commit('setList', { + ...list, + isFavorite: !list.isFavorite, }) - .catch(e => { - // Reset the list state to the initial one to avoid confusion for the user - ctx.commit('setList', { - ...list, - isFavorite: !list.isFavorite, - }) - throw e - }) - .finally(() => cancel()) + throw e + } finally { + cancel() + } }, - deleteList(ctx, list) { + + async deleteList(ctx, list) { const cancel = setLoading(ctx, 'lists') const listService = new ListService() - return listService.delete(list) - .then(r => { - ctx.commit('removeListById', list) - ctx.commit('namespaces/removeListFromNamespaceById', list, {root: true}) - removeListFromHistory({id: list.id}) - return r - }) - .finally(() => cancel()) + try { + const response = await listService.delete(list) + ctx.commit('removeListById', list) + ctx.commit('namespaces/removeListFromNamespaceById', list, {root: true}) + removeListFromHistory({id: list.id}) + return response + } finally{ + cancel() + } }, }, } \ No newline at end of file diff --git a/src/store/modules/namespaces.js b/src/store/modules/namespaces.js index b27225d6..cb78f560 100644 --- a/src/store/modules/namespaces.js +++ b/src/store/modules/namespaces.js @@ -94,63 +94,63 @@ export default { }, }, actions: { - loadNamespaces(ctx) { + async loadNamespaces(ctx) { const cancel = setLoading(ctx, 'namespaces') const namespaceService = new NamespaceService() - // We always load all namespaces and filter them on the frontend - return namespaceService.getAll({}, {is_archived: true}) - .then(r => { - ctx.commit('namespaces', r) - - // Put all lists in the list state - const lists = [] - r.forEach(n => { - n.lists.forEach(l => { - lists.push(l) - }) - }) - - ctx.commit('lists/setLists', lists, {root: true}) - - return r - }) - .finally(() => { - cancel() - }) + try { + // We always load all namespaces and filter them on the frontend + const namespaces = await namespaceService.getAll({}, {is_archived: true}) + ctx.commit('namespaces', namespaces) + + // Put all lists in the list state + const lists = namespaces.flatMap(({lists}) => lists) + + ctx.commit('lists/setLists', lists, {root: true}) + + return namespaces + } finally { + cancel() + } }, + loadNamespacesIfFavoritesDontExist(ctx) { // The first namespace should be the one holding all favorites if (ctx.state.namespaces[0].id !== -2) { return ctx.dispatch('loadNamespaces') } }, + removeFavoritesNamespaceIfEmpty(ctx) { if (ctx.state.namespaces[0].id === -2 && ctx.state.namespaces[0].lists.length === 0) { ctx.state.namespaces.splice(0, 1) } }, - deleteNamespace(ctx, namespace) { + + async deleteNamespace(ctx, namespace) { const cancel = setLoading(ctx, 'namespaces') const namespaceService = new NamespaceService() - return namespaceService.delete(namespace) - .then(r => { - ctx.commit('removeNamespaceById', namespace.id) - return r - }) - .finally(() => cancel()) + try { + const response = await namespaceService.delete(namespace) + ctx.commit('removeNamespaceById', namespace.id) + return response + } finally { + cancel() + } }, - createNamespace(ctx, namespace) { + + async createNamespace(ctx, namespace) { const cancel = setLoading(ctx, 'namespaces') const namespaceService = new NamespaceService() - return namespaceService.create(namespace) - .then(r => { - ctx.commit('addNamespace', r) - return r - }) - .finally(() => cancel()) + try { + const createdNamespace = await namespaceService.create(namespace) + ctx.commit('addNamespace', createdNamespace) + return createdNamespace + } finally { + cancel() + } }, }, } \ No newline at end of file diff --git a/src/store/modules/tasks.js b/src/store/modules/tasks.js index 29b1271f..d9ca0317 100644 --- a/src/store/modules/tasks.js +++ b/src/store/modules/tasks.js @@ -34,17 +34,15 @@ function validateLabel(labels, label) { return findPropertyByValue(labels, 'title', label) } -function addLabelToTask(task, label) { +async function addLabelToTask(task, label) { const labelTask = new LabelTask({ taskId: task.id, labelId: label.id, }) const labelTaskService = new LabelTaskService() - return labelTaskService.create(labelTask) - .then(result => { - task.labels.push(label) - return result - }) + const response = await labelTaskService.create(labelTask) + task.labels.push(label) + return response } async function findAssignees(parsedTaskAssignees) { @@ -67,41 +65,39 @@ export default { namespaced: true, state: () => ({}), actions: { - loadTasks(ctx, params) { + async loadTasks(ctx, params) { const taskService = new TaskService() const cancel = setLoading(ctx, 'tasks') - return taskService.getAll({}, params) - .then(r => { - ctx.commit(HAS_TASKS, r.length > 0, {root: true}) - return r - }) - .finally(() => { - cancel() - }) - + try { + const tasks = await taskService.getAll({}, params) + ctx.commit(HAS_TASKS, tasks.length > 0, {root: true}) + return tasks + } finally { + cancel() + } }, - update(ctx, task) { + + async update(ctx, task) { const cancel = setLoading(ctx, 'tasks') const taskService = new TaskService() - return taskService.update(task) - .then(t => { - ctx.commit('kanban/setTaskInBucket', t, {root: true}) - return t - }) - .finally(() => { - cancel() - }) + try { + const updatedTask = await taskService.update(task) + ctx.commit('kanban/setTaskInBucket', updatedTask, {root: true}) + return updatedTask + } finally { + cancel() + } }, - delete(ctx, task) { + + async delete(ctx, task) { const taskService = new TaskService() - return taskService.delete(task) - .then(t => { - ctx.commit('kanban/removeTaskInBucket', task, {root: true}) - return t - }) + const response = await taskService.delete(task) + ctx.commit('kanban/removeTaskInBucket', task, {root: true}) + return response }, + // Adds a task attachment in store. // This is an action to be able to commit other mutations addTaskAttachment(ctx, {taskId, attachment}) { @@ -124,106 +120,97 @@ export default { ctx.commit('attachments/add', attachment, {root: true}) }, - addAssignee(ctx, {user, taskId}) { + async addAssignee(ctx, {user, taskId}) { const taskAssignee = new TaskAssigneeModel({userId: user.id, taskId: taskId}) const taskAssigneeService = new TaskAssigneeService() - return taskAssigneeService.create(taskAssignee) - .then(r => { - const t = ctx.rootGetters['kanban/getTaskById'](taskId) - if (t.task === null) { - // Don't try further adding a label if the task is not in kanban - // Usually this means the kanban board hasn't been accessed until now. - // Vuex seems to have its difficulties with that, so we just log the error and fail silently. - console.debug('Could not add assignee to task in kanban, task not found', t) - return r - } - // FIXME: direct store manipulation (task) - t.task.assignees.push(user) - ctx.commit('kanban/setTaskInBucketByIndex', t, {root: true}) - return r - }) + const r = await taskAssigneeService.create(taskAssignee) + const t = ctx.rootGetters['kanban/getTaskById'](taskId) + if (t.task === null) { + // Don't try further adding a label if the task is not in kanban + // Usually this means the kanban board hasn't been accessed until now. + // Vuex seems to have its difficulties with that, so we just log the error and fail silently. + console.debug('Could not add assignee to task in kanban, task not found', t) + return r + } + // FIXME: direct store manipulation (task) + t.task.assignees.push(user) + ctx.commit('kanban/setTaskInBucketByIndex', t, { root: true }) + return r }, - removeAssignee(ctx, {user, taskId}) { + async removeAssignee(ctx, {user, taskId}) { const taskAssignee = new TaskAssigneeModel({userId: user.id, taskId: taskId}) const taskAssigneeService = new TaskAssigneeService() - return taskAssigneeService.delete(taskAssignee) - .then(r => { - const t = ctx.rootGetters['kanban/getTaskById'](taskId) - if (t.task === null) { - // Don't try further adding a label if the task is not in kanban - // Usually this means the kanban board hasn't been accessed until now. - // Vuex seems to have its difficulties with that, so we just log the error and fail silently. - console.debug('Could not remove assignee from task in kanban, task not found', t) - return r - } + const response = await taskAssigneeService.delete(taskAssignee) + const t = ctx.rootGetters['kanban/getTaskById'](taskId) + if (t.task === null) { + // Don't try further adding a label if the task is not in kanban + // Usually this means the kanban board hasn't been accessed until now. + // Vuex seems to have its difficulties with that, so we just log the error and fail silently. + console.debug('Could not remove assignee from task in kanban, task not found', t) + return response + } - for (const a in t.task.assignees) { - if (t.task.assignees[a].id === user.id) { - // FIXME: direct store manipulation (task) - t.task.assignees.splice(a, 1) - break - } - } - - ctx.commit('kanban/setTaskInBucketByIndex', t, {root: true}) - return r - }) - - }, - - addLabel(ctx, {label, taskId}) { - const labelTask = new LabelTaskModel({taskId: taskId, labelId: label.id}) - - const labelTaskService = new LabelTaskService() - return labelTaskService.create(labelTask) - .then(r => { - const t = ctx.rootGetters['kanban/getTaskById'](taskId) - if (t.task === null) { - // Don't try further adding a label if the task is not in kanban - // Usually this means the kanban board hasn't been accessed until now. - // Vuex seems to have its difficulties with that, so we just log the error and fail silently. - console.debug('Could not add label to task in kanban, task not found', t) - return r - } + for (const a in t.task.assignees) { + if (t.task.assignees[a].id === user.id) { // FIXME: direct store manipulation (task) - t.task.labels.push(label) - ctx.commit('kanban/setTaskInBucketByIndex', t, {root: true}) + t.task.assignees.splice(a, 1) + break + } + } + + ctx.commit('kanban/setTaskInBucketByIndex', t, {root: true}) + return response - return r - }) }, - removeLabel(ctx, {label, taskId}) { + async addLabel(ctx, {label, taskId}) { const labelTask = new LabelTaskModel({taskId: taskId, labelId: label.id}) const labelTaskService = new LabelTaskService() - return labelTaskService.delete(labelTask) - .then(r => { - const t = ctx.rootGetters['kanban/getTaskById'](taskId) - if (t.task === null) { - // Don't try further adding a label if the task is not in kanban - // Usually this means the kanban board hasn't been accessed until now. - // Vuex seems to have its difficulties with that, so we just log the error and fail silently. - console.debug('Could not remove label from task in kanban, task not found', t) - return r - } + const r = await labelTaskService.create(labelTask) + const t = ctx.rootGetters['kanban/getTaskById'](taskId) + if (t.task === null) { + // Don't try further adding a label if the task is not in kanban + // Usually this means the kanban board hasn't been accessed until now. + // Vuex seems to have its difficulties with that, so we just log the error and fail silently. + console.debug('Could not add label to task in kanban, task not found', t) + return r + } + // FIXME: direct store manipulation (task) + t.task.labels.push(label) + ctx.commit('kanban/setTaskInBucketByIndex', t, { root: true }) + return r + }, - // Remove the label from the list - for (const l in t.task.labels) { - if (t.task.labels[l].id === label.id) { - // FIXME: direct store manipulation (task) - t.task.labels.splice(l, 1) - break - } - } + async removeLabel(ctx, {label, taskId}) { + const labelTask = new LabelTaskModel({taskId: taskId, labelId: label.id}) - ctx.commit('kanban/setTaskInBucketByIndex', t, {root: true}) + const labelTaskService = new LabelTaskService() + const response = await labelTaskService.delete(labelTask) + const t = ctx.rootGetters['kanban/getTaskById'](taskId) + if (t.task === null) { + // Don't try further adding a label if the task is not in kanban + // Usually this means the kanban board hasn't been accessed until now. + // Vuex seems to have its difficulties with that, so we just log the error and fail silently. + console.debug('Could not remove label from task in kanban, task not found', t) + return response + } - return r - }) + // Remove the label from the list + for (const l in t.task.labels) { + if (t.task.labels[l].id === label.id) { + // FIXME: direct store manipulation (task) + t.task.labels.splice(l, 1) + break + } + } + + ctx.commit('kanban/setTaskInBucketByIndex', t, {root: true}) + + return response }, // Do everything that is involved in finding, creating and adding the label to the task @@ -308,11 +295,11 @@ export default { }) const taskService = new TaskService() - return taskService.create(task) - .then(task => dispatch('addLabelsToTask', { - task, - parsedLabels:parsedTask.labels, - })) + const createdTask = await taskService.create(task) + return dispatch('addLabelsToTask', { + task: createdTask, + parsedLabels:parsedTask.labels, + }) }, }, } \ No newline at end of file diff --git a/src/views/Home.vue b/src/views/Home.vue index 7bf6a9e6..d5231bd9 100644 --- a/src/views/Home.vue +++ b/src/views/Home.vue @@ -1,6 +1,6 @@