Files
habitica/website/client/src/components/settings/usernameForm.vue
T
negue fe6c21800c Fixing layout issues for the private messages page (#11766)
* fix: first batch of layout issues for private messages + auto sizing textarea

* username second line - open profile on face-avatar/conversation name - fix textarea height

* refresh on sync

* new "you dont have any messages" style + changed min textarea height

* new conversationItem style / layout

* reset message unread on reload

* fix styles / textarea height

* list optOut / chatRevoked informations for each conversation + show why its disabled

* Block / Unblock - correct disabled states - $gray-200 instead of 300/400

* canReceive not checking chatRevoked

* fix: faceAvatar / userLink open the selected conversation user

* check if the target user is blocking the logged-in user

* check if blocks is undefined

* max-height instead of height

* fix "no messages" state + canReceive on a new conversation

* fixed conversations width (280px on max 768 width page)

* call autosize after message is sent

* only color the placeholder

* only load the current user avatar/settings/flags

* show only the current avatar on private messages
2020-03-04 17:50:08 +01:00

282 lines
6.8 KiB
Vue

<template>
<div>
<div class="form-group">
<div class="d-flex align-items-center">
<label
class="mr-3"
for="displayName"
>{{ $t('displayName') }}</label>
<div class="flex-grow-1">
<input
id="displayName"
v-model="temporaryDisplayName"
class="form-control"
type="text"
:placeholder="$t('newDisplayName')"
:class="{
'is-invalid input-invalid': displayNameInvalid,
'input-valid': displayNameValid, 'text-darker': temporaryDisplayName.length > 0}"
@blur="restoreEmptyDisplayName()"
>
</div>
</div>
</div>
<div
v-if="displayNameIssues.length > 0"
class="mb-3"
>
<div
v-for="issue in displayNameIssues"
:key="issue"
class="input-error text-center"
>
{{ issue }}
</div>
</div>
<div class="form-group">
<div class="d-flex align-items-center">
<label
class="mr-3"
for="username"
>{{ $t('username') }}</label>
<div class="flex-grow-1">
<div class="input-group-prepend input-group-text">
@
<input
id="username"
v-model="temporaryUsername"
class="form-control"
type="text"
:placeholder="$t('newUsername')"
:class="{
'is-invalid input-invalid': usernameInvalid,
'input-valid': usernameValid, 'text-darker': temporaryUsername.length > 0}"
@blur="restoreEmptyUsername()"
>
</div>
</div>
</div>
</div>
<div
v-if="usernameIssues.length > 0"
class="mb-3"
>
<div
v-for="issue in usernameIssues"
:key="issue"
class="input-error text-center"
>
{{ issue }}
</div>
</div>
<div
v-if="!avatarIntro"
class="small text-center mb-3"
>
{{ $t('usernameLimitations') }}
</div>
<div class="row justify-content-center">
<button
class="btn btn-primary"
type="submit"
:class="{disabled: usernameCannotSubmit}"
:disabled="usernameCannotSubmit"
@click="submitNames()"
>
{{ $t(avatarIntro ? 'getStarted' : 'saveAndConfirm') }}
</button>
</div>
</div>
</template>
<style lang="scss" scoped>
@import '~@/assets/scss/colors.scss';
button {
margin: 0.25rem auto 1rem;
}
.col-3 {
padding-right: 0rem;
}
.form-control {
height: 2.25rem;
}
.form-group {
background-color: $gray-700;
border-radius: 2px;
border: solid 1px $gray-500;
}
input.form-control:not(:focus):not(:active) {
border: 0px;
}
.input-error {
color: $red-50;
font-size: 90%;
width: 100%;
}
.input-group-prepend {
margin-right: 0px;
}
.input-group-text {
background-color: $white;
border: 0px;
border-radius: 0px;
color: $gray-300;
padding: 0rem 0.1rem 0rem 0.75rem;
}
label {
color: $gray-100;
font-weight: bold;
margin-bottom: 0rem;
margin-left: 1rem;
min-width: 90px;
}
.small {
color: $gray-200;
}
.text-darker {
color: $gray-50;
}
#username {
padding-left: 0.25rem;
}
</style>
<script>
import axios from 'axios';
import debounce from 'lodash/debounce';
import { mapState } from '@/libs/store';
import { EVENTS } from '@/libs/events';
export default {
props: {
avatarIntro: {
type: Boolean,
default: false,
},
},
data () {
return {
displayNameIssues: [],
temporaryDisplayName: '',
temporaryUsername: '',
usernameIssues: [],
};
},
computed: {
...mapState({
user: 'user.data',
}),
displayNameInvalid () {
if (this.temporaryDisplayName.length < 1) return false;
return !this.displayNameValid;
},
displayNameValid () {
if (this.temporaryDisplayName.length < 1) return false;
return this.displayNameIssues.length === 0;
},
usernameCannotSubmit () {
if (this.temporaryUsername.length < 1) return true;
return !this.usernameValid || !this.displayNameValid;
},
usernameInvalid () {
if (this.temporaryUsername.length < 1) return false;
return !this.usernameValid;
},
usernameValid () {
if (this.temporaryUsername.length < 1) return false;
return this.usernameIssues.length === 0;
},
},
watch: {
temporaryDisplayName: {
handler () {
this.validateDisplayName(this.temporaryDisplayName);
},
deep: true,
},
temporaryUsername: {
handler () {
this.validateUsername(this.temporaryUsername);
},
deep: true,
},
},
mounted () {
this.temporaryDisplayName = this.user.profile.name;
this.temporaryUsername = this.user.auth.local.username;
},
methods: {
async close () {
this.$root.$emit(EVENTS.RESYNC_REQUESTED);
await this.$store.dispatch('user:fetch', { forceLoad: true });
this.$root.$emit(EVENTS.RESYNC_COMPLETED);
if (this.avatarIntro) {
this.$emit('usernameConfirmed');
} else {
this.$root.$emit('bv::hide::modal', 'verify-username');
this.$router.go(0);
}
},
restoreEmptyDisplayName () {
if (this.temporaryDisplayName.length < 1) {
this.temporaryDisplayName = this.user.profile.name;
}
},
restoreEmptyUsername () {
if (this.temporaryUsername.length < 1) {
this.temporaryUsername = this.user.auth.local.username;
}
},
async submitNames () {
if (this.temporaryDisplayName !== this.user.profile.name) {
await axios.put('/api/v4/user/', { 'profile.name': this.temporaryDisplayName });
}
await axios.put('/api/v4/user/auth/update-username', { username: this.temporaryUsername });
this.close();
},
validateDisplayName: debounce(function checkName (displayName) {
if (displayName.length <= 1 || displayName === this.user.profile.name) {
this.displayNameIssues = [];
return;
}
this.$store.dispatch('auth:verifyDisplayName', {
displayName,
}).then(res => {
if (res.issues !== undefined) {
this.displayNameIssues = res.issues;
} else {
this.displayNameIssues = [];
}
});
}, 500),
validateUsername: debounce(function checkName (username) {
if (username.length <= 1 || username === this.user.auth.local.username) {
this.usernameIssues = [];
return;
}
this.$store.dispatch('auth:verifyUsername', {
username,
}).then(res => {
if (res.issues !== undefined) {
this.usernameIssues = res.issues;
} else {
this.usernameIssues = [];
}
});
}, 500),
},
};
</script>