feat: improve types

This commit is contained in:
Dominik Pschenitschni 2022-07-20 21:15:35 +02:00
parent 42e72d14a4
commit c9e85cb52b
No known key found for this signature in database
GPG key ID: B257AC0149F43A77
14 changed files with 52 additions and 45 deletions

View file

@ -200,7 +200,7 @@ function handleEnter(e: KeyboardEvent) {
} }
function focusTaskInput() { function focusTaskInput() {
newTaskInput.value.focus() newTaskInput.value?.focus()
} }
defineExpose({ defineExpose({

View file

@ -117,7 +117,7 @@ async function removeLabel(label: LabelModel) {
for (const l in labels.value) { for (const l in labels.value) {
if (labels.value[l].id === label.id) { if (labels.value[l].id === label.id) {
labels.value.splice(l, 1) labels.value.splice(l, 1) // FIXME: l should be index
} }
} }
emit('update:modelValue', labels.value) emit('update:modelValue', labels.value)

View file

@ -1,5 +1,5 @@
<template> <template>
<multiselect <Multiselect
class="control is-expanded" class="control is-expanded"
:placeholder="$t('list.search')" :placeholder="$t('list.search')"
@search="findLists" @search="findLists"
@ -13,7 +13,7 @@
<span class="list-namespace-title search-result">{{ namespace(props.option.namespaceId) }} ></span> <span class="list-namespace-title search-result">{{ namespace(props.option.namespaceId) }} ></span>
{{ props.option.title }} {{ props.option.title }}
</template> </template>
</multiselect> </Multiselect>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View file

@ -45,8 +45,8 @@ const props = defineProps({
return false return false
} }
const isDate = (e: any) => e instanceof Date const isDate = (e: unknown) => e instanceof Date
const isString = (e: any) => typeof e === 'string' const isString = (e: unknown) => typeof e === 'string'
for (const e of prop) { for (const e of prop) {
if (!isDate(e) && !isString(e)) { if (!isDate(e) && !isString(e)) {

View file

@ -29,6 +29,6 @@ export async function uploadFiles(attachmentService: AttachmentService, taskId:
} }
} }
export function generateAttachmentUrl(taskId: number, attachmentId: number) : any { export function generateAttachmentUrl(taskId: number, attachmentId: number) {
return `${window.API_URL}/tasks/${taskId}/attachments/${attachmentId}` return `${window.API_URL}/tasks/${taskId}/attachments/${attachmentId}`
} }

View file

@ -11,11 +11,11 @@ export function includesById(array: [], id: string | number) {
} }
// https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_isnil // https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_isnil
export function isNil(value: any) { export function isNil(value: unknown) {
return value == null return value == null
} }
export function omitBy(obj: {}, check: (value: any) => boolean): {} { export function omitBy(obj: {}, check: (value: unknown) => boolean) {
if (isNil(obj)) { if (isNil(obj)) {
return {} return {}
} }

View file

@ -5,4 +5,6 @@ export const PRIORITIES = {
'HIGH': 3, 'HIGH': 3,
'URGENT': 4, 'URGENT': 4,
'DO_NOW': 5, 'DO_NOW': 5,
} as const } as const
export type Priority = typeof PRIORITIES[keyof typeof PRIORITIES]

View file

@ -6,6 +6,7 @@ import AttachmentModel from './attachment'
import SubscriptionModel from '@/models/subscription' import SubscriptionModel from '@/models/subscription'
import {parseDateOrNull} from '@/helpers/parseDateOrNull' import {parseDateOrNull} from '@/helpers/parseDateOrNull'
import type ListModel from './list' import type ListModel from './list'
import type { Priority } from './constants/priorities'
const SUPPORTS_TRIGGERED_NOTIFICATION = 'Notification' in window && 'showTrigger' in Notification.prototype const SUPPORTS_TRIGGERED_NOTIFICATION = 'Notification' in window && 'showTrigger' in Notification.prototype
export const TASK_DEFAULT_COLOR = '#1973ff' export const TASK_DEFAULT_COLOR = '#1973ff'
@ -29,7 +30,7 @@ export default class TaskModel extends AbstractModel {
description: string description: string
done: boolean done: boolean
doneAt: Date | null doneAt: Date | null
priority: 0 priority: Priority
labels: LabelModel[] labels: LabelModel[]
assignees: UserModel[] assignees: UserModel[]

View file

@ -2,18 +2,20 @@ import AbstractModel from './abstractModel'
import UserModel from './user' import UserModel from './user'
import type TaskModel from './task' import type TaskModel from './task'
export const RELATION_KINDS = [ export const RELATION_KIND = {
'subtask', 'SUBTASK': 'subtask',
'parenttask', 'PARENTTASK': 'parenttask',
'related', 'RELATED': 'related',
'duplicates', 'DUPLICATES': 'duplicates',
'blocking', 'BLOCKING': 'blocking',
'blocked', 'BLOCKED': 'blocked',
'precedes', 'PROCEDES': 'precedes',
'follows', 'FOLLOWS': 'follows',
'copiedfrom', 'COPIEDFROM': 'copiedfrom',
'copiedto', 'COPIEDTO': 'copiedto',
] as const } as const
export const RELATION_KINDS = [...Object.values(RELATION_KIND)] as const
export type RelationKind = typeof RELATION_KINDS[number] export type RelationKind = typeof RELATION_KINDS[number]

View file

@ -47,7 +47,7 @@ interface repeatParsedResult {
repeats: Repeats | null, repeats: Repeats | null,
} }
interface ParsedTaskText { export interface ParsedTaskText {
text: string, text: string,
date: Date | null, date: Date | null,
labels: string[], labels: string[],

View file

@ -3,6 +3,8 @@ import type {Method} from 'axios'
import {objectToSnakeCase} from '@/helpers/case' import {objectToSnakeCase} from '@/helpers/case'
import AbstractModel from '@/models/abstractModel' import AbstractModel from '@/models/abstractModel'
import type { Right } from '@/models/constants/rights'
import type FileModel from '@/models/file'
interface Paths { interface Paths {
create : string create : string
@ -20,7 +22,7 @@ function convertObject(o: Record<string, unknown>) {
return o return o
} }
function prepareParams(params: Record<string, unknown | any[]>) { function prepareParams(params: Record<string, unknown | unknown[]>) {
if (typeof params !== 'object') { if (typeof params !== 'object') {
return params return params
} }
@ -37,7 +39,7 @@ function prepareParams(params: Record<string, unknown | any[]>) {
return objectToSnakeCase(params) return objectToSnakeCase(params)
} }
export default class AbstractService<Model extends AbstractModel> { export default class AbstractService<Model extends AbstractModel = AbstractModel> {
///////////////////////////// /////////////////////////////
// Initial variable definitions // Initial variable definitions
@ -124,8 +126,8 @@ export default class AbstractService<Model extends AbstractModel> {
/** /**
* Returns an object with all route parameters and their values. * Returns an object with all route parameters and their values.
*/ */
getRouteReplacements(route : string, parameters = {}) { getRouteReplacements(route : string, parameters : Record<string, unknown> = {}) {
const replace$$1: {} = {} const replace$$1: Record<string, unknown> = {}
let pattern = this.getRouteParameterPattern() let pattern = this.getRouteParameterPattern()
pattern = new RegExp(pattern instanceof RegExp ? pattern.source : pattern, 'g') pattern = new RegExp(pattern instanceof RegExp ? pattern.source : pattern, 'g')
@ -161,7 +163,7 @@ export default class AbstractService<Model extends AbstractModel> {
* But because the timeout is created using setTimeout, it will still trigger even if the request is * 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. * already finished, so we return a method to call in that case.
*/ */
setLoading(): Function { setLoading() {
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
this.loading = true this.loading = true
}, 100) }, 100)
@ -267,7 +269,7 @@ export default class AbstractService<Model extends AbstractModel> {
* This is a more abstract implementation which only does a get request. * This is a more abstract implementation which only does a get request.
* Services which need more flexibility can use this. * Services which need more flexibility can use this.
*/ */
async getM(url : string, model = new AbstractModel({}), params = {}) { async getM(url : string, model = new AbstractModel({}) as Model, params: Record<string, unknown> = {}) {
const cancel = this.setLoading() const cancel = this.setLoading()
model = this.beforeGet(model) model = this.beforeGet(model)
@ -276,7 +278,7 @@ export default class AbstractService<Model extends AbstractModel> {
try { try {
const response = await this.http.get(finalUrl, {params: prepareParams(params)}) const response = await this.http.get(finalUrl, {params: prepareParams(params)})
const result = this.modelGetFactory(response.data) const result = this.modelGetFactory(response.data)
result.maxRight = Number(response.headers['x-max-right']) result.maxRight = Number(response.headers['x-max-right']) as Right
return result return result
} finally { } finally {
cancel() cancel()
@ -300,7 +302,7 @@ export default class AbstractService<Model extends AbstractModel> {
* @param params Optional query parameters * @param params Optional query parameters
* @param page The page to get * @param page The page to get
*/ */
async getAll(model : Model = new AbstractModel({}), params = {}, page = 1) { async getAll(model : Model = new AbstractModel({}) as Model, params = {}, page = 1) {
if (this.paths.getAll === '') { if (this.paths.getAll === '') {
throw new Error('This model is not able to get data.') throw new Error('This model is not able to get data.')
} }
@ -406,10 +408,10 @@ export default class AbstractService<Model extends AbstractModel> {
/** /**
* Uploads a file to a url. * Uploads a file to a url.
* @param url * @param url
* @param file * @param file {FileModel}
* @param fieldName The name of the field the file is uploaded to. * @param fieldName The name of the field the file is uploaded to.
*/ */
uploadFile(url : string, file, fieldName : string) { uploadFile(url : string, file: FileModel, fieldName : string) {
return this.uploadBlob(url, new Blob([file]), fieldName, file.name) return this.uploadBlob(url, new Blob([file]), fieldName, file.name)
} }
@ -425,7 +427,7 @@ export default class AbstractService<Model extends AbstractModel> {
/** /**
* Uploads a form data object. * Uploads a form data object.
*/ */
async uploadFormData(url : string, formData: Record<string, unknown>) { async uploadFormData(url : string, formData: FormData) {
const cancel = this.setLoading() const cancel = this.setLoading()
try { try {
const response = await this.http.put( const response = await this.http.put(

View file

@ -2,8 +2,9 @@ import AbstractService from './abstractService'
import AttachmentModel from '../models/attachment' import AttachmentModel from '../models/attachment'
import {formatISO} from 'date-fns' import {formatISO} from 'date-fns'
import {downloadBlob} from '@/helpers/downloadBlob' import {downloadBlob} from '@/helpers/downloadBlob'
import type FileModel from '@/models/file'
export default class AttachmentService extends AbstractService { export default class AttachmentService extends AbstractService<AttachmentModel> {
constructor() { constructor() {
super({ super({
create: '/tasks/{taskId}/attachments', create: '/tasks/{taskId}/attachments',
@ -12,7 +13,7 @@ export default class AttachmentService extends AbstractService {
}) })
} }
processModel(model) { processModel(model: AttachmentModel) {
model.created = formatISO(new Date(model.created)) model.created = formatISO(new Date(model.created))
return model return model
} }
@ -33,26 +34,25 @@ export default class AttachmentService extends AbstractService {
return data return data
} }
getBlobUrl(model) { getBlobUrl(model: AttachmentModel) {
return AbstractService.prototype.getBlobUrl.call(this, '/tasks/' + model.taskId + '/attachments/' + model.id) return AbstractService.prototype.getBlobUrl.call(this, '/tasks/' + model.taskId + '/attachments/' + model.id)
} }
async download(model) { async download(model: AttachmentModel) {
const url = await this.getBlobUrl(model) const url = await this.getBlobUrl(model)
return downloadBlob(url, model.file.name) return downloadBlob(url, model.file.name)
} }
/** /**
* Uploads a file to the server * Uploads a file to the server
* @param model
* @param files * @param files
* @returns {Promise<any|never>} * @returns {Promise<any|never>}
*/ */
create(model, files) { create(model: AttachmentModel, files: FileModel[]) {
const data = new FormData() const data = new FormData()
for (let i = 0; i < files.length; i++) { for (let i = 0; i < files.length; i++) {
// TODO: Validation of file size // TODO: Validation of file size
data.append('files', new Blob([files[i]]), files[i].name) data.append('files', new Blob([JSON.stringify(files[i], null, 2)]), files[i].name)
} }
return this.uploadFormData( return this.uploadFormData(

View file

@ -1,5 +1,6 @@
import AbstractService from './abstractService' import AbstractService from './abstractService'
import ListModel from '../models/list' import ListModel from '../models/list'
import type FileModel from '@/models/file'
export default class BackgroundUploadService extends AbstractService { export default class BackgroundUploadService extends AbstractService {
constructor() { constructor() {
@ -18,11 +19,10 @@ export default class BackgroundUploadService extends AbstractService {
/** /**
* Uploads a file to the server * Uploads a file to the server
* @param listId
* @param file * @param file
* @returns {Promise<any|never>} * @returns {Promise<any|never>}
*/ */
create(listId: ListModel['id'], file) { create(listId: ListModel['id'], file: FileModel) {
return this.uploadFile( return this.uploadFile(
this.getReplacedRoute(this.paths.create, {listId}), this.getReplacedRoute(this.paths.create, {listId}),
file, file,

View file

@ -113,7 +113,7 @@ export default class TaskService extends AbstractService {
model.labels = model.labels.map(l => labelService.processModel(l)) model.labels = model.labels.map(l => labelService.processModel(l))
} }
return model return model as TaskModel
} }
} }