fix(quick add magic): don't create a new label multiple times if it is used in multiple tasks
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
Resolves https://github.com/go-vikunja/frontend/issues/94
This commit is contained in:
parent
33494cab6b
commit
463d22b07c
@ -53,6 +53,7 @@ import {RELATION_KIND} from '@/types/IRelationKind'
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
import {useAutoHeightTextarea} from '@/composables/useAutoHeightTextarea'
|
||||
import {getLabelsFromPrefix} from '@/modules/parseTaskText'
|
||||
|
||||
const props = defineProps({
|
||||
defaultPosition: {
|
||||
@ -82,6 +83,7 @@ function resetEmptyTitleError(e) {
|
||||
}
|
||||
|
||||
const loading = computed(() => taskStore.isLoading)
|
||||
|
||||
async function addTask() {
|
||||
if (newTaskTitle.value === '') {
|
||||
errorMessage.value = t('list.create.addTitleRequired')
|
||||
@ -98,11 +100,18 @@ async function addTask() {
|
||||
// by quick add magic.
|
||||
const createdTasks: { [key: ITask['title']]: ITask } = {}
|
||||
const tasksToCreate = parseSubtasksViaIndention(newTaskTitle.value)
|
||||
|
||||
// We ensure all labels exist prior to passing them down to the create task method
|
||||
// In the store it will only ever see one task at a time so there's no way to reliably
|
||||
// check if a new label was created before (because everything happens async).
|
||||
const allLabels = tasksToCreate.map(({title}) => getLabelsFromPrefix(title) ?? [])
|
||||
await taskStore.ensureLabelsExist(allLabels.flat())
|
||||
|
||||
const newTasks = tasksToCreate.map(async ({title, list}) => {
|
||||
if (title === '') {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// If the task has a list specified, make sure to use it
|
||||
let listId = null
|
||||
if (list !== null) {
|
||||
|
@ -72,7 +72,7 @@ export const parseTaskText = (text: string, prefixesMode: PrefixMode = PrefixMod
|
||||
return result
|
||||
}
|
||||
|
||||
result.labels = getItemsFromPrefix(text, prefixes.label)
|
||||
result.labels = getLabelsFromPrefix(text, prefixes.label) ?? []
|
||||
result.text = cleanupItemText(result.text, result.labels, prefixes.label)
|
||||
|
||||
result.list = getListFromPrefix(result.text, prefixes.list)
|
||||
@ -142,6 +142,17 @@ export const getListFromPrefix = (text: string, listPrefix: string | null = null
|
||||
return lists.length > 0 ? lists[0] : null
|
||||
}
|
||||
|
||||
export const getLabelsFromPrefix = (text: string, listPrefix: string | null = null): string[] | null => {
|
||||
if (listPrefix === null) {
|
||||
const prefixes = PREFIXES[getQuickAddMagicMode()]
|
||||
if (prefixes === undefined) {
|
||||
return null
|
||||
}
|
||||
listPrefix = prefixes.label
|
||||
}
|
||||
return getItemsFromPrefix(text, listPrefix)
|
||||
}
|
||||
|
||||
const getPriority = (text: string, prefix: string): number | null => {
|
||||
const ps = getItemsFromPrefix(text, prefix)
|
||||
if (ps.length === 0) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {computed, ref} from 'vue'
|
||||
import {defineStore, acceptHMRUpdate} from 'pinia'
|
||||
import {acceptHMRUpdate, defineStore} from 'pinia'
|
||||
import router from '@/router'
|
||||
import {formatISO} from 'date-fns'
|
||||
|
||||
@ -14,8 +14,8 @@ import {parseTaskText} from '@/modules/parseTaskText'
|
||||
|
||||
import TaskAssigneeModel from '@/models/taskAssignee'
|
||||
import LabelTaskModel from '@/models/labelTask'
|
||||
import TaskModel from '@/models/task'
|
||||
import LabelTask from '@/models/labelTask'
|
||||
import TaskModel from '@/models/task'
|
||||
import LabelModel from '@/models/label'
|
||||
|
||||
import type {ILabel} from '@/modelTypes/ILabel'
|
||||
@ -306,6 +306,20 @@ export const useTaskStore = defineStore('task', () => {
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
async function ensureLabelsExist(labels: string[]): Promise<LabelModel[]> {
|
||||
const all = [...new Set(labels)]
|
||||
const mustCreateLabel = all.map(async labelTitle => {
|
||||
let label = validateLabel(Object.values(labelStore.labels), labelTitle)
|
||||
if (typeof label === 'undefined') {
|
||||
// label not found, create it
|
||||
const labelModel = new LabelModel({title: labelTitle})
|
||||
label = await labelStore.createLabel(labelModel)
|
||||
}
|
||||
return label
|
||||
})
|
||||
return Promise.all(mustCreateLabel)
|
||||
}
|
||||
|
||||
// Do everything that is involved in finding, creating and adding the label to the task
|
||||
async function addLabelsToTask(
|
||||
@ -316,16 +330,8 @@ export const useTaskStore = defineStore('task', () => {
|
||||
return task
|
||||
}
|
||||
|
||||
const labelAddsToWaitFor = parsedLabels.map(async labelTitle => {
|
||||
let label = validateLabel(Object.values(labelStore.labels), labelTitle)
|
||||
if (typeof label === 'undefined') {
|
||||
// label not found, create it
|
||||
const labelModel = new LabelModel({title: labelTitle})
|
||||
label = await labelStore.createLabel(labelModel)
|
||||
}
|
||||
|
||||
return addLabelToTask(task, label)
|
||||
})
|
||||
const labels = await ensureLabelsExist(parsedLabels)
|
||||
const labelAddsToWaitFor = labels.map(async l => addLabelToTask(task, l))
|
||||
|
||||
// This waits until all labels are created and added to the task
|
||||
await Promise.all(labelAddsToWaitFor)
|
||||
@ -402,11 +408,10 @@ export const useTaskStore = defineStore('task', () => {
|
||||
const taskService = new TaskService()
|
||||
try {
|
||||
const createdTask = await taskService.create(task)
|
||||
const result = await addLabelsToTask({
|
||||
return await addLabelsToTask({
|
||||
task: createdTask,
|
||||
parsedLabels: parsedTask.labels,
|
||||
})
|
||||
return result
|
||||
} finally {
|
||||
cancel()
|
||||
}
|
||||
@ -438,6 +443,7 @@ export const useTaskStore = defineStore('task', () => {
|
||||
createNewTask,
|
||||
setCoverImage,
|
||||
findListId,
|
||||
ensureLabelsExist,
|
||||
}
|
||||
})
|
||||
|
||||
|
Reference in New Issue
Block a user