Add showing and modifying user name (#306)

Make sure to use the user name field everywhere

Add showing and modifying user name

Co-authored-by: kolaente <k@knt.li>
Reviewed-on: https://kolaente.dev/vikunja/frontend/pulls/306
Co-Authored-By: konrad <konrad@kola-entertainments.de>
Co-Committed-By: konrad <konrad@kola-entertainments.de>
This commit is contained in:
konrad 2020-11-21 21:25:00 +00:00
parent c536707f3a
commit 3343b1c240
14 changed files with 92 additions and 10 deletions

View file

@ -45,7 +45,7 @@
<div class="dropdown is-right is-active"> <div class="dropdown is-right is-active">
<div class="dropdown-trigger"> <div class="dropdown-trigger">
<button @click.stop="userMenuActive = !userMenuActive" class="button noshadow"> <button @click.stop="userMenuActive = !userMenuActive" class="button noshadow">
<span class="username">{{ userInfo.username }}</span> <span class="username">{{ userInfo.name !== '' ? userInfo.name : userInfo.username }}</span>
<span class="icon is-small"> <span class="icon is-small">
<icon icon="chevron-down"/> <icon icon="chevron-down"/>
</span> </span>

View file

@ -6,8 +6,8 @@
:width="avatarSize" :width="avatarSize"
alt="" alt=""
class="avatar" class="avatar"
v-tooltip="user.username"/> v-tooltip="user.getDisplayName()"/>
<span class="username" v-if="showUsername">{{ user.username }}</span> <span class="username" v-if="showUsername">{{ user.getDisplayName() }}</span>
</div> </div>
</template> </template>

View file

@ -52,7 +52,7 @@
</div> </div>
</td> </td>
<td> <td>
{{ s.sharedBy.username }} {{ s.sharedBy.getDisplayName() }}
</td> </td>
<td class="type"> <td class="type">
<template v-if="s.right === rights.ADMIN"> <template v-if="s.right === rights.ADMIN">

View file

@ -46,7 +46,7 @@
<tbody> <tbody>
<tr :key="s.id" v-for="s in sharables"> <tr :key="s.id" v-for="s in sharables">
<template v-if="shareType === 'user'"> <template v-if="shareType === 'user'">
<td>{{ s.username }}</td> <td>{{ s.getDisplayName() }}</td>
<td> <td>
<template v-if="s.id === userInfo.id"> <template v-if="s.id === userInfo.id">
<b class="is-success">You</b> <b class="is-success">You</b>

View file

@ -107,7 +107,7 @@
<label class="label" for="">Assignees</label> <label class="label" for="">Assignees</label>
<ul class="assingees"> <ul class="assingees">
<li :key="a.id" v-for="(a, index) in taskEditTask.assignees"> <li :key="a.id" v-for="(a, index) in taskEditTask.assignees">
{{ a.username }} {{ a.getDisplayName() }}
<a @click="deleteAssigneeByIndex(index)"> <a @click="deleteAssigneeByIndex(index)">
<icon icon="times"/> <icon icon="times"/>
</a> </a>

View file

@ -17,7 +17,7 @@
<div class="media-content"> <div class="media-content">
<div class="comment-info"> <div class="comment-info">
<img :src="c.author.getAvatarUrl(20)" alt="" class="image is-avatar" height="20" width="20"/> <img :src="c.author.getAvatarUrl(20)" alt="" class="image is-avatar" height="20" width="20"/>
<strong>{{ c.author.username }}</strong>&nbsp; <strong>{{ c.author.getDisplayName() }}</strong>&nbsp;
<span v-tooltip="formatDate(c.created)">{{ formatDateSince(c.created) }}</span> <span v-tooltip="formatDate(c.created)">{{ formatDateSince(c.created) }}</span>
<span v-if="+new Date(c.created) !== +new Date(c.updated)" v-tooltip="formatDate(c.updated)"> <span v-if="+new Date(c.created) !== +new Date(c.updated)" v-tooltip="formatDate(c.updated)">
· edited {{ formatDateSince(c.updated) }} · edited {{ formatDateSince(c.updated) }}

View file

@ -12,6 +12,7 @@ export default class UserModel extends AbstractModel {
id: 0, id: 0,
email: '', email: '',
username: '', username: '',
name: '',
created: null, created: null,
updated: null, updated: null,
} }
@ -20,4 +21,12 @@ export default class UserModel extends AbstractModel {
getAvatarUrl(size = 50) { getAvatarUrl(size = 50) {
return `${window.API_URL}/${this.username}/avatar?size=${size}` return `${window.API_URL}/${this.username}/avatar?size=${size}`
} }
getDisplayName() {
if (this.name !== '') {
return this.name
}
return this.username
}
} }

10
src/models/userName.js Normal file
View file

@ -0,0 +1,10 @@
import AbstractModel from './abstractModel'
export default class UserNameModel extends AbstractModel {
defaults() {
return {
name: '',
}
}
}

10
src/services/userName.js Normal file
View file

@ -0,0 +1,10 @@
import AbstractService from './abstractService'
export default class UserNameService extends AbstractService {
constructor() {
super({
update: '/user/settings/name',
})
}
}

View file

@ -16,6 +16,9 @@ export default {
state.info = info state.info = info
state.avatarUrl = info.getAvatarUrl() state.avatarUrl = info.getAvatarUrl()
}, },
setUserName(state, name) {
state.info.name = name
},
authenticated(state, authenticated) { authenticated(state, authenticated) {
state.authenticated = authenticated state.authenticated = authenticated
}, },

View file

@ -1,6 +1,6 @@
<template> <template>
<div class="content has-text-centered"> <div class="content has-text-centered">
<h2>Hi {{ userInfo.username }}!</h2> <h2>Hi {{ userInfo.name !== '' ? userInfo.name : userInfo.username }}!</h2>
<template v-if="!hasTasks"> <template v-if="!hasTasks">
<p>Click on a list or namespace on the left to get started.</p> <p>Click on a list or namespace on the left to get started.</p>
<router-link <router-link

View file

@ -346,7 +346,7 @@
<!-- Created / Updated [by] --> <!-- Created / Updated [by] -->
<p class="created"> <p class="created">
Created <span v-tooltip="formatDate(task.created)">{{ formatDateSince(task.created) }}</span> by {{ task.createdBy.username }} Created <span v-tooltip="formatDate(task.created)">{{ formatDateSince(task.created) }}</span> by {{ task.createdBy.getDisplayName() }}
<template v-if="+new Date(task.created) !== +new Date(task.updated)"> <template v-if="+new Date(task.created) !== +new Date(task.updated)">
<br/> <br/>
<!-- Computed properties to show the actual date every time it gets updated --> <!-- Computed properties to show the actual date every time it gets updated -->

View file

@ -106,7 +106,7 @@
<table class="table is-striped is-hoverable is-fullwidth"> <table class="table is-striped is-hoverable is-fullwidth">
<tbody> <tbody>
<tr :key="m.id" v-for="m in team.members"> <tr :key="m.id" v-for="m in team.members">
<td>{{ m.username }}</td> <td>{{ m.getDisplayName() }}</td>
<td> <td>
<template v-if="m.id === userInfo.id"> <template v-if="m.id === userInfo.id">
<b class="is-success">You</b> <b class="is-success">You</b>

View file

@ -106,6 +106,38 @@
</div> </div>
</div> </div>
<!-- Name -->
<div class="card">
<header class="card-header">
<p class="card-header-title">
Update your name
</p>
</header>
<div class="card-content">
<div class="content">
<div class="field">
<label class="label" for="newEmail">Name</label>
<div class="control">
<input
@keyup.enter="updateName"
class="input"
id="newEmail"
placeholder="The new name"
type="text"
v-model="name"/>
</div>
</div>
<div class="bigbuttons">
<button :class="{ 'is-loading': userNameService.loading}" @click="updateName()"
class="button is-primary is-fullwidth">
Save
</button>
</div>
</div>
</div>
</div>
<!-- Avatar --> <!-- Avatar -->
<avatar-settings/> <avatar-settings/>
@ -234,6 +266,8 @@ import EmailUpdateService from '../../services/emailUpdate'
import EmailUpdateModel from '../../models/emailUpdate' import EmailUpdateModel from '../../models/emailUpdate'
import TotpModel from '../../models/totp' import TotpModel from '../../models/totp'
import TotpService from '../../services/totp' import TotpService from '../../services/totp'
import UserNameService from '../../services/userName'
import UserNameModel from '../../models/userName'
import {mapState} from 'vuex' import {mapState} from 'vuex'
@ -260,6 +294,9 @@ export default {
totpDisablePassword: '', totpDisablePassword: '',
caldavUrl: '', caldavUrl: '',
name: '',
userNameService: UserNameService,
} }
}, },
components: { components: {
@ -275,6 +312,9 @@ export default {
this.totpService = new TotpService() this.totpService = new TotpService()
this.totp = new TotpModel() this.totp = new TotpModel()
this.userNameService = new UserNameService()
this.name = this.$store.state.auth.info.name
this.totpStatus() this.totpStatus()
this.buildCaldavUrl() this.buildCaldavUrl()
}, },
@ -359,6 +399,16 @@ export default {
}) })
.catch(e => this.error(e, this)) .catch(e => this.error(e, this))
}, },
updateName() {
const name = new UserNameModel({name: this.name})
this.userNameService.update(name)
.then(() => {
this.$store.commit('auth/setUserName', this.name)
this.success({message: 'The name was successfully changed.'}, this)
})
.catch(e => this.error(e, this))
},
buildCaldavUrl() { buildCaldavUrl() {
const apiBase = window.API_URL.replace('/api/v1', '') const apiBase = window.API_URL.replace('/api/v1', '')
this.caldavUrl = `${apiBase}/dav/principals/${this.userInfo.username}/` this.caldavUrl = `${apiBase}/dav/principals/${this.userInfo.username}/`