mirror of
https://github.com/mayswind/ezbookkeeping.git
synced 2026-05-16 07:57:33 +08:00
add email verification
This commit is contained in:
@@ -37,6 +37,10 @@ export function isUserForgetPasswordEnabled() {
|
||||
return getServerSetting('f') === '1';
|
||||
}
|
||||
|
||||
export function isUserVerifyEmailEnabled() {
|
||||
return getServerSetting('v') === '1';
|
||||
}
|
||||
|
||||
export function isDataExportingEnabled() {
|
||||
return getServerSetting('e') === '1';
|
||||
}
|
||||
|
||||
@@ -102,6 +102,22 @@ export default {
|
||||
firstDayOfWeek
|
||||
});
|
||||
},
|
||||
verifyEmail: ({ token, requestNewToken }) => {
|
||||
return axios.post('verify_email/by_token.json?token=' + token, {
|
||||
requestNewToken
|
||||
}, {
|
||||
noAuth: true,
|
||||
ignoreError: true
|
||||
});
|
||||
},
|
||||
resendVerifyEmailByUnloginUser: ({ email, password }) => {
|
||||
return axios.post('verify_email/resend.json', {
|
||||
email,
|
||||
password
|
||||
}, {
|
||||
timeout: api.requestForgetPasswordTimeout
|
||||
});
|
||||
},
|
||||
requestResetPassword: ({ email }) => {
|
||||
return axios.post('forget_password/request.json', {
|
||||
email
|
||||
@@ -173,6 +189,11 @@ export default {
|
||||
shortTimeFormat
|
||||
});
|
||||
},
|
||||
resendVerifyEmailByLoginedUser: () => {
|
||||
return axios.post('v1/users/verify_email/resend.json', {}, {
|
||||
timeout: api.requestForgetPasswordTimeout
|
||||
});
|
||||
},
|
||||
get2FAStatus: () => {
|
||||
return axios.get('v1/users/2fa/status.json');
|
||||
},
|
||||
|
||||
+12
-1
@@ -585,6 +585,7 @@ export default {
|
||||
'email is empty or invalid': 'Email is empty or invalid',
|
||||
'new password equals old password': 'New password equals old password',
|
||||
'email is not verified': 'Email is not verified',
|
||||
'email is verified': 'Email is verified',
|
||||
'unauthorized access': 'Unauthorized access',
|
||||
'current token is invalid': 'Current token is invalid',
|
||||
'current token is expired': 'Current token is expired',
|
||||
@@ -597,6 +598,7 @@ export default {
|
||||
'token is not found': 'Token is not found',
|
||||
'token is expired': 'Token is expired',
|
||||
'token is empty': 'Token is empty',
|
||||
'email verify token is invalid or expired': 'Email verify token is invalid or expired',
|
||||
'password reset token is invalid or expired': 'Password reset token is invalid or expired',
|
||||
'passcode is invalid': 'Passcode is invalid',
|
||||
'two factor backup code is invalid': 'Two factor backup code is invalid',
|
||||
@@ -847,6 +849,14 @@ export default {
|
||||
'Use a passcode': 'Use a passcode',
|
||||
'PIN code is invalid': 'PIN code is invalid',
|
||||
'PIN code is wrong': 'PIN code is wrong',
|
||||
'Verify your email': 'Verify your email',
|
||||
'Verifying...': 'Verifying...',
|
||||
'Account activation link has been sent to your email address:': 'Account activation link has been sent to your email address:',
|
||||
', If you don\'t receive the mail, fill password and click the button below to resend the verify mail.': ', If you don\'t receive the mail, fill password and click the button below to resend the verify mail.',
|
||||
'Resend Validation Email': 'Resend Validation Email',
|
||||
'Validation email has been sent': 'Validation email has been sent',
|
||||
'Unable to verify email': 'Unable to verify email',
|
||||
'Unable to resend verify email': 'Unable to resend verify email',
|
||||
'Send Reset Link': 'Send Reset Link',
|
||||
'Please input your email address used for registration and we\'ll send you an email with reset password link': 'Please input your email address used for registration and we\'ll send you an email with reset password link',
|
||||
'Password reset email has been sent': 'Password reset email has been sent',
|
||||
@@ -1055,8 +1065,9 @@ export default {
|
||||
'Basic Settings': 'Basic Settings',
|
||||
'Security Settings': 'Security Settings',
|
||||
'Two-Factor Authentication Settings': 'Two-Factor Authentication Settings',
|
||||
'Email has been verified': 'Email has been verified',
|
||||
'Email has not been verified': 'Email has not been verified',
|
||||
'Username:': 'Username:',
|
||||
'Avatar Provider:': 'Avatar Provider:',
|
||||
'Current Password': 'Current Password',
|
||||
'New Password': 'New Password',
|
||||
'Modify Password': 'Modify Password',
|
||||
|
||||
+13
-2
@@ -584,7 +584,8 @@ export default {
|
||||
'email is invalid': '邮箱无效',
|
||||
'email is empty or invalid': '邮箱为空或无效',
|
||||
'new password equals old password': '新密码与旧密码相同',
|
||||
'email is not verified': '邮箱没有验证过',
|
||||
'email is not verified': '邮箱还未验证通过',
|
||||
'email is verified': '邮箱已经验证过',
|
||||
'unauthorized access': '未授权的登录',
|
||||
'current token is invalid': '当前认证令牌无效',
|
||||
'current token is expired': '当前认证令牌已过期',
|
||||
@@ -597,6 +598,7 @@ export default {
|
||||
'token is not found': '认证令牌不存在',
|
||||
'token is expired': '认证令牌已过期',
|
||||
'token is empty': '认证令牌为空',
|
||||
'email verify token is invalid or expired': '邮箱验证令牌无效或已过期',
|
||||
'password reset token is invalid or expired': '密码重置令牌无效或已过期',
|
||||
'passcode is invalid': '验证码无效',
|
||||
'two factor backup code is invalid': '两步验证备用码无效',
|
||||
@@ -847,6 +849,14 @@ export default {
|
||||
'Use a passcode': '使用验证码',
|
||||
'PIN code is invalid': 'PIN码无效',
|
||||
'PIN code is wrong': 'PIN码错误',
|
||||
'Verify your email': '验证您的邮箱',
|
||||
'Verifying...': '正在验证...',
|
||||
'Account activation link has been sent to your email address:': '账号激活链接已经发送到您的邮箱地址:',
|
||||
', If you don\'t receive the mail, fill password and click the button below to resend the verify mail.': ',如果您没有收到邮件,输入密码并点击下方的按钮重新发送验证邮件。',
|
||||
'Resend Validation Email': '重发验证邮件',
|
||||
'Validation email has been sent': '验证邮件已发送',
|
||||
'Unable to verify email': '无法验证邮箱',
|
||||
'Unable to resend verify email': '无法重新发送验证邮件',
|
||||
'Send Reset Link': '发送重置链接',
|
||||
'Please input your email address used for registration and we\'ll send you an email with reset password link': '请输入您注册时使用的电子邮箱地址,我们将发送一封包含重置密码链接的邮件给您',
|
||||
'Password reset email has been sent': '重置密码邮件已发送',
|
||||
@@ -1055,8 +1065,9 @@ export default {
|
||||
'Basic Settings': '基本设置',
|
||||
'Security Settings': '安全设置',
|
||||
'Two-Factor Authentication Settings': '两步验证设置',
|
||||
'Email has been verified': '邮箱地址已验证',
|
||||
'Email has not been verified': '邮箱地址未验证',
|
||||
'Username:': '用户名:',
|
||||
'Avatar Provider:': '头像提供方:',
|
||||
'Current Password': '当前密码',
|
||||
'New Password': '新密码',
|
||||
'Modify Password': '修改密码',
|
||||
|
||||
@@ -5,6 +5,7 @@ import userState from '@/lib/userstate.js';
|
||||
import MainLayout from '@/views/desktop/MainLayout.vue';
|
||||
import LoginPage from '@/views/desktop/LoginPage.vue';
|
||||
import SignUpPage from '@/views/desktop/SignupPage.vue';
|
||||
import VerifyEmailPage from '@/views/desktop/VerifyEmailPage.vue';
|
||||
import ForgetPasswordPage from '@/views/desktop/ForgetPasswordPage.vue';
|
||||
import ResetPasswordPage from '@/views/desktop/ResetPasswordPage.vue';
|
||||
import UnlockPage from '@/views/desktop/UnlockPage.vue';
|
||||
@@ -159,6 +160,14 @@ const router = createRouter({
|
||||
component: SignUpPage,
|
||||
beforeEnter: checkNotLogin
|
||||
},
|
||||
{
|
||||
path: '/verify_email',
|
||||
component: VerifyEmailPage,
|
||||
props: route => ({
|
||||
email: route.query.email,
|
||||
token: route.query.token
|
||||
})
|
||||
},
|
||||
{
|
||||
path: '/forgetpassword',
|
||||
component: ForgetPasswordPage,
|
||||
|
||||
@@ -247,6 +247,69 @@ export const useRootStore = defineStore('root', {
|
||||
userState.clearWebAuthnConfig();
|
||||
this.resetAllStates(true);
|
||||
},
|
||||
verifyEmail({ token, requestNewToken }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.verifyEmail({
|
||||
token,
|
||||
requestNewToken
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to verify email' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.result.newToken && isString(data.result.newToken)) {
|
||||
userState.updateToken(data.result.newToken);
|
||||
}
|
||||
|
||||
if (data.result.user && isObject(data.result.user)) {
|
||||
const userStore = useUserStore();
|
||||
userStore.storeUserInfo(data.result.user);
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to verify email', error);
|
||||
|
||||
if (error && error.processed) {
|
||||
reject(error);
|
||||
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else {
|
||||
reject({ message: 'Unable to verify email' });
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
resendVerifyEmailByUnloginUser({ email, password }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.resendVerifyEmailByUnloginUser({
|
||||
email,
|
||||
password
|
||||
}).then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to resend verify email' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to resend verify email', error);
|
||||
|
||||
if (error && error.processed) {
|
||||
reject(error);
|
||||
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else {
|
||||
reject({ message: 'Unable to resend verify email' });
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
requestResetPassword({ email }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.requestResetPassword({
|
||||
@@ -363,6 +426,30 @@ export const useRootStore = defineStore('root', {
|
||||
});
|
||||
});
|
||||
},
|
||||
resendVerifyEmailByLoginedUser() {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.resendVerifyEmailByLoginedUser().then(response => {
|
||||
const data = response.data;
|
||||
|
||||
if (!data || !data.success || !data.result) {
|
||||
reject({ message: 'Unable to resend verify email' });
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(data.result);
|
||||
}).catch(error => {
|
||||
logger.error('failed to resend verify email', error);
|
||||
|
||||
if (error && error.processed) {
|
||||
reject(error);
|
||||
} else if (error.response && error.response.data && error.response.data.errorMessage) {
|
||||
reject({ error: error.response.data });
|
||||
} else {
|
||||
reject({ message: 'Unable to resend verify email' });
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
clearUserData({ password }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
services.clearData({
|
||||
|
||||
@@ -312,6 +312,11 @@ export default {
|
||||
}).catch(error => {
|
||||
self.logining = false;
|
||||
|
||||
if (error.error && error.error.errorCode === 201020 && error.error.context && error.error.context.email) {
|
||||
self.$router.push('/verify_email?email=' + encodeURIComponent(error.error.context.email));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!error.processed) {
|
||||
self.$refs.snackbar.showError(error);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,241 @@
|
||||
<template>
|
||||
<div class="layout-wrapper">
|
||||
<router-link to="/">
|
||||
<div class="auth-logo d-flex align-start gap-x-3">
|
||||
<img alt="logo" class="login-page-logo" :src="ezBookkeepingLogoPath" />
|
||||
<h1 class="font-weight-medium leading-normal text-2xl">{{ $t('global.app.title') }}</h1>
|
||||
</div>
|
||||
</router-link>
|
||||
<v-row no-gutters class="auth-wrapper">
|
||||
<v-col cols="12" md="8" class="d-none d-md-flex align-center justify-center position-relative">
|
||||
<div class="d-flex auth-img-footer" v-if="!isDarkMode">
|
||||
<v-img src="img/desktop/background.svg"/>
|
||||
</div>
|
||||
<div class="d-flex auth-img-footer" v-if="isDarkMode">
|
||||
<v-img src="img/desktop/background-dark.svg"/>
|
||||
</div>
|
||||
<div class="d-flex align-center justify-center w-100 pt-10">
|
||||
<v-img max-width="320px" src="img/desktop/people2.svg"/>
|
||||
</div>
|
||||
</v-col>
|
||||
<v-col cols="12" md="4" class="auth-card d-flex flex-column">
|
||||
<div class="d-flex align-center justify-center h-100">
|
||||
<v-card variant="flat" class="w-100 mt-0 px-4 pt-12" max-width="500">
|
||||
<v-card-text>
|
||||
<h5 class="text-h5 mb-3">{{ $t('Verify your email') }}</h5>
|
||||
<p class="mb-0" v-if="token && loading">{{ $t('Verifying...') }}</p>
|
||||
<p class="mb-0" v-if="token && verified">{{ $t('Email has been verified') }}</p>
|
||||
<p class="mb-0" v-if="token && !verified && errorMessage">{{ errorMessage }}</p>
|
||||
<p class="mb-0" v-if="!token && !email">{{ $t('Parameter Invalid') }}</p>
|
||||
<p class="mb-0" v-if="!token && email">
|
||||
<span>{{ $t('Account activation link has been sent to your email address:') }}</span>
|
||||
<span class="ml-1">{{ email }}</span>
|
||||
<span class="ml-1">{{ $t(', If you don\'t receive the mail, fill password and click the button below to resend the verify mail.') }}</span>
|
||||
</p>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-text class="pb-0 mb-6">
|
||||
<v-form>
|
||||
<v-row>
|
||||
<v-col cols="12" v-if="!loading && !token && email && isUserVerifyEmailEnabled">
|
||||
<v-text-field
|
||||
autocomplete="password"
|
||||
clearable
|
||||
:type="isPasswordVisible ? 'text' : 'password'"
|
||||
:disabled="loading || resending"
|
||||
:label="$t('Password')"
|
||||
:placeholder="$t('Your password')"
|
||||
:append-inner-icon="isPasswordVisible ? icons.eyeSlash : icons.eye"
|
||||
v-model="password"
|
||||
@click:append-inner="isPasswordVisible = !isPasswordVisible"
|
||||
@keyup.enter="resendEmail"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" v-if="!loading && !token && email && isUserVerifyEmailEnabled">
|
||||
<v-btn block type="submit" :disabled="loading || resending || !password" @click="resendEmail">
|
||||
{{ $t('Resend Validation Email') }}
|
||||
<v-progress-circular indeterminate size="24" class="ml-2" v-if="resending"></v-progress-circular>
|
||||
</v-btn>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12">
|
||||
<router-link class="d-flex align-center justify-center" to="/login"
|
||||
:class="{ 'disabled': loading || resending }">
|
||||
<v-icon :icon="icons.left"/>
|
||||
<span>{{ $t('Back to log in') }}</span>
|
||||
</router-link>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</div>
|
||||
<v-spacer/>
|
||||
<div class="d-flex align-center justify-center">
|
||||
<v-card variant="flat" class="w-100 px-4 pb-4" max-width="500">
|
||||
<v-card-text class="pt-0">
|
||||
<v-row>
|
||||
<v-col cols="12" class="text-center">
|
||||
<v-menu location="bottom">
|
||||
<template #activator="{ props }">
|
||||
<v-btn variant="text"
|
||||
:disabled="resending"
|
||||
v-bind="props">{{ currentLanguageName }}</v-btn>
|
||||
</template>
|
||||
<v-list>
|
||||
<v-list-item v-for="(lang, locale) in allLanguages" :key="locale">
|
||||
<v-list-item-title
|
||||
class="cursor-pointer"
|
||||
@click="changeLanguage(locale)">
|
||||
{{ lang.displayName }}
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" class="d-flex align-center pt-0">
|
||||
<v-divider />
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" class="text-center text-sm">
|
||||
<span>Powered by </span>
|
||||
<a href="https://github.com/mayswind/ezbookkeeping" target="_blank">ezBookkeeping</a> <span>{{ version }}</span>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<confirm-dialog ref="confirmDialog"/>
|
||||
<snack-bar ref="snackbar" @update:show="onSnackbarShowStateChanged" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useTheme } from 'vuetify';
|
||||
|
||||
import { mapStores } from 'pinia';
|
||||
import { useRootStore } from '@/stores/index.js';
|
||||
import { useSettingsStore } from '@/stores/setting.js';
|
||||
|
||||
import assetConstants from '@/consts/asset.js';
|
||||
import { isUserVerifyEmailEnabled } from '@/lib/server_settings.js';
|
||||
|
||||
import {
|
||||
mdiChevronLeft,
|
||||
mdiEyeOffOutline,
|
||||
mdiEyeOutline
|
||||
} from '@mdi/js';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'email',
|
||||
'token'
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
password: '',
|
||||
isPasswordVisible: false,
|
||||
loading: true,
|
||||
resending: false,
|
||||
verified: false,
|
||||
errorMessage: '',
|
||||
icons: {
|
||||
left: mdiChevronLeft,
|
||||
eye: mdiEyeOutline,
|
||||
eyeSlash: mdiEyeOffOutline
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useRootStore, useSettingsStore),
|
||||
ezBookkeepingLogoPath() {
|
||||
return assetConstants.ezBookkeepingLogoPath;
|
||||
},
|
||||
version() {
|
||||
return 'v' + this.$version;
|
||||
},
|
||||
allLanguages() {
|
||||
return this.$locale.getAllLanguageInfos();
|
||||
},
|
||||
isDarkMode() {
|
||||
return this.globalTheme.global.name.value === 'dark';
|
||||
},
|
||||
currentLanguageName() {
|
||||
return this.$locale.getCurrentLanguageDisplayName();
|
||||
},
|
||||
isUserVerifyEmailEnabled() {
|
||||
return isUserVerifyEmailEnabled();
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
const theme = useTheme();
|
||||
|
||||
return {
|
||||
globalTheme: theme
|
||||
};
|
||||
},
|
||||
created() {
|
||||
const self = this;
|
||||
|
||||
self.verified = false;
|
||||
self.loading = true;
|
||||
|
||||
if (!self.token) {
|
||||
self.loading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
self.rootStore.verifyEmail({
|
||||
token: self.token,
|
||||
requestNewToken: !self.$user.isUserLogined()
|
||||
}).then(() => {
|
||||
self.loading = false;
|
||||
self.verified = true;
|
||||
self.$refs.snackbar.showMessage('Email has been verified');
|
||||
}).catch(error => {
|
||||
self.loading = false;
|
||||
self.verified = false;
|
||||
|
||||
if (!error.processed) {
|
||||
self.errorMessage = self.$tError(error.message || error);
|
||||
self.$refs.snackbar.showError(error);
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
resendEmail() {
|
||||
const self = this;
|
||||
|
||||
self.resending = true;
|
||||
|
||||
self.rootStore.resendVerifyEmailByUnloginUser({
|
||||
email: self.email,
|
||||
password: self.password
|
||||
}).then(() => {
|
||||
self.resending = false;
|
||||
self.$refs.snackbar.showMessage('Validation email has been sent');
|
||||
}).catch(error => {
|
||||
self.resending = false;
|
||||
|
||||
if (!error.processed) {
|
||||
self.$refs.snackbar.showError(error);
|
||||
}
|
||||
});
|
||||
},
|
||||
onSnackbarShowStateChanged(newValue) {
|
||||
if (!newValue && this.verified && this.$user.isUserLogined()) {
|
||||
this.$router.replace('/');
|
||||
}
|
||||
},
|
||||
changeLanguage(locale) {
|
||||
const localeDefaultSettings = this.$locale.setLanguage(locale);
|
||||
this.settingsStore.updateLocalizedDefaultSettings(localeDefaultSettings);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -25,9 +25,9 @@
|
||||
<span v-if="!loading">{{ oldProfile.username }}</span>
|
||||
</div>
|
||||
<div class="d-flex text-body-1">
|
||||
<span class="me-1">{{ $t('Avatar Provider:') }}</span>
|
||||
<v-skeleton-loader class="skeleton-no-margin" type="text" style="width: 100px" :loading="true" v-if="loading"></v-skeleton-loader>
|
||||
<span v-if="!loading">{{ currentUserAvatarProvider }}</span>
|
||||
<span class="me-1" v-if="!loading && emailVerified">{{ $t('Email has been verified') }}</span>
|
||||
<span class="me-1" v-if="!loading && !emailVerified">{{ $t('Email has not been verified') }}</span>
|
||||
<v-skeleton-loader class="skeleton-no-margin mt-2 mb-1" type="text" style="width: 160px" :loading="true" v-if="loading"></v-skeleton-loader>
|
||||
</div>
|
||||
</div>
|
||||
</v-card-text>
|
||||
@@ -268,6 +268,7 @@ export default {
|
||||
longTimeFormat: 0,
|
||||
shortTimeFormat: 0
|
||||
},
|
||||
emailVerified: false,
|
||||
loading: true,
|
||||
saving: false,
|
||||
icons: {
|
||||
@@ -310,13 +311,6 @@ export default {
|
||||
allTransactionEditScopeTypes() {
|
||||
return this.$locale.getAllTransactionEditScopeTypes();
|
||||
},
|
||||
currentUserAvatarProvider() {
|
||||
if (this.oldProfile.avatarProvider === 'gravatar') {
|
||||
return 'Gravatar';
|
||||
} else {
|
||||
return this.$t('None');
|
||||
}
|
||||
},
|
||||
inputIsNotChanged() {
|
||||
return !!this.inputIsNotChangedProblemMessage;
|
||||
},
|
||||
@@ -383,6 +377,7 @@ export default {
|
||||
Promise.all(promises).then(responses => {
|
||||
const profile = responses[1];
|
||||
self.setCurrentUserProfile(profile);
|
||||
self.emailVerified = profile.emailVerified;
|
||||
self.loading = false;
|
||||
}).catch(error => {
|
||||
self.oldProfile.nickname = '';
|
||||
|
||||
Reference in New Issue
Block a user