Add ability to change email in client
This commit is contained in:
parent
d1ab89deb7
commit
0ba5f5baad
|
@ -8,7 +8,8 @@
|
||||||
"secure": false
|
"secure": false
|
||||||
},
|
},
|
||||||
"/socket.io": {
|
"/socket.io": {
|
||||||
"target": "http://localhost:9000",
|
"target": "ws://localhost:9000",
|
||||||
"secure": false
|
"secure": false,
|
||||||
|
"ws": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './my-account-change-email.component'
|
|
@ -0,0 +1,36 @@
|
||||||
|
<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
|
||||||
|
<div *ngIf="success" class="alert alert-success">{{ success }}</div>
|
||||||
|
|
||||||
|
<div i18n class="current-email">
|
||||||
|
Your current email is <span class="email">{{ user.email }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div i18n class="pending-email" *ngIf="user.pendingEmail">
|
||||||
|
<span class="email">{{ user.pendingEmail }}</span> is awaiting email verification
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form role="form" (ngSubmit)="changeEmail()" [formGroup]="form">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label i18n for="new-email">New email</label>
|
||||||
|
<input
|
||||||
|
type="email" id="new-email" i18n-placeholder placeholder="Your new email"
|
||||||
|
formControlName="new-email" [ngClass]="{ 'input-error': formErrors['new-email'] }"
|
||||||
|
>
|
||||||
|
<div *ngIf="formErrors['new-email']" class="form-error">
|
||||||
|
{{ formErrors['new-email'] }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<input
|
||||||
|
type="password" id="password" i18n-placeholder placeholder="Your password"
|
||||||
|
formControlName="password" [ngClass]="{ 'input-error': formErrors['password'] }"
|
||||||
|
>
|
||||||
|
<div *ngIf="formErrors['password']" class="form-error">
|
||||||
|
{{ formErrors['password'] }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="submit" i18n-value value="Change email" [disabled]="!form.valid">
|
||||||
|
</form>
|
|
@ -0,0 +1,24 @@
|
||||||
|
@import '_variables';
|
||||||
|
@import '_mixins';
|
||||||
|
|
||||||
|
input[type=password],
|
||||||
|
input[type=email] {
|
||||||
|
@include peertube-input-text(340px);
|
||||||
|
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=submit] {
|
||||||
|
@include peertube-button;
|
||||||
|
@include orange-button;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-email,
|
||||||
|
.pending-email {
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 15px 0;
|
||||||
|
|
||||||
|
.email {
|
||||||
|
font-weight: $font-semibold;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
import { Component, OnInit } from '@angular/core'
|
||||||
|
import { AuthService, Notifier, ServerService } from '@app/core'
|
||||||
|
import { FormReactive, UserService } from '../../../shared'
|
||||||
|
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||||
|
import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
|
||||||
|
import { UserValidatorsService } from '@app/shared/forms/form-validators/user-validators.service'
|
||||||
|
import { User } from '../../../../../../shared'
|
||||||
|
import { switchMap, tap } from 'rxjs/operators'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-account-change-email',
|
||||||
|
templateUrl: './my-account-change-email.component.html',
|
||||||
|
styleUrls: [ './my-account-change-email.component.scss' ]
|
||||||
|
})
|
||||||
|
export class MyAccountChangeEmailComponent extends FormReactive implements OnInit {
|
||||||
|
error: string = null
|
||||||
|
success: string = null
|
||||||
|
user: User = null
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
protected formValidatorService: FormValidatorService,
|
||||||
|
private userValidatorsService: UserValidatorsService,
|
||||||
|
private notifier: Notifier,
|
||||||
|
private authService: AuthService,
|
||||||
|
private userService: UserService,
|
||||||
|
private serverService: ServerService,
|
||||||
|
private i18n: I18n
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit () {
|
||||||
|
this.buildForm({
|
||||||
|
'new-email': this.userValidatorsService.USER_EMAIL,
|
||||||
|
'password': this.userValidatorsService.USER_PASSWORD
|
||||||
|
})
|
||||||
|
|
||||||
|
this.user = this.authService.getUser()
|
||||||
|
}
|
||||||
|
|
||||||
|
changeEmail () {
|
||||||
|
this.error = null
|
||||||
|
this.success = null
|
||||||
|
|
||||||
|
const password = this.form.value[ 'password' ]
|
||||||
|
const email = this.form.value[ 'new-email' ]
|
||||||
|
|
||||||
|
this.userService.changeEmail(password, email)
|
||||||
|
.pipe(
|
||||||
|
tap(() => this.authService.refreshUserInformation())
|
||||||
|
)
|
||||||
|
.subscribe(
|
||||||
|
() => {
|
||||||
|
this.form.reset()
|
||||||
|
|
||||||
|
if (this.serverService.getConfig().signup.requiresEmailVerification) {
|
||||||
|
this.success = this.i18n('Please check your emails to verify your new email.')
|
||||||
|
} else {
|
||||||
|
this.success = this.i18n('Email updated.')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
err => {
|
||||||
|
if (err.status === 401) {
|
||||||
|
this.error = this.i18n('You current password is invalid.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.error = err.message
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<form role="form" (ngSubmit)="changePassword()" [formGroup]="form">
|
<form role="form" (ngSubmit)="changePassword()" [formGroup]="form">
|
||||||
|
|
||||||
<label i18n for="new-password">Change password</label>
|
<label i18n for="current-password">Change password</label>
|
||||||
<input
|
<input
|
||||||
type="password" id="current-password" i18n-placeholder placeholder="Current password"
|
type="password" id="current-password" i18n-placeholder placeholder="Current password"
|
||||||
formControlName="current-password" [ngClass]="{ 'input-error': formErrors['current-password'] }"
|
formControlName="current-password" [ngClass]="{ 'input-error': formErrors['current-password'] }"
|
||||||
|
|
|
@ -13,6 +13,9 @@
|
||||||
<div i18n class="account-title">Password</div>
|
<div i18n class="account-title">Password</div>
|
||||||
<my-account-change-password></my-account-change-password>
|
<my-account-change-password></my-account-change-password>
|
||||||
|
|
||||||
|
<div i18n class="account-title">Email</div>
|
||||||
|
<my-account-change-email></my-account-change-email>
|
||||||
|
|
||||||
<div i18n class="account-title">Video settings</div>
|
<div i18n class="account-title">Video settings</div>
|
||||||
<my-account-video-settings [user]="user" [userInformationLoaded]="userInformationLoaded"></my-account-video-settings>
|
<my-account-video-settings [user]="user" [userInformationLoaded]="userInformationLoaded"></my-account-video-settings>
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ import {
|
||||||
MyAccountVideoPlaylistElementsComponent
|
MyAccountVideoPlaylistElementsComponent
|
||||||
} from '@app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component'
|
} from '@app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component'
|
||||||
import { DragDropModule } from '@angular/cdk/drag-drop'
|
import { DragDropModule } from '@angular/cdk/drag-drop'
|
||||||
|
import { MyAccountChangeEmailComponent } from '@app/+my-account/my-account-settings/my-account-change-email'
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -54,7 +55,10 @@ import { DragDropModule } from '@angular/cdk/drag-drop'
|
||||||
MyAccountChangePasswordComponent,
|
MyAccountChangePasswordComponent,
|
||||||
MyAccountVideoSettingsComponent,
|
MyAccountVideoSettingsComponent,
|
||||||
MyAccountProfileComponent,
|
MyAccountProfileComponent,
|
||||||
|
MyAccountChangeEmailComponent,
|
||||||
|
|
||||||
MyAccountVideosComponent,
|
MyAccountVideosComponent,
|
||||||
|
|
||||||
VideoChangeOwnershipComponent,
|
VideoChangeOwnershipComponent,
|
||||||
MyAccountOwnershipComponent,
|
MyAccountOwnershipComponent,
|
||||||
MyAccountAcceptOwnershipComponent,
|
MyAccountAcceptOwnershipComponent,
|
||||||
|
|
|
@ -3,13 +3,16 @@
|
||||||
Verify account email confirmation
|
Verify account email confirmation
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<my-signup-success i18n *ngIf="success; else verificationError" message="Your email has been verified and you may now login.">
|
<my-signup-success i18n *ngIf="!isPendingEmail && success" message="Your email has been verified and you may now login.">
|
||||||
</my-signup-success>
|
</my-signup-success>
|
||||||
|
|
||||||
<ng-template #verificationError>
|
<div i18n class="alert alert-success" *ngIf="isPendingEmail && success">
|
||||||
<div>
|
Email updated.
|
||||||
<span i18n>An error occurred. </span>
|
</div>
|
||||||
<a i18n routerLink="/verify-account/ask-send-email">Request new verification email.</a>
|
|
||||||
|
<div *ngIf="failed">
|
||||||
|
<span i18n>An error occurred.</span>
|
||||||
|
|
||||||
|
<a i18n routerLink="/verify-account/ask-send-email" [queryParams]="{ isPendingEmail: isPendingEmail }">Request new verification email.</a>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Component, OnInit } from '@angular/core'
|
import { Component, OnInit } from '@angular/core'
|
||||||
import { ActivatedRoute, Router } from '@angular/router'
|
import { ActivatedRoute, Router } from '@angular/router'
|
||||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||||
import { Notifier } from '@app/core'
|
import { AuthService, Notifier } from '@app/core'
|
||||||
import { UserService } from '@app/shared'
|
import { UserService } from '@app/shared'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -11,12 +11,15 @@ import { UserService } from '@app/shared'
|
||||||
|
|
||||||
export class VerifyAccountEmailComponent implements OnInit {
|
export class VerifyAccountEmailComponent implements OnInit {
|
||||||
success = false
|
success = false
|
||||||
|
failed = false
|
||||||
|
isPendingEmail = false
|
||||||
|
|
||||||
private userId: number
|
private userId: number
|
||||||
private verificationString: string
|
private verificationString: string
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private userService: UserService,
|
private userService: UserService,
|
||||||
|
private authService: AuthService,
|
||||||
private notifier: Notifier,
|
private notifier: Notifier,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
|
@ -25,8 +28,12 @@ export class VerifyAccountEmailComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
this.userId = this.route.snapshot.queryParams['userId']
|
const queryParams = this.route.snapshot.queryParams
|
||||||
this.verificationString = this.route.snapshot.queryParams['verificationString']
|
this.userId = queryParams['userId']
|
||||||
|
this.verificationString = queryParams['verificationString']
|
||||||
|
this.isPendingEmail = queryParams['isPendingEmail'] === 'true'
|
||||||
|
|
||||||
|
console.log(this.isPendingEmail)
|
||||||
|
|
||||||
if (!this.userId || !this.verificationString) {
|
if (!this.userId || !this.verificationString) {
|
||||||
this.notifier.error(this.i18n('Unable to find user id or verification string.'))
|
this.notifier.error(this.i18n('Unable to find user id or verification string.'))
|
||||||
|
@ -36,13 +43,17 @@ export class VerifyAccountEmailComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
verifyEmail () {
|
verifyEmail () {
|
||||||
this.userService.verifyEmail(this.userId, this.verificationString)
|
this.userService.verifyEmail(this.userId, this.verificationString, this.isPendingEmail)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
() => {
|
() => {
|
||||||
|
this.authService.refreshUserInformation()
|
||||||
|
|
||||||
this.success = true
|
this.success = true
|
||||||
},
|
},
|
||||||
|
|
||||||
err => {
|
err => {
|
||||||
|
this.failed = true
|
||||||
|
|
||||||
this.notifier.error(err.message)
|
this.notifier.error(err.message)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -8,6 +8,7 @@ export class User implements UserServerModel {
|
||||||
id: number
|
id: number
|
||||||
username: string
|
username: string
|
||||||
email: string
|
email: string
|
||||||
|
pendingEmail: string | null
|
||||||
emailVerified: boolean
|
emailVerified: boolean
|
||||||
nsfwPolicy: NSFWPolicyType
|
nsfwPolicy: NSFWPolicyType
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,20 @@ export class UserService {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changeEmail (password: string, newEmail: string) {
|
||||||
|
const url = UserService.BASE_USERS_URL + 'me'
|
||||||
|
const body: UserUpdateMe = {
|
||||||
|
currentPassword: password,
|
||||||
|
email: newEmail
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.authHttp.put(url, body)
|
||||||
|
.pipe(
|
||||||
|
map(this.restExtractor.extractDataBool),
|
||||||
|
catchError(err => this.restExtractor.handleError(err))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
updateMyProfile (profile: UserUpdateMe) {
|
updateMyProfile (profile: UserUpdateMe) {
|
||||||
const url = UserService.BASE_USERS_URL + 'me'
|
const url = UserService.BASE_USERS_URL + 'me'
|
||||||
|
|
||||||
|
@ -104,10 +118,11 @@ export class UserService {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
verifyEmail (userId: number, verificationString: string) {
|
verifyEmail (userId: number, verificationString: string, isPendingEmail: boolean) {
|
||||||
const url = `${UserService.BASE_USERS_URL}/${userId}/verify-email`
|
const url = `${UserService.BASE_USERS_URL}/${userId}/verify-email`
|
||||||
const body = {
|
const body = {
|
||||||
verificationString
|
verificationString,
|
||||||
|
isPendingEmail
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.authHttp.post(url, body)
|
return this.authHttp.post(url, body)
|
||||||
|
|
|
@ -205,7 +205,7 @@ const usersUpdateMeValidator = [
|
||||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||||
logger.debug('Checking usersUpdateMe parameters', { parameters: omit(req.body, 'password') })
|
logger.debug('Checking usersUpdateMe parameters', { parameters: omit(req.body, 'password') })
|
||||||
|
|
||||||
if (req.body.password) {
|
if (req.body.password || req.body.email) {
|
||||||
if (!req.body.currentPassword) {
|
if (!req.body.currentPassword) {
|
||||||
return res.status(400)
|
return res.status(400)
|
||||||
.send({ error: 'currentPassword parameter is missing.' })
|
.send({ error: 'currentPassword parameter is missing.' })
|
||||||
|
|
Loading…
Reference in New Issue