Pagingation for tasks in kanban buckets (#419)
Co-authored-by: kolaente <k@knt.li> Reviewed-on: https://kolaente.dev/vikunja/frontend/pulls/419 Co-authored-by: konrad <konrad@kola-entertainments.de> Co-committed-by: konrad <konrad@kola-entertainments.de>
This commit is contained in:
parent
1f33477f57
commit
f7d8095b5a
3 changed files with 110 additions and 8 deletions
|
@ -22,8 +22,6 @@ export default {
|
||||||
text: err,
|
text: err,
|
||||||
actions: actions,
|
actions: actions,
|
||||||
})
|
})
|
||||||
|
|
||||||
context.loading = false
|
|
||||||
},
|
},
|
||||||
success(e, context, actions = []) {
|
success(e, context, actions = []) {
|
||||||
// Build the notification text from error response
|
// Build the notification text from error response
|
||||||
|
@ -41,7 +39,5 @@ export default {
|
||||||
actions: actions,
|
actions: actions,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
context.loading = false
|
|
||||||
},
|
},
|
||||||
}
|
}
|
|
@ -1,8 +1,12 @@
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
|
import {cloneDeep} from 'lodash'
|
||||||
|
|
||||||
import BucketService from '../../services/bucket'
|
import BucketService from '../../services/bucket'
|
||||||
import {filterObject} from '@/helpers/filterObject'
|
import {filterObject} from '@/helpers/filterObject'
|
||||||
import {setLoading} from '../helper'
|
import {setLoading} from '../helper'
|
||||||
|
import TaskCollectionService from '@/services/taskCollection'
|
||||||
|
|
||||||
|
const tasksPerBucket = 25
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This store is intended to hold the currently active kanban view.
|
* This store is intended to hold the currently active kanban view.
|
||||||
|
@ -13,6 +17,9 @@ export default {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
buckets: [],
|
buckets: [],
|
||||||
listId: 0,
|
listId: 0,
|
||||||
|
bucketLoading: {},
|
||||||
|
taskPagesPerBucket: {},
|
||||||
|
allTasksLoadedForBucket: {},
|
||||||
}),
|
}),
|
||||||
mutations: {
|
mutations: {
|
||||||
setListId(state, listId) {
|
setListId(state, listId) {
|
||||||
|
@ -20,6 +27,10 @@ export default {
|
||||||
},
|
},
|
||||||
setBuckets(state, buckets) {
|
setBuckets(state, buckets) {
|
||||||
state.buckets = buckets
|
state.buckets = buckets
|
||||||
|
buckets.forEach(b => {
|
||||||
|
Vue.set(state.taskPagesPerBucket, b.id, 1)
|
||||||
|
Vue.set(state.allTasksLoadedForBucket, b.id, false)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
addBucket(state, bucket) {
|
addBucket(state, bucket) {
|
||||||
state.buckets.push(bucket)
|
state.buckets.push(bucket)
|
||||||
|
@ -71,6 +82,13 @@ export default {
|
||||||
const bi = filterObject(state.buckets, b => b.id === task.bucketId)
|
const bi = filterObject(state.buckets, b => b.id === task.bucketId)
|
||||||
state.buckets[bi].tasks.push(task)
|
state.buckets[bi].tasks.push(task)
|
||||||
},
|
},
|
||||||
|
addTasksToBucket(state, {tasks, bucketId}) {
|
||||||
|
const bi = filterObject(state.buckets, b => b.id === bucketId)
|
||||||
|
|
||||||
|
tasks.forEach(t => {
|
||||||
|
state.buckets[bi].tasks.push(t)
|
||||||
|
})
|
||||||
|
},
|
||||||
removeTaskInBucket(state, task) {
|
removeTaskInBucket(state, task) {
|
||||||
// If this gets invoked without any tasks actually loaded, we can save the hassle of finding the task
|
// If this gets invoked without any tasks actually loaded, we can save the hassle of finding the task
|
||||||
if (state.buckets.length === 0) {
|
if (state.buckets.length === 0) {
|
||||||
|
@ -91,6 +109,15 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
setBucketLoading(state, {bucketId, loading}) {
|
||||||
|
Vue.set(state.bucketLoading, bucketId, loading)
|
||||||
|
},
|
||||||
|
setTasksLoadedForBucketPage(state, {bucketId, page}) {
|
||||||
|
Vue.set(state.taskPagesPerBucket, bucketId, page)
|
||||||
|
},
|
||||||
|
setAllTasksLoadedForBucket(state, bucketId) {
|
||||||
|
Vue.set(state.allTasksLoadedForBucket, bucketId, true)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
getTaskById: state => id => {
|
getTaskById: state => id => {
|
||||||
|
@ -119,6 +146,8 @@ export default {
|
||||||
// Clear everything to prevent having old buckets in the list if loading the buckets from this list takes a few moments
|
// Clear everything to prevent having old buckets in the list if loading the buckets from this list takes a few moments
|
||||||
ctx.commit('setBuckets', [])
|
ctx.commit('setBuckets', [])
|
||||||
|
|
||||||
|
params.per_page = tasksPerBucket
|
||||||
|
|
||||||
const bucketService = new BucketService()
|
const bucketService = new BucketService()
|
||||||
return bucketService.getAll({listId: listId}, params)
|
return bucketService.getAll({listId: listId}, params)
|
||||||
.then(r => {
|
.then(r => {
|
||||||
|
@ -133,6 +162,64 @@ export default {
|
||||||
cancel()
|
cancel()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
loadNextTasksForBucket(ctx, {listId, ps = {}, bucketId}) {
|
||||||
|
const isLoading = ctx.state.bucketLoading[bucketId] ?? false
|
||||||
|
if (isLoading) {
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
const page = (ctx.state.taskPagesPerBucket[bucketId] ?? 1) + 1
|
||||||
|
|
||||||
|
const alreadyLoaded = ctx.state.allTasksLoadedForBucket[bucketId] ?? false
|
||||||
|
if (alreadyLoaded) {
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancel = setLoading(ctx, 'kanban')
|
||||||
|
ctx.commit('setBucketLoading', {bucketId: bucketId, loading: true})
|
||||||
|
|
||||||
|
const params = cloneDeep(ps)
|
||||||
|
|
||||||
|
params.sort_by = 'position'
|
||||||
|
params.order_by = 'asc'
|
||||||
|
|
||||||
|
let hasBucketFilter = false
|
||||||
|
for (const f in params.filter_by) {
|
||||||
|
if (params.filter_by[f] === 'bucket_id') {
|
||||||
|
hasBucketFilter = true
|
||||||
|
if (params.filter_value[f] !== bucketId) {
|
||||||
|
params.filter_value[f] = bucketId
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasBucketFilter) {
|
||||||
|
params.filter_by = [...(params.filter_by ?? []), 'bucket_id']
|
||||||
|
params.filter_value = [...(params.filter_value ?? []), bucketId]
|
||||||
|
params.filter_comparator = [...(params.filter_comparator ?? []), 'equals']
|
||||||
|
}
|
||||||
|
|
||||||
|
params.per_page = tasksPerBucket
|
||||||
|
|
||||||
|
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 Promise.resolve(r)
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
return Promise.reject(e)
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
cancel()
|
||||||
|
ctx.commit('setBucketLoading', {bucketId: bucketId, loading: false})
|
||||||
|
})
|
||||||
|
},
|
||||||
createBucket(ctx, bucket) {
|
createBucket(ctx, bucket) {
|
||||||
const cancel = setLoading(ctx, 'kanban')
|
const cancel = setLoading(ctx, 'kanban')
|
||||||
|
|
||||||
|
@ -149,7 +236,7 @@ export default {
|
||||||
cancel()
|
cancel()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
deleteBucket(ctx, bucket) {
|
deleteBucket(ctx, {bucket, params}) {
|
||||||
const cancel = setLoading(ctx, 'kanban')
|
const cancel = setLoading(ctx, 'kanban')
|
||||||
|
|
||||||
const bucketService = new BucketService()
|
const bucketService = new BucketService()
|
||||||
|
@ -157,7 +244,7 @@ export default {
|
||||||
.then(r => {
|
.then(r => {
|
||||||
ctx.commit('removeBucket', bucket)
|
ctx.commit('removeBucket', bucket)
|
||||||
// We reload all buckets because tasks are being moved from the deleted bucket
|
// We reload all buckets because tasks are being moved from the deleted bucket
|
||||||
ctx.dispatch('loadBucketsForList', {listId: bucket.listId})
|
ctx.dispatch('loadBucketsForList', {listId: bucket.listId, params: params})
|
||||||
return Promise.resolve(r)
|
return Promise.resolve(r)
|
||||||
})
|
})
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
|
|
|
@ -352,7 +352,26 @@ export default {
|
||||||
console.debug(`Loading buckets, loadedListId = ${this.loadedListId}, $route.params =`, this.$route.params)
|
console.debug(`Loading buckets, loadedListId = ${this.loadedListId}, $route.params =`, this.$route.params)
|
||||||
this.filtersChanged = false
|
this.filtersChanged = false
|
||||||
|
|
||||||
|
const minScrollHeightPercent = 0.25
|
||||||
|
|
||||||
this.$store.dispatch('kanban/loadBucketsForList', {listId: this.$route.params.listId, params: this.params})
|
this.$store.dispatch('kanban/loadBucketsForList', {listId: this.$route.params.listId, params: this.params})
|
||||||
|
.then(bs => {
|
||||||
|
bs.forEach(b => {
|
||||||
|
const e = this.$refs[`tasks-container${b.id}`][0]
|
||||||
|
e.onscroll = () => {
|
||||||
|
if (e.scrollTopMax <= e.scrollTop + e.scrollTop * minScrollHeightPercent) {
|
||||||
|
this.$store.dispatch('kanban/loadNextTasksForBucket', {
|
||||||
|
listId: this.$route.params.listId,
|
||||||
|
params: this.params,
|
||||||
|
bucketId: b.id,
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
this.error(e, this)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
this.error(e, this)
|
this.error(e, this)
|
||||||
})
|
})
|
||||||
|
@ -518,7 +537,7 @@ export default {
|
||||||
listId: this.$route.params.listId,
|
listId: this.$route.params.listId,
|
||||||
})
|
})
|
||||||
|
|
||||||
this.$store.dispatch('kanban/deleteBucket', bucket)
|
this.$store.dispatch('kanban/deleteBucket', {bucket: bucket, params: this.params})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.success({message: 'The bucket has been deleted successfully.'}, this)
|
this.success({message: 'The bucket has been deleted successfully.'}, this)
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue