1
0

feat: use async / await where it makes sense

This commit is contained in:
Dominik Pschenitschni
2021-10-11 19:37:20 +02:00
parent a776e1d2f3
commit bb94c1ba3a
74 changed files with 1458 additions and 1662 deletions

View File

@ -1,6 +1,6 @@
<template>
<div class="content has-text-centered">
<h2>
<h2 v-if="userInfo">
{{ $t(`home.welcome${welcome}`, {username: userInfo.name !== '' ? userInfo.name : userInfo.username}) }}!
</h2>
<div class="notification is-danger" v-if="deletionScheduledAt !== null">

View File

@ -93,13 +93,11 @@ export default {
this.$nextTick(() => this.editorActive = true)
},
methods: {
create() {
async create() {
this.savedFilter.filters = this.filters
this.savedFilterService.create(this.savedFilter)
.then(r => {
this.$store.dispatch('namespaces/loadNamespaces')
this.$router.push({name: 'list.index', params: {listId: r.getListId()}})
})
const savedFilter = await this.savedFilterService.create(this.savedFilter)
await this.$store.dispatch('namespaces/loadNamespaces')
this.$router.push({name: 'list.index', params: {listId: savedFilter.getListId()}})
},
},
}

View File

@ -24,17 +24,15 @@ export default {
}
},
methods: {
deleteSavedFilter() {
async deleteSavedFilter() {
// We assume the listId in the route is the pseudolist
const list = new ListModel({id: this.$route.params.listId})
const filter = new SavedFilterModel({id: list.getSavedFilterId()})
this.filterService.delete(filter)
.then(() => {
this.$store.dispatch('namespaces/loadNamespaces')
this.$message.success({message: this.$t('filters.delete.success')})
this.$router.push({name: 'namespaces.index'})
})
await this.filterService.delete(filter)
await this.$store.dispatch('namespaces/loadNamespaces')
this.$message.success({message: this.$t('filters.delete.success')})
this.$router.push({name: 'namespaces.index'})
},
},
}

View File

@ -95,27 +95,22 @@ export default {
},
},
methods: {
loadSavedFilter() {
async loadSavedFilter() {
// We assume the listId in the route is the pseudolist
const list = new ListModel({id: this.$route.params.listId})
this.filter = new SavedFilterModel({id: list.getSavedFilterId()})
this.filterService.get(this.filter)
.then(r => {
this.filter = r
this.filters = objectToSnakeCase(this.filter.filters)
})
this.filter = await this.filterService.get(this.filter)
this.filters = objectToSnakeCase(this.filter.filters)
},
save() {
async save() {
this.filter.filters = this.filters
this.filterService.update(this.filter)
.then(r => {
this.$store.dispatch('namespaces/loadNamespaces')
this.$message.success({message: this.$t('filters.edit.success')})
this.filter = r
this.filters = objectToSnakeCase(this.filter.filters)
this.$router.back()
})
const filter = await this.filterService.update(this.filter)
await this.$store.dispatch('namespaces/loadNamespaces')
this.$message.success({message: this.$t('filters.edit.success')})
this.filter = filter
this.filters = objectToSnakeCase(this.filter.filters)
this.$router.back()
},
},
}

View File

@ -60,21 +60,19 @@ export default {
loading: state => state[LOADING] && state[LOADING_MODULE] === 'labels',
}),
methods: {
newLabel() {
async newLabel() {
if (this.label.title === '') {
this.showError = true
return
}
this.showError = false
this.$store.dispatch('labels/createLabel', this.label)
.then(r => {
this.$router.push({
name: 'labels.index',
params: {id: r.id},
})
this.$message.success({message: this.$t('label.create.success')})
})
const label = this.$store.dispatch('labels/createLabel', this.label)
this.$router.push({
name: 'labels.index',
params: {id: label.id},
})
this.$message.success({message: this.$t('label.create.success')})
},
},
}

View File

@ -54,7 +54,7 @@ export default {
this.setTitle(this.$t('list.create.header'))
},
methods: {
newList() {
async newList() {
if (this.list.title === '') {
this.showError = true
return
@ -62,15 +62,12 @@ export default {
this.showError = false
this.list.namespaceId = parseInt(this.$route.params.id)
this.$store
.dispatch('lists/createList', this.list)
.then((r) => {
this.$message.success({message: this.$t('list.create.createdSuccess') })
this.$router.push({
name: 'list.index',
params: { listId: r.id },
})
})
const list = await this.$store.dispatch('lists/createList', this.list)
this.$message.success({message: this.$t('list.create.createdSuccess') })
this.$router.push({
name: 'list.index',
params: { listId: list.id },
})
},
},
}

View File

@ -83,7 +83,8 @@ export default {
this.$router.replace({name: savedListView, params: {id: this.$route.params.listId}})
console.debug('Replaced list view with', savedListView)
},
loadList() {
async loadList() {
if (this.$route.name.includes('.settings.')) {
return
}
@ -139,14 +140,13 @@ export default {
// We create an extra list object instead of creating it in this.list because that would trigger a ui update which would result in bad ux.
const list = new ListModel(listData)
this.listService.get(list)
.then(r => {
this.$store.dispatch(CURRENT_LIST, r)
this.setTitle(this.getListTitle(r))
})
.finally(() => {
this.listLoaded = this.$route.params.listId
})
try {
const loadedList = await this.listService.get(list)
this.$store.commit(CURRENT_LIST, loadedList)
this.setTitle(this.getListTitle(loadedList))
} finally {
this.listLoaded = this.$route.params.listId
}
},
},
}

View File

@ -30,21 +30,20 @@ export default {
},
},
methods: {
archiveList() {
async archiveList() {
const newList = {
...this.list,
isArchived: !this.list.isArchived,
}
this.listService.update(newList)
.then(r => {
this.$store.commit('currentList', r)
this.$store.commit('namespaces/setListInNamespaceById', r)
this.$message.success({message: this.$t('list.archive.success')})
})
.finally(() => {
this.$router.back()
})
try {
const list = await this.listService.update(newList)
this.$store.commit('currentList', list)
this.$store.commit('namespaces/setListInNamespaceById', list)
this.$message.success({message: this.$t('list.archive.success')})
} finally {
this.$router.back()
}
},
},
}

View File

@ -71,6 +71,10 @@ import ListService from '@/services/list'
import {CURRENT_LIST} from '@/store/mutation-types'
import CreateEdit from '@/components/misc/create-edit.vue'
function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
export default {
name: 'list-setting-background',
components: {CreateEdit},
@ -108,61 +112,53 @@ export default {
this.backgroundThumbs = {}
this.searchBackgrounds()
},
searchBackgrounds(page = 1) {
async searchBackgrounds(page = 1) {
if (this.backgroundSearchTimeout !== null) {
clearTimeout(this.backgroundSearchTimeout)
}
// We're using the timeout to not search on every keypress but with a 300ms delay.
// TODO: use throttle
// FIXME: We're using the timeout to not search on every keypress but with a 300ms delay.
// If another key is pressed within these 300ms, the last search request is dropped and a new one is scheduled.
this.backgroundSearchTimeout = setTimeout(() => {
this.currentPage = page
this.backgroundService.getAll({}, {s: this.backgroundSearchTerm, p: page})
.then(r => {
this.backgroundSearchResult = this.backgroundSearchResult.concat(r)
r.forEach(b => {
this.backgroundService.thumb(b)
.then(t => {
this.backgroundThumbs[b.id] = t
})
})
})
}, 300)
this.backgroundSearchTimeout = await timeout(300)
this.currentPage = page
const r = await this.backgroundService.getAll({}, {s: this.backgroundSearchTerm, p: page})
this.backgroundSearchResult = this.backgroundSearchResult.concat(r)
r.forEach(async b => {
this.backgroundThumbs[b.id] = await this.backgroundService.thumb(b)
})
},
setBackground(backgroundId) {
async setBackground(backgroundId) {
// Don't set a background if we're in the process of setting one
if (this.backgroundService.loading) {
return
}
this.backgroundService.update({id: backgroundId, listId: this.$route.params.listId})
.then(l => {
this.$store.commit(CURRENT_LIST, l)
this.$store.commit('namespaces/setListInNamespaceById', l)
this.$message.success({message: this.$t('list.background.success')})
})
const list = await this.backgroundService.update({id: backgroundId, listId: this.$route.params.listId})
this.$store.commit(CURRENT_LIST, list)
this.$store.commit('namespaces/setListInNamespaceById', list)
this.$message.success({message: this.$t('list.background.success')})
},
uploadBackground() {
async uploadBackground() {
if (this.$refs.backgroundUploadInput.files.length === 0) {
return
}
this.backgroundUploadService.create(this.$route.params.listId, this.$refs.backgroundUploadInput.files[0])
.then(l => {
this.$store.commit(CURRENT_LIST, l)
this.$store.commit('namespaces/setListInNamespaceById', l)
this.$message.success({message: this.$t('list.background.success')})
})
const list = await this.backgroundUploadService.create(this.$route.params.listId, this.$refs.backgroundUploadInput.files[0])
this.$store.commit(CURRENT_LIST, list)
this.$store.commit('namespaces/setListInNamespaceById', list)
this.$message.success({message: this.$t('list.background.success')})
},
removeBackground() {
this.listService.removeBackground(this.currentList)
.then(l => {
this.$store.commit(CURRENT_LIST, l)
this.$store.commit('namespaces/setListInNamespaceById', l)
this.$message.success({message: this.$t('list.background.removeSuccess')})
this.$router.back()
})
async removeBackground() {
const list = await this.listService.removeBackground(this.currentList)
this.$store.commit(CURRENT_LIST, list)
this.$store.commit('namespaces/setListInNamespaceById', list)
this.$message.success({message: this.$t('list.background.removeSuccess')})
this.$router.back()
},
},
}

View File

@ -24,12 +24,10 @@ export default {
},
},
methods: {
deleteList() {
this.$store.dispatch('lists/deleteList', this.list)
.then(() => {
this.$message.success({message: this.$t('list.delete.success')})
this.$router.push({name: 'home'})
})
async deleteList() {
await this.$store.dispatch('lists/deleteList', this.list)
this.$message.success({message: this.$t('list.delete.success')})
this.$router.push({name: 'home'})
},
},
}

View File

@ -38,18 +38,17 @@ export default {
selectNamespace(namespace) {
this.selectedNamespace = namespace
},
duplicateList() {
async duplicateList() {
const listDuplicate = new ListDuplicateModel({
listId: this.$route.params.listId,
namespaceId: this.selectedNamespace.id,
})
this.listDuplicateService.create(listDuplicate)
.then(r => {
this.$store.commit('namespaces/addListToNamespace', r.list)
this.$store.commit('lists/setList', r.list)
this.$message.success({message: this.$t('list.duplicate.success')})
this.$router.push({name: 'list.index', params: {listId: r.list.id}})
})
const duplicate = await this.listDuplicateService.create(listDuplicate)
this.$store.commit('namespaces/addListToNamespace', duplicate.list)
this.$store.commit('lists/setList', duplicate.list)
this.$message.success({message: this.$t('list.duplicate.success')})
this.$router.push({name: 'list.index', params: {listId: duplicate.list.id}})
},
},
}

View File

@ -94,22 +94,19 @@ export default {
},
},
methods: {
loadList() {
async loadList() {
const list = new ListModel({id: this.$route.params.listId})
this.listService.get(list)
.then(r => {
this.list = { ...r }
})
const loadedList = await this.listService.get(list)
this.list = { ...loadedList }
},
save() {
this.$store.dispatch('lists/updateList', this.list)
.then(() => {
this.$store.commit(CURRENT_LIST, this.list)
this.setTitle(this.$t('list.edit.title', {list: this.list.title}))
this.$message.success({message: this.$t('list.edit.success')})
this.$router.back()
})
async save() {
await this.$store.dispatch('lists/updateList', this.list)
this.$store.commit(CURRENT_LIST, this.list)
this.setTitle(this.$t('list.edit.title', {list: this.list.title}))
this.$message.success({message: this.$t('list.edit.success')})
this.$router.back()
},
},
}

View File

@ -56,18 +56,15 @@ export default {
this.loadList()
},
methods: {
loadList() {
async loadList() {
const list = new ListModel({id: this.$route.params.listId})
this.listService.get(list)
.then(r => {
this.list = r
this.$store.commit(CURRENT_LIST, r)
// This will trigger the dynamic loading of components once we actually have all the data to pass to them
this.manageTeamsComponent = 'userTeam'
this.manageUsersComponent = 'userTeam'
this.setTitle(this.$t('list.share.title', {list: this.list.title}))
})
this.list = await this.listService.get(list)
this.$store.commit(CURRENT_LIST, this.list)
// This will trigger the dynamic loading of components once we actually have all the data to pass to them
this.manageTeamsComponent = 'userTeam'
this.manageUsersComponent = 'userTeam'
this.setTitle(this.$t('list.share.title', {list: this.list.title}))
},
},
}

View File

@ -408,7 +408,7 @@ export default {
this.$store.dispatch('kanban/updateBucket', newBucket)
},
updateTaskPosition(e) {
async updateTaskPosition(e) {
this.drag = false
// While we could just pass the bucket index in through the function call, this would not give us the
@ -427,34 +427,35 @@ export default {
kanbanPosition: calculateItemPosition(taskBefore !== null ? taskBefore.kanbanPosition : null, taskAfter !== null ? taskAfter.kanbanPosition : null),
}
this.$store.dispatch('tasks/update', newTask)
// .finally(() => {
this.taskUpdating[task.id] = false
this.oneTaskUpdating = false
// })
try {
await this.$store.dispatch('tasks/update', newTask)
} finally {
this.taskUpdating[task.id] = false
this.oneTaskUpdating = false
}
},
toggleShowNewTaskInput(bucketId) {
this.showNewTaskInput[bucketId] = !this.showNewTaskInput[bucketId]
},
addTaskToBucket(bucketId) {
async addTaskToBucket(bucketId) {
if (this.newTaskText === '') {
this.newTaskError[bucketId] = true
return
}
this.newTaskError[bucketId] = false
this.$store.dispatch('tasks/createNewTask', {
const task = await this.$store.dispatch('tasks/createNewTask', {
title: this.newTaskText,
bucketId,
listId: this.$route.params.listId,
})
.then(r => {
this.newTaskText = ''
this.$store.commit('kanban/addTaskToBucket', r)
this.scrollTaskContainerToBottom(bucketId)
})
this.newTaskText = ''
this.$store.commit('kanban/addTaskToBucket', task)
this.scrollTaskContainerToBottom(bucketId)
},
scrollTaskContainerToBottom(bucketId) {
const bucketEl = this.taskContainerRefs[bucketId]
if (!bucketEl) {
@ -462,7 +463,8 @@ export default {
}
bucketEl.scrollTop = bucketEl.scrollHeight
},
createNewBucket() {
async createNewBucket() {
if (this.newBucketTitle === '') {
return
}
@ -472,12 +474,11 @@ export default {
listId: parseInt(this.$route.params.listId),
})
this.$store.dispatch('kanban/createBucket', newBucket)
.then(() => {
this.newBucketTitle = ''
this.showNewBucketInput = false
})
await this.$store.dispatch('kanban/createBucket', newBucket)
this.newBucketTitle = ''
this.showNewBucketInput = false
},
deleteBucketModal(bucketId) {
if (this.buckets.length <= 1) {
return
@ -486,33 +487,39 @@ export default {
this.bucketToDelete = bucketId
this.showBucketDeleteModal = true
},
deleteBucket() {
this.$store.dispatch('kanban/deleteBucket', {bucket: {
async deleteBucket() {
const bucket = new BucketModel({
id: this.bucketToDelete,
listId: parseInt(this.$route.params.listId),
}, params: this.params})
.then(() => this.$message.success({message: this.$t('list.kanban.deleteBucketSuccess')}))
.finally(() => {
this.showBucketDeleteModal = false
})
try {
await this.$store.dispatch('kanban/deleteBucket', {
bucket,
params: this.params,
})
this.$message.success({message: this.$t('list.kanban.deleteBucketSuccess')})
} finally {
this.showBucketDeleteModal = false
}
},
focusBucketTitle(e) {
// This little helper allows us to drag a bucket around at the title without focusing on it right away.
this.bucketTitleEditable = true
this.$nextTick(() => e.target.focus())
},
saveBucketTitle(bucketId, bucketTitle) {
async saveBucketTitle(bucketId, bucketTitle) {
const updatedBucketData = {
id: bucketId,
title: bucketTitle,
}
this.$store.dispatch('kanban/updateBucketTitle', updatedBucketData)
.then(() => {
this.bucketTitleEditable = false
this.$message.success({message: this.$t('list.kanban.bucketTitleSavedSuccess')})
})
await this.$store.dispatch('kanban/updateBucketTitle', updatedBucketData)
this.bucketTitleEditable = false
this.$message.success({message: this.$t('list.kanban.bucketTitleSavedSuccess')})
},
updateBuckets(value) {
@ -535,7 +542,8 @@ export default {
this.$store.dispatch('kanban/updateBucket', updatedData)
},
setBucketLimit(bucketId, limit) {
async setBucketLimit(bucketId, limit) {
if (limit < 0) {
return
}
@ -545,27 +553,30 @@ export default {
limit,
}
this.$store.dispatch('kanban/updateBucket', newBucket)
.then(() => this.$message.success({message: this.$t('list.kanban.bucketLimitSavedSuccess')}))
await this.$store.dispatch('kanban/updateBucket', newBucket)
this.$message.success({message: this.$t('list.kanban.bucketLimitSavedSuccess')})
},
shouldAcceptDrop(bucket) {
return bucket.id === this.sourceBucket || // When dragging from a bucket who has its limit reached, dragging should still be possible
bucket.limit === 0 || // If there is no limit set, dragging & dropping should always work
bucket.tasks.length < bucket.limit // Disallow dropping to buckets which have their limit reached
},
dragstart(bucket) {
this.drag = true
this.sourceBucket = bucket.id
},
toggleDoneBucket(bucket) {
async toggleDoneBucket(bucket) {
const newBucket = {
...bucket,
isDoneBucket: !bucket.isDoneBucket,
}
this.$store.dispatch('kanban/updateBucket', newBucket)
.then(() => this.$message.success({message: this.$t('list.kanban.doneBucketSavedSuccess')}))
await this.$store.dispatch('kanban/updateBucket', newBucket)
this.$message.success({message: this.$t('list.kanban.doneBucketSavedSuccess')})
},
collapseBucket(bucket) {
this.collapsedBuckets[bucket.id] = true
saveCollapsedBucketState(this.$route.params.listId, this.collapsedBuckets)

View File

@ -291,7 +291,8 @@ export default {
}
sortTasks(this.tasks)
},
saveTaskPosition(e) {
async saveTaskPosition(e) {
this.drag = false
const task = this.tasks[e.newIndex]
@ -303,10 +304,8 @@ export default {
position: calculateItemPosition(taskBefore !== null ? taskBefore.position : null, taskAfter !== null ? taskAfter.position : null),
}
this.$store.dispatch('tasks/update', newTask)
.then(r => {
this.tasks[e.newIndex] = r
})
const updatedTask = await this.$store.dispatch('tasks/update', newTask)
this.tasks[e.newIndex] = updatedTask
},
},
}

View File

@ -63,20 +63,17 @@ export default {
this.setTitle(this.$t('namespace.create.title'))
},
methods: {
newNamespace() {
async newNamespace() {
if (this.namespace.title === '') {
this.showError = true
return
}
this.showError = false
this.namespaceService
.create(this.namespace)
.then((r) => {
this.$store.commit('namespaces/addNamespace', r)
this.$message.success({message: this.$t('namespace.create.success') })
this.$router.back()
})
const namespace = this.namespaceService.create(this.namespace)
this.$store.commit('namespaces/addNamespace', namespace)
this.$message.success({message: this.$t('namespace.create.success') })
this.$router.back()
},
},
}

View File

@ -23,6 +23,7 @@ export default {
title: '',
}
},
created() {
this.namespace = this.$store.getters['namespaces/getNamespaceById'](this.$route.params.id)
this.title = this.namespace.isArchived ?
@ -30,19 +31,18 @@ export default {
this.$t('namespace.archive.titleArchive', { namespace: this.namespace.title })
this.setTitle(this.title)
},
methods: {
archiveNamespace() {
methods: {
async archiveNamespace() {
this.namespace.isArchived = !this.namespace.isArchived
this.namespaceService.update(this.namespace)
.then(r => {
this.$store.commit('namespaces/setNamespaceById', r)
this.$message.success({message: this.$t('namespace.archive.success')})
})
.finally(() => {
this.$router.back()
})
try {
const namespace = await this.namespaceService.update(this.namespace)
this.$store.commit('namespaces/setNamespaceById', namespace)
this.$message.success({message: this.$t('namespace.archive.success')})
} finally {
this.$router.back()
}
},
},
}

View File

@ -35,12 +35,10 @@ export default {
},
},
methods: {
deleteNamespace() {
this.$store.dispatch('namespaces/deleteNamespace', this.namespace)
.then(() => {
this.$message.success({message: this.$t('namespace.delete.success')})
this.$router.push({name: 'home'})
})
async deleteNamespace() {
await this.$store.dispatch('namespaces/deleteNamespace', this.namespace)
this.$message.success({message: this.$t('namespace.delete.success')})
this.$router.push({name: 'home'})
},
},
}

View File

@ -93,8 +93,8 @@ export default {
},
},
methods: {
loadNamespace() {
// This makes the editor trigger its mounted function again which makes it forget every input
async loadNamespace() {
// HACK: This makes the editor trigger its mounted function again which makes it forget every input
// it currently has in its textarea. This is a counter-hack to a hack inside of vue-easymde
// which made it impossible to detect change from the outside. Therefore the component would
// not update if new content from the outside was made available.
@ -103,24 +103,20 @@ export default {
this.$nextTick(() => this.editorActive = true)
const namespace = new NamespaceModel({id: this.$route.params.id})
this.namespaceService.get(namespace)
.then(r => {
this.namespace = r
// This will trigger the dynamic loading of components once we actually have all the data to pass to them
this.manageTeamsComponent = 'manageSharing'
this.manageUsersComponent = 'manageSharing'
this.title = this.$t('namespace.edit.title', {namespace: r.title})
this.setTitle(this.title)
})
this.namespace = await this.namespaceService.get(namespace)
// This will trigger the dynamic loading of components once we actually have all the data to pass to them
this.manageTeamsComponent = 'manageSharing'
this.manageUsersComponent = 'manageSharing'
this.title = this.$t('namespace.edit.title', {namespace: this.namespace.title})
this.setTitle(this.title)
},
save() {
this.namespaceService.update(this.namespace)
.then(r => {
// Update the namespace in the parent
this.$store.commit('namespaces/setNamespaceById', r)
this.$message.success({message: this.$t('namespace.edit.success')})
this.$router.back()
})
async save() {
const namespace = await this.namespaceService.update(this.namespace)
// Update the namespace in the parent
this.$store.commit('namespaces/setNamespaceById', namespace)
this.$message.success({message: this.$t('namespace.edit.success')})
this.$router.back()
},
},
}

View File

@ -57,17 +57,14 @@ export default {
},
},
methods: {
loadNamespace() {
async loadNamespace() {
const namespace = new NamespaceModel({id: this.$route.params.id})
this.namespaceService.get(namespace)
.then(r => {
this.namespace = r
// This will trigger the dynamic loading of components once we actually have all the data to pass to them
this.manageTeamsComponent = 'manageSharing'
this.manageUsersComponent = 'manageSharing'
this.title = this.$t('namespace.share.title', { namespace: this.namespace.title })
this.setTitle(this.title)
})
this.namespace = await this.namespaceService.get(namespace)
// This will trigger the dynamic loading of components once we actually have all the data to pass to them
this.manageTeamsComponent = 'manageSharing'
this.manageUsersComponent = 'manageSharing'
this.title = this.$t('namespace.share.title', { namespace: this.namespace.title })
this.setTitle(this.title)
},
},
}

View File

@ -57,7 +57,7 @@ export default {
'authLinkShare',
]),
methods: {
auth() {
async auth() {
this.errorMessage = ''
if (this.authLinkShare) {
@ -66,29 +66,30 @@ export default {
this.loading = true
this.$store.dispatch('auth/linkShareAuth', {hash: this.$route.params.share, password: this.password})
.then((r) => {
this.$router.push({name: 'list.list', params: {listId: r.list_id}})
try {
const r = await this.$store.dispatch('auth/linkShareAuth', {
hash: this.$route.params.share,
password: this.password,
})
.catch(e => {
if (typeof e.response.data.code !== 'undefined' && e.response.data.code === 13001) {
this.authenticateWithPassword = true
return
}
this.$router.push({name: 'list.list', params: {listId: r.list_id}})
} catch(e) {
if (typeof e.response.data.code !== 'undefined' && e.response.data.code === 13001) {
this.authenticateWithPassword = true
return
}
// TODO: Put this logic in a global errorMessage handler method which checks all auth codes
let errorMessage = this.$t('sharing.error')
if (e.response && e.response.data && e.response.data.message) {
errorMessage = e.response.data.message
}
if (typeof e.response.data.code !== 'undefined' && e.response.data.code === 13002) {
errorMessage = this.$t('sharing.invalidPassword')
}
this.errorMessage = errorMessage
})
.finally(() => {
this.loading = false
})
// TODO: Put this logic in a global errorMessage handler method which checks all auth codes
let errorMessage = this.$t('sharing.error')
if (e.response && e.response.data && e.response.data.message) {
errorMessage = e.response.data.message
}
if (typeof e.response.data.code !== 'undefined' && e.response.data.code === 13002) {
errorMessage = this.$t('sharing.invalidPassword')
}
this.errorMessage = errorMessage
} finally {
this.loading = false
}
},
},
}

View File

@ -141,7 +141,7 @@ export default {
},
})
},
loadPendingTasks() {
async loadPendingTasks() {
// Since this route is authentication only, users would get an error message if they access the page unauthenticated.
// Since this component is mounted as the home page before unauthenticated users get redirected
// to the login page, they will almost always see the error message.
@ -163,7 +163,10 @@ export default {
if (this.showAll) {
this.setTitle(this.$t('task.show.titleCurrent'))
} else {
this.setTitle(this.$t('task.show.titleDates', { from: this.cStartDate.toLocaleDateString(), to: this.cEndDate.toLocaleDateString()}))
this.setTitle(this.$t('task.show.titleDates', {
from: this.cStartDate.toLocaleDateString(),
to: this.cEndDate.toLocaleDateString(),
}))
}
const params = {
@ -197,21 +200,19 @@ export default {
}
}
this.$store.dispatch('tasks/loadTasks', params)
.then(r => {
const tasks = await this.$store.dispatch('tasks/loadTasks', params)
// Sort all tasks to put those with a due date before the ones without a due date, the
// soonest before the later ones.
// We can't use the api sorting here because that sorts tasks with a due date after
// ones without a due date.
r.sort((a, b) => {
return a.dueDate === null && b.dueDate === null ? -1 : 1
})
const tasks = r.filter(t => t.dueDate !== null).concat(r.filter(t => t.dueDate === null))
this.tasks = tasks
})
// FIXME: sort tasks in computed
// Sort all tasks to put those with a due date before the ones without a due date, the
// soonest before the later ones.
// We can't use the api sorting here because that sorts tasks with a due date after
// ones without a due date.
this.tasks = tasks.sort((a, b) => {
return Number(b.dueDate === null) - Number(a.dueDate === null)
})
},
// FIXME: this modification should happen in the store
updateTasks(updatedTask) {
for (const t in this.tasks) {
if (this.tasks[t].id === updatedTask.id) {
@ -225,18 +226,21 @@ export default {
}
}
},
setDatesToNextWeek() {
this.cStartDate = new Date()
this.cEndDate = new Date((new Date()).getTime() + 7 * 24 * 60 * 60 * 1000)
this.showOverdue = false
this.setDate()
},
setDatesToNextMonth() {
this.cStartDate = new Date()
this.cEndDate = new Date((new Date()).setMonth((new Date()).getMonth() + 1))
this.showOverdue = false
this.setDate()
},
showTodaysTasks() {
const d = new Date()
this.cStartDate = new Date()

View File

@ -561,23 +561,22 @@ export default {
return uploadFile(this.taskId, ...args)
},
loadTask(taskId) {
async loadTask(taskId) {
if (taskId === undefined) {
return
}
this.taskService.get({id: taskId})
.then(r => {
this.task = r
this.$store.commit('attachments/set', r.attachments)
this.taskColor = this.task.hexColor
this.setActiveFields()
this.setTitle(this.task.title)
})
.finally(() => {
this.$nextTick(() => this.visible = true)
this.scrollToHeading()
})
try {
this.task = await this.taskService.get({id: taskId})
this.$store.commit('attachments/set', this.task.attachments)
this.taskColor = this.task.hexColor
this.setActiveFields()
this.setTitle(this.task.title)
} finally {
this.scrollToHeading()
await this.$nextTick()
this.visible = true
}
},
scrollToHeading() {
this.$refs.heading.$el.scrollIntoView({block: 'center'})
@ -633,6 +632,7 @@ export default {
}
this.$message.success({message: this.$t('task.detail.updateSuccess')}, actions)
},
setFieldActive(fieldName) {
this.activeFields[fieldName] = true
this.$nextTick(() => {
@ -651,13 +651,13 @@ export default {
}
})
},
deleteTask() {
this.$store.dispatch('tasks/delete', this.task)
.then(() => {
this.$message.success({message: this.$t('task.detail.deleteSuccess')})
this.$router.push({name: 'list.index', params: {listId: this.task.listId}})
})
async deleteTask() {
await this.$store.dispatch('tasks/delete', this.task)
this.$message.success({message: this.$t('task.detail.deleteSuccess')})
this.$router.push({name: 'list.index', params: {listId: this.task.listId}})
},
toggleTaskDone() {
this.task.done = !this.task.done
@ -665,36 +665,26 @@ export default {
playPop()
}
this.saveTask(true, () => this.toggleTaskDone())
this.saveTask(true, this.toggleTaskDone)
},
setDescriptionChanged(e) {
if (e.key === 'Enter' || e.key === 'Control') {
return
}
this.descriptionChanged = true
},
saveTaskIfDescriptionChanged() {
// We want to only save the description if it was changed.
// Since we can either trigger this with ctrl+enter or @change, it would be possible to save a task first
// with ctrl+enter and then with @change although nothing changed since the last save when @change gets fired.
// To only save one time we added this method.
if (this.descriptionChanged) {
this.descriptionChanged = false
this.saveTask()
}
},
async changeList(list) {
this.$store.commit('kanban/removeTaskInBucket', this.task)
this.task.listId = list.id
await this.saveTask()
},
toggleFavorite() {
async toggleFavorite() {
this.task.isFavorite = !this.task.isFavorite
this.taskService.update(this.task)
.then(t => {
this.task = t
this.$store.dispatch('namespaces/loadNamespacesIfFavoritesDontExist')
})
this.task = await this.taskService.update(this.task)
this.$store.dispatch('namespaces/loadNamespacesIfFavoritesDontExist')
},
},
}

View File

@ -219,93 +219,79 @@ export default {
userInfo: (state) => state.auth.info,
}),
},
methods: {
loadTeam() {
async loadTeam() {
this.team = new TeamModel({id: this.teamId})
this.teamService
.get(this.team)
.then((response) => {
this.team = response
this.title = this.$t('team.edit.title', {team: this.team.name})
this.setTitle(this.title)
})
this.team = await this.teamService.get(this.team)
this.title = this.$t('team.edit.title', {team: this.team.name})
this.setTitle(this.title)
},
save() {
async save() {
if (this.team.name === '') {
this.showError = true
return
}
this.showError = false
this.teamService
.update(this.team)
.then((response) => {
this.team = response
this.$message.success({message: this.$t('team.edit.success')})
})
this.team = await this.teamService.update(this.team)
this.$message.success({message: this.$t('team.edit.success')})
},
deleteTeam() {
this.teamService
.delete(this.team)
.then(() => {
this.$message.success({message: this.$t('team.edit.delete.success')})
this.$router.push({name: 'teams.index'})
})
async deleteTeam() {
await this.teamService.delete(this.team)
this.$message.success({message: this.$t('team.edit.delete.success')})
this.$router.push({name: 'teams.index'})
},
deleteUser() {
this.teamMemberService
.delete(this.member)
.then(() => {
this.$message.success({message: this.$t('team.edit.deleteUser.success')})
this.loadTeam()
})
.finally(() => {
this.showUserDeleteModal = false
})
async deleteUser() {
try {
await this.teamMemberService.delete(this.member)
this.$message.success({message: this.$t('team.edit.deleteUser.success')})
this.loadTeam()
} finally {
this.showUserDeleteModal = false
}
},
addUser() {
async addUser() {
const newMember = new TeamMemberModel({
teamId: this.teamId,
username: this.newMember.username,
})
this.teamMemberService
.create(newMember)
.then(() => {
this.loadTeam()
this.$message.success({message: this.$t('team.edit.userAddedSuccess')})
})
await this.teamMemberService.create(newMember)
this.loadTeam()
this.$message.success({message: this.$t('team.edit.userAddedSuccess')})
},
toggleUserType(member) {
async toggleUserType(member) {
// FIXME: direct manipulation
member.admin = !member.admin
member.teamId = this.teamId
this.teamMemberService
.update(member)
.then((r) => {
for (const tm in this.team.members) {
if (this.team.members[tm].id === member.id) {
this.team.members[tm].admin = r.admin
break
}
}
this.$message.success({
message: member.admin ?
this.$t('team.edit.madeAdmin') :
this.$t('team.edit.madeMember'),
})
})
const r = await this.teamMemberService.update(member)
for (const tm in this.team.members) {
if (this.team.members[tm].id === member.id) {
this.team.members[tm].admin = r.admin
break
}
}
this.$message.success({
message: member.admin ?
this.$t('team.edit.madeAdmin') :
this.$t('team.edit.madeMember'),
})
},
findUser(query) {
async findUser(query) {
if (query === '') {
this.clearAll()
return
}
this.userService
.getAll({}, {s: query})
.then((response) => {
this.foundUsers = response
})
this.foundUsers = await this.userService.getAll({}, {s: query})
},
clearAll() {
this.foundUsers = []
},

View File

@ -43,11 +43,8 @@ export default {
this.setTitle(this.$t('team.title'))
},
methods: {
loadTeams() {
this.teamService.getAll()
.then(response => {
this.teams = response
})
async loadTeams() {
this.teams = await this.teamService.getAll()
},
},
}

View File

@ -49,22 +49,19 @@ export default {
this.setTitle(this.$t('team.create.title'))
},
methods: {
newTeam() {
async newTeam() {
if (this.team.name === '') {
this.showError = true
return
}
this.showError = false
this.teamService
.create(this.team)
.then((response) => {
this.$router.push({
name: 'teams.edit',
params: { id: response.id },
})
this.$message.success({message: this.$t('team.create.success') })
})
const response = await this.teamService.create(this.team)
this.$router.push({
name: 'teams.edit',
params: { id: response.id },
})
this.$message.success({message: this.$t('team.create.success') })
},
},
}

View File

@ -187,7 +187,8 @@ export default {
this.loading = false
}
},
submit() {
async submit() {
this.$store.commit(ERROR_MESSAGE, '')
// Some browsers prevent Vue bindings from working with autofilled values.
// To work around this, we're manually getting the values here instead of relying on vue bindings.
@ -201,24 +202,24 @@ export default {
credentials.totpPasscode = this.$refs.totpPasscode.value
}
this.$store.dispatch('auth/login', credentials)
.then(() => {
this.$store.commit('auth/needsTotpPasscode', false)
})
.catch(e => {
if (e.response && e.response.data.code === 1017 && !credentials.totpPasscode) {
return
}
try {
await this.$store.dispatch('auth/login', credentials)
this.$store.commit('auth/needsTotpPasscode', false)
} catch(e) {
if (e.response && e.response.data.code === 1017 && !credentials.totpPasscode) {
return
}
const err = getErrorText(e)
if (typeof err[1] !== 'undefined') {
this.$store.commit(ERROR_MESSAGE, err[1])
return
}
const err = getErrorText(e)
if (typeof err[1] !== 'undefined') {
this.$store.commit(ERROR_MESSAGE, err[1])
return
}
this.$store.commit(ERROR_MESSAGE, err[0])
})
this.$store.commit(ERROR_MESSAGE, err[0])
}
},
redirectToProvider(provider) {
redirectToProvider(provider, this.openidConnect.redirectUrl)
},

View File

@ -26,7 +26,7 @@ export default {
this.authenticateWithCode()
},
methods: {
authenticateWithCode() {
async authenticateWithCode() {
// This component gets mounted twice: The first time when the actual auth request hits the frontend,
// the second time after that auth request succeeded and the outer component "content-no-auth" isn't used
// but instead the "content-auth" component is used. Because this component is just a route and thus
@ -59,34 +59,32 @@ export default {
this.$store.commit(ERROR_MESSAGE, '')
this.$store.dispatch('auth/openIdAuth', {
provider: this.$route.params.provider,
code: this.$route.query.code,
})
.then(() => {
const last = getLastVisited()
if (last !== null) {
this.$router.push({
name: last.name,
params: last.params,
})
clearLastVisited()
} else {
this.$router.push({name: 'home'})
}
try {
await this.$store.dispatch('auth/openIdAuth', {
provider: this.$route.params.provider,
code: this.$route.query.code,
})
.catch(e => {
const err = getErrorText(e)
if (typeof err[1] !== 'undefined') {
this.$store.commit(ERROR_MESSAGE, err[1])
return
}
const last = getLastVisited()
if (last !== null) {
this.$router.push({
name: last.name,
params: last.params,
})
clearLastVisited()
} else {
this.$router.push({name: 'home'})
}
} catch(e) {
const err = getErrorText(e)
if (typeof err[1] !== 'undefined') {
this.$store.commit(ERROR_MESSAGE, err[1])
return
}
this.$store.commit(ERROR_MESSAGE, err[0])
})
.finally(() => {
localStorage.removeItem('authenticating')
})
this.$store.commit(ERROR_MESSAGE, err[0])
} finally {
localStorage.removeItem('authenticating')
}
},
},
}

View File

@ -85,11 +85,13 @@ export default {
successMessage: '',
}
},
mounted() {
this.setTitle(this.$t('user.auth.resetPassword'))
},
methods: {
submit() {
async submit() {
this.errorMsg = ''
if (this.credentials.password2 !== this.credentials.password) {
@ -98,14 +100,13 @@ export default {
}
let passwordReset = new PasswordResetModel({newPassword: this.credentials.password})
this.passwordResetService.resetPassword(passwordReset)
.then(response => {
this.successMessage = response.message
localStorage.removeItem('passwordResetToken')
})
.catch(e => {
this.errorMsg = e.response.data.message
})
try {
const { message } = this.passwordResetService.resetPassword(passwordReset)
this.successMessage = message
localStorage.removeItem('passwordResetToken')
} catch(e) {
this.errorMsg = e.response.data.message
}
},
},
}

View File

@ -69,15 +69,14 @@ export default {
this.setTitle(this.$t('user.auth.resetPassword'))
},
methods: {
submit() {
async submit() {
this.errorMsg = ''
this.passwordResetService.requestResetPassword(this.passwordReset)
.then(() => {
this.isSuccess = true
})
.catch(e => {
this.errorMsg = e.response.data.message
})
try {
await this.passwordResetService.requestResetPassword(this.passwordReset)
this.isSuccess = true
} catch(e) {
this.errorMsg = e.response.data.message
}
},
},
}

View File

@ -385,84 +385,75 @@ export default {
methods: {
copy,
updatePassword() {
async updatePassword() {
if (this.passwordConfirm !== this.passwordUpdate.newPassword) {
this.$message.error({message: this.$t('user.settings.passwordsDontMatch')})
return
}
this.passwordUpdateService.update(this.passwordUpdate)
.then(() => {
this.$message.success({message: this.$t('user.settings.passwordUpdateSuccess')})
})
await this.passwordUpdateService.update(this.passwordUpdate)
this.$message.success({message: this.$t('user.settings.passwordUpdateSuccess')})
},
updateEmail() {
this.emailUpdateService.update(this.emailUpdate)
.then(() => {
this.$message.success({message: this.$t('user.settings.updateEmailSuccess')})
})
async updateEmail() {
await this.emailUpdateService.update(this.emailUpdate)
this.$message.success({message: this.$t('user.settings.updateEmailSuccess')})
},
totpStatus() {
async totpStatus() {
if (!this.totpEnabled) {
return
}
this.totpService.get()
.then(r => {
this.totp = r
this.totpSetQrCode()
})
.catch(e => {
// Error code 1016 means totp is not enabled, we don't need an error in that case.
if (e.response && e.response.data && e.response.data.code && e.response.data.code === 1016) {
this.totpEnrolled = false
return
}
throw e
})
},
totpSetQrCode() {
this.totpService.qrcode()
.then(qr => {
const urlCreator = window.URL || window.webkitURL
this.totpQR = urlCreator.createObjectURL(qr)
})
},
totpEnroll() {
this.totpService.enroll()
.then(r => {
this.totpEnrolled = true
this.totp = r
this.totpSetQrCode()
})
},
totpConfirm() {
this.totpService.enable({passcode: this.totpConfirmPasscode})
.then(() => {
this.totp.enabled = true
this.$message.success({message: this.$t('user.settings.totp.confirmSuccess')})
})
},
totpDisable() {
this.totpService.disable({password: this.totpDisablePassword})
.then(() => {
try {
this.totp = await this.totpService.get()
this.totpSetQrCode()
} catch(e) {
// Error code 1016 means totp is not enabled, we don't need an error in that case.
if (e.response && e.response.data && e.response.data.code && e.response.data.code === 1016) {
this.totpEnrolled = false
this.totp = new TotpModel()
this.$message.success({message: this.$t('user.settings.totp.disableSuccess')})
})
return
}
throw e
}
},
updateSettings() {
async totpSetQrCode() {
const qr = await this.totpService.qrcode()
const urlCreator = window.URL || window.webkitURL
this.totpQR = urlCreator.createObjectURL(qr)
},
async totpEnroll() {
this.totp = await this.totpService.enroll()
this.totpEnrolled = true
this.totpSetQrCode()
},
async totpConfirm() {
await this.totpService.enable({passcode: this.totpConfirmPasscode})
this.totp.enabled = true
this.$message.success({message: this.$t('user.settings.totp.confirmSuccess')})
},
async totpDisable() {
await this.totpService.disable({password: this.totpDisablePassword})
this.totpEnrolled = false
this.totp = new TotpModel()
this.$message.success({message: this.$t('user.settings.totp.disableSuccess')})
},
async updateSettings() {
localStorage.setItem(playSoundWhenDoneKey, this.playSoundWhenDone)
saveLanguage(this.language)
setQuickAddMagicMode(this.quickAddMagicMode)
this.settings.defaultListId = this.defaultList ? this.defaultList.id : 0
this.userSettingsService.update(this.settings)
.then(() => {
this.$store.commit('auth/setUserSettings', this.settings)
this.$message.success({message: this.$t('user.settings.general.savedSuccess')})
})
await this.userSettingsService.update(this.settings)
this.$store.commit('auth/setUserSettings', this.settings)
this.$message.success({message: this.$t('user.settings.general.savedSuccess')})
},
anchorHashCheck() {
if (window.location.hash === this.$route.hash) {
const el = document.getElementById(this.$route.hash.slice(1))