feat: improve store and model typing
This commit is contained in:
@ -1,4 +1,6 @@
|
||||
import type { ActionContext } from 'vuex'
|
||||
import {LOADING, LOADING_MODULE} from './mutation-types'
|
||||
import type { RootStoreState } from './types'
|
||||
|
||||
/**
|
||||
* This helper sets the loading state with a 100ms delay to avoid flickering.
|
||||
@ -7,7 +9,11 @@ import {LOADING, LOADING_MODULE} from './mutation-types'
|
||||
* @param {null|String} module The module that is loading. This parameter allows components to listen for specific parts of the application loading.
|
||||
* @param {null|function} loadFunc If not null, this function will be executed instead of the default setting loading.
|
||||
*/
|
||||
export const setLoading = (context, module = null, loadFunc = null) => {
|
||||
export function setLoading<State>(
|
||||
context : ActionContext<State, RootStoreState>,
|
||||
module : string | null = null,
|
||||
loadFunc : (() => void) | null = null,
|
||||
) {
|
||||
const timeout = setTimeout(() => {
|
||||
if (loadFunc === null) {
|
||||
context.commit(LOADING, true, {root: true})
|
||||
|
@ -25,7 +25,9 @@ import ListModel from '@/models/list'
|
||||
import ListService from '../services/list'
|
||||
import {checkAndSetApiUrl} from '@/helpers/checkAndSetApiUrl'
|
||||
|
||||
export const store = createStore({
|
||||
import type { RootStoreState } from './types'
|
||||
|
||||
export const store = createStore<RootStoreState>({
|
||||
strict: import.meta.env.DEV,
|
||||
modules: {
|
||||
config,
|
||||
@ -37,7 +39,7 @@ export const store = createStore({
|
||||
attachments,
|
||||
labels,
|
||||
},
|
||||
state: {
|
||||
state: () => ({
|
||||
loading: false,
|
||||
loadingModule: null,
|
||||
// This is used to highlight the current list in menu for all list related views
|
||||
@ -51,7 +53,7 @@ export const store = createStore({
|
||||
menuActive: true,
|
||||
keyboardShortcutsActive: false,
|
||||
quickActionsActive: false,
|
||||
},
|
||||
}),
|
||||
mutations: {
|
||||
[LOADING](state, loading) {
|
||||
state.loading = loading
|
||||
|
@ -1,21 +1,24 @@
|
||||
import {findIndexById} from '@/helpers/utils'
|
||||
|
||||
import type { AttachmentState } from '@/store/types'
|
||||
import type { IAttachment } from '@/models/attachment'
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: () => ({
|
||||
state: (): AttachmentState => ({
|
||||
attachments: [],
|
||||
}),
|
||||
mutations: {
|
||||
set(state, attachments) {
|
||||
set(state: AttachmentState, attachments: IAttachment[]) {
|
||||
console.debug('Set attachments', attachments)
|
||||
state.attachments = attachments
|
||||
},
|
||||
add(state, attachment) {
|
||||
add(state: AttachmentState, attachment: IAttachment) {
|
||||
console.debug('Add attachement', attachment)
|
||||
state.attachments.push(attachment)
|
||||
},
|
||||
removeById(state, id) {
|
||||
const attachmentIndex = findIndexById(state.attachments, id)
|
||||
removeById(state: AttachmentState, id: IAttachment['id']) {
|
||||
const attachmentIndex = findIndexById<IAttachment>(state.attachments, id)
|
||||
state.attachments.splice(attachmentIndex, 1)
|
||||
console.debug('Remove attachement', id)
|
||||
},
|
||||
|
@ -1,3 +1,5 @@
|
||||
import type { ActionContext } from 'vuex'
|
||||
|
||||
import {HTTPFactory, AuthenticatedHTTPFactory} from '@/http-common'
|
||||
import {i18n, getCurrentLanguage, saveLanguage} from '@/i18n'
|
||||
import {objectToSnakeCase} from '@/helpers/case'
|
||||
@ -8,12 +10,10 @@ import {getToken, refreshToken, removeToken, saveToken} from '@/helpers/auth'
|
||||
import {setLoading} from '@/store/helper'
|
||||
import {success} from '@/message'
|
||||
import {redirectToProvider} from '@/helpers/redirectToProvider'
|
||||
import type { RootStoreState, AuthState, Info} from '@/store/types'
|
||||
import {AUTH_TYPES} from '@/store/types'
|
||||
import type { IUserSettings } from '@/models/userSettings'
|
||||
|
||||
const AUTH_TYPES = {
|
||||
'UNKNOWN': 0,
|
||||
'USER': 1,
|
||||
'LINK_SHARE': 2,
|
||||
}
|
||||
|
||||
const defaultSettings = settings => {
|
||||
if (typeof settings.weekStart === 'undefined' || settings.weekStart === '') {
|
||||
@ -24,7 +24,7 @@ const defaultSettings = settings => {
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: () => ({
|
||||
state: (): AuthState => ({
|
||||
authenticated: false,
|
||||
isLinkShareAuth: false,
|
||||
info: null,
|
||||
@ -34,13 +34,13 @@ export default {
|
||||
settings: {},
|
||||
}),
|
||||
getters: {
|
||||
authUser(state) {
|
||||
authUser(state: AuthState) {
|
||||
return state.authenticated && (
|
||||
state.info &&
|
||||
state.info.type === AUTH_TYPES.USER
|
||||
)
|
||||
},
|
||||
authLinkShare(state) {
|
||||
authLinkShare(state: AuthState) {
|
||||
return state.authenticated && (
|
||||
state.info &&
|
||||
state.info.type === AUTH_TYPES.LINK_SHARE
|
||||
@ -48,7 +48,7 @@ export default {
|
||||
},
|
||||
},
|
||||
mutations: {
|
||||
info(state, info) {
|
||||
info(state: AuthState, info: Info) {
|
||||
state.info = info
|
||||
if (info !== null) {
|
||||
state.avatarUrl = info.getAvatarUrl()
|
||||
@ -60,31 +60,32 @@ export default {
|
||||
state.isLinkShareAuth = info.id < 0
|
||||
}
|
||||
},
|
||||
setUserSettings(state, settings) {
|
||||
setUserSettings(state: AuthState, settings: IUserSettings) {
|
||||
state.settings = defaultSettings(settings)
|
||||
const info = state.info !== null ? state.info : {}
|
||||
const info = state.info !== null ? state.info : {} as Info
|
||||
info.name = settings.name
|
||||
state.info = info
|
||||
},
|
||||
authenticated(state, authenticated) {
|
||||
authenticated(state: AuthState, authenticated: boolean) {
|
||||
state.authenticated = authenticated
|
||||
},
|
||||
isLinkShareAuth(state, is) {
|
||||
state.isLinkShareAuth = is
|
||||
isLinkShareAuth(state: AuthState, isLinkShareAuth: boolean) {
|
||||
state.isLinkShareAuth = isLinkShareAuth
|
||||
},
|
||||
needsTotpPasscode(state, needs) {
|
||||
state.needsTotpPasscode = needs
|
||||
needsTotpPasscode(state: AuthState, needsTotpPasscode: boolean) {
|
||||
state.needsTotpPasscode = needsTotpPasscode
|
||||
},
|
||||
reloadAvatar(state) {
|
||||
reloadAvatar(state: AuthState) {
|
||||
if (!state.info) return
|
||||
state.avatarUrl = `${state.info.getAvatarUrl()}&=${+new Date()}`
|
||||
},
|
||||
lastUserRefresh(state) {
|
||||
lastUserRefresh(state: AuthState) {
|
||||
state.lastUserInfoRefresh = new Date()
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
// Logs a user in with a set of credentials.
|
||||
async login(ctx, credentials) {
|
||||
async login(ctx: ActionContext<AuthState, RootStoreState>, credentials) {
|
||||
const HTTP = HTTPFactory()
|
||||
ctx.commit(LOADING, true, {root: true})
|
||||
|
||||
@ -115,7 +116,7 @@ export default {
|
||||
|
||||
// Registers a new user and logs them in.
|
||||
// Not sure if this is the right place to put the logic in, maybe a seperate js component would be better suited.
|
||||
async register(ctx, credentials) {
|
||||
async register(ctx: ActionContext<AuthState, RootStoreState>, credentials) {
|
||||
const HTTP = HTTPFactory()
|
||||
ctx.commit(LOADING, true, {root: true})
|
||||
try {
|
||||
@ -132,7 +133,7 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
async openIdAuth(ctx, {provider, code}) {
|
||||
async openIdAuth(ctx: ActionContext<AuthState, RootStoreState>, {provider, code}) {
|
||||
const HTTP = HTTPFactory()
|
||||
ctx.commit(LOADING, true, {root: true})
|
||||
|
||||
@ -154,7 +155,7 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
async linkShareAuth(ctx, {hash, password}) {
|
||||
async linkShareAuth(ctx: ActionContext<AuthState, RootStoreState>, {hash, password}) {
|
||||
const HTTP = HTTPFactory()
|
||||
const response = await HTTP.post('/shares/' + hash + '/auth', {
|
||||
password: password,
|
||||
@ -165,7 +166,7 @@ export default {
|
||||
},
|
||||
|
||||
// Populates user information from jwt token saved in local storage in store
|
||||
checkAuth(ctx) {
|
||||
checkAuth(ctx: ActionContext<AuthState, RootStoreState>) {
|
||||
|
||||
// This function can be called from multiple places at the same time and shortly after one another.
|
||||
// To prevent hitting the api too frequently or race conditions, we check at most once per minute.
|
||||
@ -197,7 +198,7 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
redirectToProviderIfNothingElseIsEnabled({rootState}) {
|
||||
redirectToProviderIfNothingElseIsEnabled({rootState}: ActionContext<AuthState, RootStoreState>) {
|
||||
const {auth} = rootState.config
|
||||
if (
|
||||
auth.local.enabled === false &&
|
||||
@ -209,7 +210,7 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
async refreshUserInfo({state, commit, dispatch}) {
|
||||
async refreshUserInfo({state, commit, dispatch}: ActionContext<AuthState, RootStoreState>) {
|
||||
const jwt = getToken()
|
||||
if (!jwt) {
|
||||
return
|
||||
@ -243,7 +244,7 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
async saveUserSettings(ctx, payload) {
|
||||
async saveUserSettings(ctx: ActionContext<AuthState, RootStoreState>, payload) {
|
||||
const {settings} = payload
|
||||
const showMessage = payload.showMessage ?? true
|
||||
const userSettingsService = new UserSettingsService()
|
||||
@ -264,7 +265,7 @@ export default {
|
||||
},
|
||||
|
||||
// Renews the api token and saves it to local storage
|
||||
renewToken(ctx) {
|
||||
renewToken(ctx: ActionContext<AuthState, RootStoreState>) {
|
||||
// FIXME: Timeout to avoid race conditions when authenticated as a user (=auth token in localStorage) and as a
|
||||
// link share in another tab. Without the timeout both the token renew and link share auth are executed at
|
||||
// the same time and one might win over the other.
|
||||
@ -285,7 +286,7 @@ export default {
|
||||
}
|
||||
}, 5000)
|
||||
},
|
||||
logout(ctx) {
|
||||
logout(ctx: ActionContext<AuthState, RootStoreState>) {
|
||||
removeToken()
|
||||
window.localStorage.clear() // Clear all settings and history we might have saved in local storage.
|
||||
ctx.dispatch('checkAuth')
|
||||
|
@ -1,11 +1,14 @@
|
||||
import type { ActionContext } from 'vuex'
|
||||
import {parseURL} from 'ufo'
|
||||
|
||||
import {CONFIG} from '../mutation-types'
|
||||
import {HTTPFactory} from '@/http-common'
|
||||
import {objectToCamelCase} from '@/helpers/case'
|
||||
import {parseURL} from 'ufo'
|
||||
import type { RootStoreState, ConfigState } from '@/store/types'
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: () => ({
|
||||
state: (): ConfigState => ({
|
||||
// These are the api defaults.
|
||||
version: '',
|
||||
frontendUrl: '',
|
||||
@ -36,19 +39,19 @@ export default {
|
||||
},
|
||||
}),
|
||||
getters: {
|
||||
migratorsEnabled: state => state.availableMigrators?.length > 0,
|
||||
migratorsEnabled: (state: ConfigState) => state.availableMigrators?.length > 0,
|
||||
apiBase() {
|
||||
const {host, protocol} = parseURL(window.API_URL)
|
||||
return protocol + '//' + host
|
||||
},
|
||||
},
|
||||
mutations: {
|
||||
[CONFIG](state, config) {
|
||||
[CONFIG](state: ConfigState, config: ConfigState) {
|
||||
Object.assign(state, config)
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
async update(ctx) {
|
||||
async update(ctx: ActionContext<ConfigState, RootStoreState>) {
|
||||
const HTTP = HTTPFactory()
|
||||
const {data: config} = await HTTP.get('info')
|
||||
ctx.commit(CONFIG, objectToCamelCase(config))
|
||||
|
@ -7,10 +7,15 @@ import {success} from '@/message'
|
||||
import BucketService from '../../services/bucket'
|
||||
import {setLoading} from '../helper'
|
||||
import TaskCollectionService from '@/services/taskCollection'
|
||||
import type { ActionContext } from 'vuex'
|
||||
import type { RootStoreState, KanbanState } from '@/store/types'
|
||||
import type { ITask } from '@/models/task'
|
||||
import type { IList } from '@/models/list'
|
||||
import type { IBucket } from '@/models/bucket'
|
||||
|
||||
const TASKS_PER_BUCKET = 25
|
||||
|
||||
function getTaskIndicesById(state, taskId) {
|
||||
function getTaskIndicesById(state: KanbanState, taskId: ITask['id']) {
|
||||
let taskIndex
|
||||
const bucketIndex = state.buckets.findIndex(({ tasks }) => {
|
||||
taskIndex = findIndexById(tasks, taskId)
|
||||
@ -23,7 +28,7 @@ function getTaskIndicesById(state, taskId) {
|
||||
}
|
||||
}
|
||||
|
||||
const addTaskToBucketAndSort = (state, task) => {
|
||||
const addTaskToBucketAndSort = (state: KanbanState, task: ITask) => {
|
||||
const bucketIndex = findIndexById(state.buckets, task.bucketId)
|
||||
state.buckets[bucketIndex].tasks.push(task)
|
||||
state.buckets[bucketIndex].tasks.sort((a, b) => a.kanbanPosition > b.kanbanPosition ? 1 : -1)
|
||||
@ -36,7 +41,7 @@ const addTaskToBucketAndSort = (state, task) => {
|
||||
export default {
|
||||
namespaced: true,
|
||||
|
||||
state: () => ({
|
||||
state: (): KanbanState => ({
|
||||
buckets: [],
|
||||
listId: 0,
|
||||
bucketLoading: {},
|
||||
@ -45,11 +50,11 @@ export default {
|
||||
}),
|
||||
|
||||
mutations: {
|
||||
setListId(state, listId) {
|
||||
setListId(state: KanbanState, listId: IList['id']) {
|
||||
state.listId = parseInt(listId)
|
||||
},
|
||||
|
||||
setBuckets(state, buckets) {
|
||||
setBuckets(state: KanbanState, buckets: IBucket[]) {
|
||||
state.buckets = buckets
|
||||
buckets.forEach(b => {
|
||||
state.taskPagesPerBucket[b.id] = 1
|
||||
@ -57,31 +62,51 @@ export default {
|
||||
})
|
||||
},
|
||||
|
||||
addBucket(state, bucket) {
|
||||
addBucket(state: KanbanState, bucket: IBucket) {
|
||||
state.buckets.push(bucket)
|
||||
},
|
||||
|
||||
removeBucket(state, bucket) {
|
||||
removeBucket(state: KanbanState, bucket: IBucket) {
|
||||
const bucketIndex = findIndexById(state.buckets, bucket.id)
|
||||
state.buckets.splice(bucketIndex, 1)
|
||||
},
|
||||
|
||||
setBucketById(state, bucket) {
|
||||
setBucketById(state: KanbanState, bucket: IBucket) {
|
||||
const bucketIndex = findIndexById(state.buckets, bucket.id)
|
||||
state.buckets[bucketIndex] = bucket
|
||||
},
|
||||
|
||||
setBucketByIndex(state, {bucketIndex, bucket}) {
|
||||
setBucketByIndex(state: KanbanState, {
|
||||
bucketIndex,
|
||||
bucket,
|
||||
} : {
|
||||
bucketIndex: number,
|
||||
bucket: IBucket
|
||||
}) {
|
||||
state.buckets[bucketIndex] = bucket
|
||||
},
|
||||
|
||||
setTaskInBucketByIndex(state, {bucketIndex, taskIndex, task}) {
|
||||
setTaskInBucketByIndex(state: KanbanState, {
|
||||
bucketIndex,
|
||||
taskIndex,
|
||||
task,
|
||||
} : {
|
||||
bucketIndex: number,
|
||||
taskIndex: number,
|
||||
task: ITask
|
||||
}) {
|
||||
const bucket = state.buckets[bucketIndex]
|
||||
bucket.tasks[taskIndex] = task
|
||||
state.buckets[bucketIndex] = bucket
|
||||
},
|
||||
|
||||
setTasksInBucketByBucketId(state, {bucketId, tasks}) {
|
||||
setTasksInBucketByBucketId(state: KanbanState, {
|
||||
bucketId,
|
||||
tasks,
|
||||
} : {
|
||||
bucketId: IBucket['id'],
|
||||
tasks: ITask[],
|
||||
}) {
|
||||
const bucketIndex = findIndexById(state.buckets, bucketId)
|
||||
state.buckets[bucketIndex] = {
|
||||
...state.buckets[bucketIndex],
|
||||
@ -89,7 +114,7 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
setTaskInBucket(state, task) {
|
||||
setTaskInBucket(state: KanbanState, task: ITask) {
|
||||
// If this gets invoked without any tasks actually loaded, we can save the hassle of finding the task
|
||||
if (state.buckets.length === 0) {
|
||||
return
|
||||
@ -133,7 +158,7 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
addTaskToBucket(state, task) {
|
||||
addTaskToBucket(state: KanbanState, task: ITask) {
|
||||
const bucketIndex = findIndexById(state.buckets, task.bucketId)
|
||||
const oldBucket = state.buckets[bucketIndex]
|
||||
const newBucket = {
|
||||
@ -146,7 +171,10 @@ export default {
|
||||
state.buckets[bucketIndex] = newBucket
|
||||
},
|
||||
|
||||
addTasksToBucket(state, {tasks, bucketId}) {
|
||||
addTasksToBucket(state: KanbanState, {tasks, bucketId}: {
|
||||
tasks: ITask[];
|
||||
bucketId: IBucket['id'];
|
||||
}) {
|
||||
const bucketIndex = findIndexById(state.buckets, bucketId)
|
||||
const oldBucket = state.buckets[bucketIndex]
|
||||
const newBucket = {
|
||||
@ -159,7 +187,7 @@ export default {
|
||||
state.buckets[bucketIndex] = newBucket
|
||||
},
|
||||
|
||||
removeTaskInBucket(state, task) {
|
||||
removeTaskInBucket(state: KanbanState, task: ITask) {
|
||||
// If this gets invoked without any tasks actually loaded, we can save the hassle of finding the task
|
||||
if (state.buckets.length === 0) {
|
||||
return
|
||||
@ -168,8 +196,10 @@ export default {
|
||||
const { bucketIndex, taskIndex } = getTaskIndicesById(state, task.id)
|
||||
|
||||
if (
|
||||
!bucketIndex ||
|
||||
state.buckets[bucketIndex]?.id !== task.bucketId ||
|
||||
state.buckets[bucketIndex]?.tasks[taskIndex]?.id !== task.id
|
||||
!taskIndex ||
|
||||
(state.buckets[bucketIndex]?.tasks[taskIndex]?.id !== task.id)
|
||||
) {
|
||||
return
|
||||
}
|
||||
@ -177,39 +207,40 @@ export default {
|
||||
state.buckets[bucketIndex].tasks.splice(taskIndex, 1)
|
||||
},
|
||||
|
||||
setBucketLoading(state, {bucketId, loading}) {
|
||||
setBucketLoading(state: KanbanState, {bucketId, loading}) {
|
||||
state.bucketLoading[bucketId] = loading
|
||||
},
|
||||
|
||||
setTasksLoadedForBucketPage(state, {bucketId, page}) {
|
||||
setTasksLoadedForBucketPage(state: KanbanState, {bucketId, page}) {
|
||||
state.taskPagesPerBucket[bucketId] = page
|
||||
},
|
||||
|
||||
setAllTasksLoadedForBucket(state, bucketId) {
|
||||
setAllTasksLoadedForBucket(state: KanbanState, bucketId) {
|
||||
state.allTasksLoadedForBucket[bucketId] = true
|
||||
},
|
||||
},
|
||||
|
||||
getters: {
|
||||
getBucketById(state) {
|
||||
getBucketById(state: KanbanState) {
|
||||
return (bucketId) => findById(state.buckets, bucketId)
|
||||
},
|
||||
|
||||
getTaskById(state) {
|
||||
getTaskById(state: KanbanState) {
|
||||
return (id) => {
|
||||
const { bucketIndex, taskIndex } = getTaskIndicesById(state, id)
|
||||
|
||||
|
||||
return {
|
||||
bucketIndex,
|
||||
taskIndex,
|
||||
task: state.buckets[bucketIndex]?.tasks?.[taskIndex] || null,
|
||||
task: bucketIndex && taskIndex && state.buckets[bucketIndex]?.tasks?.[taskIndex] || null,
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
actions: {
|
||||
async loadBucketsForList(ctx, {listId, params}) {
|
||||
async loadBucketsForList(ctx: ActionContext<KanbanState, RootStoreState>, {listId, params}) {
|
||||
const cancel = setLoading(ctx, 'kanban')
|
||||
|
||||
// Clear everything to prevent having old buckets in the list if loading the buckets from this list takes a few moments
|
||||
@ -219,7 +250,7 @@ export default {
|
||||
|
||||
const bucketService = new BucketService()
|
||||
try {
|
||||
const response = await bucketService.getAll({listId: listId}, params)
|
||||
const response = await bucketService.getAll({listId}, params)
|
||||
ctx.commit('setBuckets', response)
|
||||
ctx.commit('setListId', listId)
|
||||
return response
|
||||
@ -228,7 +259,7 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
async loadNextTasksForBucket(ctx, {listId, ps = {}, bucketId}) {
|
||||
async loadNextTasksForBucket(ctx: ActionContext<KanbanState, RootStoreState>, {listId, ps = {}, bucketId}) {
|
||||
const isLoading = ctx.state.bucketLoading[bucketId] ?? false
|
||||
if (isLoading) {
|
||||
return
|
||||
@ -270,7 +301,7 @@ export default {
|
||||
|
||||
const taskService = new TaskCollectionService()
|
||||
try {
|
||||
const tasks = await taskService.getAll({listId: listId}, params, page)
|
||||
const tasks = await taskService.getAll({listId}, params, page)
|
||||
ctx.commit('addTasksToBucket', {tasks, bucketId: bucketId})
|
||||
ctx.commit('setTasksLoadedForBucketPage', {bucketId, page})
|
||||
if (taskService.totalPages <= page) {
|
||||
@ -283,7 +314,7 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
async createBucket(ctx, bucket) {
|
||||
async createBucket(ctx: ActionContext<KanbanState, RootStoreState>, bucket: IBucket) {
|
||||
const cancel = setLoading(ctx, 'kanban')
|
||||
|
||||
const bucketService = new BucketService()
|
||||
@ -296,7 +327,7 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
async deleteBucket(ctx, {bucket, params}) {
|
||||
async deleteBucket(ctx: ActionContext<KanbanState, RootStoreState>, {bucket, params}) {
|
||||
const cancel = setLoading(ctx, 'kanban')
|
||||
|
||||
const bucketService = new BucketService()
|
||||
@ -304,14 +335,14 @@ export default {
|
||||
const response = await bucketService.delete(bucket)
|
||||
ctx.commit('removeBucket', bucket)
|
||||
// We reload all buckets because tasks are being moved from the deleted bucket
|
||||
ctx.dispatch('loadBucketsForList', {listId: bucket.listId, params: params})
|
||||
ctx.dispatch('loadBucketsForList', {listId: bucket.listId, params})
|
||||
return response
|
||||
} finally {
|
||||
cancel()
|
||||
}
|
||||
},
|
||||
|
||||
async updateBucket(ctx, updatedBucketData) {
|
||||
async updateBucket(ctx: ActionContext<KanbanState, RootStoreState>, updatedBucketData) {
|
||||
const cancel = setLoading(ctx, 'kanban')
|
||||
|
||||
const bucketIndex = findIndexById(ctx.state.buckets, updatedBucketData.id)
|
||||
@ -339,10 +370,10 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
async updateBucketTitle(ctx, { id, title }) {
|
||||
async updateBucketTitle(ctx: ActionContext<KanbanState, RootStoreState>, { id, title }) {
|
||||
const bucket = findById(ctx.state.buckets, id)
|
||||
|
||||
if (bucket.title === title) {
|
||||
if (bucket?.title === title) {
|
||||
// bucket title has not changed
|
||||
return
|
||||
}
|
||||
|
@ -1,15 +1,19 @@
|
||||
import type { ActionContext } from 'vuex'
|
||||
|
||||
import {i18n} from '@/i18n'
|
||||
import {success} from '@/message'
|
||||
import LabelService from '@/services/label'
|
||||
import {setLoading} from '@/store/helper'
|
||||
import {success} from '@/message'
|
||||
import {i18n} from '@/i18n'
|
||||
import type { LabelState, RootStoreState } from '@/store/types'
|
||||
import {getLabelsByIds, filterLabelsByQuery} from '@/helpers/labels'
|
||||
import {createNewIndexer} from '@/indexes'
|
||||
import type { ILabel } from '@/models/label'
|
||||
|
||||
const {add, remove, update} = createNewIndexer('labels', ['title', 'description'])
|
||||
|
||||
async function getAllLabels(page = 1) {
|
||||
async function getAllLabels(page = 1): Promise<ILabel[]> {
|
||||
const labelService = new LabelService()
|
||||
const labels = await labelService.getAll({}, {}, page)
|
||||
const labels = await labelService.getAll({}, {}, page) as ILabel[]
|
||||
if (page < labelService.totalPages) {
|
||||
const nextLabels = await getAllLabels(page + 1)
|
||||
return labels.concat(nextLabels)
|
||||
@ -20,45 +24,44 @@ async function getAllLabels(page = 1) {
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: () => ({
|
||||
// The labels are stored as an object which has the label ids as keys.
|
||||
state: (): LabelState => ({
|
||||
labels: {},
|
||||
loaded: false,
|
||||
}),
|
||||
mutations: {
|
||||
setLabels(state, labels) {
|
||||
setLabels(state: LabelState, labels: ILabel[]) {
|
||||
labels.forEach(l => {
|
||||
state.labels[l.id] = l
|
||||
add(l)
|
||||
})
|
||||
},
|
||||
setLabel(state, label) {
|
||||
setLabel(state: LabelState, label: ILabel) {
|
||||
state.labels[label.id] = label
|
||||
update(label)
|
||||
},
|
||||
removeLabelById(state, label) {
|
||||
removeLabelById(state: LabelState, label: ILabel) {
|
||||
remove(label)
|
||||
delete state.labels[label.id]
|
||||
},
|
||||
setLoaded(state, loaded) {
|
||||
setLoaded(state: LabelState, loaded: boolean) {
|
||||
state.loaded = loaded
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
getLabelsByIds(state) {
|
||||
return (ids) => getLabelsByIds(state, ids)
|
||||
getLabelsByIds(state: LabelState) {
|
||||
return (ids: ILabel['id'][]) => getLabelsByIds(state, ids)
|
||||
},
|
||||
filterLabelsByQuery(state) {
|
||||
return (labelsToHide, query) => filterLabelsByQuery(state, labelsToHide, query)
|
||||
filterLabelsByQuery(state: LabelState) {
|
||||
return (labelsToHide: ILabel[], query: string) => filterLabelsByQuery(state, labelsToHide, query)
|
||||
},
|
||||
getLabelsByExactTitles(state) {
|
||||
getLabelsByExactTitles(state: LabelState) {
|
||||
return labelTitles => Object
|
||||
.values(state.labels)
|
||||
.filter(({title}) => labelTitles.some(l => l.toLowerCase() === title.toLowerCase()))
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
async loadAllLabels(ctx, {forceLoad} = {}) {
|
||||
async loadAllLabels(ctx: ActionContext<LabelState, RootStoreState>, {forceLoad} = {}) {
|
||||
if (ctx.state.loaded && !forceLoad) {
|
||||
return
|
||||
}
|
||||
@ -74,7 +77,7 @@ export default {
|
||||
cancel()
|
||||
}
|
||||
},
|
||||
async deleteLabel(ctx, label) {
|
||||
async deleteLabel(ctx: ActionContext<LabelState, RootStoreState>, label: ILabel) {
|
||||
const cancel = setLoading(ctx, 'labels')
|
||||
const labelService = new LabelService()
|
||||
|
||||
@ -87,7 +90,7 @@ export default {
|
||||
cancel()
|
||||
}
|
||||
},
|
||||
async updateLabel(ctx, label) {
|
||||
async updateLabel(ctx: ActionContext<LabelState, RootStoreState>, label: ILabel) {
|
||||
const cancel = setLoading(ctx, 'labels')
|
||||
const labelService = new LabelService()
|
||||
|
||||
@ -100,7 +103,7 @@ export default {
|
||||
cancel()
|
||||
}
|
||||
},
|
||||
async createLabel(ctx, label) {
|
||||
async createLabel(ctx: ActionContext<LabelState, RootStoreState>, label: ILabel) {
|
||||
const cancel = setLoading(ctx, 'labels')
|
||||
const labelService = new LabelService()
|
||||
|
||||
|
@ -2,6 +2,9 @@ import ListService from '@/services/list'
|
||||
import {setLoading} from '@/store/helper'
|
||||
import {removeListFromHistory} from '@/modules/listHistory'
|
||||
import {createNewIndexer} from '@/indexes'
|
||||
import type {ListState, RootStoreState} from '@/store/types'
|
||||
import type {ActionContext} from 'vuex'
|
||||
import type {IList} from '@/models/list'
|
||||
|
||||
const {add, remove, search, update} = createNewIndexer('lists', ['title', 'description'])
|
||||
|
||||
@ -10,37 +13,37 @@ const FavoriteListsNamespace = -2
|
||||
export default {
|
||||
namespaced: true,
|
||||
// The state is an object which has the list ids as keys.
|
||||
state: () => ({}),
|
||||
state: (): ListState => ({}),
|
||||
mutations: {
|
||||
setList(state, list) {
|
||||
setList(state: ListState, list: IList) {
|
||||
state[list.id] = list
|
||||
update(list)
|
||||
},
|
||||
setLists(state, lists) {
|
||||
setLists(state: ListState, lists: IList[]) {
|
||||
lists.forEach(l => {
|
||||
state[l.id] = l
|
||||
add(l)
|
||||
})
|
||||
},
|
||||
removeListById(state, list) {
|
||||
removeListById(state: ListState, list: IList) {
|
||||
remove(list)
|
||||
delete state[list.id]
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
getListById: state => id => {
|
||||
getListById: (state: ListState) => (id: IList['id']) => {
|
||||
if (typeof state[id] !== 'undefined') {
|
||||
return state[id]
|
||||
}
|
||||
return null
|
||||
},
|
||||
findListByExactname: state => name => {
|
||||
findListByExactname: (state: ListState) => (name: string) => {
|
||||
const list = Object.values(state).find(l => {
|
||||
return l.title.toLowerCase() === name.toLowerCase()
|
||||
})
|
||||
return typeof list === 'undefined' ? null : list
|
||||
},
|
||||
searchList: state => (query, includeArchived = false) => {
|
||||
searchList: (state: ListState) => (query: string, includeArchived = false) => {
|
||||
return search(query)
|
||||
?.filter(value => value > 0)
|
||||
.map(id => state[id])
|
||||
@ -49,14 +52,14 @@ export default {
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
toggleListFavorite(ctx, list) {
|
||||
toggleListFavorite(ctx: ActionContext<ListState, RootStoreState>, list: IList) {
|
||||
return ctx.dispatch('updateList', {
|
||||
...list,
|
||||
isFavorite: !list.isFavorite,
|
||||
})
|
||||
},
|
||||
|
||||
async createList(ctx, list) {
|
||||
async createList(ctx: ActionContext<ListState, RootStoreState>, list: IList) {
|
||||
const cancel = setLoading(ctx, 'lists')
|
||||
const listService = new ListService()
|
||||
|
||||
@ -71,7 +74,7 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
async updateList(ctx, list) {
|
||||
async updateList(ctx: ActionContext<ListState, RootStoreState>, list: IList) {
|
||||
const cancel = setLoading(ctx, 'lists')
|
||||
const listService = new ListService()
|
||||
|
||||
@ -106,7 +109,7 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
async deleteList(ctx, list) {
|
||||
async deleteList(ctx: ActionContext<ListState, RootStoreState>, list: IList) {
|
||||
const cancel = setLoading(ctx, 'lists')
|
||||
const listService = new ListService()
|
||||
|
||||
|
@ -1,22 +1,27 @@
|
||||
import type {ActionContext} from 'vuex'
|
||||
|
||||
import NamespaceService from '../../services/namespace'
|
||||
import {setLoading} from '@/store/helper'
|
||||
import {createNewIndexer} from '@/indexes'
|
||||
import type {NamespaceState, RootStoreState} from '@/store/types'
|
||||
import type {INamespace} from '@/models/namespace'
|
||||
import type {IList} from '@/models/list'
|
||||
|
||||
const {add, remove, search, update} = createNewIndexer('namespaces', ['title', 'description'])
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: () => ({
|
||||
state: (): NamespaceState => ({
|
||||
namespaces: [],
|
||||
}),
|
||||
mutations: {
|
||||
namespaces(state, namespaces) {
|
||||
namespaces(state: NamespaceState, namespaces: INamespace[]) {
|
||||
state.namespaces = namespaces
|
||||
namespaces.forEach(n => {
|
||||
add(n)
|
||||
})
|
||||
},
|
||||
setNamespaceById(state, namespace) {
|
||||
setNamespaceById(state: NamespaceState, namespace: INamespace) {
|
||||
const namespaceIndex = state.namespaces.findIndex(n => n.id === namespace.id)
|
||||
|
||||
if (namespaceIndex === -1) {
|
||||
@ -30,7 +35,7 @@ export default {
|
||||
state.namespaces[namespaceIndex] = namespace
|
||||
update(namespace)
|
||||
},
|
||||
setListInNamespaceById(state, list) {
|
||||
setListInNamespaceById(state: NamespaceState, list: IList) {
|
||||
for (const n in state.namespaces) {
|
||||
// We don't have the namespace id on the list which means we need to loop over all lists until we find it.
|
||||
// FIXME: Not ideal at all - we should fix that at the api level.
|
||||
@ -46,11 +51,11 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
addNamespace(state, namespace) {
|
||||
addNamespace(state: NamespaceState, namespace: INamespace) {
|
||||
state.namespaces.push(namespace)
|
||||
add(namespace)
|
||||
},
|
||||
removeNamespaceById(state, namespaceId) {
|
||||
removeNamespaceById(state: NamespaceState, namespaceId: INamespace['id']) {
|
||||
for (const n in state.namespaces) {
|
||||
if (state.namespaces[n].id === namespaceId) {
|
||||
remove(state.namespaces[n])
|
||||
@ -59,7 +64,7 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
addListToNamespace(state, list) {
|
||||
addListToNamespace(state: NamespaceState, list: IList) {
|
||||
for (const n in state.namespaces) {
|
||||
if (state.namespaces[n].id === list.namespaceId) {
|
||||
state.namespaces[n].lists.push(list)
|
||||
@ -67,7 +72,7 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
removeListFromNamespaceById(state, list) {
|
||||
removeListFromNamespaceById(state: NamespaceState, list: IList) {
|
||||
for (const n in state.namespaces) {
|
||||
// We don't have the namespace id on the list which means we need to loop over all lists until we find it.
|
||||
// FIXME: Not ideal at all - we should fix that at the api level.
|
||||
@ -83,7 +88,7 @@ export default {
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
getListAndNamespaceById: state => (listId, ignorePseudoNamespaces = false) => {
|
||||
getListAndNamespaceById: (state: NamespaceState) => (listId: IList['id'], ignorePseudoNamespaces = false) => {
|
||||
for (const n in state.namespaces) {
|
||||
|
||||
if (ignorePseudoNamespaces && state.namespaces[n].id < 0) {
|
||||
@ -101,10 +106,10 @@ export default {
|
||||
}
|
||||
return null
|
||||
},
|
||||
getNamespaceById: state => namespaceId => {
|
||||
getNamespaceById: (state: NamespaceState) => (namespaceId: INamespace['id']) => {
|
||||
return state.namespaces.find(({id}) => id == namespaceId) || null
|
||||
},
|
||||
searchNamespace: (state, getters) => query => {
|
||||
searchNamespace: (state: NamespaceState, getters) => (query: string) => {
|
||||
return search(query)
|
||||
?.filter(value => value > 0)
|
||||
.map(getters.getNamespaceById)
|
||||
@ -113,7 +118,7 @@ export default {
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
async loadNamespaces(ctx) {
|
||||
async loadNamespaces(ctx: ActionContext<NamespaceState, RootStoreState>) {
|
||||
const cancel = setLoading(ctx, 'namespaces')
|
||||
|
||||
const namespaceService = new NamespaceService()
|
||||
@ -133,20 +138,20 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
loadNamespacesIfFavoritesDontExist(ctx) {
|
||||
loadNamespacesIfFavoritesDontExist(ctx: ActionContext<NamespaceState, RootStoreState>) {
|
||||
// The first or second namespace should be the one holding all favorites
|
||||
if (ctx.state.namespaces[0].id !== -2 && ctx.state.namespaces[1]?.id !== -2) {
|
||||
return ctx.dispatch('loadNamespaces')
|
||||
}
|
||||
},
|
||||
|
||||
removeFavoritesNamespaceIfEmpty(ctx) {
|
||||
removeFavoritesNamespaceIfEmpty(ctx: ActionContext<NamespaceState, RootStoreState>) {
|
||||
if (ctx.state.namespaces[0].id === -2 && ctx.state.namespaces[0].lists.length === 0) {
|
||||
ctx.state.namespaces.splice(0, 1)
|
||||
}
|
||||
},
|
||||
|
||||
async deleteNamespace(ctx, namespace) {
|
||||
async deleteNamespace(ctx: ActionContext<NamespaceState, RootStoreState>, namespace: INamespace) {
|
||||
const cancel = setLoading(ctx, 'namespaces')
|
||||
const namespaceService = new NamespaceService()
|
||||
|
||||
@ -159,7 +164,7 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
async createNamespace(ctx, namespace) {
|
||||
async createNamespace(ctx: ActionContext<NamespaceState, RootStoreState>, namespace: INamespace) {
|
||||
const cancel = setLoading(ctx, 'namespaces')
|
||||
const namespaceService = new NamespaceService()
|
||||
|
||||
|
@ -1,20 +1,25 @@
|
||||
import router from '@/router'
|
||||
import type { ActionContext } from 'vuex'
|
||||
import {formatISO} from 'date-fns'
|
||||
|
||||
import TaskService from '@/services/task'
|
||||
import TaskAssigneeService from '@/services/taskAssignee'
|
||||
import TaskAssigneeModel from '../../models/taskAssignee'
|
||||
import LabelTaskModel from '../../models/labelTask'
|
||||
import TaskAssigneeModel from '@/models/taskAssignee'
|
||||
import LabelTaskModel from '@/models/labelTask'
|
||||
import LabelTaskService from '@/services/labelTask'
|
||||
import {HAS_TASKS} from '../mutation-types'
|
||||
import {setLoading} from '../helper'
|
||||
import {getQuickAddMagicMode} from '@/helpers/quickAddMagicMode'
|
||||
|
||||
import {parseTaskText} from '@/modules/parseTaskText'
|
||||
import TaskModel from '@/models/task'
|
||||
import {formatISO} from 'date-fns'
|
||||
import TaskModel, { type ITask } from '@/models/task'
|
||||
import LabelTask from '@/models/labelTask'
|
||||
import LabelModel from '@/models/label'
|
||||
import LabelModel, { type ILabel } from '@/models/label'
|
||||
import UserService from '@/services/user'
|
||||
import type { RootStoreState, TaskState } from '@/store/types'
|
||||
import type { IUser } from '@/models/user'
|
||||
import type { IAttachment } from '@/models/attachment'
|
||||
import type { IList } from '@/models/list'
|
||||
|
||||
// IDEA: maybe use a small fuzzy search here to prevent errors
|
||||
function findPropertyByValue(object, key, value) {
|
||||
@ -24,16 +29,16 @@ function findPropertyByValue(object, key, value) {
|
||||
}
|
||||
|
||||
// Check if the user exists
|
||||
function validateUsername(users, username) {
|
||||
function validateUsername(users: IUser[], username: IUser['username']) {
|
||||
return findPropertyByValue(users, 'username', username)
|
||||
}
|
||||
|
||||
// Check if the label exists
|
||||
function validateLabel(labels, label) {
|
||||
function validateLabel(labels: ILabel[], label: ILabel) {
|
||||
return findPropertyByValue(labels, 'title', label)
|
||||
}
|
||||
|
||||
async function addLabelToTask(task, label) {
|
||||
async function addLabelToTask(task: ITask, label: ILabel) {
|
||||
const labelTask = new LabelTask({
|
||||
taskId: task.id,
|
||||
labelId: label.id,
|
||||
@ -62,9 +67,9 @@ async function findAssignees(parsedTaskAssignees) {
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: () => ({}),
|
||||
state: (): TaskState => ({}),
|
||||
actions: {
|
||||
async loadTasks(ctx, params) {
|
||||
async loadTasks(ctx: ActionContext<TaskState, RootStoreState>, params) {
|
||||
const taskService = new TaskService()
|
||||
|
||||
const cancel = setLoading(ctx, 'tasks')
|
||||
@ -77,7 +82,7 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
async update(ctx, task) {
|
||||
async update(ctx: ActionContext<TaskState, RootStoreState>, task: ITask) {
|
||||
const cancel = setLoading(ctx, 'tasks')
|
||||
|
||||
const taskService = new TaskService()
|
||||
@ -90,7 +95,7 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
async delete(ctx, task) {
|
||||
async delete(ctx: ActionContext<TaskState, RootStoreState>, task: ITask) {
|
||||
const taskService = new TaskService()
|
||||
const response = await taskService.delete(task)
|
||||
ctx.commit('kanban/removeTaskInBucket', task, {root: true})
|
||||
@ -99,7 +104,13 @@ export default {
|
||||
|
||||
// Adds a task attachment in store.
|
||||
// This is an action to be able to commit other mutations
|
||||
addTaskAttachment(ctx, {taskId, attachment}) {
|
||||
addTaskAttachment(ctx: ActionContext<TaskState, RootStoreState>, {
|
||||
taskId,
|
||||
attachment,
|
||||
}: {
|
||||
taskId: ITask['id']
|
||||
attachment: IAttachment
|
||||
}) {
|
||||
const t = ctx.rootGetters['kanban/getTaskById'](taskId)
|
||||
if (t.task !== null) {
|
||||
const attachments = [
|
||||
@ -119,7 +130,13 @@ export default {
|
||||
ctx.commit('attachments/add', attachment, {root: true})
|
||||
},
|
||||
|
||||
async addAssignee(ctx, {user, taskId}) {
|
||||
async addAssignee(ctx: ActionContext<TaskState, RootStoreState>, {
|
||||
user,
|
||||
taskId,
|
||||
}: {
|
||||
user: IUser,
|
||||
taskId: ITask['id']
|
||||
}) {
|
||||
const taskAssignee = new TaskAssigneeModel({userId: user.id, taskId: taskId})
|
||||
|
||||
const taskAssigneeService = new TaskAssigneeService()
|
||||
@ -148,7 +165,13 @@ export default {
|
||||
return r
|
||||
},
|
||||
|
||||
async removeAssignee(ctx, {user, taskId}) {
|
||||
async removeAssignee(ctx: ActionContext<TaskState, RootStoreState>, {
|
||||
user,
|
||||
taskId,
|
||||
}: {
|
||||
user: IUser,
|
||||
taskId: ITask['id']
|
||||
}) {
|
||||
const taskAssignee = new TaskAssigneeModel({userId: user.id, taskId: taskId})
|
||||
|
||||
const taskAssigneeService = new TaskAssigneeService()
|
||||
@ -175,8 +198,14 @@ export default {
|
||||
|
||||
},
|
||||
|
||||
async addLabel(ctx, {label, taskId}) {
|
||||
const labelTask = new LabelTaskModel({taskId: taskId, labelId: label.id})
|
||||
async addLabel(ctx: ActionContext<TaskState, RootStoreState>, {
|
||||
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)
|
||||
@ -205,8 +234,8 @@ export default {
|
||||
return r
|
||||
},
|
||||
|
||||
async removeLabel(ctx, {label, taskId}) {
|
||||
const labelTask = new LabelTaskModel({taskId: taskId, labelId: label.id})
|
||||
async removeLabel(ctx: ActionContext<TaskState, RootStoreState>, {label, taskId}) {
|
||||
const labelTask = new LabelTaskModel({taskId, labelId: label.id})
|
||||
|
||||
const labelTaskService = new LabelTaskService()
|
||||
const response = await labelTaskService.delete(labelTask)
|
||||
@ -234,7 +263,10 @@ export default {
|
||||
},
|
||||
|
||||
// Do everything that is involved in finding, creating and adding the label to the task
|
||||
async addLabelsToTask({rootState, dispatch}, { task, parsedLabels }) {
|
||||
async addLabelsToTask({rootState, dispatch}: ActionContext<TaskState, RootStoreState>, {
|
||||
task,
|
||||
parsedLabels,
|
||||
}) {
|
||||
if (parsedLabels.length <= 0) {
|
||||
return task
|
||||
}
|
||||
@ -257,7 +289,10 @@ export default {
|
||||
return task
|
||||
},
|
||||
|
||||
findListId({ rootGetters }, { list: listName, listId }) {
|
||||
findListId({ rootGetters }: ActionContext<TaskState, RootStoreState>, { list: listName, listId }: {
|
||||
list: string,
|
||||
listId: IList['id']
|
||||
}) {
|
||||
let foundListId = null
|
||||
|
||||
// Uses the following ways to get the list id of the new task:
|
||||
@ -285,12 +320,14 @@ export default {
|
||||
return foundListId
|
||||
},
|
||||
|
||||
async createNewTask({dispatch, commit}, {
|
||||
async createNewTask({dispatch, commit}: ActionContext<TaskState, RootStoreState>, {
|
||||
title,
|
||||
bucketId,
|
||||
listId,
|
||||
position,
|
||||
}) {
|
||||
} :
|
||||
Partial<ITask>,
|
||||
) {
|
||||
const cancel = setLoading({commit}, 'tasks')
|
||||
const parsedTask = parseTaskText(title, getQuickAddMagicMode())
|
||||
|
||||
|
116
src/store/types.ts
Normal file
116
src/store/types.ts
Normal file
@ -0,0 +1,116 @@
|
||||
import type { IBucket } from '@/models/bucket'
|
||||
import type { IUserSettings } from '@/models/userSettings'
|
||||
import type { IList } from '@/models/list'
|
||||
import type { IAttachment } from '@/models/attachment'
|
||||
import type { ILabel } from '@/models/label'
|
||||
import type { INamespace } from '@/models/namespace'
|
||||
|
||||
export interface RootStoreState {
|
||||
loading: boolean,
|
||||
loadingModule: null,
|
||||
currentList: IList,
|
||||
background: string,
|
||||
blurHash: string,
|
||||
hasTasks: boolean,
|
||||
menuActive: boolean,
|
||||
keyboardShortcutsActive: boolean,
|
||||
quickActionsActive: boolean,
|
||||
|
||||
// modules
|
||||
attachments: AttachmentState,
|
||||
auth: AuthState,
|
||||
config: ConfigState,
|
||||
kanban: KanbanState,
|
||||
labels: LabelState,
|
||||
lists: ListState,
|
||||
namespaces: NamespaceState,
|
||||
tasks: TaskState,
|
||||
}
|
||||
|
||||
export interface AttachmentState {
|
||||
attachments: IAttachment[],
|
||||
}
|
||||
|
||||
export const AUTH_TYPES = {
|
||||
'UNKNOWN': 0,
|
||||
'USER': 1,
|
||||
'LINK_SHARE': 2,
|
||||
} as const
|
||||
|
||||
export interface Info {
|
||||
id: number // what kind of id is this?
|
||||
type: typeof AUTH_TYPES[keyof typeof AUTH_TYPES],
|
||||
getAvatarUrl: () => string
|
||||
settings: IUserSettings
|
||||
name: string
|
||||
email: string
|
||||
exp: any
|
||||
}
|
||||
export interface AuthState {
|
||||
authenticated: boolean,
|
||||
isLinkShareAuth: boolean,
|
||||
info: Info | null,
|
||||
needsTotpPasscode: boolean,
|
||||
avatarUrl: string,
|
||||
lastUserInfoRefresh: Date | null,
|
||||
settings: IUserSettings,
|
||||
}
|
||||
|
||||
export interface ConfigState {
|
||||
version: string,
|
||||
frontendUrl: string,
|
||||
motd: string,
|
||||
linkSharingEnabled: boolean,
|
||||
maxFileSize: '20MB',
|
||||
registrationEnabled: boolean,
|
||||
availableMigrators: [],
|
||||
taskAttachmentsEnabled: boolean,
|
||||
totpEnabled: boolean,
|
||||
enabledBackgroundProviders: [],
|
||||
legal: {
|
||||
imprintUrl: string,
|
||||
privacyPolicyUrl: string,
|
||||
},
|
||||
caldavEnabled: boolean,
|
||||
userDeletionEnabled: boolean,
|
||||
taskCommentsEnabled: boolean,
|
||||
auth: {
|
||||
local: {
|
||||
enabled: boolean,
|
||||
},
|
||||
openidConnect: {
|
||||
enabled: boolean,
|
||||
redirectUrl: string,
|
||||
providers: [],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export interface KanbanState {
|
||||
buckets: IBucket[],
|
||||
listId: IList['id'],
|
||||
bucketLoading: {},
|
||||
taskPagesPerBucket: {
|
||||
[id: IBucket['id']]: number
|
||||
},
|
||||
allTasksLoadedForBucket: {
|
||||
[id: IBucket['id']]: boolean
|
||||
},
|
||||
}
|
||||
|
||||
export interface LabelState {
|
||||
labels: {
|
||||
[id: ILabel['id']]: ILabel
|
||||
},
|
||||
loaded: boolean,
|
||||
}
|
||||
|
||||
export interface ListState {
|
||||
[id: IList['id']]: IList
|
||||
}
|
||||
|
||||
export interface NamespaceState {
|
||||
namespaces: INamespace[]
|
||||
}
|
||||
|
||||
export interface TaskState {}
|
Reference in New Issue
Block a user