profile page supports resending verify email

This commit is contained in:
MaysWind
2023-09-09 22:08:29 +08:00
parent 205363dd42
commit 22fffc2f8c
9 changed files with 90 additions and 17 deletions
+1 -1
View File
@@ -131,7 +131,7 @@ func (a *ForgetPasswordsApi) UserResetPasswordHandler(c *core.Context) (interfac
Password: request.Password,
}
_, err = a.users.UpdateUser(c, userNew, false)
_, _, err = a.users.UpdateUser(c, userNew, false)
if err != nil {
log.ErrorfWithRequestId(c, "[forget_passwords.UserResetPasswordHandler] failed to update user \"uid:%d\", because %s", user.Uid, err.Error())
+5 -1
View File
@@ -324,13 +324,17 @@ func (a *UsersApi) UserUpdateProfileHandler(c *core.Context) (interface{}, *errs
return nil, errs.ErrNothingWillBeUpdated
}
keyProfileUpdated, err := a.users.UpdateUser(c, userNew, modifyUserLanguage)
keyProfileUpdated, emailSetToUnverified, err := a.users.UpdateUser(c, userNew, modifyUserLanguage)
if err != nil {
log.ErrorfWithRequestId(c, "[users.UserUpdateProfileHandler] failed to update user \"uid:%d\", because %s", user.Uid, err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
if emailSetToUnverified {
user.EmailVerified = false
}
log.InfofWithRequestId(c, "[users.UserUpdateProfileHandler] user \"uid:%d\" has updated successfully", user.Uid)
resp := &models.UserProfileUpdateResponse{
+1 -1
View File
@@ -145,7 +145,7 @@ func (l *UserDataCli) ModifyUserPassword(c *cli.Context, username string, passwo
Password: password,
}
_, err = l.users.UpdateUser(nil, userNew, false)
_, _, err = l.users.UpdateUser(nil, userNew, false)
if err != nil {
log.BootErrorf("[user_data.ModifyUserPassword] failed to update user \"%s\" password, because %s", user.Username, err.Error())
+2
View File
@@ -89,6 +89,7 @@ type UserBasicInfo struct {
ShortDateFormat ShortDateFormat `json:"shortDateFormat"`
LongTimeFormat LongTimeFormat `json:"longTimeFormat"`
ShortTimeFormat ShortTimeFormat `json:"shortTimeFormat"`
EmailVerified bool `json:"emailVerified"`
}
// UserLoginRequest represents all parameters of user login request
@@ -227,6 +228,7 @@ func (u *User) ToUserBasicInfo() *UserBasicInfo {
ShortDateFormat: u.ShortDateFormat,
LongTimeFormat: u.LongTimeFormat,
ShortTimeFormat: u.ShortTimeFormat,
EmailVerified: u.EmailVerified,
}
}
+9 -6
View File
@@ -168,29 +168,32 @@ func (s *UserService) CreateUser(c *core.Context, user *models.User) error {
}
// UpdateUser saves an existed user model to database
func (s *UserService) UpdateUser(c *core.Context, user *models.User, modifyUserLanguage bool) (keyProfileUpdated bool, err error) {
func (s *UserService) UpdateUser(c *core.Context, user *models.User, modifyUserLanguage bool) (keyProfileUpdated bool, emailSetToUnverified bool, err error) {
if user.Uid <= 0 {
return false, errs.ErrUserIdInvalid
return false, false, errs.ErrUserIdInvalid
}
updateCols := make([]string, 0, 8)
now := time.Now().Unix()
keyProfileUpdated = false
emailSetToUnverified = false
if user.Email != "" {
exists, err := s.ExistsEmail(c, user.Email)
if err != nil {
return false, err
return false, false, err
} else if exists {
return false, errs.ErrUserEmailAlreadyExists
return false, false, errs.ErrUserEmailAlreadyExists
}
user.EmailVerified = false
updateCols = append(updateCols, "email")
updateCols = append(updateCols, "email_verified")
emailSetToUnverified = true
}
if user.Password != "" {
@@ -256,10 +259,10 @@ func (s *UserService) UpdateUser(c *core.Context, user *models.User, modifyUserL
})
if err != nil {
return false, err
return false, false, err
}
return keyProfileUpdated, nil
return keyProfileUpdated, emailSetToUnverified, nil
}
// UpdateUserLastLoginTime updates the last login time field
+2
View File
@@ -1066,6 +1066,8 @@ export default {
'Basic Settings': 'Basic Settings',
'Security Settings': 'Security Settings',
'Two-Factor Authentication Settings': 'Two-Factor Authentication Settings',
'(Verified)': '(Verified)',
'(Unverified)': '(Unverified)',
'Email has been verified': 'Email has been verified',
'Email has not been verified': 'Email has not been verified',
'Username:': 'Username:',
+2
View File
@@ -1066,6 +1066,8 @@ export default {
'Basic Settings': '基本设置',
'Security Settings': '安全设置',
'Two-Factor Authentication Settings': '两步验证设置',
'(Verified)': '(已验证)',
'(Unverified)': '(未验证)',
'Email has been verified': '邮箱地址已验证',
'Email has not been verified': '邮箱地址未验证',
'Username:': '用户名:',
@@ -18,15 +18,20 @@
</v-img>
<v-icon size="48" :icon="icons.user" v-else-if="!oldProfile.avatar"/>
</v-avatar>
<div class="d-flex flex-column justify-center gap-5">
<div class="d-flex flex-column justify-center gap-3">
<div class="d-flex text-body-1">
<span class="me-1">{{ $t('Username:') }}</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">{{ oldProfile.username }}</span>
</div>
<div class="d-flex text-body-1">
<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>
<div class="d-flex text-body-1 align-center" style="height: 40px;">
<span v-if="!loading && emailVerified">{{ $t('Email has been verified') }}</span>
<span v-if="!loading && !emailVerified">{{ $t('Email has not been verified') }}</span>
<v-btn class="ml-2 px-2" size="small" variant="text" :disabled="loading || resending"
@click="resendVerifyEmail" v-if="!loading && !emailVerified">
{{ $t('Resend Validation Email') }}
<v-progress-circular indeterminate size="18" class="ml-2" v-if="resending"></v-progress-circular>
</v-btn>
<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>
@@ -270,6 +275,7 @@ export default {
},
emailVerified: false,
loading: true,
resending: false,
saving: false,
icons: {
user: mdiAccount
@@ -377,7 +383,6 @@ 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 = '';
@@ -428,10 +433,28 @@ export default {
reset() {
this.setCurrentUserProfile(this.oldProfile);
},
resendVerifyEmail() {
const self = this;
self.resending = true;
self.rootStore.resendVerifyEmailByLoginedUser().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);
}
});
},
getNameByKeyValue(src, value, keyField, nameField, defaultName) {
return getNameByKeyValue(src, value, keyField, nameField, defaultName);
},
setCurrentUserProfile(profile) {
this.emailVerified = profile.emailVerified;
this.oldProfile.username = profile.username;
this.oldProfile.email = profile.email;
this.oldProfile.nickname = profile.nickname;
+40 -3
View File
@@ -3,7 +3,8 @@
<f7-navbar>
<f7-nav-left :back-link="$t('Back')"></f7-nav-left>
<f7-nav-title :title="$t('User Profile')"></f7-nav-title>
<f7-nav-right>
<f7-nav-right class="navbar-compact-icons">
<f7-link icon-f7="ellipsis" :class="{ 'disabled': loading || emailVerified }" @click="showMoreActionSheet = true"></f7-link>
<f7-link :class="{ 'disabled': inputIsNotChanged || inputIsInvalid || saving }" :text="$t('Save')" @click="save"></f7-link>
</f7-nav-right>
</f7-navbar>
@@ -53,7 +54,7 @@
type="email"
autocomplete="email"
clear-button
:label="$t('E-mail')"
:label="$t('E-mail') + ' ' + (emailVerified ? $t('(Verified)') : $t('(Unverified)'))"
:placeholder="$t('Your email address')"
v-model:value="newProfile.email"
></f7-list-input>
@@ -208,6 +209,17 @@
<f7-list-item class="ebk-list-item-error-info" v-if="langAndRegionInputIsInvalid" :footer="$t(langAndRegionInputInvalidProblemMessage)"></f7-list-item>
</f7-list>
<f7-actions close-by-outside-click close-on-escape :opened="showMoreActionSheet" @actions:closed="showMoreActionSheet = false">
<f7-actions-group>
<f7-actions-button :class="{ 'disabled': loading || resending }" @click="resendVerifyEmail"
v-if="!loading && !emailVerified"
>{{ $t('Resend Validation Email') }}</f7-actions-button>
</f7-actions-group>
<f7-actions-group>
<f7-actions-button bold close>{{ $t('Cancel') }}</f7-actions-button>
</f7-actions-group>
</f7-actions>
<password-input-sheet :title="$t('Modify Password')"
:hint="$t('Please enter your current password when modifying your password')"
:confirm-disabled="saving"
@@ -263,12 +275,15 @@ export default {
longTimeFormat: 0,
shortTimeFormat: 0
},
emailVerified: false,
currentPassword: '',
loading: true,
loadingError: null,
resending: false,
saving: false,
showInputPasswordSheet: false,
showAccountSheet: false
showAccountSheet: false,
showMoreActionSheet: false
};
},
computed: {
@@ -453,6 +468,26 @@ export default {
}
});
},
resendVerifyEmail() {
const self = this;
self.resending = true;
self.$showLoading(() => self.resending);
self.rootStore.resendVerifyEmailByLoginedUser().then(() => {
self.resending = false;
self.$hideLoading();
self.$toast('Validation email has been sent');
}).catch(error => {
self.resending = false;
self.$hideLoading();
if (!error.processed) {
self.$toast(error.message || error);
}
});
},
getNameByKeyValue(src, value, keyField, nameField, defaultName) {
return getNameByKeyValue(src, value, keyField, nameField, defaultName);
},
@@ -460,6 +495,8 @@ export default {
return this.$locale.getCurrencyName(currencyCode);
},
setCurrentUserProfile(profile) {
this.emailVerified = profile.emailVerified;
this.oldProfile.email = profile.email;
this.oldProfile.nickname = profile.nickname;
this.oldProfile.defaultAccountId = profile.defaultAccountId;