feat: port tasks store to pina (#2409)
Reviewed-on: https://kolaente.dev/vikunja/frontend/pulls/2409
This commit is contained in:
commit
8c394d8024
20 changed files with 158 additions and 131 deletions
|
@ -73,6 +73,7 @@ import {PREFIXES} from '@/modules/parseTaskText'
|
|||
import {useListStore} from '@/stores/lists'
|
||||
import {useNamespaceStore} from '@/stores/namespaces'
|
||||
import {useLabelStore} from '@/stores/labels'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
|
||||
const TYPE_LIST = 'list'
|
||||
const TYPE_TASK = 'task'
|
||||
|
@ -412,7 +413,8 @@ export default defineComponent({
|
|||
return
|
||||
}
|
||||
|
||||
const task = await this.$store.dispatch('tasks/createNewTask', {
|
||||
const taskStore = useTaskStore()
|
||||
const task = await taskStore.createNewTask({
|
||||
title: this.query,
|
||||
listId: this.currentList.id,
|
||||
})
|
||||
|
|
|
@ -43,12 +43,11 @@
|
|||
<script setup lang="ts">
|
||||
import {ref, watch, unref, computed} from 'vue'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import {useStore} from '@/store'
|
||||
import {tryOnMounted, debouncedWatch, useWindowSize, type MaybeRef} from '@vueuse/core'
|
||||
|
||||
import QuickAddMagic from '@/components/tasks/partials/quick-add-magic.vue'
|
||||
import {LOADING, LOADING_MODULE} from '@/store/mutation-types'
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
|
||||
function cleanupTitle(title: string) {
|
||||
return title.replace(/^((\* |\+ |- )(\[ \] )?)/g, '')
|
||||
|
@ -135,8 +134,8 @@ const newTaskTitle = ref('')
|
|||
const newTaskInput = useAutoHeightTextarea(newTaskTitle)
|
||||
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
const store = useStore()
|
||||
const authStore = useAuthStore()
|
||||
const taskStore = useTaskStore()
|
||||
|
||||
const errorMessage = ref('')
|
||||
|
||||
|
@ -149,7 +148,7 @@ function resetEmptyTitleError(e) {
|
|||
}
|
||||
}
|
||||
|
||||
const loading = computed(() => store.state[LOADING] && store.state[LOADING_MODULE] === 'tasks')
|
||||
const loading = computed(() => taskStore.isLoading)
|
||||
async function addTask() {
|
||||
if (newTaskTitle.value === '') {
|
||||
errorMessage.value = t('list.create.addTitleRequired')
|
||||
|
@ -168,7 +167,7 @@ async function addTask() {
|
|||
return
|
||||
}
|
||||
|
||||
const task = await store.dispatch('tasks/createNewTask', {
|
||||
const task = await taskStore.createNewTask({
|
||||
title,
|
||||
listId: authStore.settings.defaultListId,
|
||||
position: props.defaultPosition,
|
||||
|
|
|
@ -32,11 +32,12 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import {ref,computed, watch, type PropType} from 'vue'
|
||||
import {useStore} from '@/store'
|
||||
|
||||
import Editor from '@/components/input/AsyncEditor'
|
||||
|
||||
import type {ITask} from '@/modelTypes/ITask'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
import TaskModel from '@/models/task'
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
|
@ -55,14 +56,14 @@ const props = defineProps({
|
|||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const task = ref<ITask>({description: ''})
|
||||
const task = ref<ITask>(new TaskModel())
|
||||
const saved = ref(false)
|
||||
|
||||
// Since loading is global state, this variable ensures we're only showing the saving icon when saving the description.
|
||||
const saving = ref(false)
|
||||
|
||||
const store = useStore()
|
||||
const loading = computed(() => store.state.loading)
|
||||
const taskStore = useTaskStore()
|
||||
const loading = computed(() => taskStore.isLoading)
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
|
@ -77,7 +78,7 @@ async function save() {
|
|||
|
||||
try {
|
||||
// FIXME: don't update state from internal.
|
||||
task.value = await store.dispatch('tasks/update', task.value)
|
||||
task.value = await taskStore.update(task.value)
|
||||
emit('update:modelValue', task.value)
|
||||
|
||||
saved.value = true
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import {ref, shallowReactive, watch, type PropType} from 'vue'
|
||||
import {useStore} from '@/store'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
|
||||
import User from '@/components/misc/user.vue'
|
||||
|
@ -39,7 +38,9 @@ import BaseButton from '@/components/base/BaseButton.vue'
|
|||
import {includesById} from '@/helpers/utils'
|
||||
import ListUserService from '@/services/listUsers'
|
||||
import {success} from '@/message'
|
||||
import type { IUser } from '@/modelTypes/IUser'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
|
||||
import type {IUser} from '@/modelTypes/IUser'
|
||||
|
||||
const props = defineProps({
|
||||
taskId: {
|
||||
|
@ -60,7 +61,7 @@ const props = defineProps({
|
|||
})
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const store = useStore()
|
||||
const taskStore = useTaskStore()
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
|
||||
const listUserService = shallowReactive(new ListUserService())
|
||||
|
@ -79,13 +80,13 @@ watch(
|
|||
)
|
||||
|
||||
async function addAssignee(user: IUser) {
|
||||
await store.dispatch('tasks/addAssignee', {user: user, taskId: props.taskId})
|
||||
await taskStore.addAssignee({user: user, taskId: props.taskId})
|
||||
emit('update:modelValue', assignees.value)
|
||||
success({message: t('task.assignee.assignSuccess')})
|
||||
}
|
||||
|
||||
async function removeAssignee(user: IUser) {
|
||||
await store.dispatch('tasks/removeAssignee', {user: user, taskId: props.taskId})
|
||||
await taskStore.removeAssignee({user: user, taskId: props.taskId})
|
||||
|
||||
// Remove the assignee from the list
|
||||
for (const a in assignees.value) {
|
||||
|
|
|
@ -40,7 +40,6 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import {type PropType, ref, computed, shallowReactive, watch} from 'vue'
|
||||
import {useStore} from '@/store'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
|
||||
import LabelModel from '@/models/label'
|
||||
|
@ -49,8 +48,9 @@ import {success} from '@/message'
|
|||
|
||||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
import Multiselect from '@/components/input/multiselect.vue'
|
||||
import type { ILabel } from '@/modelTypes/ILabel'
|
||||
import { useLabelStore } from '@/stores/labels'
|
||||
import type {ILabel} from '@/modelTypes/ILabel'
|
||||
import {useLabelStore} from '@/stores/labels'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
|
@ -69,7 +69,6 @@ const props = defineProps({
|
|||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const store = useStore()
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
|
||||
const labelTaskService = shallowReactive(new LabelTaskService())
|
||||
|
@ -87,6 +86,7 @@ watch(
|
|||
},
|
||||
)
|
||||
|
||||
const taskStore = useTaskStore()
|
||||
const labelStore = useLabelStore()
|
||||
|
||||
const foundLabels = computed(() => labelStore.filterLabelsByQuery(labels.value, query.value))
|
||||
|
@ -97,17 +97,13 @@ function findLabel(newQuery: string) {
|
|||
}
|
||||
|
||||
async function addLabel(label: ILabel, showNotification = true) {
|
||||
const bubble = () => {
|
||||
emit('update:modelValue', labels.value)
|
||||
}
|
||||
|
||||
if (props.taskId === 0) {
|
||||
bubble()
|
||||
emit('update:modelValue', labels.value)
|
||||
return
|
||||
}
|
||||
|
||||
await store.dispatch('tasks/addLabel', {label, taskId: props.taskId})
|
||||
bubble()
|
||||
await taskStore.addLabel({label, taskId: props.taskId})
|
||||
emit('update:modelValue', labels.value)
|
||||
if (showNotification) {
|
||||
success({message: t('task.label.addSuccess')})
|
||||
}
|
||||
|
@ -115,7 +111,7 @@ async function addLabel(label: ILabel, showNotification = true) {
|
|||
|
||||
async function removeLabel(label: ILabel) {
|
||||
if (props.taskId !== 0) {
|
||||
await store.dispatch('tasks/removeLabel', {label, taskId: props.taskId})
|
||||
await taskStore.removeLabel({label, taskId: props.taskId})
|
||||
}
|
||||
|
||||
for (const l in labels.value) {
|
||||
|
|
|
@ -38,7 +38,6 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import {ref, computed, type PropType} from 'vue'
|
||||
import {useStore} from '@/store'
|
||||
import {useRouter} from 'vue-router'
|
||||
|
||||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
|
@ -48,6 +47,7 @@ import {useCopyToClipboard} from '@/composables/useCopyToClipboard'
|
|||
|
||||
import type {ITask} from '@/modelTypes/ITask'
|
||||
import ColorBubble from '@/components/misc/colorBubble.vue'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
|
||||
const props = defineProps({
|
||||
task: {
|
||||
|
@ -72,8 +72,8 @@ async function copyUrl() {
|
|||
await copy(absoluteURL)
|
||||
}
|
||||
|
||||
const store = useStore()
|
||||
const loading = computed(() => store.state.loading)
|
||||
const taskStore = useTaskStore()
|
||||
const loading = computed(() => taskStore.isLoading)
|
||||
|
||||
const textIdentifier = computed(() => props.task?.getTextIdentifier() || '')
|
||||
|
||||
|
@ -93,7 +93,7 @@ async function save(title: string) {
|
|||
|
||||
try {
|
||||
saving.value = true
|
||||
const newTask = await store.dispatch('tasks/update', {
|
||||
const newTask = await taskStore.update({
|
||||
...props.task,
|
||||
title,
|
||||
})
|
||||
|
|
|
@ -78,6 +78,7 @@ import type {ITask} from '@/modelTypes/ITask'
|
|||
|
||||
import {formatDateLong, formatISO, formatDateSince} from '@/helpers/time/formatDate'
|
||||
import {colorIsDark} from '@/helpers/color/colorIsDark'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'kanban-card',
|
||||
|
@ -121,7 +122,7 @@ export default defineComponent({
|
|||
this.loadingInternal = true
|
||||
try {
|
||||
const done = !task.done
|
||||
await this.$store.dispatch('tasks/update', {
|
||||
await useTaskStore().update({
|
||||
...task,
|
||||
done,
|
||||
})
|
||||
|
|
|
@ -149,7 +149,6 @@
|
|||
import {ref, reactive, shallowReactive, watch, computed, type PropType} from 'vue'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import {useRoute} from 'vue-router'
|
||||
import {useStore} from '@/store'
|
||||
|
||||
import TaskService from '@/services/task'
|
||||
import TaskModel from '@/models/task'
|
||||
|
@ -167,6 +166,7 @@ import Fancycheckbox from '@/components/input/fancycheckbox.vue'
|
|||
import {useNamespaceStore} from '@/stores/namespaces'
|
||||
|
||||
import {error, success} from '@/message'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
|
||||
const props = defineProps({
|
||||
taskId: {
|
||||
|
@ -190,7 +190,7 @@ const props = defineProps({
|
|||
},
|
||||
})
|
||||
|
||||
const store = useStore()
|
||||
const taskStore = useTaskStore()
|
||||
const namespaceStore = useNamespaceStore()
|
||||
const route = useRoute()
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
|
@ -344,7 +344,7 @@ async function createAndRelateTask(title: string) {
|
|||
}
|
||||
|
||||
async function toggleTaskDone(task: ITask) {
|
||||
await store.dispatch('tasks/update', task)
|
||||
await taskStore.update(task)
|
||||
|
||||
// Find the task in the list and update it so that it is correctly strike through
|
||||
Object.entries(relatedTasks.value).some(([kind, tasks]) => {
|
||||
|
|
|
@ -119,6 +119,7 @@ import {formatDateSince, formatISO, formatDateLong} from '@/helpers/time/formatD
|
|||
import ColorBubble from '@/components/misc/colorBubble.vue'
|
||||
import {useListStore} from '@/stores/lists'
|
||||
import {useNamespaceStore} from '@/stores/namespaces'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'singleTaskInList',
|
||||
|
@ -208,7 +209,7 @@ export default defineComponent({
|
|||
|
||||
async markAsDone(checked: boolean) {
|
||||
const updateFunc = async () => {
|
||||
const task = await this.$store.dispatch('tasks/update', this.task)
|
||||
const task = await useTaskStore().update(this.task)
|
||||
this.task = task
|
||||
this.$emit('task-updated', task)
|
||||
this.$message.success({
|
||||
|
|
|
@ -2,7 +2,7 @@ import AttachmentModel from '@/models/attachment'
|
|||
import type {IAttachment} from '@/modelTypes/IAttachment'
|
||||
|
||||
import AttachmentService from '@/services/attachment'
|
||||
import { store } from '@/store'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
|
||||
export function uploadFile(taskId: number, file: File, onSuccess: (url: string) => void) {
|
||||
const attachmentService = new AttachmentService()
|
||||
|
@ -22,7 +22,7 @@ export async function uploadFiles(
|
|||
console.debug(`Uploaded attachments for task ${taskId}, response was`, response)
|
||||
|
||||
response.success?.map((attachment: IAttachment) => {
|
||||
store.dispatch('tasks/addTaskAttachment', {
|
||||
useTaskStore().addTaskAttachment({
|
||||
taskId,
|
||||
attachment,
|
||||
})
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import {createApp} from 'vue'
|
||||
|
||||
import App from './App.vue'
|
||||
import pinia from './pinia'
|
||||
import router from './router'
|
||||
import { createPinia } from 'pinia'
|
||||
import App from './App.vue'
|
||||
|
||||
import {error, success} from './message'
|
||||
|
||||
|
@ -105,7 +105,6 @@ if (window.SENTRY_ENABLED) {
|
|||
import('./sentry').then(sentry => sentry.default(app, router))
|
||||
}
|
||||
|
||||
const pinia = createPinia()
|
||||
app.use(pinia)
|
||||
|
||||
app.use(store, key) // pass the injection key
|
||||
|
|
5
src/pinia.ts
Normal file
5
src/pinia.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import {createPinia} from 'pinia'
|
||||
|
||||
const pinia = createPinia()
|
||||
|
||||
export default pinia
|
|
@ -14,7 +14,6 @@ import {
|
|||
QUICK_ACTIONS_ACTIVE,
|
||||
} from './mutation-types'
|
||||
import kanban from './modules/kanban'
|
||||
import tasks from './modules/tasks'
|
||||
|
||||
import ListModel from '@/models/list'
|
||||
|
||||
|
@ -22,6 +21,7 @@ import ListService from '../services/list'
|
|||
import {checkAndSetApiUrl} from '@/helpers/checkAndSetApiUrl'
|
||||
|
||||
import type { RootStoreState, StoreState } from './types'
|
||||
import pinia from '@/pinia'
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
|
||||
export const key: InjectionKey<Store<StoreState>> = Symbol()
|
||||
|
@ -35,7 +35,6 @@ export const store = createStore<RootStoreState>({
|
|||
strict: import.meta.env.DEV,
|
||||
modules: {
|
||||
kanban,
|
||||
tasks,
|
||||
},
|
||||
state: () => ({
|
||||
loading: false,
|
||||
|
@ -132,7 +131,7 @@ export const store = createStore<RootStoreState>({
|
|||
},
|
||||
async loadApp() {
|
||||
await checkAndSetApiUrl(window.API_URL)
|
||||
const authStore = useAuthStore()
|
||||
const authStore = useAuthStore(pinia)
|
||||
await authStore.checkAuth()
|
||||
},
|
||||
},
|
||||
|
|
|
@ -94,7 +94,9 @@ export interface NamespaceState {
|
|||
isLoading: boolean,
|
||||
}
|
||||
|
||||
export interface TaskState {}
|
||||
export interface TaskState {
|
||||
isLoading: boolean,
|
||||
}
|
||||
|
||||
|
||||
export type StoreState = RootStoreState & {
|
||||
|
|
|
@ -124,7 +124,7 @@ export const useLabelStore = defineStore('label', {
|
|||
const labelService = new LabelService()
|
||||
|
||||
try {
|
||||
const newLabel = await labelService.create(label)
|
||||
const newLabel = await labelService.create(label) as ILabel
|
||||
this.setLabel(newLabel)
|
||||
return newLabel
|
||||
} finally {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { Module } from 'vuex'
|
||||
import {defineStore, acceptHMRUpdate} from 'pinia'
|
||||
import router from '@/router'
|
||||
import {formatISO} from 'date-fns'
|
||||
|
||||
|
@ -7,8 +7,8 @@ import TaskAssigneeService from '@/services/taskAssignee'
|
|||
import LabelTaskService from '@/services/labelTask'
|
||||
import UserService from '@/services/user'
|
||||
|
||||
import {HAS_TASKS} from '../mutation-types'
|
||||
import {setLoading} from '../helper'
|
||||
import {HAS_TASKS} from '../store/mutation-types'
|
||||
import {setLoadingPinia} from '../store/helper'
|
||||
import {getQuickAddMagicMode} from '@/helpers/quickAddMagicMode'
|
||||
import {parseTaskText} from '@/modules/parseTaskText'
|
||||
|
||||
|
@ -20,15 +20,16 @@ import LabelModel from '@/models/label'
|
|||
|
||||
import type {ILabel} from '@/modelTypes/ILabel'
|
||||
import type {ITask} from '@/modelTypes/ITask'
|
||||
import type { IUser } from '@/modelTypes/IUser'
|
||||
import type { IAttachment } from '@/modelTypes/IAttachment'
|
||||
import type { IList } from '@/modelTypes/IList'
|
||||
import type {IUser} from '@/modelTypes/IUser'
|
||||
import type {IAttachment} from '@/modelTypes/IAttachment'
|
||||
import type {IList} from '@/modelTypes/IList'
|
||||
|
||||
import type { RootStoreState, TaskState } from '@/store/types'
|
||||
import type {TaskState} from '@/store/types'
|
||||
import {useLabelStore} from '@/stores/labels'
|
||||
import {useListStore} from '@/stores/lists'
|
||||
import {useAttachmentStore} from '@/stores/attachments'
|
||||
import {playPop} from '@/helpers/playPop'
|
||||
import {store} from '@/store'
|
||||
|
||||
// IDEA: maybe use a small fuzzy search here to prevent errors
|
||||
function findPropertyByValue(object, key, value) {
|
||||
|
@ -43,7 +44,7 @@ function validateUsername(users: IUser[], username: IUser['username']) {
|
|||
}
|
||||
|
||||
// Check if the label exists
|
||||
function validateLabel(labels: ILabel[], label: ILabel) {
|
||||
function validateLabel(labels: ILabel[], label: string) {
|
||||
return findPropertyByValue(labels, 'title', label)
|
||||
}
|
||||
|
||||
|
@ -58,7 +59,7 @@ async function addLabelToTask(task: ITask, label: ILabel) {
|
|||
return response
|
||||
}
|
||||
|
||||
async function findAssignees(parsedTaskAssignees) {
|
||||
async function findAssignees(parsedTaskAssignees: string[]) {
|
||||
if (parsedTaskAssignees.length <= 0) {
|
||||
return []
|
||||
}
|
||||
|
@ -74,30 +75,31 @@ async function findAssignees(parsedTaskAssignees) {
|
|||
}
|
||||
|
||||
|
||||
const tasksStore : Module<TaskState, RootStoreState>= {
|
||||
namespaced: true,
|
||||
state: () => ({}),
|
||||
export const useTaskStore = defineStore('task', {
|
||||
state: () : TaskState => ({
|
||||
isLoading: false,
|
||||
}),
|
||||
actions: {
|
||||
async loadTasks(ctx, params) {
|
||||
async loadTasks(params) {
|
||||
const taskService = new TaskService()
|
||||
|
||||
const cancel = setLoading(ctx, 'tasks')
|
||||
const cancel = setLoadingPinia(this)
|
||||
try {
|
||||
const tasks = await taskService.getAll({}, params)
|
||||
ctx.commit(HAS_TASKS, tasks.length > 0, {root: true})
|
||||
store.commit(HAS_TASKS, tasks.length > 0)
|
||||
return tasks
|
||||
} finally {
|
||||
cancel()
|
||||
}
|
||||
},
|
||||
|
||||
async update(ctx, task: ITask) {
|
||||
const cancel = setLoading(ctx, 'tasks')
|
||||
async update(task: ITask) {
|
||||
const cancel = setLoadingPinia(this)
|
||||
|
||||
const taskService = new TaskService()
|
||||
try {
|
||||
const updatedTask = await taskService.update(task)
|
||||
ctx.commit('kanban/setTaskInBucket', updatedTask, {root: true})
|
||||
store.commit('kanban/setTaskInBucket', updatedTask)
|
||||
if (task.done) {
|
||||
playPop()
|
||||
}
|
||||
|
@ -107,23 +109,23 @@ const tasksStore : Module<TaskState, RootStoreState>= {
|
|||
}
|
||||
},
|
||||
|
||||
async delete(ctx, task: ITask) {
|
||||
async delete(task: ITask) {
|
||||
const taskService = new TaskService()
|
||||
const response = await taskService.delete(task)
|
||||
ctx.commit('kanban/removeTaskInBucket', task, {root: true})
|
||||
store.commit('kanban/removeTaskInBucket', task)
|
||||
return response
|
||||
},
|
||||
|
||||
// Adds a task attachment in store.
|
||||
// This is an action to be able to commit other mutations
|
||||
addTaskAttachment(ctx, {
|
||||
addTaskAttachment({
|
||||
taskId,
|
||||
attachment,
|
||||
}: {
|
||||
taskId: ITask['id']
|
||||
attachment: IAttachment
|
||||
}) {
|
||||
const t = ctx.rootGetters['kanban/getTaskById'](taskId)
|
||||
const t = store.getters['kanban/getTaskById'](taskId)
|
||||
if (t.task !== null) {
|
||||
const attachments = [
|
||||
...t.task.attachments,
|
||||
|
@ -137,24 +139,25 @@ const tasksStore : Module<TaskState, RootStoreState>= {
|
|||
attachments,
|
||||
},
|
||||
}
|
||||
ctx.commit('kanban/setTaskInBucketByIndex', newTask, {root: true})
|
||||
store.commit('kanban/setTaskInBucketByIndex', newTask)
|
||||
}
|
||||
const attachmentStore = useAttachmentStore()
|
||||
attachmentStore.add(attachment)
|
||||
},
|
||||
|
||||
async addAssignee(ctx, {
|
||||
async addAssignee({
|
||||
user,
|
||||
taskId,
|
||||
}: {
|
||||
user: IUser,
|
||||
taskId: ITask['id']
|
||||
}) {
|
||||
const taskAssignee = new TaskAssigneeModel({userId: user.id, taskId: taskId})
|
||||
|
||||
const taskAssigneeService = new TaskAssigneeService()
|
||||
const r = await taskAssigneeService.create(taskAssignee)
|
||||
const t = ctx.rootGetters['kanban/getTaskById'](taskId)
|
||||
const r = await taskAssigneeService.create(new TaskAssigneeModel({
|
||||
userId: user.id,
|
||||
taskId: taskId,
|
||||
}))
|
||||
const t = store.getters['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.
|
||||
|
@ -163,33 +166,32 @@ const tasksStore : Module<TaskState, RootStoreState>= {
|
|||
return r
|
||||
}
|
||||
|
||||
const assignees = [
|
||||
...t.task.assignees,
|
||||
user,
|
||||
]
|
||||
|
||||
ctx.commit('kanban/setTaskInBucketByIndex', {
|
||||
store.commit('kanban/setTaskInBucketByIndex', {
|
||||
...t,
|
||||
task: {
|
||||
...t.task,
|
||||
assignees,
|
||||
assignees: [
|
||||
...t.task.assignees,
|
||||
user,
|
||||
],
|
||||
},
|
||||
}, {root: true})
|
||||
})
|
||||
return r
|
||||
},
|
||||
|
||||
async removeAssignee(ctx, {
|
||||
async removeAssignee({
|
||||
user,
|
||||
taskId,
|
||||
}: {
|
||||
user: IUser,
|
||||
taskId: ITask['id']
|
||||
}) {
|
||||
const taskAssignee = new TaskAssigneeModel({userId: user.id, taskId: taskId})
|
||||
|
||||
const taskAssigneeService = new TaskAssigneeService()
|
||||
const response = await taskAssigneeService.delete(taskAssignee)
|
||||
const t = ctx.rootGetters['kanban/getTaskById'](taskId)
|
||||
const response = await taskAssigneeService.delete(new TaskAssigneeModel({
|
||||
userId: user.id,
|
||||
taskId: taskId,
|
||||
}))
|
||||
const t = store.getters['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.
|
||||
|
@ -200,29 +202,30 @@ const tasksStore : Module<TaskState, RootStoreState>= {
|
|||
|
||||
const assignees = t.task.assignees.filter(({ id }) => id !== user.id)
|
||||
|
||||
ctx.commit('kanban/setTaskInBucketByIndex', {
|
||||
store.commit('kanban/setTaskInBucketByIndex', {
|
||||
...t,
|
||||
task: {
|
||||
...t.task,
|
||||
assignees,
|
||||
},
|
||||
}, {root: true})
|
||||
})
|
||||
return response
|
||||
|
||||
},
|
||||
|
||||
async addLabel(ctx, {
|
||||
async addLabel({
|
||||
label,
|
||||
taskId,
|
||||
} : {
|
||||
label: ILabel,
|
||||
taskId: ITask['id']
|
||||
}) {
|
||||
const labelTask = new LabelTaskModel({taskId, labelId: label.id})
|
||||
|
||||
const labelTaskService = new LabelTaskService()
|
||||
const r = await labelTaskService.create(labelTask)
|
||||
const t = ctx.rootGetters['kanban/getTaskById'](taskId)
|
||||
const r = await labelTaskService.create(new LabelTaskModel({
|
||||
taskId,
|
||||
labelId: label.id,
|
||||
}))
|
||||
const t = store.getters['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.
|
||||
|
@ -231,28 +234,30 @@ const tasksStore : Module<TaskState, RootStoreState>= {
|
|||
return r
|
||||
}
|
||||
|
||||
const labels = [
|
||||
...t.task.labels,
|
||||
label,
|
||||
]
|
||||
|
||||
ctx.commit('kanban/setTaskInBucketByIndex', {
|
||||
store.commit('kanban/setTaskInBucketByIndex', {
|
||||
...t,
|
||||
task: {
|
||||
...t.task,
|
||||
labels,
|
||||
labels: [
|
||||
...t.task.labels,
|
||||
label,
|
||||
],
|
||||
},
|
||||
}, {root: true})
|
||||
})
|
||||
|
||||
return r
|
||||
},
|
||||
|
||||
async removeLabel(ctx, {label, taskId}) {
|
||||
const labelTask = new LabelTaskModel({taskId, labelId: label.id})
|
||||
|
||||
async removeLabel(
|
||||
{label, taskId}:
|
||||
{label: ILabel, taskId: ITask['id']},
|
||||
) {
|
||||
const labelTaskService = new LabelTaskService()
|
||||
const response = await labelTaskService.delete(labelTask)
|
||||
const t = ctx.rootGetters['kanban/getTaskById'](taskId)
|
||||
const response = await labelTaskService.delete(new LabelTaskModel({
|
||||
taskId, labelId:
|
||||
label.id,
|
||||
}))
|
||||
const t = store.getters['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.
|
||||
|
@ -264,19 +269,22 @@ const tasksStore : Module<TaskState, RootStoreState>= {
|
|||
// Remove the label from the list
|
||||
const labels = t.task.labels.filter(({ id }) => id !== label.id)
|
||||
|
||||
ctx.commit('kanban/setTaskInBucketByIndex', {
|
||||
store.commit('kanban/setTaskInBucketByIndex', {
|
||||
...t,
|
||||
task: {
|
||||
...t.task,
|
||||
labels,
|
||||
},
|
||||
}, {root: true})
|
||||
})
|
||||
|
||||
return response
|
||||
},
|
||||
|
||||
// Do everything that is involved in finding, creating and adding the label to the task
|
||||
async addLabelsToTask(_, { task, parsedLabels }) {
|
||||
async addLabelsToTask(
|
||||
{ task, parsedLabels }:
|
||||
{ task: ITask, parsedLabels: string[] },
|
||||
) {
|
||||
if (parsedLabels.length <= 0) {
|
||||
return task
|
||||
}
|
||||
|
@ -299,10 +307,9 @@ const tasksStore : Module<TaskState, RootStoreState>= {
|
|||
return task
|
||||
},
|
||||
|
||||
findListId(_, { list: listName, listId }: {
|
||||
list: string,
|
||||
listId: IList['id']
|
||||
}) {
|
||||
findListId(
|
||||
{ list: listName, listId }:
|
||||
{ list: string, listId: IList['id'] }) {
|
||||
let foundListId = null
|
||||
|
||||
// Uses the following ways to get the list id of the new task:
|
||||
|
@ -320,7 +327,7 @@ const tasksStore : Module<TaskState, RootStoreState>= {
|
|||
|
||||
// 3. Otherwise use the id from the route parameter
|
||||
if (typeof router.currentRoute.value.params.listId !== 'undefined') {
|
||||
foundListId = parseInt(router.currentRoute.value.params.listId)
|
||||
foundListId = Number(router.currentRoute.value.params.listId)
|
||||
}
|
||||
|
||||
// 4. If none of the above worked, reject the promise with an error.
|
||||
|
@ -331,7 +338,7 @@ const tasksStore : Module<TaskState, RootStoreState>= {
|
|||
return foundListId
|
||||
},
|
||||
|
||||
async createNewTask(ctx, {
|
||||
async createNewTask({
|
||||
title,
|
||||
bucketId,
|
||||
listId,
|
||||
|
@ -339,10 +346,10 @@ const tasksStore : Module<TaskState, RootStoreState>= {
|
|||
} :
|
||||
Partial<ITask>,
|
||||
) {
|
||||
const cancel = setLoading(ctx, 'tasks')
|
||||
const cancel = setLoadingPinia(this)
|
||||
const parsedTask = parseTaskText(title, getQuickAddMagicMode())
|
||||
|
||||
const foundListId = await ctx.dispatch('findListId', {
|
||||
const foundListId = await this.findListId({
|
||||
list: parsedTask.list,
|
||||
listId: listId || 0,
|
||||
})
|
||||
|
@ -369,7 +376,7 @@ const tasksStore : Module<TaskState, RootStoreState>= {
|
|||
|
||||
const taskService = new TaskService()
|
||||
const createdTask = await taskService.create(task)
|
||||
const result = await ctx.dispatch('addLabelsToTask', {
|
||||
const result = await this.addLabelsToTask({
|
||||
task: createdTask,
|
||||
parsedLabels: parsedTask.labels,
|
||||
})
|
||||
|
@ -377,6 +384,9 @@ const tasksStore : Module<TaskState, RootStoreState>= {
|
|||
return result
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
export default tasksStore
|
||||
// support hot reloading
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.accept(acceptHMRUpdate(useTaskStore, import.meta.hot))
|
||||
}
|
|
@ -240,6 +240,7 @@ import {calculateItemPosition} from '../../helpers/calculateItemPosition'
|
|||
import KanbanCard from '@/components/tasks/partials/kanban-card.vue'
|
||||
import DropdownItem from '@/components/misc/dropdown-item.vue'
|
||||
import {isSavedFilter} from '@/helpers/savedFilter'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
|
||||
const DRAG_OPTIONS = {
|
||||
// sortable options
|
||||
|
@ -433,7 +434,8 @@ export default defineComponent({
|
|||
)
|
||||
|
||||
try {
|
||||
await this.$store.dispatch('tasks/update', newTask)
|
||||
const taskStore = useTaskStore()
|
||||
await taskStore.update(newTask)
|
||||
|
||||
// Make sure the first and second task don't both get position 0 assigned
|
||||
if(newTaskIndex === 0 && taskAfter !== null && taskAfter.kanbanPosition === 0) {
|
||||
|
@ -445,7 +447,7 @@ export default defineComponent({
|
|||
taskAfterAfter !== null ? taskAfterAfter.kanbanPosition : null,
|
||||
)
|
||||
|
||||
await this.$store.dispatch('tasks/update', newTaskAfter)
|
||||
await taskStore.update(newTaskAfter)
|
||||
}
|
||||
} finally {
|
||||
this.taskUpdating[task.id] = false
|
||||
|
@ -464,7 +466,7 @@ export default defineComponent({
|
|||
}
|
||||
this.newTaskError[bucketId] = false
|
||||
|
||||
const task = await this.$store.dispatch('tasks/createNewTask', {
|
||||
const task = await useTaskStore().createNewTask({
|
||||
title: this.newTaskText,
|
||||
bucketId,
|
||||
listId: this.listId,
|
||||
|
|
|
@ -139,7 +139,7 @@ export default { name: 'List' }
|
|||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref, computed, toRef, nextTick, onMounted} from 'vue'
|
||||
import {ref, computed, toRef, nextTick, onMounted, type PropType} from 'vue'
|
||||
import draggable from 'zhyswan-vuedraggable'
|
||||
import {useRoute, useRouter} from 'vue-router'
|
||||
|
||||
|
@ -161,6 +161,9 @@ import {RIGHTS as Rights} from '@/constants/rights'
|
|||
import {calculateItemPosition} from '@/helpers/calculateItemPosition'
|
||||
import type {ITask} from '@/modelTypes/ITask'
|
||||
import {isSavedFilter} from '@/helpers/savedFilter'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
|
||||
import type {IList} from '@/modelTypes/IList'
|
||||
|
||||
function sortTasks(tasks: ITask[]) {
|
||||
if (tasks === null || Array.isArray(tasks) && tasks.length === 0) {
|
||||
|
@ -182,7 +185,7 @@ function sortTasks(tasks: ITask[]) {
|
|||
|
||||
const props = defineProps({
|
||||
listId: {
|
||||
type: Number,
|
||||
type: Number as PropType<IList['id']>,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
@ -224,6 +227,7 @@ const firstNewPosition = computed(() => {
|
|||
return calculateItemPosition(null, tasks.value[0].position)
|
||||
})
|
||||
|
||||
const taskStore = useTaskStore()
|
||||
const store = useStore()
|
||||
const list = computed(() => store.state.currentList)
|
||||
|
||||
|
@ -238,6 +242,7 @@ onMounted(async () => {
|
|||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
function searchTasks() {
|
||||
// Only search if the search term changed
|
||||
if (route.query as unknown as string === searchTerm.value) {
|
||||
|
@ -309,7 +314,7 @@ async function saveTaskPosition(e) {
|
|||
position: calculateItemPosition(taskBefore !== null ? taskBefore.position : null, taskAfter !== null ? taskAfter.position : null),
|
||||
}
|
||||
|
||||
const updatedTask = await this.$store.dispatch('tasks/update', newTask)
|
||||
const updatedTask = await taskStore.update(newTask)
|
||||
tasks.value[e.newIndex] = updatedTask
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -60,9 +60,11 @@ import {LOADING, LOADING_MODULE} from '@/store/mutation-types'
|
|||
import LlamaCool from '@/assets/llama-cool.svg?component'
|
||||
import type {ITask} from '@/modelTypes/ITask'
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
|
||||
const store = useStore()
|
||||
const authStore = useAuthStore()
|
||||
const taskStore = useTaskStore()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
|
@ -180,7 +182,7 @@ async function loadPendingTasks(from: string, to: string) {
|
|||
}
|
||||
}
|
||||
|
||||
tasks.value = await store.dispatch('tasks/loadTasks', params)
|
||||
tasks.value = await taskStore.loadTasks(params)
|
||||
}
|
||||
|
||||
// FIXME: this modification should happen in the store
|
||||
|
|
|
@ -464,6 +464,7 @@ import type {IList} from '@/modelTypes/IList'
|
|||
import {colorIsDark} from '@/helpers/color/colorIsDark'
|
||||
import {useNamespaceStore} from '@/stores/namespaces'
|
||||
import {useAttachmentStore} from '@/stores/attachments'
|
||||
import {useTaskStore} from '@/stores/tasks'
|
||||
|
||||
function scrollIntoView(el) {
|
||||
if (!el) {
|
||||
|
@ -696,7 +697,8 @@ export default defineComponent({
|
|||
task.endDate = task.dueDate
|
||||
}
|
||||
|
||||
this.task = await this.$store.dispatch('tasks/update', task)
|
||||
|
||||
this.task = await useTaskStore().update(task)
|
||||
|
||||
if (!showNotification) {
|
||||
return
|
||||
|
@ -728,7 +730,7 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
async deleteTask() {
|
||||
await this.$store.dispatch('tasks/delete', this.task)
|
||||
await useTaskStore().delete(this.task)
|
||||
this.$message.success({message: this.$t('task.detail.deleteSuccess')})
|
||||
this.$router.push({name: 'list.index', params: {listId: this.task.listId}})
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue