vikunja/frontend/src/composables/useTaskList.ts
kolaente 6e5b31f1e0
Some checks failed
continuous-integration/drone/push Build is failing
fix(filters): always persist filter or search in query path and load it correctly into filter query input when loading the page
Previously, when using the filter query as a search input, it would load the search as requested but the filter query parameter in the url would be empty, which meant the search would not be loaded correctly when reloading (or otherwise newly accessing) the page. We're now persisting the filter and search in the task loading logic, to make sure they are always populated correctly.
2024-04-13 23:34:25 +02:00

171 lines
3.9 KiB
TypeScript

import {ref, shallowReactive, watch, computed, type ComputedGetter} from 'vue'
import {useRoute, useRouter} from 'vue-router'
import {useRouteQuery} from '@vueuse/router'
import TaskCollectionService, {getDefaultTaskFilterParams, type TaskFilterParams} from '@/services/taskCollection'
import type {ITask} from '@/modelTypes/ITask'
import {error} from '@/message'
import type {IProject} from '@/modelTypes/IProject'
import {useAuthStore} from '@/stores/auth'
import type {IProjectView} from '@/modelTypes/IProjectView'
export type Order = 'asc' | 'desc' | 'none'
export interface SortBy {
id?: Order
index?: Order
done?: Order
title?: Order
priority?: Order
due_date?: Order
start_date?: Order
end_date?: Order
percent_done?: Order
created?: Order
updated?: Order
done_at?: Order,
}
const SORT_BY_DEFAULT: SortBy = {
id: 'desc',
}
// This makes sure an id sort order is always sorted last.
// When tasks would be sorted first by id and then by whatever else was specified, the id sort takes
// precedence over everything else, making any other sort columns pretty useless.
function formatSortOrder(sortBy, params) {
let hasIdFilter = false
const sortKeys = Object.keys(sortBy)
for (const s of sortKeys) {
if (s === 'id') {
sortKeys.splice(s, 1)
hasIdFilter = true
break
}
}
if (hasIdFilter) {
sortKeys.push('id')
}
params.sort_by = sortKeys
params.order_by = sortKeys.map(s => sortBy[s])
return params
}
/**
* This mixin provides a base set of methods and properties to get tasks.
*/
export function useTaskList(
projectIdGetter: ComputedGetter<IProject['id']>,
projectViewIdGetter: ComputedGetter<IProjectView['id']>,
sortByDefault: SortBy = SORT_BY_DEFAULT,
) {
const projectId = computed(() => projectIdGetter())
const projectViewId = computed(() => projectViewIdGetter())
const params = ref<TaskFilterParams>({...getDefaultTaskFilterParams()})
const page = useRouteQuery('page', '1', { transform: Number })
const sortBy = ref({ ...sortByDefault })
const allParams = computed(() => {
const loadParams = {...params.value}
return formatSortOrder(sortBy.value, loadParams)
})
watch(
() => allParams.value,
() => {
// When parameters change, the page should always be the first
page.value = 1
},
)
const authStore = useAuthStore()
const getAllTasksParams = computed(() => {
return [
{
projectId: projectId.value,
viewId: projectViewId.value,
},
{
...allParams.value,
filter_timezone: authStore.settings.timezone,
},
page.value,
]
})
const taskCollectionService = shallowReactive(new TaskCollectionService())
const loading = computed(() => taskCollectionService.loading)
const totalPages = computed(() => taskCollectionService.totalPages)
const tasks = ref<ITask[]>([])
async function loadTasks() {
tasks.value = []
try {
tasks.value = await taskCollectionService.getAll(...getAllTasksParams.value)
} catch (e) {
error(e)
}
return tasks.value
}
const route = useRoute()
watch(() => route.query, (query) => {
const {
page: pageQueryValue,
s,
filter,
} = query
if (s !== undefined) {
params.value.s = s as string
}
if (pageQueryValue !== undefined) {
page.value = Number(pageQueryValue)
}
if (filter !== undefined) {
params.value.filter = filter
}
}, { immediate: true })
const router = useRouter()
watch(
() => [page.value, params.value.filter, params.value.s],
() => {
router.replace({
name: route.name,
params: route.params,
query: {
page: page.value,
filter: params.value.filter || undefined,
s: params.value.s || undefined,
},
})
},
{ deep: true },
)
// Only listen for query path changes
watch(() => JSON.stringify(getAllTasksParams.value), (newParams, oldParams) => {
if (oldParams === newParams) {
return
}
loadTasks()
}, { immediate: true })
return {
tasks,
loading,
totalPages,
currentPage: page,
loadTasks,
params,
sortByParam: sortBy,
}
}