feat: port label store to pinia | pinia 1/9 #2391
|
@ -46,6 +46,7 @@
|
||||||
"lodash.debounce": "4.0.8",
|
"lodash.debounce": "4.0.8",
|
||||||
"marked": "4.1.0",
|
"marked": "4.1.0",
|
||||||
"minimist": "1.2.6",
|
"minimist": "1.2.6",
|
||||||
|
"pinia": "^2.0.21",
|
||||||
"register-service-worker": "1.7.2",
|
"register-service-worker": "1.7.2",
|
||||||
"snake-case": "3.0.4",
|
"snake-case": "3.0.4",
|
||||||
"ufo": "0.8.5",
|
"ufo": "0.8.5",
|
||||||
|
|
|
@ -66,6 +66,7 @@ import {useRoute, useRouter} from 'vue-router'
|
||||||
import {useEventListener} from '@vueuse/core'
|
import {useEventListener} from '@vueuse/core'
|
||||||
|
|
||||||
import {CURRENT_LIST, KEYBOARD_SHORTCUTS_ACTIVE, MENU_ACTIVE} from '@/store/mutation-types'
|
import {CURRENT_LIST, KEYBOARD_SHORTCUTS_ACTIVE, MENU_ACTIVE} from '@/store/mutation-types'
|
||||||
|
import {useLabelStore} from '@/stores/labels'
|
||||||
import Navigation from '@/components/home/navigation.vue'
|
import Navigation from '@/components/home/navigation.vue'
|
||||||
import QuickActions from '@/components/quick-actions/quick-actions.vue'
|
import QuickActions from '@/components/quick-actions/quick-actions.vue'
|
||||||
import BaseButton from '@/components/base/BaseButton.vue'
|
import BaseButton from '@/components/base/BaseButton.vue'
|
||||||
|
@ -197,7 +198,8 @@ function useRenewTokenOnFocus() {
|
||||||
}
|
}
|
||||||
|
|
||||||
useRenewTokenOnFocus()
|
useRenewTokenOnFocus()
|
||||||
store.dispatch('labels/loadAllLabels')
|
const labelStore = useLabelStore()
|
||||||
|
labelStore.loadAllLabels()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -190,6 +190,8 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {defineComponent} from 'vue'
|
import {defineComponent} from 'vue'
|
||||||
|
|
||||||
|
import {useLabelStore} from '@/stores/labels'
|
||||||
|
|
||||||
import DatepickerWithRange from '@/components/date/datepickerWithRange.vue'
|
import DatepickerWithRange from '@/components/date/datepickerWithRange.vue'
|
||||||
import Fancycheckbox from '@/components/input/fancycheckbox.vue'
|
import Fancycheckbox from '@/components/input/fancycheckbox.vue'
|
||||||
|
|
||||||
|
@ -307,8 +309,10 @@ export default defineComponent({
|
||||||
this.change()
|
this.change()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
foundLabels() {
|
foundLabels() {
|
||||||
return this.$store.getters['labels/filterLabelsByQuery'](this.labels, this.query)
|
const labelStore = useLabelStore()
|
||||||
dpschen marked this conversation as resolved
|
|||||||
|
return labelStore.filterLabelsByQuery(this.labels, this.labelQuery)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -336,7 +340,8 @@ export default defineComponent({
|
||||||
: ''
|
: ''
|
||||||
const labelIds = labels.split(',').map(i => parseInt(i))
|
const labelIds = labels.split(',').map(i => parseInt(i))
|
||||||
|
|
||||||
this.labels = this.$store.getters['labels/getLabelsByIds'](labelIds)
|
const labelStore = useLabelStore()
|
||||||
|
this.labels = labelStore.getLabelsByIds(labelIds)
|
||||||
},
|
},
|
||||||
removePropertyFromFilter(propertyName) {
|
removePropertyFromFilter(propertyName) {
|
||||||
// Because of the way arrays work, we can only ever remove one element at once.
|
// Because of the way arrays work, we can only ever remove one element at once.
|
||||||
|
|
|
@ -50,6 +50,7 @@ import {success} from '@/message'
|
||||||
import BaseButton from '@/components/base/BaseButton.vue'
|
import BaseButton from '@/components/base/BaseButton.vue'
|
||||||
import Multiselect from '@/components/input/multiselect.vue'
|
import Multiselect from '@/components/input/multiselect.vue'
|
||||||
import type { ILabel } from '@/modelTypes/ILabel'
|
import type { ILabel } from '@/modelTypes/ILabel'
|
||||||
|
import { useLabelStore } from '@/stores/labels'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
|
@ -86,8 +87,10 @@ watch(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
const foundLabels = computed(() => store.getters['labels/filterLabelsByQuery'](labels.value, query.value))
|
const labelStore = useLabelStore()
|
||||||
const loading = computed(() => labelTaskService.loading || (store.state.loading && store.state.loadingModule === 'labels'))
|
|
||||||
|
const foundLabels = computed(() => labelStore.filterLabelsByQuery(labels.value, query.value))
|
||||||
|
const loading = computed(() => labelTaskService.loading || labelStore.isLoading)
|
||||||
|
|
||||||
function findLabel(newQuery: string) {
|
function findLabel(newQuery: string) {
|
||||||
query.value = newQuery
|
query.value = newQuery
|
||||||
|
@ -129,7 +132,8 @@ async function createAndAddLabel(title: string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const newLabel = await store.dispatch('labels/createLabel', new LabelModel({title}))
|
const labelStore = useLabelStore()
|
||||||
|
const newLabel = await labelStore.createLabel(new LabelModel({title}))
|
||||||
addLabel(newLabel, false)
|
addLabel(newLabel, false)
|
||||||
labels.value.push(newLabel)
|
labels.value.push(newLabel)
|
||||||
success({message: t('task.label.addCreateSuccess')})
|
success({message: t('task.label.addCreateSuccess')})
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
import {describe, it, expect} from 'vitest'
|
|
||||||
|
|
||||||
import {filterLabelsByQuery} from './labels'
|
|
||||||
import {createNewIndexer} from '../indexes'
|
|
||||||
|
|
||||||
const {add} = createNewIndexer('labels', ['title', 'description'])
|
|
||||||
|
|
||||||
describe('filter labels', () => {
|
|
||||||
const state = {
|
|
||||||
labels: {
|
|
||||||
1: {id: 1, title: 'label1'},
|
|
||||||
2: {id: 2, title: 'label2'},
|
|
||||||
3: {id: 3, title: 'label3'},
|
|
||||||
4: {id: 4, title: 'label4'},
|
|
||||||
5: {id: 5, title: 'label5'},
|
|
||||||
6: {id: 6, title: 'label6'},
|
|
||||||
7: {id: 7, title: 'label7'},
|
|
||||||
8: {id: 8, title: 'label8'},
|
|
||||||
9: {id: 9, title: 'label9'},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.values(state.labels).forEach(add)
|
|
||||||
|
|
||||||
it('should return an empty array for an empty query', () => {
|
|
||||||
const labels = filterLabelsByQuery(state, [], '')
|
|
||||||
|
|
||||||
expect(labels).toHaveLength(0)
|
|
||||||
})
|
|
||||||
it('should return labels for a query', () => {
|
|
||||||
const labels = filterLabelsByQuery(state, [], 'label2')
|
|
||||||
|
|
||||||
expect(labels).toHaveLength(1)
|
|
||||||
expect(labels[0].title).toBe('label2')
|
|
||||||
})
|
|
||||||
it('should not return found but hidden labels', () => {
|
|
||||||
interface label {
|
|
||||||
id: number,
|
|
||||||
title: string,
|
|
||||||
}
|
|
||||||
|
|
||||||
const labelsToHide: label[] = [{id: 1, title: 'label1'}]
|
|
||||||
const labels = filterLabelsByQuery(state, labelsToHide, 'label1')
|
|
||||||
|
|
||||||
expect(labels).toHaveLength(0)
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,33 +0,0 @@
|
||||||
import {createNewIndexer} from '../indexes'
|
|
||||||
|
|
||||||
import type {LabelState} from '@/store/types'
|
|
||||||
import type {ILabel} from '@/modelTypes/ILabel'
|
|
||||||
|
|
||||||
const {search} = createNewIndexer('labels', ['title', 'description'])
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if a list of labels is available in the store and filters them then query
|
|
||||||
* @param {Object} state
|
|
||||||
* @param {Array} labelsToHide
|
|
||||||
* @param {String} query
|
|
||||||
* @returns {Array}
|
|
||||||
*/
|
|
||||||
export function filterLabelsByQuery(state: LabelState, labelsToHide: ILabel[], query: string) {
|
|
||||||
const labelIdsToHide: number[] = labelsToHide.map(({id}) => id)
|
|
||||||
|
|
||||||
return search(query)
|
|
||||||
?.filter(value => !labelIdsToHide.includes(value))
|
|
||||||
.map(id => state.labels[id])
|
|
||||||
|| []
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the labels by id if found
|
|
||||||
* @param {Object} state
|
|
||||||
* @param {Array} ids
|
|
||||||
* @returns {Array}
|
|
||||||
*/
|
|
||||||
export function getLabelsByIds(state: LabelState, ids: ILabel['id'][]) {
|
|
||||||
return Object.values(state.labels).filter(({id}) => ids.includes(id))
|
|
||||||
}
|
|
|
@ -2,6 +2,7 @@ import {createApp} from 'vue'
|
||||||
|
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
|
import { createPinia } from 'pinia'
|
||||||
|
|
||||||
import {error, success} from './message'
|
import {error, success} from './message'
|
||||||
|
|
||||||
|
@ -104,6 +105,9 @@ if (window.SENTRY_ENABLED) {
|
||||||
import('./sentry').then(sentry => sentry.default(app, router))
|
import('./sentry').then(sentry => sentry.default(app, router))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pinia = createPinia()
|
||||||
|
app.use(pinia)
|
||||||
|
|
||||||
app.use(store, key) // pass the injection key
|
app.use(store, key) // pass the injection key
|
||||||
app.use(router)
|
app.use(router)
|
||||||
app.use(i18n)
|
app.use(i18n)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import type { ActionContext } from 'vuex'
|
import type { ActionContext } from 'vuex'
|
||||||
|
import type { StoreDefinition } from 'pinia'
|
||||||
import {LOADING, LOADING_MODULE} from './mutation-types'
|
import {LOADING, LOADING_MODULE} from './mutation-types'
|
||||||
import type { RootStoreState } from './types'
|
import type { RootStoreState } from './types'
|
||||||
|
|
||||||
|
@ -31,4 +32,22 @@ export function setLoading<State>(
|
||||||
loadFunc(false)
|
loadFunc(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setLoadingPinia = (store: StoreDefinition, loadFunc : ((isLoading: boolean) => void) | null = null) => {
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
if (loadFunc === null) {
|
||||||
|
store.isLoading = true
|
||||||
|
} else {
|
||||||
|
loadFunc(true)
|
||||||
|
}
|
||||||
|
}, 100)
|
||||||
|
return () => {
|
||||||
|
clearTimeout(timeout)
|
||||||
|
if (loadFunc === null) {
|
||||||
|
store.isLoading = false
|
||||||
|
} else {
|
||||||
|
loadFunc(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -20,7 +20,6 @@ import kanban from './modules/kanban'
|
||||||
import tasks from './modules/tasks'
|
import tasks from './modules/tasks'
|
||||||
import lists from './modules/lists'
|
import lists from './modules/lists'
|
||||||
import attachments from './modules/attachments'
|
import attachments from './modules/attachments'
|
||||||
import labels from './modules/labels'
|
|
||||||
|
|
||||||
import ListModel from '@/models/list'
|
import ListModel from '@/models/list'
|
||||||
|
|
||||||
|
@ -46,7 +45,6 @@ export const store = createStore<RootStoreState>({
|
||||||
tasks,
|
tasks,
|
||||||
lists,
|
lists,
|
||||||
attachments,
|
attachments,
|
||||||
labels,
|
|
||||||
},
|
},
|
||||||
state: () => ({
|
state: () => ({
|
||||||
loading: false,
|
loading: false,
|
||||||
|
|
|
@ -25,6 +25,7 @@ import type { IAttachment } from '@/modelTypes/IAttachment'
|
||||||
import type { IList } from '@/modelTypes/IList'
|
import type { IList } from '@/modelTypes/IList'
|
||||||
|
|
||||||
import type { RootStoreState, TaskState } from '@/store/types'
|
import type { RootStoreState, TaskState } from '@/store/types'
|
||||||
|
import { useLabelStore } from '@/stores/labels'
|
||||||
|
|
||||||
// IDEA: maybe use a small fuzzy search here to prevent errors
|
// IDEA: maybe use a small fuzzy search here to prevent errors
|
||||||
function findPropertyByValue(object, key, value) {
|
function findPropertyByValue(object, key, value) {
|
||||||
|
@ -268,22 +269,19 @@ const tasksStore : Module<TaskState, RootStoreState>= {
|
||||||
},
|
},
|
||||||
|
|
||||||
// Do everything that is involved in finding, creating and adding the label to the task
|
// Do everything that is involved in finding, creating and adding the label to the task
|
||||||
async addLabelsToTask({rootState, dispatch}, {
|
async addLabelsToTask(_, { task, parsedLabels }) {
|
||||||
task,
|
|
||||||
parsedLabels,
|
|
||||||
}) {
|
|
||||||
if (parsedLabels.length <= 0) {
|
if (parsedLabels.length <= 0) {
|
||||||
return task
|
return task
|
||||||
}
|
}
|
||||||
|
|
||||||
const {labels} = rootState.labels
|
const labelStore = useLabelStore()
|
||||||
|
|
||||||
const labelAddsToWaitFor = parsedLabels.map(async labelTitle => {
|
const labelAddsToWaitFor = parsedLabels.map(async labelTitle => {
|
||||||
let label = validateLabel(labels, labelTitle)
|
let label = validateLabel(labelStore.labels, labelTitle)
|
||||||
if (typeof label === 'undefined') {
|
if (typeof label === 'undefined') {
|
||||||
// label not found, create it
|
// label not found, create it
|
||||||
const labelModel = new LabelModel({title: labelTitle})
|
const labelModel = new LabelModel({title: labelTitle})
|
||||||
label = await dispatch('labels/createLabel', labelModel, {root: true})
|
label = await labelStore.createLabel(labelModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
return addLabelToTask(task, label)
|
return addLabelToTask(task, label)
|
||||||
|
|
|
@ -93,7 +93,7 @@ export interface LabelState {
|
||||||
labels: {
|
labels: {
|
||||||
[id: ILabel['id']]: ILabel
|
[id: ILabel['id']]: ILabel
|
||||||
},
|
},
|
||||||
loaded: boolean,
|
isLoading: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ListState {
|
export interface ListState {
|
||||||
|
|
55
src/stores/labels.test.ts
Normal file
55
src/stores/labels.test.ts
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import {setActivePinia, createPinia} from 'pinia'
|
||||||
|
import {describe, it, expect, beforeEach} from 'vitest'
|
||||||
|
|
||||||
|
import {useLabelStore} from './labels'
|
||||||
|
|
||||||
|
import type { ILabel } from '@/modelTypes/ILabel'
|
||||||
|
|
||||||
|
const MOCK_LABELS = {
|
||||||
|
1: {id: 1, title: 'label1'},
|
||||||
|
2: {id: 2, title: 'label2'},
|
||||||
|
3: {id: 3, title: 'label3'},
|
||||||
|
4: {id: 4, title: 'label4'},
|
||||||
|
5: {id: 5, title: 'label5'},
|
||||||
|
6: {id: 6, title: 'label6'},
|
||||||
|
7: {id: 7, title: 'label7'},
|
||||||
|
8: {id: 8, title: 'label8'},
|
||||||
|
9: {id: 9, title: 'label9'},
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupStore() {
|
||||||
|
const store = useLabelStore()
|
||||||
|
store.setLabels(Object.values(MOCK_LABELS) as ILabel[])
|
||||||
|
return store
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('filter labels', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
// creates a fresh pinia and make it active so it's automatically picked
|
||||||
|
// up by any useStore() call without having to pass it to it:
|
||||||
|
// `useStore(pinia)`
|
||||||
|
setActivePinia(createPinia())
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return an empty array for an empty query', () => {
|
||||||
|
const store = setupStore()
|
||||||
|
const labels = store.filterLabelsByQuery([], '')
|
||||||
|
|
||||||
|
expect(labels).toHaveLength(0)
|
||||||
|
})
|
||||||
|
it('should return labels for a query', () => {
|
||||||
|
const store = setupStore()
|
||||||
|
const labels = store.filterLabelsByQuery([], 'label2')
|
||||||
|
|
||||||
|
expect(labels).toHaveLength(1)
|
||||||
|
expect(labels[0].title).toBe('label2')
|
||||||
|
})
|
||||||
|
it('should not return found but hidden labels', () => {
|
||||||
|
const store = setupStore()
|
||||||
|
|
||||||
|
const labelsToHide = [{id: 1, title: 'label1'}] as ILabel[]
|
||||||
|
const labels = store.filterLabelsByQuery(labelsToHide, 'label1')
|
||||||
|
|
||||||
|
expect(labels).toHaveLength(0)
|
||||||
|
})
|
||||||
|
})
|
|
@ -1,15 +1,13 @@
|
||||||
import type { Module } from 'vuex'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
import {i18n} from '@/i18n'
|
|
||||||
import {success} from '@/message'
|
|
||||||
import LabelService from '@/services/label'
|
import LabelService from '@/services/label'
|
||||||
import {setLoading} from '@/store/helper'
|
import {success} from '@/message'
|
||||||
import type { LabelState, RootStoreState } from '@/store/types'
|
import {i18n} from '@/i18n'
|
||||||
import {getLabelsByIds, filterLabelsByQuery} from '@/helpers/labels'
|
|
||||||
import {createNewIndexer} from '@/indexes'
|
import {createNewIndexer} from '@/indexes'
|
||||||
import type { ILabel } from '@/modelTypes/ILabel'
|
import {setLoadingPinia} from '@/store/helper'
|
||||||
|
import type {ILabel} from '@/modelTypes/ILabel'
|
||||||
|
|
||||||
const {add, remove, update} = createNewIndexer('labels', ['title', 'description'])
|
const {add, remove, update, search} = createNewIndexer('labels', ['title', 'description'])
|
||||||
|
|
||||||
async function getAllLabels(page = 1): Promise<ILabel[]> {
|
async function getAllLabels(page = 1): Promise<ILabel[]> {
|
||||||
const labelService = new LabelService()
|
const labelService = new LabelService()
|
||||||
|
@ -22,100 +20,117 @@ async function getAllLabels(page = 1): Promise<ILabel[]> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const LabelStore : Module<LabelState, RootStoreState> = {
|
import type {LabelState} from '@/store/types'
|
||||||
namespaced: true,
|
|
||||||
state: () => ({
|
export const useLabelStore = defineStore('label', {
|
||||||
|
state: () : LabelState => ({
|
||||||
|
// The labels are stored as an object which has the label ids as keys.
|
||||||
labels: {},
|
labels: {},
|
||||||
loaded: false,
|
isLoading: false,
|
||||||
}),
|
}),
|
||||||
mutations: {
|
|
||||||
setLabels(state, labels: ILabel[]) {
|
|
||||||
labels.forEach(l => {
|
|
||||||
state.labels[l.id] = l
|
|
||||||
add(l)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
setLabel(state, label: ILabel) {
|
|
||||||
state.labels[label.id] = label
|
|
||||||
update(label)
|
|
||||||
},
|
|
||||||
removeLabelById(state, label: ILabel) {
|
|
||||||
remove(label)
|
|
||||||
delete state.labels[label.id]
|
|
||||||
},
|
|
||||||
setLoaded(state, loaded: boolean) {
|
|
||||||
state.loaded = loaded
|
|
||||||
},
|
|
||||||
},
|
|
||||||
getters: {
|
getters: {
|
||||||
getLabelsByIds(state) {
|
getLabelsByIds(state) {
|
||||||
return (ids: ILabel['id'][]) => getLabelsByIds(state, ids)
|
return (ids: ILabel['id'][]) => Object.values(state.labels).filter(({id}) => ids.includes(id))
|
||||||
},
|
},
|
||||||
|
// **
|
||||||
|
// * Checks if a list of labels is available in the store and filters them then query
|
||||||
|
// **
|
||||||
filterLabelsByQuery(state) {
|
filterLabelsByQuery(state) {
|
||||||
return (labelsToHide: ILabel[], query: string) => filterLabelsByQuery(state, labelsToHide, query)
|
return (labelsToHide: ILabel[], query: string) => {
|
||||||
|
const labelIdsToHide: number[] = labelsToHide.map(({id}) => id)
|
||||||
|
|
||||||
|
return search(query)
|
||||||
|
?.filter(value => !labelIdsToHide.includes(value))
|
||||||
|
.map(id => state.labels[id])
|
||||||
|
|| []
|
||||||
|
}
|
||||||
},
|
},
|
||||||
getLabelsByExactTitles(state) {
|
getLabelsByExactTitles(state) {
|
||||||
return labelTitles => Object
|
return (labelTitles: string[]) => Object
|
||||||
.values(state.labels)
|
.values(state.labels)
|
||||||
.filter(({title}) => labelTitles.some(l => l.toLowerCase() === title.toLowerCase()))
|
.filter(({title}) => labelTitles.some(l => l.toLowerCase() === title.toLowerCase()))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
async loadAllLabels(ctx, {forceLoad} = {}) {
|
setIsLoading(isLoading: boolean) {
|
||||||
if (ctx.state.loaded && !forceLoad) {
|
this.isLoading = isLoading
|
||||||
|
},
|
||||||
|
|
||||||
|
setLabels(labels: ILabel[]) {
|
||||||
|
labels.forEach(l => {
|
||||||
|
this.labels[l.id] = l
|
||||||
|
add(l)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
setLabel(label: ILabel) {
|
||||||
|
this.labels[label.id] = label
|
||||||
|
update(label)
|
||||||
|
},
|
||||||
|
|
||||||
|
removeLabelById(label: ILabel) {
|
||||||
|
remove(label)
|
||||||
|
delete this.labels[label.id]
|
||||||
|
},
|
||||||
|
|
||||||
|
async loadAllLabels({forceLoad} : {forceLoad?: boolean} = {}) {
|
||||||
|
if (this.isLoading && !forceLoad) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const cancel = setLoading(ctx, 'labels')
|
const cancel = setLoadingPinia(useLabelStore)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const labels = await getAllLabels()
|
const labels = await getAllLabels()
|
||||||
ctx.commit('setLabels', labels)
|
this.setLabels(labels)
|
||||||
ctx.commit('setLoaded', true)
|
this.setIsLoading(true)
|
||||||
return labels
|
return labels
|
||||||
} finally {
|
} finally {
|
||||||
cancel()
|
cancel()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async deleteLabel(ctx, label: ILabel) {
|
|
||||||
const cancel = setLoading(ctx, 'labels')
|
async deleteLabel(label: ILabel) {
|
||||||
|
const cancel = setLoadingPinia(useLabelStore)
|
||||||
const labelService = new LabelService()
|
const labelService = new LabelService()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await labelService.delete(label)
|
const result = await labelService.delete(label)
|
||||||
ctx.commit('removeLabelById', label)
|
this.removeLabelById(label)
|
||||||
success({message: i18n.global.t('label.deleteSuccess')})
|
success({message: i18n.global.t('label.deleteSuccess')})
|
||||||
return result
|
return result
|
||||||
} finally {
|
} finally {
|
||||||
cancel()
|
cancel()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async updateLabel(ctx, label: ILabel) {
|
|
||||||
const cancel = setLoading(ctx, 'labels')
|
async updateLabel(label: ILabel) {
|
||||||
|
const cancel = setLoadingPinia(useLabelStore)
|
||||||
const labelService = new LabelService()
|
const labelService = new LabelService()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const newLabel = await labelService.update(label)
|
const newLabel = await labelService.update(label)
|
||||||
ctx.commit('setLabel', newLabel)
|
this.setLabel(newLabel)
|
||||||
success({message: i18n.global.t('label.edit.success')})
|
success({message: i18n.global.t('label.edit.success')})
|
||||||
return newLabel
|
return newLabel
|
||||||
} finally {
|
} finally {
|
||||||
cancel()
|
cancel()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async createLabel(ctx, label: ILabel) {
|
|
||||||
const cancel = setLoading(ctx, 'labels')
|
async createLabel(label: ILabel) {
|
||||||
|
const cancel = setLoadingPinia(useLabelStore)
|
||||||
const labelService = new LabelService()
|
const labelService = new LabelService()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const newLabel = await labelService.create(label)
|
const newLabel = await labelService.create(label)
|
||||||
ctx.commit('setLabel', newLabel)
|
this.setLabel(newLabel)
|
||||||
return newLabel
|
return newLabel
|
||||||
} finally {
|
} finally {
|
||||||
cancel()
|
cancel()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
|
|
||||||
export default LabelStore
|
|
||||||
|
|
|
@ -111,11 +111,12 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {defineComponent} from 'vue'
|
import {defineComponent} from 'vue'
|
||||||
import {mapState} from 'vuex'
|
import {mapState as mapVuexState} from 'vuex'
|
||||||
|
import {mapState} from 'pinia'
|
||||||
|
|
||||||
import LabelModel from '@/models/label'
|
import LabelModel from '../../models/label'
|
||||||
import type {ILabel} from '@/modelTypes/ILabel'
|
import type {ILabel} from '@/modelTypes/ILabel'
|
||||||
import {LOADING, LOADING_MODULE} from '@/store/mutation-types'
|
import {useLabelStore} from '@/stores/labels'
|
||||||
|
|
||||||
import BaseButton from '@/components/base/BaseButton.vue'
|
import BaseButton from '@/components/base/BaseButton.vue'
|
||||||
import AsyncEditor from '@/components/input/AsyncEditor'
|
import AsyncEditor from '@/components/input/AsyncEditor'
|
||||||
|
@ -139,25 +140,32 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.$store.dispatch('labels/loadAllLabels')
|
const labelStore = useLabelStore()
|
||||||
|
labelStore.loadAllLabels()
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
setTitle(this.$t('label.title'))
|
setTitle(this.$t('label.title'))
|
||||||
},
|
},
|
||||||
computed: mapState({
|
computed: {
|
||||||
userInfo: state => state.auth.info,
|
...mapVuexState({
|
||||||
// Alphabetically sort the labels
|
userInfo: state => state.auth.info,
|
||||||
labels: state => Object.values(state.labels.labels).sort((f, s) => f.title > s.title ? 1 : -1),
|
}),
|
||||||
loading: state => state[LOADING] && state[LOADING_MODULE] === 'labels',
|
...mapState(useLabelStore, {
|
||||||
}),
|
// Alphabetically sort the labels
|
||||||
|
labels: state => Object.values(state.labels).sort((f, s) => f.title > s.title ? 1 : -1),
|
||||||
|
loading: state => state.isLoading,
|
||||||
|
}),
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
deleteLabel(label: ILabel) {
|
deleteLabel(label: ILabel) {
|
||||||
this.showDeleteModal = false
|
this.showDeleteModal = false
|
||||||
this.isLabelEdit = false
|
this.isLabelEdit = false
|
||||||
return this.$store.dispatch('labels/deleteLabel', label)
|
const labelStore = useLabelStore()
|
||||||
|
return labelStore.deleteLabel(label)
|
||||||
},
|
},
|
||||||
editLabelSubmit() {
|
editLabelSubmit() {
|
||||||
return this.$store.dispatch('labels/updateLabel', this.labelEditLabel)
|
const labelStore = useLabelStore()
|
||||||
|
return labelStore.updateLabel(this.labelEditLabel)
|
||||||
},
|
},
|
||||||
editLabel(label: ILabel) {
|
editLabel(label: ILabel) {
|
||||||
if (label.createdBy.id !== this.userInfo.id) {
|
if (label.createdBy.id !== this.userInfo.id) {
|
||||||
|
|
|
@ -36,12 +36,13 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {defineComponent} from 'vue'
|
import {defineComponent} from 'vue'
|
||||||
|
import {mapState} from 'pinia'
|
||||||
|
|
||||||
import LabelModel from '../../models/label'
|
import LabelModel from '../../models/label'
|
||||||
import CreateEdit from '@/components/misc/create-edit.vue'
|
import CreateEdit from '@/components/misc/create-edit.vue'
|
||||||
import ColorPicker from '../../components/input/colorPicker.vue'
|
import ColorPicker from '../../components/input/colorPicker.vue'
|
||||||
import {mapState} from 'vuex'
|
|
||||||
import {LOADING, LOADING_MODULE} from '@/store/mutation-types'
|
|
||||||
import { setTitle } from '@/helpers/setTitle'
|
import { setTitle } from '@/helpers/setTitle'
|
||||||
|
import { useLabelStore } from '@/stores/labels'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'NewLabel',
|
name: 'NewLabel',
|
||||||
|
@ -58,9 +59,11 @@ export default defineComponent({
|
||||||
mounted() {
|
mounted() {
|
||||||
setTitle(this.$t('label.create.title'))
|
setTitle(this.$t('label.create.title'))
|
||||||
},
|
},
|
||||||
computed: mapState({
|
computed: {
|
||||||
loading: state => state[LOADING] && state[LOADING_MODULE] === 'labels',
|
...mapState(useLabelStore, {
|
||||||
}),
|
loading: state => state.isLoading,
|
||||||
|
}),
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async newLabel() {
|
async newLabel() {
|
||||||
if (this.label.title === '') {
|
if (this.label.title === '') {
|
||||||
|
@ -69,7 +72,8 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
this.showError = false
|
this.showError = false
|
||||||
|
|
||||||
const label = this.$store.dispatch('labels/createLabel', this.label)
|
const labelStore = useLabelStore()
|
||||||
|
const label = labelStore.createLabel(this.label)
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
name: 'labels.index',
|
name: 'labels.index',
|
||||||
params: {id: label.id},
|
params: {id: label.id},
|
||||||
|
|
|
@ -10240,6 +10240,14 @@ pify@^4.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
|
resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
|
||||||
integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
|
integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
|
||||||
|
|
||||||
|
pinia@^2.0.21:
|
||||||
|
version "2.0.21"
|
||||||
|
resolved "https://registry.yarnpkg.com/pinia/-/pinia-2.0.21.tgz#2a6599ad3736fa71866f4b053ffb0073cd482270"
|
||||||
|
integrity sha512-6ol04PtL29O0Z6JHI47O3JUSoyOJ7Og0rstXrHVMZSP4zAldsQBXJCNF0i/H7m8vp/Hjd/CSmuPl7C5QAwpeWQ==
|
||||||
|
dependencies:
|
||||||
|
"@vue/devtools-api" "^6.2.1"
|
||||||
|
vue-demi "*"
|
||||||
|
|
||||||
pinkie-promise@^2.0.0:
|
pinkie-promise@^2.0.0:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
|
resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
|
||||||
|
|
Reference in New Issue
Block a user
Shouldn't this use the setup function? (not
script setup
).We could introduce a setup block in this component.
But since this pull request was only about having the fastest way to integrate the store I didn't see that necessary.
Setup would only require the additional import of
computed
. Plus we would need to export it again from that function.So overall more complex then this quick fix here.
Makes sense, especially considering this would be redundant once we'll have everything migrated over to
script setup
.Exactly :)