feat: convert model methods to named functions
continuous-integration/drone/pr Build is passing Details

This commit is contained in:
Dominik Pschenitschni 2022-09-23 16:51:35 +02:00
parent 176ad565cc
commit 8e3f54ae42
Signed by: dpschen
GPG Key ID: B257AC0149F43A77
16 changed files with 75 additions and 51 deletions

View File

@ -2,34 +2,39 @@
<div :class="{'is-inline': isInline}" class="user">
<img
:height="avatarSize"
:src="user.getAvatarUrl(avatarSize)"
:src="getAvatarUrl(user, avatarSize)"
:width="avatarSize"
alt=""
class="avatar"
v-tooltip="user.getDisplayName()"/>
<span class="username" v-if="showUsername">{{ user.getDisplayName() }}</span>
v-tooltip="getDisplayName(user)"/>
<span class="username" v-if="showUsername">{{ getDisplayName(user) }}</span>
</div>
</template>
<script lang="ts" setup>
import type {PropType} from 'vue'
import {getAvatarUrl, getDisplayName} from '@/models/user'
import type {IUser} from '@/modelTypes/IUser'
defineProps({
user: {
type: Object as PropType<IUser>,
required: true,
type: Object,
},
showUsername: {
required: false,
type: Boolean,
required: false,
default: true,
},
avatarSize: {
required: false,
type: Number,
required: false,
default: 50,
},
isInline: {
required: false,
type: Boolean,
required: false,
default: false,
},
})

View File

@ -24,7 +24,7 @@
<div class="detail">
<div>
<span class="has-text-weight-bold mr-1" v-if="n.notification.doer">
{{ n.notification.doer.getDisplayName() }}
{{ getDisplayName(n.notification.doer) }}
</span>
<BaseButton @click="() => to(n, index)()">
{{ n.toText(userInfo) }}
@ -56,6 +56,7 @@ import User from '@/components/misc/user.vue'
import { NOTIFICATION_NAMES as names, type INotification} from '@/modelTypes/INotification'
import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
import {formatDateLong, formatDateSince} from '@/helpers/time/formatDate'
import {getDisplayName} from '@/models/user'
import {useAuthStore} from '@/stores/auth'
const LOAD_NOTIFICATIONS_INTERVAL = 10000

View File

@ -93,7 +93,7 @@
<p class="mb-2">
<i18n-t keypath="list.share.links.sharedBy" scope="global">
<strong>{{ s.sharedBy.getDisplayName() }}</strong>
<strong>{{ getDisplayName(s.sharedBy) }}</strong>
</i18n-t>
</p>
@ -201,6 +201,7 @@ import LinkShareService from '@/services/linkShare'
import {useCopyToClipboard} from '@/composables/useCopyToClipboard'
import {success} from '@/message'
import {getDisplayName} from '@/models/user'
import type {ListView} from '@/types/ListView'
import {LIST_VIEWS} from '@/types/ListView'
import {useConfigStore} from '@/stores/config'

View File

@ -28,7 +28,7 @@
<tbody>
<tr :key="s.id" v-for="s in sharables">
<template v-if="shareType === 'user'">
<td>{{ s.getDisplayName() }}</td>
<td>{{ getDisplayName(s) }}</td>
<td>
<template v-if="s.id === userInfo.id">
<b class="is-success">{{ $t('list.share.userTeam.you') }}</b>
@ -150,7 +150,7 @@ import UserListModel from '@/models/userList'
import type {IUserList} from '@/modelTypes/IUserList'
import UserService from '@/services/user'
import UserModel from '@/models/user'
import UserModel, { getDisplayName } from '@/models/user'
import type {IUser} from '@/modelTypes/IUser'
import TeamNamespaceService from '@/services/teamNamespace'

View File

@ -17,7 +17,7 @@
<div :key="c.id" class="media comment" v-for="c in comments">
<figure class="media-left is-hidden-mobile">
<img
:src="c.author.getAvatarUrl(48)"
:src="getAvatarUrl(c.author, 48)"
alt=""
class="image is-avatar"
height="48"
@ -27,13 +27,13 @@
<div class="media-content">
<div class="comment-info">
<img
:src="c.author.getAvatarUrl(20)"
:src="getAvatarUrl(c.author, 20)"
alt=""
class="image is-avatar d-print-none"
height="20"
width="20"
/>
<strong>{{ c.author.getDisplayName() }}</strong>&nbsp;
<strong>{{ getDisplayName(c.author) }}</strong>&nbsp;
<span v-tooltip="formatDateLong(c.created)" class="has-text-grey">
{{ formatDateSince(c.created) }}
</span>
@ -166,6 +166,7 @@ import type {ITask} from '@/modelTypes/ITask'
import {uploadFile} from '@/helpers/attachments'
import {success} from '@/message'
import {formatDateLong, formatDateSince} from '@/helpers/time/formatDate'
import {getAvatarUrl, getDisplayName} from '@/models/user'
import {useConfigStore} from '@/stores/config'
import {useAuthStore} from '@/stores/auth'
@ -196,7 +197,7 @@ const newComment = reactive(new TaskCommentModel())
const saved = ref<ITask['id'] | null>(null)
const saving = ref<ITask['id'] | null>(null)
const userAvatar = computed(() => authStore.info.getAvatarUrl(48))
const userAvatar = computed(() => getAvatarUrl(authStore.info, 48))
const currentUserId = computed(() => authStore.info.id)
const enabled = computed(() => configStore.taskCommentsEnabled)
const actions = computed(() => {

View File

@ -3,7 +3,7 @@
<time :datetime="formatISO(task.created)" v-tooltip="formatDateLong(task.created)">
<i18n-t keypath="task.detail.created" scope="global">
<span>{{ formatDateSince(task.created) }}</span>
{{ task.createdBy.getDisplayName() }}
{{ getDisplayName(task.createdBy) }}
</i18n-t>
</time>
<template v-if="+new Date(task.created) !== +new Date(task.updated)">
@ -30,6 +30,7 @@
import {computed, toRefs, type PropType} from 'vue'
import type {ITask} from '@/modelTypes/ITask'
import {formatISO, formatDateLong, formatDateSince} from '@/helpers/time/formatDate'
import {getDisplayName} from '@/models/user'
const props = defineProps({
task: {

View File

@ -38,7 +38,7 @@
</template>
<script setup lang="ts">
import {ref, shallowReactive, computed, watch, onMounted, onBeforeUnmount, type PropType} from 'vue'
import {ref, shallowReactive, computed, watch, onMounted, onBeforeUnmount, toRef, type PropType} from 'vue'
import {useI18n} from 'vue-i18n'
import flatPickr from 'vue-flatpickr-component'
@ -63,12 +63,12 @@ const task = ref<ITask>()
// We're saving the due date seperately to prevent null errors in very short periods where the task is null.
const dueDate = ref<Date>()
const lastValue = ref<Date>()
const changeInterval = ref<number>()
const changeInterval = ref<ReturnType<typeof setInterval>>()
watch(
() => props.modelValue,
toRef(props, 'modelValue'),
(value) => {
task.value = value
task.value = { ...value }
dueDate.value = value.dueDate
lastValue.value = value.dueDate
},
@ -123,7 +123,6 @@ async function updateDueDate() {
return
}
// FIXME: direct prop manipulation
const newTask = await taskService.update({
...task.value,
dueDate: new Date(dueDate.value),

View File

@ -7,13 +7,15 @@ export const AUTH_TYPES = {
'LINK_SHARE': 2,
} as const
type AuthType = typeof AUTH_TYPES[keyof typeof AUTH_TYPES]
export interface IUser extends IAbstract {
id: number
email: string
username: string
name: string
exp: number
type: typeof AUTH_TYPES[keyof typeof AUTH_TYPES],
type: AuthType
created: Date
updated: Date

View File

@ -1,12 +1,13 @@
import AbstractModel from './abstractModel'
import {parseDateOrNull} from '@/helpers/parseDateOrNull'
import UserModel from '@/models/user'
import UserModel, {getDisplayName} from '@/models/user'
import TaskModel from '@/models/task'
import TaskCommentModel from '@/models/taskComment'
import ListModel from '@/models/list'
import TeamModel from '@/models/team'
import {NOTIFICATION_NAMES, type INotification} from '@/modelTypes/INotification'
import type { IUser } from '@/modelTypes/IUser'
export default class NotificationModel extends AbstractModel<INotification> implements INotification {
id = 0
@ -61,14 +62,14 @@ export default class NotificationModel extends AbstractModel<INotification> impl
this.readAt = parseDateOrNull(this.readAt)
}
toText(user = null) {
toText(user: IUser | null = null) {
let who = ''
switch (this.name) {
case NOTIFICATION_NAMES.TASK_COMMENT:
return `commented on ${this.notification.task.getTextIdentifier()}`
case NOTIFICATION_NAMES.TASK_ASSIGNED:
who = `${this.notification.assignee.getDisplayName()}`
who = `${getDisplayName(this.notification.assignee)}`
if (user !== null && user.id === this.notification.assignee.id) {
who = 'you'
@ -80,7 +81,7 @@ export default class NotificationModel extends AbstractModel<INotification> impl
case NOTIFICATION_NAMES.LIST_CREATED:
return `created ${this.notification.list.title}`
case NOTIFICATION_NAMES.TEAM_MEMBER_ADDED:
who = `${this.notification.member.getDisplayName()}`
who = `${getDisplayName(this.notification.member)}`
if (user !== null && user.id === this.notification.member.id) {
who = 'you'

View File

@ -4,6 +4,18 @@ import UserSettingsModel from '@/models/userSettings'
import { AUTH_TYPES, type IUser } from '@/modelTypes/IUser'
import type { IUserSettings } from '@/modelTypes/IUserSettings'
export function getAvatarUrl(user: IUser, size = 50) {
return `${window.API_URL}/avatar/${user.username}?size=${size}`
}
export function getDisplayName(user: IUser) {
if (user.name !== '') {
return user.name
}
return user.username
}
export default class UserModel extends AbstractModel<IUser> implements IUser {
id = 0
email = ''
@ -25,16 +37,4 @@ export default class UserModel extends AbstractModel<IUser> implements IUser {
this.settings = new UserSettingsModel(this.settings || {})
}
getAvatarUrl(size = 50) {
return `${window.API_URL}/avatar/${this.username}?size=${size}`
}
getDisplayName() {
if (this.name !== '') {
return this.name
}
return this.username
}
}

View File

@ -32,6 +32,7 @@ export interface AuthState {
lastUserInfoRefresh: Date | null,
settings: IUserSettings,
isLoading: boolean,
isLoadingGeneralSettings: boolean
}
export interface ConfigState {

View File

@ -3,7 +3,7 @@ import {defineStore, acceptHMRUpdate} from 'pinia'
import {HTTPFactory, AuthenticatedHTTPFactory} from '@/http-common'
import {i18n, getCurrentLanguage, saveLanguage} from '@/i18n'
import {objectToSnakeCase} from '@/helpers/case'
import UserModel from '@/models/user'
import UserModel, { getAvatarUrl } from '@/models/user'
import UserSettingsService from '@/services/userSettings'
import {getToken, refreshToken, removeToken, saveToken} from '@/helpers/auth'
import {setLoadingPinia} from '@/store/helper'
@ -15,6 +15,7 @@ import type {IUserSettings} from '@/modelTypes/IUserSettings'
import router from '@/router'
import {useConfigStore} from '@/stores/config'
import UserSettingsModel from '@/models/userSettings'
import {store} from '@/store'
export const useAuthStore = defineStore('auth', {
state: () : AuthState => ({
@ -28,6 +29,7 @@ export const useAuthStore = defineStore('auth', {
lastUserInfoRefresh: null,
isLoading: false,
isLoadingGeneralSettings: false,
}),
getters: {
authUser(state) {
@ -48,6 +50,10 @@ export const useAuthStore = defineStore('auth', {
this.isLoading = isLoading
},
setIsLoadingGeneralSettings(isLoading: boolean) {
this.isLoadingGeneralSettings = isLoading
},
setUser(info: IUser | null) {
this.info = info
if (info !== null) {
@ -78,7 +84,7 @@ export const useAuthStore = defineStore('auth', {
},
reloadAvatar() {
if (!this.info) return
this.avatarUrl = `${this.info.getAvatarUrl()}&=${+new Date()}`
this.avatarUrl = `${getAvatarUrl(this.info)}&=${+new Date()}`
},
updateLastUserRefresh() {
this.lastUserInfoRefresh = new Date()
@ -87,6 +93,7 @@ export const useAuthStore = defineStore('auth', {
// Logs a user in with a set of credentials.
async login(credentials) {
const HTTP = HTTPFactory()
store.commit('loading', true)
this.setIsLoading(true)
// Delete an eventually preexisting old token
@ -110,6 +117,7 @@ export const useAuthStore = defineStore('auth', {
throw e
} finally {
store.commit('loading', false)
this.setIsLoading(false)
}
},
@ -118,6 +126,7 @@ export const useAuthStore = defineStore('auth', {
// Not sure if this is the right place to put the logic in, maybe a seperate js component would be better suited.
async register(credentials) {
const HTTP = HTTPFactory()
store.commit('loading', true)
this.setIsLoading(true)
try {
await HTTP.post('register', credentials)
@ -129,12 +138,14 @@ export const useAuthStore = defineStore('auth', {
throw e
} finally {
store.commit('loading', false)
this.setIsLoading(false)
}
},
async openIdAuth({provider, code}) {
const HTTP = HTTPFactory()
store.commit('loading', true)
this.setIsLoading(true)
const data = {
@ -151,6 +162,7 @@ export const useAuthStore = defineStore('auth', {
// Tell others the user is autheticated
this.checkAuth()
} finally {
store.commit('loading', false)
this.setIsLoading(false)
}
},
@ -266,11 +278,10 @@ export const useAuthStore = defineStore('auth', {
settings: IUserSettings
showMessage : boolean
}) {
// const showMessage = payload.showMessage ?? true
const userSettingsService = new UserSettingsService()
// FIXME
const cancel = setLoadingPinia(useAuthStore, 'general-settings')
const cancel = setLoadingPinia(this, this.setIsLoadingGeneralSettings)
try {
saveLanguage(settings.language)
await userSettingsService.update(settings)

View File

@ -80,7 +80,7 @@ export const useLabelStore = defineStore('label', {
return
}
const cancel = setLoadingPinia(useLabelStore, this.setIsLoading)
const cancel = setLoadingPinia(this)
try {
const labels = await getAllLabels()
@ -92,7 +92,7 @@ export const useLabelStore = defineStore('label', {
},
async deleteLabel(label: ILabel) {
const cancel = setLoadingPinia(useLabelStore)
const cancel = setLoadingPinia(this)
const labelService = new LabelService()
try {
@ -106,7 +106,7 @@ export const useLabelStore = defineStore('label', {
},
async updateLabel(label: ILabel) {
const cancel = setLoadingPinia(useLabelStore)
const cancel = setLoadingPinia(this)
const labelService = new LabelService()
try {
@ -120,7 +120,7 @@ export const useLabelStore = defineStore('label', {
},
async createLabel(label: ILabel) {
const cancel = setLoadingPinia(useLabelStore)
const cancel = setLoadingPinia(this)
const labelService = new LabelService()
try {

View File

@ -87,7 +87,7 @@ export const useListStore = defineStore('list', {
},
async createList(list: IList) {
const cancel = setLoadingPinia(useListStore)
const cancel = setLoadingPinia(this)
const listService = new ListService()
try {
@ -103,7 +103,7 @@ export const useListStore = defineStore('list', {
},
async updateList(list: IList) {
const cancel = setLoadingPinia(useListStore)
const cancel = setLoadingPinia(this)
const listService = new ListService()
try {
@ -139,7 +139,7 @@ export const useListStore = defineStore('list', {
},
async deleteList(list: IList) {
const cancel = setLoadingPinia(useListStore)
const cancel = setLoadingPinia(this)
const listService = new ListService()
try {

View File

@ -36,7 +36,7 @@ import CreateEdit from '@/components/misc/create-edit.vue'
import manageSharing from '@/components/sharing/userTeam.vue'
import {useTitle} from '@/composables/useTitle'
import {useAuthStore} from '@/stores/auth'
import type { INamespace } from '@/modelTypes/INamespace'
import type {INamespace} from '@/modelTypes/INamespace'
const {t} = useI18n({useScope: 'global'})

View File

@ -85,7 +85,7 @@
<table class="table has-actions is-striped is-hoverable is-fullwidth">
<tbody>
<tr :key="m.id" v-for="m in team?.members">
<td>{{ m.getDisplayName() }}</td>
<td>{{ getDisplayName(m) }}</td>
<td>
<template v-if="m.id === userInfo.id">
<b class="is-success">You</b>
@ -176,6 +176,7 @@ import {RIGHTS as Rights} from '@/constants/rights'
import {useTitle} from '@/composables/useTitle'
import {success} from '@/message'
import {getDisplayName} from '@/models/user'
import {useAuthStore} from '@/stores/auth'
import type {ITeam} from '@/modelTypes/ITeam'