From 74ad6e65e88d6aa5702686dd0b6f55e2dc6b7b77 Mon Sep 17 00:00:00 2001 From: Dominik Pschenitschni Date: Sun, 13 Feb 2022 21:40:39 +0100 Subject: [PATCH 01/32] feat: convert abstractService to ts --- src/services/abstractService.ts | 154 +++++++++++--------------------- 1 file changed, 53 insertions(+), 101 deletions(-) diff --git a/src/services/abstractService.ts b/src/services/abstractService.ts index c2a7b457..255bed68 100644 --- a/src/services/abstractService.ts +++ b/src/services/abstractService.ts @@ -1,7 +1,18 @@ -import {objectToSnakeCase} from '@/helpers/case' import {AuthenticatedHTTPFactory} from '@/http-common' +import type {Method} from 'axios' -function convertObject(o) { +import {objectToSnakeCase} from '@/helpers/case' +import AbstractModel from '@/models/abstractModel' + +interface Paths { + create : string + get : string + getAll : string + update : string + delete : string +} + +function convertObject(o: Record) { if (o instanceof Date) { return o.toISOString() } @@ -9,7 +20,7 @@ function convertObject(o) { return o } -function prepareParams(params) { +function prepareParams(params: Record) { if (typeof params !== 'object') { return params } @@ -26,16 +37,16 @@ function prepareParams(params) { return objectToSnakeCase(params) } -export default class AbstractService { +export default class AbstractService { ///////////////////////////// // Initial variable definitions /////////////////////////// - http = null + http loading = false uploadProgress = 0 - paths = { + paths: Paths = { create: '', get: '', getAll: '', @@ -52,9 +63,9 @@ export default class AbstractService { /** * The abstract constructor. - * @param [paths] An object with all paths. Default values are specified above. + * @param [paths] An object with all paths. */ - constructor(paths) { + constructor(paths : Partial = {}) { this.http = AuthenticatedHTTPFactory() // Set the interceptors to process every request @@ -82,38 +93,27 @@ export default class AbstractService { return config }) - if (paths) { - this.paths = { - create: paths.create !== undefined ? paths.create : '', - get: paths.get !== undefined ? paths.get : '', - getAll: paths.getAll !== undefined ? paths.getAll : '', - update: paths.update !== undefined ? paths.update : '', - delete: paths.delete !== undefined ? paths.delete : '', - } - } + Object.assign(this.paths, paths) } /** * Whether or not to use the create interceptor which processes a request payload into json - * @returns {boolean} */ - useCreateInterceptor() { + useCreateInterceptor(): boolean { return true } /** * Whether or not to use the update interceptor which processes a request payload into json - * @returns {boolean} */ - useUpdateInterceptor() { + useUpdateInterceptor(): boolean { return true } /** * Whether or not to use the delete interceptor which processes a request payload into json - * @returns {boolean} */ - useDeleteInterceptor() { + useDeleteInterceptor(): boolean { return true } @@ -123,11 +123,9 @@ export default class AbstractService { /** * Returns an object with all route parameters and their values. - * @param route - * @returns object */ - getRouteReplacements(route, parameters = {}) { - const replace$$1 = {} + getRouteReplacements(route : string, parameters = {}) { + const replace$$1: {} = {} let pattern = this.getRouteParameterPattern() pattern = new RegExp(pattern instanceof RegExp ? pattern.source : pattern, 'g') @@ -140,22 +138,18 @@ export default class AbstractService { /** * Holds the replacement pattern for url paths, can be overwritten by implementations. - * @return {RegExp} */ - getRouteParameterPattern() { + getRouteParameterPattern(): RegExp { return /{([^}]+)}/ } /** * Returns a fully-ready-ready-to-make-a-request-to route with replaced parameters. - * @param path - * @param pathparams - * @return string */ - getReplacedRoute(path, pathparams) { + getReplacedRoute(path : string, pathparams : {}) : string { const replacements = this.getRouteReplacements(path, pathparams) return Object.entries(replacements).reduce( - (result, [parameter, value]) => result.replace(parameter, value), + (result, [parameter, value]) => result.replace(parameter, value as string), path, ) } @@ -166,9 +160,8 @@ export default class AbstractService { * case the api returns a response in < 100ms. * But because the timeout is created using setTimeout, it will still trigger even if the request is * already finished, so we return a method to call in that case. - * @returns {Function} */ - setLoading() { + setLoading(): Function { const timeout = setTimeout(() => { this.loading = true }, 100) @@ -188,46 +181,36 @@ export default class AbstractService { /** * The modelFactory returns an model from an object. * This one here is the default one, usually the service definitions for a model will override this. - * @param data - * @returns {*} */ - modelFactory(data) { - return data + modelFactory(data : Partial) { + return new AbstractModel(data) } /** * This is the model factory for get requests. - * @param data - * @return {*} */ - modelGetFactory(data) { + modelGetFactory(data : Partial) { return this.modelFactory(data) } /** * This is the model factory for get all requests. - * @param data - * @return {*} */ - modelGetAllFactory(data) { + modelGetAllFactory(data : Partial) { return this.modelFactory(data) } /** * This is the model factory for create requests. - * @param data - * @return {*} */ - modelCreateFactory(data) { + modelCreateFactory(data : Partial) { return this.modelFactory(data) } /** * This is the model factory for update requests. - * @param data - * @return {*} */ - modelUpdateFactory(data) { + modelUpdateFactory(data : Partial) { return this.modelFactory(data) } @@ -237,37 +220,29 @@ export default class AbstractService { /** * Default preprocessor for get requests - * @param model - * @return {*} */ - beforeGet(model) { + beforeGet(model : Model) { return model } /** * Default preprocessor for create requests - * @param model - * @return {*} */ - beforeCreate(model) { + beforeCreate(model : Model) { return model } /** * Default preprocessor for update requests - * @param model - * @return {*} */ - beforeUpdate(model) { + beforeUpdate(model : Model) { return model } /** * Default preprocessor for delete requests - * @param model - * @return {*} */ - beforeDelete(model) { + beforeDelete(model : Model) { return model } @@ -279,9 +254,8 @@ export default class AbstractService { * Performs a get request to the url specified before. * @param model The model to use. The request path is built using the values from the model. * @param params Optional query parameters - * @returns {Q.Promise} */ - get(model, params = {}) { + get(model : Model, params = {}) { if (this.paths.get === '') { throw new Error('This model is not able to get data.') } @@ -292,12 +266,8 @@ export default class AbstractService { /** * This is a more abstract implementation which only does a get request. * Services which need more flexibility can use this. - * @param url - * @param model - * @param params - * @returns {Q.Promise} */ - async getM(url, model = {}, params = {}) { + async getM(url : string, model = new AbstractModel({}), params = {}) { const cancel = this.setLoading() model = this.beforeGet(model) @@ -313,12 +283,12 @@ export default class AbstractService { } } - async getBlobUrl(url, method = 'GET', data = {}) { + async getBlobUrl(url : string, method = 'GET' as Method, data = {}) { const response = await this.http({ - url: url, - method: method, + url, + method, responseType: 'blob', - data: data, + data, }) return window.URL.createObjectURL(new Blob([response.data])) } @@ -329,9 +299,8 @@ export default class AbstractService { * @param model The model to use. The request path is built using the values from the model. * @param params Optional query parameters * @param page The page to get - * @returns {Q.Promise} */ - async getAll(model = {}, params = {}, page = 1) { + async getAll(model : Model = new AbstractModel({}), params = {}, page = 1) { if (this.paths.getAll === '') { throw new Error('This model is not able to get data.') } @@ -362,10 +331,9 @@ export default class AbstractService { /** * Performs a put request to the url specified before - * @param model * @returns {Promise} */ - async create(model) { + async create(model : Model) { if (this.paths.create === '') { throw new Error('This model is not able to create data.') } @@ -388,11 +356,8 @@ export default class AbstractService { /** * An abstract implementation to send post requests. * Services can use this to implement functions to do post requests other than using the update method. - * @param url - * @param model - * @returns {Q.Promise} */ - async post(url, model) { + async post(url : string, model : Model) { const cancel = this.setLoading() try { @@ -409,10 +374,8 @@ export default class AbstractService { /** * Performs a post request to the update url - * @param model - * @returns {Q.Promise} */ - update(model) { + update(model : Model) { if (this.paths.update === '') { throw new Error('This model is not able to update data.') } @@ -423,10 +386,8 @@ export default class AbstractService { /** * Performs a delete request to the update url - * @param model - * @returns {Q.Promise} */ - async delete(model) { + async delete(model : Model) { if (this.paths.delete === '') { throw new Error('This model is not able to delete data.') } @@ -447,21 +408,15 @@ export default class AbstractService { * @param url * @param file * @param fieldName The name of the field the file is uploaded to. - * @returns {Q.Promise} */ - uploadFile(url, file, fieldName) { + uploadFile(url : string, file, fieldName : string) { return this.uploadBlob(url, new Blob([file]), fieldName, file.name) } /** * Uploads a blob to a url. - * @param url - * @param blob - * @param fieldName - * @param filename - * @returns {Q.Promise} */ - uploadBlob(url, blob, fieldName, filename) { + uploadBlob(url : string, blob: Blob, fieldName: string, filename : string) { const data = new FormData() data.append(fieldName, blob, filename) return this.uploadFormData(url, data) @@ -469,11 +424,8 @@ export default class AbstractService { /** * Uploads a form data object. - * @param url - * @param formData - * @returns {Q.Promise} */ - async uploadFormData(url, formData) { + async uploadFormData(url : string, formData: Record) { const cancel = this.setLoading() try { const response = await this.http.put( From 797de0c5432face3887f4d77bcb7dd7ee2e7e0c1 Mon Sep 17 00:00:00 2001 From: Dominik Pschenitschni Date: Thu, 23 Jun 2022 03:22:21 +0200 Subject: [PATCH 02/32] feat: add properties to models --- src/models/attachment.ts | 11 ++- src/models/avatar.ts | 4 + src/models/backgroundImage.ts | 9 +++ src/models/bucket.ts | 12 +++ src/models/caldavToken.ts | 5 +- src/models/emailUpdate.ts | 3 + src/models/file.ts | 6 ++ src/models/label.ts | 11 +++ src/models/labelTask.ts | 4 + src/models/linkShare.ts | 10 +++ src/models/list.ts | 60 +++++---------- src/models/listDuplicateModel.ts | 5 ++ src/models/namespace.ts | 22 +++--- src/models/notification.ts | 99 ++++++++++++++++++------ src/models/passwordReset.ts | 4 + src/models/passwordUpdate.ts | 3 + src/models/savedFilter.ts | 17 +++++ src/models/subscription.ts | 19 ++--- src/models/task.ts | 124 +++++++++++++------------------ src/models/taskAssignee.ts | 6 ++ src/models/taskComment.ts | 11 ++- src/models/taskRelation.ts | 24 ++++++ src/models/team.ts | 10 +++ src/models/teamList.ts | 3 + src/models/teamMember.ts | 4 + src/models/teamNamespace.ts | 3 + src/models/teamShareBase.ts | 8 ++ src/models/totp.ts | 4 + src/models/user.ts | 27 +++---- src/models/userList.ts | 4 +- src/models/userNamespace.ts | 3 + src/models/userSettings.ts | 10 +++ src/models/userShareBase.ts | 8 ++ 33 files changed, 367 insertions(+), 186 deletions(-) diff --git a/src/models/attachment.ts b/src/models/attachment.ts index b4234980..87d90d5d 100644 --- a/src/models/attachment.ts +++ b/src/models/attachment.ts @@ -3,22 +3,25 @@ import UserModel from './user' import FileModel from './file' export default class AttachmentModel extends AbstractModel { + id: number + taskId: number + createdBy: UserModel + file: FileModel + created: Date + constructor(data) { super(data) this.createdBy = new UserModel(this.createdBy) this.file = new FileModel(this.file) this.created = new Date(this.created) - - /** @type {number} */ - this.id } defaults() { return { id: 0, taskId: 0, - file: FileModel, createdBy: UserModel, + file: FileModel, created: null, } } diff --git a/src/models/avatar.ts b/src/models/avatar.ts index 33f3c4c1..2ac7a560 100644 --- a/src/models/avatar.ts +++ b/src/models/avatar.ts @@ -1,6 +1,10 @@ import AbstractModel from './abstractModel' +export type AVATAR_PROVIDERS = 'default' | 'initials' | 'gravatar' | 'marble' | 'upload' + export default class AvatarModel extends AbstractModel { + avatarProvider: AVATAR_PROVIDERS + defaults() { return { avatarProvider: '', diff --git a/src/models/backgroundImage.ts b/src/models/backgroundImage.ts index 70e44122..07c9ee31 100644 --- a/src/models/backgroundImage.ts +++ b/src/models/backgroundImage.ts @@ -1,6 +1,15 @@ import AbstractModel from './abstractModel' export default class BackgroundImageModel extends AbstractModel { + id: number + url: string + thumb: string + info: { + author: string + authorName: string + } + blurHash: string + defaults() { return { id: 0, diff --git a/src/models/bucket.ts b/src/models/bucket.ts index 28d0558d..269b5430 100644 --- a/src/models/bucket.ts +++ b/src/models/bucket.ts @@ -3,6 +3,18 @@ import UserModel from './user' import TaskModel from './task' export default class BucketModel extends AbstractModel { + id: number + title: string + listId: number + limit: number + tasks: TaskModel[] + isDoneBucket: boolean + position: number + + createdBy: UserModel + created: Date + updated: Date + constructor(bucket) { super(bucket) diff --git a/src/models/caldavToken.ts b/src/models/caldavToken.ts index 2f8f1d54..708ed229 100644 --- a/src/models/caldavToken.ts +++ b/src/models/caldavToken.ts @@ -1,14 +1,15 @@ import AbstractModel from './abstractModel' export default class CaldavTokenModel extends AbstractModel { + id: number + created: Date + constructor(data? : Object) { super(data) - /** @type {number} */ this.id if (this.created) { - /** @type {Date} */ this.created = new Date(this.created) } } diff --git a/src/models/emailUpdate.ts b/src/models/emailUpdate.ts index 04ed3045..229be90e 100644 --- a/src/models/emailUpdate.ts +++ b/src/models/emailUpdate.ts @@ -1,6 +1,9 @@ import AbstractModel from './abstractModel' export default class EmailUpdateModel extends AbstractModel { + newEmail: string + password: string + defaults() { return { newEmail: '', diff --git a/src/models/file.ts b/src/models/file.ts index 09497963..4b65ae33 100644 --- a/src/models/file.ts +++ b/src/models/file.ts @@ -1,6 +1,12 @@ import AbstractModel from './abstractModel' export default class FileModel extends AbstractModel { + id: number + mime: string + name: string + size: number + created: Date + constructor(data) { super(data) this.created = new Date(this.created) diff --git a/src/models/label.ts b/src/models/label.ts index f61658a7..4f7fa0af 100644 --- a/src/models/label.ts +++ b/src/models/label.ts @@ -4,6 +4,17 @@ import {colorIsDark} from '@/helpers/color/colorIsDark' const DEFAULT_LABEL_BACKGROUND_COLOR = 'e8e8e8' export default class LabelModel extends AbstractModel { + id: number + title: string + hexColor: string + description: string + createdBy: UserModel + listId: number + textColor: string + + created: Date + updated: Date + constructor(data) { super(data) // FIXME: this should be empty and be definied in the client. diff --git a/src/models/labelTask.ts b/src/models/labelTask.ts index 9363cc73..6a4c6809 100644 --- a/src/models/labelTask.ts +++ b/src/models/labelTask.ts @@ -1,6 +1,10 @@ import AbstractModel from './abstractModel' export default class LabelTask extends AbstractModel { + id: number + taskId: number + labelId: number + defaults() { return { id: 0, diff --git a/src/models/linkShare.ts b/src/models/linkShare.ts index 852cd8b7..2d6b4ab1 100644 --- a/src/models/linkShare.ts +++ b/src/models/linkShare.ts @@ -2,6 +2,16 @@ import AbstractModel from './abstractModel' import UserModel from './user' export default class LinkShareModel extends AbstractModel { + id: number + hash: string + right: Right + sharedBy: UserModel + sharingType: number // FIXME: use correct numbers + listId: number + name: string + password: string + created: Date + updated: Date constructor(data) { // The constructor of AbstractModel handles all the default parsing. diff --git a/src/models/list.ts b/src/models/list.ts index f846920c..8ee6a0f1 100644 --- a/src/models/list.ts +++ b/src/models/list.ts @@ -1,72 +1,48 @@ import AbstractModel from './abstractModel' import TaskModel from './task' import UserModel from './user' +import type NamespaceModel from './namespace' import {getSavedFilterIdFromListId} from '@/helpers/savedFilter' import SubscriptionModel from '@/models/subscription' export default class ListModel extends AbstractModel { + id: number + title: string + description: string + owner: UserModel + tasks: TaskModel[] + namespaceId: NamespaceModel['id'] + isArchived: boolean + hexColor: string + identifier: string + backgroundInformation: any + isFavorite: boolean + subscription: SubscriptionModel + position: number + backgroundBlurHash: string + + created: Date + updated: Date constructor(data) { super(data) this.owner = new UserModel(this.owner) - /** @type {number} */ - this.id - - /** @type {string} */ - this.title - - /** @type {string} */ - this.description - - /** @type {UserModel} */ - this.owner - - /** @type {TaskModel[]} */ - this.tasks - // Make all tasks to task models this.tasks = this.tasks.map(t => { return new TaskModel(t) }) - - /** @type {number} */ - this.namespaceId - - /** @type {boolean} */ - this.isArchived - - /** @type {string} */ - this.hexColor if (this.hexColor !== '' && this.hexColor.substring(0, 1) !== '#') { this.hexColor = '#' + this.hexColor } - /** @type {string} */ - this.identifier - - /** @type */ - this.backgroundInformation - - /** @type {boolean} */ - this.isFavorite - - /** @type */ - this.subscription - if (typeof this.subscription !== 'undefined' && this.subscription !== null) { this.subscription = new SubscriptionModel(this.subscription) } - /** @type {number} */ - this.position - - /** @type {Date} */ this.created = new Date(this.created) - - /** @type {Date} */ this.updated = new Date(this.updated) } diff --git a/src/models/listDuplicateModel.ts b/src/models/listDuplicateModel.ts index 28444185..15a9735c 100644 --- a/src/models/listDuplicateModel.ts +++ b/src/models/listDuplicateModel.ts @@ -1,7 +1,12 @@ import AbstractModel from './abstractModel' import ListModel from './list' +import NamespaceModel from './namespace' export default class ListDuplicateModel extends AbstractModel { + listId: number + namespaceId: NamespaceModel['id'] + list: ListModel + constructor(data) { super(data) this.list = new ListModel(this.list) diff --git a/src/models/namespace.ts b/src/models/namespace.ts index 7a06a19b..cae06128 100644 --- a/src/models/namespace.ts +++ b/src/models/namespace.ts @@ -4,6 +4,18 @@ import UserModel from './user' import SubscriptionModel from '@/models/subscription' export default class NamespaceModel extends AbstractModel { + id: number + title: string + description: string + owner: UserModel + lists: ListModel[] + isArchived: boolean + hexColor: string + subscription: SubscriptionModel + + created: Date + updated: Date + constructor(data) { super(data) @@ -11,7 +23,6 @@ export default class NamespaceModel extends AbstractModel { this.hexColor = '#' + this.hexColor } - /** @type {ListModel[]} */ this.lists = this.lists.map(l => { return new ListModel(l) }) @@ -22,15 +33,6 @@ export default class NamespaceModel extends AbstractModel { this.subscription = new SubscriptionModel(this.subscription) } - /** @type {number} */ - this.id - - /** @type {string} */ - this.title - - /** @type {boolean} */ - this.isArchived - this.created = new Date(this.created) this.updated = new Date(this.updated) } diff --git a/src/models/notification.ts b/src/models/notification.ts index ab522dc7..b130ce72 100644 --- a/src/models/notification.ts +++ b/src/models/notification.ts @@ -5,35 +5,86 @@ import TaskModel from '@/models/task' import TaskCommentModel from '@/models/taskComment' import ListModel from '@/models/list' import TeamModel from '@/models/team' -import names from './constants/notificationNames.json' + +export const NOTIFICATION_NAMES = { + 'TASK_COMMENT': 'task.comment', + 'TASK_ASSIGNED': 'task.assigned', + 'TASK_DELETED': 'task.deleted', + 'LIST_CREATED': 'list.created', + 'TEAM_MEMBER_ADDED': 'team.member.added', +} as const + +interface Notification { + doer: UserModel +} +interface NotificationTask extends Notification { + task: TaskModel + comment: TaskCommentModel +} + +interface NotificationAssigned extends Notification { + task: TaskModel + assignee: UserModel +} + +interface NotificationDeleted extends Notification { + task: TaskModel +} + +interface NotificationCreated extends Notification { + task: TaskModel +} + +interface NotificationMemberAdded extends Notification { + member: UserModel + team: TeamModel +} export default class NotificationModel extends AbstractModel { + id: number + name: string + notification: NotificationTask | NotificationAssigned | NotificationDeleted | NotificationCreated | NotificationMemberAdded + read: boolean + readAt: Date | null + + created: Date + constructor(data) { super(data) switch (this.name) { - case names.TASK_COMMENT: - this.notification.doer = new UserModel(this.notification.doer) - this.notification.task = new TaskModel(this.notification.task) - this.notification.comment = new TaskCommentModel(this.notification.comment) + case NOTIFICATION_NAMES.TASK_COMMENT: + this.notification = { + doer: new UserModel(this.notification.doer), + task: new TaskModel(this.notification.task), + comment: new TaskCommentModel(this.notification.comment), + } break - case names.TASK_ASSIGNED: - this.notification.doer = new UserModel(this.notification.doer) - this.notification.task = new TaskModel(this.notification.task) - this.notification.assignee = new UserModel(this.notification.assignee) + case NOTIFICATION_NAMES.TASK_ASSIGNED: + this.notification = { + doer: new UserModel(this.notification.doer), + task: new TaskModel(this.notification.task), + assignee: new UserModel(this.notification.assignee), + } break - case names.TASK_DELETED: - this.notification.doer = new UserModel(this.notification.doer) - this.notification.task = new TaskModel(this.notification.task) + case NOTIFICATION_NAMES.TASK_DELETED: + this.notification = { + doer: new UserModel(this.notification.doer), + task: new TaskModel(this.notification.task), + } break - case names.LIST_CREATED: - this.notification.doer = new UserModel(this.notification.doer) - this.notification.list = new ListModel(this.notification.list) + case NOTIFICATION_NAMES.LIST_CREATED: + this.notification = { + doer: new UserModel(this.notification.doer), + list: new ListModel(this.notification.list), + } break - case names.TEAM_MEMBER_ADDED: - this.notification.doer = new UserModel(this.notification.doer) - this.notification.member = new UserModel(this.notification.member) - this.notification.team = new TeamModel(this.notification.team) + case NOTIFICATION_NAMES.TEAM_MEMBER_ADDED: + this.notification = { + doer: new UserModel(this.notification.doer), + member: new UserModel(this.notification.member), + team: new TeamModel(this.notification.team), + } break } @@ -55,9 +106,9 @@ export default class NotificationModel extends AbstractModel { let who = '' switch (this.name) { - case names.TASK_COMMENT: + case NOTIFICATION_NAMES.TASK_COMMENT: return `commented on ${this.notification.task.getTextIdentifier()}` - case names.TASK_ASSIGNED: + case NOTIFICATION_NAMES.TASK_ASSIGNED: who = `${this.notification.assignee.getDisplayName()}` if (user !== null && user.id === this.notification.assignee.id) { @@ -65,11 +116,11 @@ export default class NotificationModel extends AbstractModel { } return `assigned ${who} to ${this.notification.task.getTextIdentifier()}` - case names.TASK_DELETED: + case NOTIFICATION_NAMES.TASK_DELETED: return `deleted ${this.notification.task.getTextIdentifier()}` - case names.LIST_CREATED: + case NOTIFICATION_NAMES.LIST_CREATED: return `created ${this.notification.list.title}` - case names.TEAM_MEMBER_ADDED: + case NOTIFICATION_NAMES.TEAM_MEMBER_ADDED: who = `${this.notification.member.getDisplayName()}` if (user !== null && user.id === this.notification.member.id) { diff --git a/src/models/passwordReset.ts b/src/models/passwordReset.ts index b84df22d..b269e54b 100644 --- a/src/models/passwordReset.ts +++ b/src/models/passwordReset.ts @@ -1,6 +1,10 @@ import AbstractModel from './abstractModel' export default class PasswordResetModel extends AbstractModel { + token: string + newPassword: string + email: string + constructor(data) { super(data) diff --git a/src/models/passwordUpdate.ts b/src/models/passwordUpdate.ts index c9869297..b59a649b 100644 --- a/src/models/passwordUpdate.ts +++ b/src/models/passwordUpdate.ts @@ -1,6 +1,9 @@ import AbstractModel from './abstractModel' export default class PasswordUpdateModel extends AbstractModel { + newPassword: string + oldPassword: string + defaults() { return { newPassword: '', diff --git a/src/models/savedFilter.ts b/src/models/savedFilter.ts index f0896b08..06334e22 100644 --- a/src/models/savedFilter.ts +++ b/src/models/savedFilter.ts @@ -2,6 +2,23 @@ import AbstractModel from '@/models/abstractModel' import UserModel from '@/models/user' export default class SavedFilterModel extends AbstractModel { + id: 0 + title: string + description: string + filters: { + sortBy: ('done' | 'id')[] + orderBy: ('asc' | 'desc')[] + filterBy: 'done'[] + filterValue: 'false'[] + filterComparator: 'equals'[] + filterConcat: 'and' + filterIncludeNulls: boolean + } + + owner: any + created: Date + updated: Date + constructor(data) { super(data) diff --git a/src/models/subscription.ts b/src/models/subscription.ts index 81a86541..a8bd7bda 100644 --- a/src/models/subscription.ts +++ b/src/models/subscription.ts @@ -2,22 +2,17 @@ import AbstractModel from '@/models/abstractModel' import UserModel from '@/models/user' export default class SubscriptionModel extends AbstractModel { + id: number + entity: string // FIXME: correct type? + entityId: number // FIXME: correct type? + user: UserModel + + created: Date + constructor(data) { super(data) - /** @type {number} */ - this.id - - /** @type {string} */ - this.entity - - /** @type {number} */ - this.entityId - - /** @type {Date} */ this.created = new Date(this.created) - - /** @type {UserModel} */ this.user = new UserModel(this.user) } diff --git a/src/models/task.ts b/src/models/task.ts index e9146e7d..540530db 100644 --- a/src/models/task.ts +++ b/src/models/task.ts @@ -2,69 +2,86 @@ import AbstractModel from './abstractModel' import UserModel from './user' import LabelModel from './label' import AttachmentModel from './attachment' -import {REPEAT_MODE_DEFAULT} from './constants/taskRepeatModes' import SubscriptionModel from '@/models/subscription' import {parseDateOrNull} from '@/helpers/parseDateOrNull' +import type ListModel from './list' const SUPPORTS_TRIGGERED_NOTIFICATION = 'Notification' in window && 'showTrigger' in Notification.prototype export const TASK_DEFAULT_COLOR = '#1973ff' +export const TASK_REPEAT_MODES = { + 'REPEAT_MODE_DEFAULT': 0, + 'REPEAT_MODE_MONTH': 1, + 'REPEAT_MODE_FROM_CURRENT_DATE': 2, +} as const + +export type TaskRepeatMode = typeof TASK_REPEAT_MODES[keyof typeof TASK_REPEAT_MODES] + +export interface RepeatAfter { + type: 'hours' | 'weeks' | 'months' | 'years' | 'days' + amount: number +} + export default class TaskModel extends AbstractModel { - constructor(data) { + id: number + title: string + description: string + done: boolean + doneAt: Date | null + priority: 0 + labels: LabelModel[] + assignees: UserModel[] + + dueDate: Date | null + startDate: Date | null + endDate: Date | null + repeatAfter: number | RepeatAfter + repeatFromCurrentDate: boolean + repeatMode: TaskRepeatMode + reminderDates: Date[] + parentTaskId: TaskModel['id'] + hexColor: string + percentDone: number + relatedTasks: { [relationKind: string]: TaskModel } // FIXME: use relationKinds + attachments: AttachmentModel[] + identifier: string + index: number + isFavorite: boolean + subscription: SubscriptionModel + + position: number + kanbanPosition: number + + createdBy: UserModel + created: Date + updated: Date + + listId: ListModel['id'] // Meta, only used when creating a new task + + constructor(data: Partial) { super(data) - /** @type {number} */ this.id = Number(this.id) - - /** @type {string} */ this.title = this.title?.trim() - - /** @type {string} */ - this.description - - /** @type {boolean} */ - this.done - - /** @type */ this.doneAt = parseDateOrNull(this.doneAt) - /** @type {number} */ - this.priority - - /** @type {LabelModel[]} */ this.labels = this.labels .map(l => new LabelModel(l)) .sort((f, s) => f.title > s.title ? 1 : -1) - /** @type {UserModel[]} */ // Parse the assignees into user models this.assignees = this.assignees.map(a => { return new UserModel(a) }) - /** @type {Date} */ this.dueDate = parseDateOrNull(this.dueDate) - - /** @type {Date} */ this.startDate = parseDateOrNull(this.startDate) - - /** @type {Date} */ this.endDate = parseDateOrNull(this.endDate) - /** @type */ - this.repeatAfter - // Parse the repeat after into something usable this.parseRepeatAfter() - /** @type {boolean} */ - this.repeatFromCurrentDate - - /** @type {TaskRepeatMode: 0 | 1 | 2} */ - this.repeatMode - - /** @type {Date[]} */ this.reminderDates = this.reminderDates.map(d => new Date(d)) // Cancel all scheduled notifications for this task to be sure to only have available notifications @@ -73,22 +90,10 @@ export default class TaskModel extends AbstractModel { this.reminderDates.forEach(d => this.scheduleNotification(d)) }) - /** @type {number} */ - this.parentTaskId - - /** @type {string} */ - this.hexColor - if (this.hexColor !== '' && this.hexColor.substring(0, 1) !== '#') { this.hexColor = '#' + this.hexColor } - /** @type {number} */ - this.percentDone - - /** @type {{ [relationKind: string]: TaskModel }} */ - this.relatedTasks - // Make all subtasks to task models Object.keys(this.relatedTasks).forEach(relationKind => { this.relatedTasks[relationKind] = this.relatedTasks[relationKind].map(t => { @@ -97,46 +102,21 @@ export default class TaskModel extends AbstractModel { }) // Make all attachments to attachment models - /** @type {AttachmentModel[]} */ this.attachments = this.attachments.map(a => new AttachmentModel(a)) - /** @type {string} */ - this.identifier - // Set the task identifier to empty if the list does not have one if (this.identifier === `-${this.index}`) { this.identifier = '' } - /** @type {number} */ - this.index - - /** @type {boolean} */ - this.isFavorite - - /** @type {SubscriptionModel} */ - this.subscription - if (typeof this.subscription !== 'undefined' && this.subscription !== null) { this.subscription = new SubscriptionModel(this.subscription) } - /** @type {number} */ - this.position - - /** @type {number} */ - this.kanbanPosition - - /** @type {UserModel} */ this.createdBy = new UserModel(this.createdBy) - - /** @type {Date} */ this.created = new Date(this.created) - - /** @type {Date} */ this.updated = new Date(this.updated) - /** @type {number} */ this.listId = Number(this.listId) } @@ -156,7 +136,7 @@ export default class TaskModel extends AbstractModel { endDate: 0, repeatAfter: 0, repeatFromCurrentDate: false, - repeatMode: REPEAT_MODE_DEFAULT, + repeatMode: TASK_REPEAT_MODES.REPEAT_MODE_DEFAULT, reminderDates: [], parentTaskId: 0, hexColor: '', @@ -204,7 +184,7 @@ export default class TaskModel extends AbstractModel { * This function should only be called from the constructor. */ parseRepeatAfter() { - const repeatAfterHours = (this.repeatAfter / 60) / 60 + const repeatAfterHours = (this.repeatAfter as number / 60) / 60 this.repeatAfter = {type: 'hours', amount: repeatAfterHours} // if its dividable by 24, its something with days, otherwise hours diff --git a/src/models/taskAssignee.ts b/src/models/taskAssignee.ts index ee697b9d..966ab640 100644 --- a/src/models/taskAssignee.ts +++ b/src/models/taskAssignee.ts @@ -1,6 +1,12 @@ import AbstractModel from './abstractModel' +import type UserModel from './user' +import type TaskModel from './task' export default class TaskAssigneeModel extends AbstractModel { + created: Date + userId: UserModel['id'] + taskId: TaskModel['id'] + constructor(data) { super(data) this.created = new Date(this.created) diff --git a/src/models/taskComment.ts b/src/models/taskComment.ts index 376195a9..d2cb6db3 100644 --- a/src/models/taskComment.ts +++ b/src/models/taskComment.ts @@ -1,7 +1,16 @@ import AbstractModel from './abstractModel' import UserModel from './user' +import type TaskModel from './task' export default class TaskCommentModel extends AbstractModel { + id: number + taskId: TaskModel['id'] + comment: string + author: UserModel + + created: Date + updated: Date + constructor(data) { super(data) this.author = new UserModel(this.author) @@ -16,7 +25,7 @@ export default class TaskCommentModel extends AbstractModel { comment: '', author: UserModel, created: null, - update: null, + updated: null, } } } diff --git a/src/models/taskRelation.ts b/src/models/taskRelation.ts index 63fcda0a..5a944c02 100644 --- a/src/models/taskRelation.ts +++ b/src/models/taskRelation.ts @@ -1,7 +1,31 @@ import AbstractModel from './abstractModel' import UserModel from './user' +import type TaskModel from './task' + +export const RELATION_KINDS = [ + 'subtask', + 'parenttask', + 'related', + 'duplicates', + 'blocking', + 'blocked', + 'precedes', + 'follows', + 'copiedfrom', + 'copiedto', + ] as const + +export type RelationKind = typeof RELATION_KINDS[number] export default class TaskRelationModel extends AbstractModel { + id: number + otherTaskId: TaskModel['id'] + taskId: TaskModel['id'] + relationKind: RelationKind + + createdBy: UserModel + created: Date + constructor(data) { super(data) this.createdBy = new UserModel(this.createdBy) diff --git a/src/models/team.ts b/src/models/team.ts index d9217926..05285ca1 100644 --- a/src/models/team.ts +++ b/src/models/team.ts @@ -3,6 +3,16 @@ import UserModel from './user' import TeamMemberModel from './teamMember' export default class TeamModel extends AbstractModel { + id: 0 + name: string + description: string + members: TeamMemberModel[] + right: Right + + createdBy: UserModel + created: Date + updated: Date + constructor(data) { super(data) diff --git a/src/models/teamList.ts b/src/models/teamList.ts index d000a01b..82f2d76c 100644 --- a/src/models/teamList.ts +++ b/src/models/teamList.ts @@ -1,6 +1,9 @@ import TeamShareBaseModel from './teamShareBase' +import type ListModel from './list' export default class TeamListModel extends TeamShareBaseModel { + listId: ListModel['id'] + defaults() { return { ...super.defaults(), diff --git a/src/models/teamMember.ts b/src/models/teamMember.ts index 2e5e236a..f0c13a77 100644 --- a/src/models/teamMember.ts +++ b/src/models/teamMember.ts @@ -1,6 +1,10 @@ import UserModel from './user' +import type ListModel from './list' export default class TeamMemberModel extends UserModel { + admin: boolean + teamId: ListModel['id'] + defaults() { return { ...super.defaults(), diff --git a/src/models/teamNamespace.ts b/src/models/teamNamespace.ts index 4c1c7521..edeb7fca 100644 --- a/src/models/teamNamespace.ts +++ b/src/models/teamNamespace.ts @@ -1,6 +1,9 @@ import TeamShareBaseModel from './teamShareBase' +import type NamespaceModel from './namespace' export default class TeamNamespaceModel extends TeamShareBaseModel { + namespaceId: NamespaceModel['id'] + defaults() { return { ...super.defaults(), diff --git a/src/models/teamShareBase.ts b/src/models/teamShareBase.ts index 08b3dc80..84e0373b 100644 --- a/src/models/teamShareBase.ts +++ b/src/models/teamShareBase.ts @@ -1,10 +1,18 @@ import AbstractModel from './abstractModel' +import type TeamModel from './team' +import type {Right} from '@/models/constants/rights' /** * This class is a base class for common team sharing model. * It is extended in a way so it can be used for namespaces as well for lists. */ export default class TeamShareBaseModel extends AbstractModel { + teamId: TeamModel['id'] + right: Right + + created: Date + updated: Date + constructor(data) { super(data) this.created = new Date(this.created) diff --git a/src/models/totp.ts b/src/models/totp.ts index 17de13b6..e631af7c 100644 --- a/src/models/totp.ts +++ b/src/models/totp.ts @@ -1,6 +1,10 @@ import AbstractModel from './abstractModel' export default class TotpModel extends AbstractModel { + secret: string + enabled: boolean + url: string + defaults() { return { secret: '', diff --git a/src/models/user.ts b/src/models/user.ts index 90d71ca7..5c212c91 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -2,30 +2,21 @@ import AbstractModel from './abstractModel' import UserSettingsModel from '@/models/userSettings' export default class UserModel extends AbstractModel { + id: number + email: string + username: string + name: string + + created: Date + updated: Date + settings: UserSettingsModel + constructor(data) { super(data) - /** @type {number} */ - this.id - - /** @type {string} */ - this.email - - /** @type {string} */ - this.username - - /** @type {string} */ - this.name - - /** @type {Date} */ this.created = new Date(this.created) - - /** @type {Date} */ this.updated = new Date(this.updated) - /** @type {UserSettingsModel} */ - this.settings - if (this.settings !== null) { this.settings = new UserSettingsModel(this.settings) } diff --git a/src/models/userList.ts b/src/models/userList.ts index 42bde509..f48989a2 100644 --- a/src/models/userList.ts +++ b/src/models/userList.ts @@ -1,7 +1,9 @@ import UserShareBaseModel from './userShareBase' - +import type ListModel from './list' // This class extends the user share model with a 'rights' parameter which is used in sharing export default class UserListModel extends UserShareBaseModel { + listId: ListModel['id'] + defaults() { return { ...super.defaults(), diff --git a/src/models/userNamespace.ts b/src/models/userNamespace.ts index 085ece65..4a4703da 100644 --- a/src/models/userNamespace.ts +++ b/src/models/userNamespace.ts @@ -1,7 +1,10 @@ import UserShareBaseModel from './userShareBase' +import type NamespaceModel from './namespace' // This class extends the user share model with a 'rights' parameter which is used in sharing export default class UserNamespaceModel extends UserShareBaseModel { + namespaceId: NamespaceModel['id'] + defaults() { return { ...super.defaults(), diff --git a/src/models/userSettings.ts b/src/models/userSettings.ts index 41932e26..3c033031 100644 --- a/src/models/userSettings.ts +++ b/src/models/userSettings.ts @@ -1,7 +1,17 @@ import AbstractModel from './abstractModel' +import type ListModel from './list' export default class UserSettingsModel extends AbstractModel { + name: string + emailRemindersEnabled: boolean + discoverableByName: boolean + discoverableByEmail: boolean + overdueTasksRemindersEnabled: boolean + defaultListId: undefined | ListModel['id'] + weekStart: 0 | 1 | 2 | 3 | 4 | 5 | 6 + timezone: string + defaults() { return { name: '', diff --git a/src/models/userShareBase.ts b/src/models/userShareBase.ts index 171643a0..3d34ac3c 100644 --- a/src/models/userShareBase.ts +++ b/src/models/userShareBase.ts @@ -1,6 +1,14 @@ import AbstractModel from './abstractModel' +import type UserModel from './user' +import type {Right} from '@/models/constants/rights' export default class UserShareBaseModel extends AbstractModel { + userId: UserModel['id'] + right: Right + + created: Date + updated: Date + constructor(data) { super(data) this.created = new Date(this.created) From af630d3b8c1536c1a9a320172aaf19e000bb2517 Mon Sep 17 00:00:00 2001 From: Dominik Pschenitschni Date: Thu, 23 Jun 2022 03:08:35 +0200 Subject: [PATCH 03/32] chore: improve type imports --- src/App.vue | 2 +- src/components/home/contentAuth.vue | 2 +- src/components/home/navigation.vue | 7 +++-- src/components/input/button.vue | 2 +- .../list/list-settings-dropdown.vue | 2 +- src/components/list/partials/filters.vue | 4 +-- src/components/list/partials/list-card.vue | 4 +-- src/components/misc/Done.vue | 2 +- src/components/misc/message.vue | 2 +- src/components/misc/subscription.vue | 27 ++++++++++-------- src/components/sharing/userTeam.vue | 2 +- src/components/tasks/add-task.vue | 2 +- src/components/tasks/gantt-component.vue | 4 +-- src/components/tasks/partials/attachments.vue | 2 +- .../tasks/partials/editAssignees.vue | 4 +-- src/components/tasks/partials/editLabels.vue | 2 +- src/components/tasks/partials/kanban-card.vue | 15 +++++----- src/components/tasks/partials/labels.vue | 4 +++ src/components/tasks/partials/reminders.vue | 2 +- .../tasks/partials/singleTaskInList.vue | 10 +++---- src/components/tasks/partials/sort.vue | 2 +- src/helpers/redirectToProvider.ts | 2 +- src/views/labels/ListLabels.vue | 2 +- src/views/labels/NewLabel.vue | 2 +- src/views/list/ListTable.vue | 4 +-- src/views/list/settings/archive.vue | 3 +- src/views/list/settings/share.vue | 6 +--- src/views/migrator/MigrateService.vue | 2 +- src/views/namespaces/NewNamespace.vue | 4 +-- src/views/tasks/ShowTasks.vue | 2 +- src/views/tasks/TaskDetailView.vue | 28 +++++++++---------- src/views/teams/EditTeam.vue | 6 ++-- src/views/user/Login.vue | 4 +-- src/views/user/OpenIdAuth.vue | 4 +-- src/views/user/Register.vue | 4 +-- src/views/user/RequestPasswordReset.vue | 2 +- src/views/user/settings/Caldav.vue | 2 +- src/views/user/settings/Deletion.vue | 6 +--- 38 files changed, 92 insertions(+), 94 deletions(-) diff --git a/src/App.vue b/src/App.vue index 9c48735c..d4ac424d 100644 --- a/src/App.vue +++ b/src/App.vue @@ -15,7 +15,7 @@ diff --git a/src/models/abstractModel.ts b/src/models/abstractModel.ts index fbf074e2..7507265b 100644 --- a/src/models/abstractModel.ts +++ b/src/models/abstractModel.ts @@ -1,12 +1,13 @@ import {objectToCamelCase} from '@/helpers/case' import {omitBy, isNil} from '@/helpers/utils' +import type {Right} from '@/models/constants/rights' export default class AbstractModel { /** * The max right the user has on this object, as returned by the x-max-right header from the api. */ - maxRight: number | null = null + maxRight: Right | null = null /** * The abstract constructor takes an object and merges its data with the default data of this model. diff --git a/src/models/constants/notificationNames.json b/src/models/constants/notificationNames.json deleted file mode 100644 index 62260fe3..00000000 --- a/src/models/constants/notificationNames.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "TASK_COMMENT": "task.comment", - "TASK_ASSIGNED": "task.assigned", - "TASK_DELETED": "task.deleted", - "LIST_CREATED": "list.created", - "TEAM_MEMBER_ADDED": "team.member.added" -} \ No newline at end of file diff --git a/src/models/constants/priorities.json b/src/models/constants/priorities.json deleted file mode 100644 index c38b195d..00000000 --- a/src/models/constants/priorities.json +++ /dev/null @@ -1,8 +0,0 @@ -{ -"UNSET": 0, -"LOW": 1, -"MEDIUM": 2, -"HIGH": 3, -"URGENT": 4, -"DO_NOW": 5 -} \ No newline at end of file diff --git a/src/models/constants/priorities.ts b/src/models/constants/priorities.ts new file mode 100644 index 00000000..b3cf85cb --- /dev/null +++ b/src/models/constants/priorities.ts @@ -0,0 +1,8 @@ +export const PRIORITIES = { + 'UNSET': 0, + 'LOW': 1, + 'MEDIUM': 2, + 'HIGH': 3, + 'URGENT': 4, + 'DO_NOW': 5, +} as const \ No newline at end of file diff --git a/src/models/constants/relationKinds.json b/src/models/constants/relationKinds.json deleted file mode 100644 index 4b3c2e51..00000000 --- a/src/models/constants/relationKinds.json +++ /dev/null @@ -1,12 +0,0 @@ -[ - "subtask", - "parenttask", - "related", - "duplicates", - "blocking", - "blocked", - "precedes", - "follows", - "copiedfrom", - "copiedto" -] \ No newline at end of file diff --git a/src/models/constants/rights.json b/src/models/constants/rights.json deleted file mode 100644 index 61e6b145..00000000 --- a/src/models/constants/rights.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "READ": 0, - "READ_WRITE": 1, - "ADMIN": 2 -} \ No newline at end of file diff --git a/src/models/constants/rights.ts b/src/models/constants/rights.ts new file mode 100644 index 00000000..1c2aeb70 --- /dev/null +++ b/src/models/constants/rights.ts @@ -0,0 +1,7 @@ +export const RIGHTS = { + 'READ': 0, + 'READ_WRITE': 1, + 'ADMIN': 2, +} as const + +export type Right = typeof RIGHTS[keyof typeof RIGHTS] \ No newline at end of file diff --git a/src/models/constants/taskRepeatModes.json b/src/models/constants/taskRepeatModes.json deleted file mode 100644 index 7d172950..00000000 --- a/src/models/constants/taskRepeatModes.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "REPEAT_MODE_DEFAULT": 0, - "REPEAT_MODE_MONTH": 1, - "REPEAT_MODE_FROM_CURRENT_DATE": 2 -} \ No newline at end of file diff --git a/src/models/linkShare.ts b/src/models/linkShare.ts index 2d6b4ab1..a0c47577 100644 --- a/src/models/linkShare.ts +++ b/src/models/linkShare.ts @@ -1,5 +1,6 @@ import AbstractModel from './abstractModel' import UserModel from './user' +import {RIGHTS, type Right} from '@/models/constants/rights' export default class LinkShareModel extends AbstractModel { id: number @@ -28,7 +29,7 @@ export default class LinkShareModel extends AbstractModel { return { id: 0, hash: '', - right: 0, + right: RIGHTS.READ, sharedBy: UserModel, sharingType: 0, listId: 0, diff --git a/src/models/team.ts b/src/models/team.ts index 05285ca1..c4e894f6 100644 --- a/src/models/team.ts +++ b/src/models/team.ts @@ -1,6 +1,7 @@ import AbstractModel from './abstractModel' import UserModel from './user' import TeamMemberModel from './teamMember' +import {RIGHTS, type Right} from '@/models/constants/rights' export default class TeamModel extends AbstractModel { id: 0 @@ -32,7 +33,7 @@ export default class TeamModel extends AbstractModel { name: '', description: '', members: [], - right: 0, + right: RIGHTS.READ, createdBy: {}, created: null, diff --git a/src/models/teamShareBase.ts b/src/models/teamShareBase.ts index 84e0373b..88060c91 100644 --- a/src/models/teamShareBase.ts +++ b/src/models/teamShareBase.ts @@ -1,6 +1,6 @@ import AbstractModel from './abstractModel' import type TeamModel from './team' -import type {Right} from '@/models/constants/rights' +import {RIGHTS, type Right} from '@/models/constants/rights' /** * This class is a base class for common team sharing model. @@ -22,7 +22,7 @@ export default class TeamShareBaseModel extends AbstractModel { defaults() { return { teamId: 0, - right: 0, + right: RIGHTS.READ, created: null, updated: null, diff --git a/src/models/userShareBase.ts b/src/models/userShareBase.ts index 3d34ac3c..9ae20de1 100644 --- a/src/models/userShareBase.ts +++ b/src/models/userShareBase.ts @@ -1,6 +1,6 @@ import AbstractModel from './abstractModel' import type UserModel from './user' -import type {Right} from '@/models/constants/rights' +import {RIGHTS, type Right} from '@/models/constants/rights' export default class UserShareBaseModel extends AbstractModel { userId: UserModel['id'] @@ -18,7 +18,7 @@ export default class UserShareBaseModel extends AbstractModel { defaults() { return { userId: '', - right: 0, + right: RIGHTS.READ, created: null, updated: null, diff --git a/src/modules/parseTaskText.test.ts b/src/modules/parseTaskText.test.ts index 90208b88..dadb9ed0 100644 --- a/src/modules/parseTaskText.test.ts +++ b/src/modules/parseTaskText.test.ts @@ -3,7 +3,7 @@ import {beforeEach, afterEach, describe, it, expect, vi} from 'vitest' import {parseTaskText} from './parseTaskText' import {getDateFromText, getDateFromTextIn} from '../helpers/time/parseDate' import {calculateDayInterval} from '../helpers/time/calculateDayInterval' -import priorities from '../models/constants/priorities.json' +import {PRIORITIES} from '@/models/constants/priorities.ts' describe('Parse Task Text', () => { beforeEach(() => { @@ -571,12 +571,12 @@ describe('Parse Task Text', () => { }) describe('Priority', () => { - for (const p in priorities) { + for (const p in PRIORITIES) { it(`should parse priority ${p}`, () => { - const result = parseTaskText(`Lorem Ipsum !${priorities[p]}`) + const result = parseTaskText(`Lorem Ipsum !${PRIORITIES[p]}`) expect(result.text).toBe('Lorem Ipsum') - expect(result.priority).toBe(priorities[p]) + expect(result.priority).toBe(PRIORITIES[p]) }) } it(`should not parse an invalid priority`, () => { diff --git a/src/modules/parseTaskText.ts b/src/modules/parseTaskText.ts index 80c33e3d..c3c8ad7d 100644 --- a/src/modules/parseTaskText.ts +++ b/src/modules/parseTaskText.ts @@ -1,5 +1,5 @@ import {parseDate} from '../helpers/time/parseDate' -import _priorities from '../models/constants/priorities.json' +import {PRIORITIES} from '@/models/constants/priorities' const VIKUNJA_PREFIXES: Prefixes = { label: '*', @@ -27,16 +27,7 @@ export const PREFIXES = { [PrefixMode.Todoist]: TODOIST_PREFIXES, } -const priorities: Priorites = _priorities - -interface Priorites { - UNSET: number, - LOW: number, - MEDIUM: number, - HIGH: number, - URGENT: number, - DO_NOW: number, -} +const priorities = PRIORITIES enum RepeatType { Hours = 'hours', diff --git a/src/views/list/ListKanban.vue b/src/views/list/ListKanban.vue index 447d849e..fc17a243 100644 --- a/src/views/list/ListKanban.vue +++ b/src/views/list/ListKanban.vue @@ -230,7 +230,7 @@ import cloneDeep from 'lodash.clonedeep' import BucketModel from '../../models/bucket' import {mapState} from 'vuex' -import Rights from '../../models/constants/rights.json' +import {RIGHTS as Rights} from '@/models/constants/rights' import {LOADING, LOADING_MODULE} from '@/store/mutation-types' import ListWrapper from './ListWrapper.vue' import FilterPopup from '@/components/list/partials/filter-popup.vue' diff --git a/src/views/list/ListList.vue b/src/views/list/ListList.vue index c94caf30..eb0c58c5 100644 --- a/src/views/list/ListList.vue +++ b/src/views/list/ListList.vue @@ -144,7 +144,7 @@ import EditTask from '@/components/tasks/edit-task.vue' import AddTask from '@/components/tasks/add-task.vue' import SingleTaskInList from '@/components/tasks/partials/singleTaskInList.vue' import { useTaskList } from '@/composables/taskList' -import Rights from '../../models/constants/rights.json' +import {RIGHTS as Rights} from '@/models/constants/rights' import FilterPopup from '@/components/list/partials/filter-popup.vue' import {HAS_TASKS} from '@/store/mutation-types' import Nothing from '@/components/misc/nothing.vue' diff --git a/src/views/tasks/TaskDetailView.vue b/src/views/tasks/TaskDetailView.vue index 02aec44a..33ed7059 100644 --- a/src/views/tasks/TaskDetailView.vue +++ b/src/views/tasks/TaskDetailView.vue @@ -427,8 +427,8 @@ import {defineComponent} from 'vue' import TaskService from '../../services/task' import TaskModel from '../../models/task' -import priorites from '../../models/constants/priorities.json' -import rights from '../../models/constants/rights.json' +import { PRIORITIES as priorites } from '@/models/constants/priorities' +import {RIGHTS as rights} from '@/models/constants/rights' import PrioritySelect from '../../components/tasks/partials/prioritySelect.vue' import PercentDoneSelect from '../../components/tasks/partials/percentDoneSelect.vue' @@ -520,7 +520,6 @@ export default defineComponent({ // Used to avoid flashing of empty elements if the task content is not yet loaded. visible: false, - priorities: priorites, activeFields: { assignees: false, priority: false, diff --git a/src/views/teams/EditTeam.vue b/src/views/teams/EditTeam.vue index b5ccd729..8496d2f5 100644 --- a/src/views/teams/EditTeam.vue +++ b/src/views/teams/EditTeam.vue @@ -172,7 +172,7 @@ import TeamMemberService from '../../services/teamMember' import type TeamMemberModel from '../../models/teamMember' import type UserModel from '../../models/user' import UserService from '../../services/user' -import Rights from '../../models/constants/rights.json' +import {RIGHTS as Rights} from '@/models/constants/rights' import Multiselect from '@/components/input/multiselect.vue' import {useRoute, useRouter} from 'vue-router' From 332acf012c423d3201ec1811093226447cd065e8 Mon Sep 17 00:00:00 2001 From: Dominik Pschenitschni Date: Thu, 23 Jun 2022 03:20:07 +0200 Subject: [PATCH 05/32] feat: function attribute typing --- src/components/home/navigation.vue | 2 +- src/components/input/fancycheckbox.vue | 2 +- src/components/sharing/linkSharing.vue | 7 ++++--- src/components/sharing/userTeam.vue | 2 +- src/components/tasks/partials/attachments.vue | 5 +++-- src/components/tasks/partials/comments.vue | 5 +++-- src/components/tasks/partials/relatedTasks.vue | 2 +- src/components/tasks/partials/singleTaskInList.vue | 4 ++-- src/services/accountDelete.ts | 12 ++++++------ src/services/backgroundUpload.ts | 4 ++-- src/services/dataExport.ts | 6 +++--- src/views/filters/FilterDelete.vue | 2 +- src/views/filters/FilterEdit.vue | 14 ++++++++------ src/views/labels/ListLabels.vue | 6 +++--- src/views/list/ListList.vue | 9 +++++---- src/views/tasks/TaskDetailView.vue | 5 +++-- src/views/teams/EditTeam.vue | 2 +- 17 files changed, 48 insertions(+), 41 deletions(-) diff --git a/src/components/home/navigation.vue b/src/components/home/navigation.vue index 3ed3044b..76a0fb23 100644 --- a/src/components/home/navigation.vue +++ b/src/components/home/navigation.vue @@ -209,7 +209,7 @@ function resize() { store.commit(MENU_ACTIVE, window.innerWidth >= 770) } -function toggleLists(namespaceId: number) { +function toggleLists(namespaceId: NamespaceModel['id']) { listsVisible.value[namespaceId] = !listsVisible.value[namespaceId] } diff --git a/src/components/input/fancycheckbox.vue b/src/components/input/fancycheckbox.vue index 204efeec..31380bf9 100644 --- a/src/components/input/fancycheckbox.vue +++ b/src/components/input/fancycheckbox.vue @@ -53,7 +53,7 @@ export default defineComponent({ }, }, methods: { - updateData(checked) { + updateData(checked: boolean) { this.checked = checked this.$emit('update:modelValue', checked) this.$emit('change', checked) diff --git a/src/components/sharing/linkSharing.vue b/src/components/sharing/linkSharing.vue index 74cae27a..b38a5834 100644 --- a/src/components/sharing/linkSharing.vue +++ b/src/components/sharing/linkSharing.vue @@ -182,6 +182,7 @@ import {useI18n} from 'vue-i18n' import {RIGHTS} from '@/models/constants/rights' import LinkShareModel from '@/models/linkShare' +import type ListModel from '@/models/list' import LinkShareService from '@/services/linkShare' @@ -216,7 +217,7 @@ watch( const store = useStore() const frontendUrl = computed(() => store.state.config.frontendUrl) -async function load(listId) { +async function load(listId: ListModel['id']) { // If listId == 0 the list on the calling component wasn't already loaded, so we just bail out here if (listId === 0) { return @@ -225,7 +226,7 @@ async function load(listId) { linkShares.value = await linkShareService.getAll({listId}) } -async function add(listId) { +async function add(listId: ListModel['id']) { const newLinkShare = new LinkShareModel({ right: selectedRight.value, listId, @@ -241,7 +242,7 @@ async function add(listId) { await load(listId) } -async function remove(listId) { +async function remove(listId: ListModel['id']) { try { await linkShareService.delete(new LinkShareModel({ id: linkIdToDelete.value, diff --git a/src/components/sharing/userTeam.vue b/src/components/sharing/userTeam.vue index 6920102c..c14902aa 100644 --- a/src/components/sharing/userTeam.vue +++ b/src/components/sharing/userTeam.vue @@ -355,7 +355,7 @@ async function toggleType(sharable) { const found = ref([]) const currentUserId = computed(() => store.state.auth.info.id) -async function find(query) { +async function find(query: string) { if (query === '') { found.value = [] return diff --git a/src/components/tasks/partials/attachments.vue b/src/components/tasks/partials/attachments.vue index 1a2c0f68..4f3292ef 100644 --- a/src/components/tasks/partials/attachments.vue +++ b/src/components/tasks/partials/attachments.vue @@ -148,6 +148,7 @@ import {defineComponent} from 'vue' import AttachmentService from '../../../services/attachment' import AttachmentModel from '../../../models/attachment' +import type FileModel from '@/models/file' import User from '@/components/misc/user.vue' import {mapState} from 'vuex' @@ -229,7 +230,7 @@ export default defineComponent({ }) }, methods: { - downloadAttachment(attachment) { + downloadAttachment(attachment: AttachmentModel) { this.attachmentService.download(attachment) }, uploadNewAttachment() { @@ -239,7 +240,7 @@ export default defineComponent({ this.uploadFiles(this.$refs.files.files) }, - uploadFiles(files) { + uploadFiles(files: FileModel[]) { uploadFiles(this.attachmentService, this.taskId, files) }, async deleteAttachment() { diff --git a/src/components/tasks/partials/comments.vue b/src/components/tasks/partials/comments.vue index 9efe0d2a..8f889e98 100644 --- a/src/components/tasks/partials/comments.vue +++ b/src/components/tasks/partials/comments.vue @@ -163,6 +163,7 @@ import TaskCommentModel from '@/models/taskComment' import {uploadFile} from '@/helpers/attachments' import {success} from '@/message' +import type TaskModel from '@/models/task' const props = defineProps({ taskId: { type: Number, @@ -213,7 +214,7 @@ function attachmentUpload(...args) { const taskCommentService = shallowReactive(new TaskCommentService()) -async function loadComments(taskId) { +async function loadComments(taskId: TaskModel['id']) { if (!enabled.value) { return } @@ -262,7 +263,7 @@ function toggleEdit(comment: TaskCommentModel) { Object.assign(commentEdit, comment) } -function toggleDelete(commentId) { +function toggleDelete(commentId: TaskCommentModel['id']) { showDeleteModal.value = !showDeleteModal.value commentToDelete.id = commentId } diff --git a/src/components/tasks/partials/relatedTasks.vue b/src/components/tasks/partials/relatedTasks.vue index c1d5c629..a46c4f63 100644 --- a/src/components/tasks/partials/relatedTasks.vue +++ b/src/components/tasks/partials/relatedTasks.vue @@ -220,7 +220,7 @@ export default defineComponent({ }, }, methods: { - async findTasks(query) { + async findTasks(query: string) { this.query = query this.foundTasks = await this.taskService.getAll({}, {s: query}) }, diff --git a/src/components/tasks/partials/singleTaskInList.vue b/src/components/tasks/partials/singleTaskInList.vue index 209633b6..6cc341d6 100644 --- a/src/components/tasks/partials/singleTaskInList.vue +++ b/src/components/tasks/partials/singleTaskInList.vue @@ -186,7 +186,7 @@ export default defineComponent({ }, }, methods: { - async markAsDone(checked) { + async markAsDone(checked: boolean) { const updateFunc = async () => { const task = await this.taskService.update(this.task) if (this.task.done) { @@ -211,7 +211,7 @@ export default defineComponent({ } }, - undoDone(checked) { + undoDone(checked: boolean) { this.task.done = !this.task.done this.markAsDone(!checked) }, diff --git a/src/services/accountDelete.ts b/src/services/accountDelete.ts index c4f443cf..52e922ca 100644 --- a/src/services/accountDelete.ts +++ b/src/services/accountDelete.ts @@ -1,15 +1,15 @@ import AbstractService from './abstractService' export default class AccountDeleteService extends AbstractService { - request(password) { - return this.post('/user/deletion/request', {password: password}) + request(password: string) { + return this.post('/user/deletion/request', {password}) } - confirm(token) { - return this.post('/user/deletion/confirm', {token: token}) + confirm(token: string) { + return this.post('/user/deletion/confirm', {token}) } - cancel(password) { - return this.post('/user/deletion/cancel', {password: password}) + cancel(password: string) { + return this.post('/user/deletion/cancel', {password}) } } \ No newline at end of file diff --git a/src/services/backgroundUpload.ts b/src/services/backgroundUpload.ts index 8f9dcf62..0f9d9792 100644 --- a/src/services/backgroundUpload.ts +++ b/src/services/backgroundUpload.ts @@ -22,9 +22,9 @@ export default class BackgroundUploadService extends AbstractService { * @param file * @returns {Promise} */ - create(listId, file) { + create(listId: ListModel['id'], file) { return this.uploadFile( - this.getReplacedRoute(this.paths.create, {listId: listId}), + this.getReplacedRoute(this.paths.create, {listId}), file, 'background', ) diff --git a/src/services/dataExport.ts b/src/services/dataExport.ts index 9f8720bf..3c4064ac 100644 --- a/src/services/dataExport.ts +++ b/src/services/dataExport.ts @@ -2,11 +2,11 @@ import AbstractService from './abstractService' import {downloadBlob} from '../helpers/downloadBlob' export default class DataExportService extends AbstractService { - request(password) { - return this.post('/user/export/request', {password: password}) + request(password: string) { + return this.post('/user/export/request', {password}) } - async download(password) { + async download(password: string) { const clear = this.setLoading() try { const url = await this.getBlobUrl('/user/export/download', 'POST', {password}) diff --git a/src/views/filters/FilterDelete.vue b/src/views/filters/FilterDelete.vue index 15238ec0..0f2c6fbf 100644 --- a/src/views/filters/FilterDelete.vue +++ b/src/views/filters/FilterDelete.vue @@ -27,7 +27,7 @@ const route = useRoute() async function deleteSavedFilter() { // We assume the listId in the route is the pseudolist - const savedFilterId = getSavedFilterIdFromListId(route.params.listId) + const savedFilterId = getSavedFilterIdFromListId(Number((route.params.listId as string))) const filterService = new SavedFilterService() const filter = new SavedFilterModel({id: savedFilterId}) diff --git a/src/views/filters/FilterEdit.vue b/src/views/filters/FilterEdit.vue index 9f978b17..5e896470 100644 --- a/src/views/filters/FilterEdit.vue +++ b/src/views/filters/FilterEdit.vue @@ -52,11 +52,12 @@