frontend/src/store/modules/tasks.js

326 lines
9.3 KiB
JavaScript
Raw Normal View History

2021-09-07 12:10:52 +00:00
import router from '@/router'
import TaskService from '@/services/task'
import TaskAssigneeService from '@/services/taskAssignee'
import TaskAssigneeModel from '../../models/taskAssignee'
import LabelTaskModel from '../../models/labelTask'
2021-09-07 12:10:52 +00:00
import LabelTaskService from '@/services/labelTask'
import {HAS_TASKS} from '../mutation-types'
import {setLoading} from '../helper'
2021-09-07 12:10:52 +00:00
import {getQuickAddMagicMode} from '@/helpers/quickAddMagicMode'
import {parseTaskText} from '@/modules/parseTaskText'
import TaskModel from '@/models/task'
import {formatISO} from 'date-fns'
import LabelTask from '@/models/labelTask'
import LabelModel from '@/models/label'
import UserService from '@/services/user'
// IDEA: maybe use a small fuzzy search here to prevent errors
function findPropertyByValue(object, key, value) {
return Object.values(object).find(
(l) => l[key]?.toLowerCase() === value.toLowerCase(),
)
}
// Check if the user exists
function validateUsername(users, username) {
return findPropertyByValue(users, 'username', username)
}
// Check if the label exists
function validateLabel(labels, label) {
return findPropertyByValue(labels, 'title', label)
}
async function addLabelToTask(task, label) {
2021-09-07 12:10:52 +00:00
const labelTask = new LabelTask({
taskId: task.id,
labelId: label.id,
})
const labelTaskService = new LabelTaskService()
const response = await labelTaskService.create(labelTask)
task.labels.push(label)
return response
2021-09-07 12:10:52 +00:00
}
async function findAssignees(parsedTaskAssignees) {
2021-09-07 12:10:52 +00:00
if (parsedTaskAssignees.length <= 0) {
return []
2021-09-07 12:10:52 +00:00
}
const userService = new UserService()
const assignees = parsedTaskAssignees.map(async a => {
const users = await userService.getAll({}, {s: a})
return validateUsername(users, a)
})
2021-09-07 12:10:52 +00:00
const validatedUsers = await Promise.all(assignees)
return validatedUsers.filter((item) => Boolean(item))
2021-09-07 12:10:52 +00:00
}
export default {
namespaced: true,
state: () => ({}),
actions: {
async loadTasks(ctx, params) {
const taskService = new TaskService()
2021-09-07 12:10:52 +00:00
const cancel = setLoading(ctx, 'tasks')
try {
const tasks = await taskService.getAll({}, params)
ctx.commit(HAS_TASKS, tasks.length > 0, {root: true})
return tasks
} finally {
cancel()
}
},
async update(ctx, task) {
const cancel = setLoading(ctx, 'tasks')
const taskService = new TaskService()
try {
const updatedTask = await taskService.update(task)
ctx.commit('kanban/setTaskInBucket', updatedTask, {root: true})
return updatedTask
} finally {
cancel()
}
},
async delete(ctx, task) {
const taskService = new TaskService()
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}) {
const t = ctx.rootGetters['kanban/getTaskById'](taskId)
if (t.task !== null) {
2021-09-11 15:53:03 +00:00
const attachments = [
...t.task.attachments,
attachment,
]
const newTask = {
...t,
task: {
...t.task,
attachments,
},
}
ctx.commit('kanban/setTaskInBucketByIndex', newTask, {root: true})
}
Add easymde & markdown preview for editing descriptions and comments (#183) Make sure no text from previous mounts is left in the editor text field Make preview not the default when rendering descrition settings Add option to show editor by default while still having the option to show preview Add option to show editor by default while still having the option to show preview Use editor component for edit labels Use editor component for edit team Use editor component for edit namespace Use editor component for edit list Use editor component for edit task Make sure we find all checkboxes Fix checking wrong checkbox Make finding and replacing checkboxes in a function actually work Add upading text with checked checkboxes Lazy load editor Remove preview since we have a better one Make easymde smaller by default Add image upload from comments Rename easymde component to editor Only show preview button if editing is currently active Make editor tabs look better when commenting Make comments meta look better Don't try to update if the value was initially set Use editor to render and edit comments Make preview optional Make tabs look better Don't switch to preview after editing Centralize attachment state Render markdown by default Fix title being "null" Fix loading attachment images Add standalone preview Fix callback url Add onsuccess callback Add file upload Fix date parsing once and for all Add more props for upload and such Fix editor border color Fix changing text after mounting Add link to guide Fix sizing of icons Add timeout for changes Add all easymde icons Co-authored-by: kolaente <k@knt.li> Reviewed-on: https://kolaente.dev/vikunja/frontend/pulls/183
2020-07-14 19:26:05 +00:00
ctx.commit('attachments/add', attachment, {root: true})
},
async addAssignee(ctx, {user, taskId}) {
const taskAssignee = new TaskAssigneeModel({userId: user.id, taskId: taskId})
2021-09-07 12:10:52 +00:00
const taskAssigneeService = new TaskAssigneeService()
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
}
const assignees = [
...t.task.assignees,
user,
]
ctx.commit('kanban/setTaskInBucketByIndex', {
...t,
task: {
...t.task,
assignees,
},
}, {root: true})
return r
},
async removeAssignee(ctx, {user, taskId}) {
const taskAssignee = new TaskAssigneeModel({userId: user.id, taskId: taskId})
2021-09-07 12:10:52 +00:00
const taskAssigneeService = new TaskAssigneeService()
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
}
const assignees = t.task.assignees.filter(({ id }) => id !== user.id)
ctx.commit('kanban/setTaskInBucketByIndex', {
...t,
task: {
...t.task,
assignees,
},
}, {root: true})
return response
},
async addLabel(ctx, {label, taskId}) {
const labelTask = new LabelTaskModel({taskId: taskId, labelId: label.id})
2021-09-07 12:10:52 +00:00
const labelTaskService = new LabelTaskService()
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
}
const labels = [
...t.task.labels,
label,
]
ctx.commit('kanban/setTaskInBucketByIndex', {
...t,
task: {
...t.task,
labels,
},
}, {root: true})
return r
},
async removeLabel(ctx, {label, taskId}) {
const labelTask = new LabelTaskModel({taskId: taskId, labelId: label.id})
2021-09-07 12:10:52 +00:00
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
}
// Remove the label from the list
const labels = t.task.labels.filter(({ id }) => id !== label.id)
ctx.commit('kanban/setTaskInBucketByIndex', {
...t,
task: {
...t.task,
labels,
},
}, {root: true})
return response
},
2021-09-07 12:10:52 +00:00
// Do everything that is involved in finding, creating and adding the label to the task
async addLabelsToTask({rootState, dispatch}, { task, parsedLabels }) {
if (parsedLabels.length <= 0) {
return task
}
const {labels} = rootState.labels
const labelAddsToWaitFor = parsedLabels.map(async labelTitle => {
2021-09-07 12:10:52 +00:00
let label = validateLabel(labels, labelTitle)
if (typeof label === 'undefined') {
// label not found, create it
const labelModel = new LabelModel({title: labelTitle})
label = await dispatch('labels/createLabel', labelModel, {root: true})
2021-09-07 12:10:52 +00:00
}
return addLabelToTask(task, label)
2021-09-07 12:10:52 +00:00
})
// This waits until all labels are created and added to the task
await Promise.all(labelAddsToWaitFor)
return task
2021-09-07 12:10:52 +00:00
},
findListId({ rootGetters }, { list: listName, listId }) {
2021-09-07 12:10:52 +00:00
let foundListId = null
// Uses the following ways to get the list id of the new task:
// 1. If specified in quick add magic, look in store if it exists and use it if it does
if (listName !== null) {
const list = rootGetters['lists/findListByExactname'](listName)
2021-09-07 12:10:52 +00:00
foundListId = list === null ? null : list.id
}
// 2. Else check if a list was passed as parameter
if (foundListId === null && listId !== 0) {
2021-09-07 12:10:52 +00:00
foundListId = listId
}
// 3. Otherwise use the id from the route parameter
if (typeof router.currentRoute.value.params.listId !== 'undefined') {
foundListId = parseInt(router.currentRoute.value.params.listId)
}
// 4. If none of the above worked, reject the promise with an error.
if (typeof foundListId === 'undefined' || listId === null) {
throw new Error('NO_LIST')
2021-09-07 12:10:52 +00:00
}
return foundListId
},
async createNewTask({dispatch}, {
title,
bucketId,
listId,
position,
}) {
const parsedTask = parseTaskText(title, getQuickAddMagicMode())
const foundListId = await dispatch('findListId', {
list: parsedTask.list,
listId: listId || 0,
})
const assignees = await findAssignees(parsedTask.assignees)
// I don't know why, but it all goes up in flames when I just pass in the date normally.
const dueDate = parsedTask.date !== null ? formatISO(parsedTask.date) : null
const task = new TaskModel({
title: parsedTask.text,
listId: foundListId,
dueDate,
priority: parsedTask.priority,
assignees,
bucketId: bucketId || 0,
position,
})
task.repeatAfter = parsedTask.repeats
2021-09-07 12:10:52 +00:00
const taskService = new TaskService()
const createdTask = await taskService.create(task)
return dispatch('addLabelsToTask', {
task: createdTask,
parsedLabels: parsedTask.labels,
})
2021-09-07 12:10:52 +00:00
},
},
}