]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/commitdiff
Add ability to change email in client
authorChocobozzz <me@florianbigard.com>
Tue, 11 Jun 2019 12:30:49 +0000 (14:30 +0200)
committerChocobozzz <me@florianbigard.com>
Tue, 11 Jun 2019 12:31:12 +0000 (14:31 +0200)
13 files changed:
client/proxy.config.json
client/src/app/+my-account/my-account-settings/my-account-change-email/index.ts [new file with mode: 0644]
client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.html [new file with mode: 0644]
client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.scss [new file with mode: 0644]
client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts [new file with mode: 0644]
client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.html
client/src/app/+my-account/my-account-settings/my-account-settings.component.html
client/src/app/+my-account/my-account.module.ts
client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.html
client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.ts
client/src/app/shared/users/user.model.ts
client/src/app/shared/users/user.service.ts
server/middlewares/validators/users.ts

index e5f0dfd617887be0ee5279b85d1aa6684915a538..4a72f1826337f49fb4d1a687ce6b81e876188f65 100644 (file)
@@ -8,7 +8,8 @@
     "secure": false
   },
   "/socket.io": {
-    "target": "http://localhost:9000",
-    "secure": false
+    "target": "ws://localhost:9000",
+    "secure": false,
+    "ws": true
   }
 }
diff --git a/client/src/app/+my-account/my-account-settings/my-account-change-email/index.ts b/client/src/app/+my-account/my-account-settings/my-account-change-email/index.ts
new file mode 100644 (file)
index 0000000..f42af36
--- /dev/null
@@ -0,0 +1 @@
+export * from './my-account-change-email.component'
diff --git a/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.html b/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.html
new file mode 100644 (file)
index 0000000..ebfe312
--- /dev/null
@@ -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>
diff --git a/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.scss b/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.scss
new file mode 100644 (file)
index 0000000..81eba3e
--- /dev/null
@@ -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;
+  }
+}
diff --git a/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts b/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts
new file mode 100644 (file)
index 0000000..577c5b1
--- /dev/null
@@ -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
+          }
+        )
+  }
+}
index ae797d1bc9276e033e1ef60a3b2e92ce90bb4f3a..a39061ee3a466203e478e46950c565428384ba55 100644 (file)
@@ -2,7 +2,7 @@
 
 <form role="form" (ngSubmit)="changePassword()" [formGroup]="form">
 
-  <label i18n for="new-password">Change password</label>
+  <label i18n for="current-password">Change password</label>
   <input
     type="password" id="current-password" i18n-placeholder placeholder="Current password"
     formControlName="current-password" [ngClass]="{ 'input-error': formErrors['current-password'] }"
index ad64f28fed3407e1557215742a8ae719ce114301..f93d4111064bf61ee2d3fef915c8255b0ffb6d46 100644 (file)
@@ -13,6 +13,9 @@
 <div i18n class="account-title">Password</div>
 <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>
 <my-account-video-settings [user]="user" [userInformationLoaded]="userInformationLoaded"></my-account-video-settings>
 
index 4a18a9968103f8574732fe621fb52eb91c1095f0..ca5b1f7cbb77e13de6287ef97ce15872fdef9bd4 100644 (file)
@@ -36,6 +36,7 @@ import {
   MyAccountVideoPlaylistElementsComponent
 } from '@app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component'
 import { DragDropModule } from '@angular/cdk/drag-drop'
+import { MyAccountChangeEmailComponent } from '@app/+my-account/my-account-settings/my-account-change-email'
 
 @NgModule({
   imports: [
@@ -54,7 +55,10 @@ import { DragDropModule } from '@angular/cdk/drag-drop'
     MyAccountChangePasswordComponent,
     MyAccountVideoSettingsComponent,
     MyAccountProfileComponent,
+    MyAccountChangeEmailComponent,
+
     MyAccountVideosComponent,
+
     VideoChangeOwnershipComponent,
     MyAccountOwnershipComponent,
     MyAccountAcceptOwnershipComponent,
index 728709ca69c47daf440bf0231ab86020baa8ec83..47519c9435da26277e9ecb97776ac5de7b7c7f9c 100644 (file)
@@ -3,13 +3,16 @@
     Verify account email confirmation
   </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>
 
-  <ng-template #verificationError>
-    <div>
-      <span i18n>An error occurred. </span>
-      <a i18n routerLink="/verify-account/ask-send-email">Request new verification email.</a>
-    </div>
-  </ng-template>
+  <div i18n class="alert alert-success" *ngIf="isPendingEmail && success">
+    Email updated.
+  </div>
+
+  <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>
index 3fb2d1cd8d33e38751c17ee77659d78fe6492bf0..054f04310b38bf0bea31fbae8b7b3a06b16ac9ab 100644 (file)
@@ -1,7 +1,7 @@
 import { Component, OnInit } from '@angular/core'
 import { ActivatedRoute, Router } from '@angular/router'
 import { I18n } from '@ngx-translate/i18n-polyfill'
-import { Notifier } from '@app/core'
+import { AuthService, Notifier } from '@app/core'
 import { UserService } from '@app/shared'
 
 @Component({
@@ -11,12 +11,15 @@ import { UserService } from '@app/shared'
 
 export class VerifyAccountEmailComponent implements OnInit {
   success = false
+  failed = false
+  isPendingEmail = false
 
   private userId: number
   private verificationString: string
 
   constructor (
     private userService: UserService,
+    private authService: AuthService,
     private notifier: Notifier,
     private router: Router,
     private route: ActivatedRoute,
@@ -25,8 +28,12 @@ export class VerifyAccountEmailComponent implements OnInit {
   }
 
   ngOnInit () {
-    this.userId = this.route.snapshot.queryParams['userId']
-    this.verificationString = this.route.snapshot.queryParams['verificationString']
+    const queryParams = this.route.snapshot.queryParams
+    this.userId = queryParams['userId']
+    this.verificationString = queryParams['verificationString']
+    this.isPendingEmail = queryParams['isPendingEmail'] === 'true'
+
+    console.log(this.isPendingEmail)
 
     if (!this.userId || !this.verificationString) {
       this.notifier.error(this.i18n('Unable to find user id or verification string.'))
@@ -36,13 +43,17 @@ export class VerifyAccountEmailComponent implements OnInit {
   }
 
   verifyEmail () {
-    this.userService.verifyEmail(this.userId, this.verificationString)
+    this.userService.verifyEmail(this.userId, this.verificationString, this.isPendingEmail)
       .subscribe(
         () => {
+          this.authService.refreshUserInformation()
+
           this.success = true
         },
 
         err => {
+          this.failed = true
+
           this.notifier.error(err.message)
         }
       )
index e3ed2dfbd589ae53f462b5ee5421b457d4c1d404..14d13959af129b7ac9716c8535e6ff580a7c601c 100644 (file)
@@ -8,6 +8,7 @@ export class User implements UserServerModel {
   id: number
   username: string
   email: string
+  pendingEmail: string | null
   emailVerified: boolean
   nsfwPolicy: NSFWPolicyType
 
index 70ff9a058616bfce137196c34250900713573693..41ee87197d66befebf2ab97a6ade88b0ad2a22af 100644 (file)
@@ -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) {
     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 body = {
-      verificationString
+      verificationString,
+      isPendingEmail
     }
 
     return this.authHttp.post(url, body)
index a4d4ae46d0a1f5980ec3ab41e602e811f969976f..ec70fa0fdb98e87b2edd5d51363399eab65861bc 100644 (file)
@@ -205,7 +205,7 @@ const usersUpdateMeValidator = [
   async (req: express.Request, res: express.Response, next: express.NextFunction) => {
     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) {
         return res.status(400)
                   .send({ error: 'currentPassword parameter is missing.' })