feat: convert model methods to named functions
This commit is contained in:
parent
176ad565cc
commit
8e3f54ae42
16 changed files with 75 additions and 51 deletions
|
@ -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,
|
||||
},
|
||||
})
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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>
|
||||
<strong>{{ getDisplayName(c.author) }}</strong>
|
||||
<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(() => {
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -32,6 +32,7 @@ export interface AuthState {
|
|||
lastUserInfoRefresh: Date | null,
|
||||
settings: IUserSettings,
|
||||
isLoading: boolean,
|
||||
isLoadingGeneralSettings: boolean
|
||||
}
|
||||
|
||||
export interface ConfigState {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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'})
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
|
Loading…
Reference in a new issue