Client: add ability for user to change nsfw settings

pull/61/head
Chocobozzz 2017-04-06 21:21:03 +02:00
parent 92fb909c9b
commit af5e743b01
13 changed files with 241 additions and 82 deletions

View File

@ -0,0 +1,24 @@
<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
<form role="form" (ngSubmit)="changePassword()" [formGroup]="form">
<div class="form-group">
<label for="new-password">New password</label>
<input
type="password" class="form-control" id="new-password"
formControlName="new-password"
>
<div *ngIf="formErrors['new-password']" class="alert alert-danger">
{{ formErrors['new-password'] }}
</div>
</div>
<div class="form-group">
<label for="name">Confirm new password</label>
<input
type="password" class="form-control" id="new-confirmed-password"
formControlName="new-confirmed-password"
>
</div>
<input type="submit" value="Change password" class="btn btn-default" [disabled]="!form.valid">
</form>

View File

@ -0,0 +1,66 @@
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { Router } from '@angular/router';
import { NotificationsService } from 'angular2-notifications';
import { FormReactive, UserService, USER_PASSWORD } from '../../shared';
@Component({
selector: 'my-account-change-password',
templateUrl: './account-change-password.component.html'
})
export class AccountChangePasswordComponent extends FormReactive implements OnInit {
error: string = null;
form: FormGroup;
formErrors = {
'new-password': '',
'new-confirmed-password': ''
};
validationMessages = {
'new-password': USER_PASSWORD.MESSAGES,
'new-confirmed-password': USER_PASSWORD.MESSAGES
};
constructor(
private formBuilder: FormBuilder,
private router: Router,
private notificationsService: NotificationsService,
private userService: UserService
) {
super();
}
buildForm() {
this.form = this.formBuilder.group({
'new-password': [ '', USER_PASSWORD.VALIDATORS ],
'new-confirmed-password': [ '', USER_PASSWORD.VALIDATORS ],
});
this.form.valueChanges.subscribe(data => this.onValueChanged(data));
}
ngOnInit() {
this.buildForm();
}
changePassword() {
const newPassword = this.form.value['new-password'];
const newConfirmedPassword = this.form.value['new-confirmed-password'];
this.error = null;
if (newPassword !== newConfirmedPassword) {
this.error = 'The new password and the confirmed password do not correspond.';
return;
}
this.userService.changePassword(newPassword).subscribe(
() => this.notificationsService.success('Success', 'Password updated.'),
err => this.error = err
);
}
}

View File

@ -0,0 +1 @@
export * from './account-change-password.component';

View File

@ -0,0 +1,16 @@
<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
<form role="form" (ngSubmit)="updateDetails()" [formGroup]="form">
<div class="form-group">
<label for="displayNSFW">Display NSFW videos</label>
<input
type="checkbox" id="displayNSFW"
formControlName="displayNSFW"
>
<div *ngIf="formErrors['displayNSFW']" class="alert alert-danger">
{{ formErrors['displayNSFW'] }}
</div>
</div>
<input type="submit" value="Update" class="btn btn-default" [disabled]="!form.valid">
</form>

View File

@ -0,0 +1,68 @@
import { Component, OnInit, Input } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { Router } from '@angular/router';
import { NotificationsService } from 'angular2-notifications';
import { AuthService } from '../../core';
import {
FormReactive,
User,
UserService,
USER_PASSWORD
} from '../../shared';
@Component({
selector: 'my-account-details',
templateUrl: './account-details.component.html'
})
export class AccountDetailsComponent extends FormReactive implements OnInit {
@Input() user: User = null;
error: string = null;
form: FormGroup;
formErrors = {};
validationMessages = {};
constructor(
private authService: AuthService,
private formBuilder: FormBuilder,
private router: Router,
private notificationsService: NotificationsService,
private userService: UserService
) {
super();
}
buildForm() {
this.form = this.formBuilder.group({
displayNSFW: [ this.user.displayNSFW ],
});
this.form.valueChanges.subscribe(data => this.onValueChanged(data));
}
ngOnInit() {
this.buildForm();
}
updateDetails() {
const displayNSFW = this.form.value['displayNSFW'];
const details = {
displayNSFW
};
this.error = null;
this.userService.updateDetails(details).subscribe(
() => {
this.notificationsService.success('Success', 'Informations updated.');
this.authService.refreshUserInformations();
},
err => this.error = err
);
}
}

View File

@ -0,0 +1 @@
export * from './account-details.component';

View File

@ -1,26 +1,11 @@
<h3>Account</h3> <h3>Account</h3>
<div *ngIf="error" class="alert alert-danger">{{ error }}</div> <div class="block">
<h4>Change password</h4>
<my-account-change-password></my-account-change-password>
</div>
<form role="form" (ngSubmit)="changePassword()" [formGroup]="form"> <div class="block">
<div class="form-group"> <h4>Update my informations</h4>
<label for="new-password">New password</label> <my-account-details [user]="user"></my-account-details>
<input </div>
type="password" class="form-control" id="new-password"
formControlName="new-password"
>
<div *ngIf="formErrors['new-password']" class="alert alert-danger">
{{ formErrors['new-password'] }}
</div>
</div>
<div class="form-group">
<label for="name">Confirm new password</label>
<input
type="password" class="form-control" id="new-confirmed-password"
formControlName="new-confirmed-password"
>
</div>
<input type="submit" value="Change password" class="btn btn-default" [disabled]="!form.valid">
</form>

View File

@ -0,0 +1,3 @@
.block {
margin-top: 40px;
}

View File

@ -4,63 +4,25 @@ import { Router } from '@angular/router';
import { NotificationsService } from 'angular2-notifications'; import { NotificationsService } from 'angular2-notifications';
import { FormReactive, UserService, USER_PASSWORD } from '../shared'; import { AuthService } from '../core';
import {
FormReactive,
User,
UserService,
USER_PASSWORD
} from '../shared';
@Component({ @Component({
selector: 'my-account', selector: 'my-account',
templateUrl: './account.component.html' templateUrl: './account.component.html',
styleUrls: [ './account.component.scss' ]
}) })
export class AccountComponent implements OnInit {
user: User = null;
export class AccountComponent extends FormReactive implements OnInit { constructor(private authService: AuthService) {}
error: string = null;
form: FormGroup;
formErrors = {
'new-password': '',
'new-confirmed-password': ''
};
validationMessages = {
'new-password': USER_PASSWORD.MESSAGES,
'new-confirmed-password': USER_PASSWORD.MESSAGES
};
constructor(
private formBuilder: FormBuilder,
private router: Router,
private notificationsService: NotificationsService,
private userService: UserService
) {
super();
}
buildForm() {
this.form = this.formBuilder.group({
'new-password': [ '', USER_PASSWORD.VALIDATORS ],
'new-confirmed-password': [ '', USER_PASSWORD.VALIDATORS ],
});
this.form.valueChanges.subscribe(data => this.onValueChanged(data));
}
ngOnInit() { ngOnInit() {
this.buildForm(); this.user = this.authService.getUser();
}
changePassword() {
const newPassword = this.form.value['new-password'];
const newConfirmedPassword = this.form.value['new-confirmed-password'];
this.error = null;
if (newPassword !== newConfirmedPassword) {
this.error = 'The new password and the confirmed password do not correspond.';
return;
}
this.userService.changePassword(newPassword).subscribe(
() => this.notificationsService.success('Success', 'Password updated.'),
err => this.error = err
);
} }
} }

View File

@ -2,6 +2,8 @@ import { NgModule } from '@angular/core';
import { AccountRoutingModule } from './account-routing.module'; import { AccountRoutingModule } from './account-routing.module';
import { AccountComponent } from './account.component'; import { AccountComponent } from './account.component';
import { AccountChangePasswordComponent } from './account-change-password';
import { AccountDetailsComponent } from './account-details';
import { AccountService } from './account.service'; import { AccountService } from './account.service';
import { SharedModule } from '../shared'; import { SharedModule } from '../shared';
@ -12,7 +14,9 @@ import { SharedModule } from '../shared';
], ],
declarations: [ declarations: [
AccountComponent AccountComponent,
AccountChangePasswordComponent,
AccountDetailsComponent
], ],
exports: [ exports: [

View File

@ -67,7 +67,7 @@ export class AuthUser extends User {
localStorage.setItem(AuthUser.KEYS.ID, this.id.toString()); localStorage.setItem(AuthUser.KEYS.ID, this.id.toString());
localStorage.setItem(AuthUser.KEYS.USERNAME, this.username); localStorage.setItem(AuthUser.KEYS.USERNAME, this.username);
localStorage.setItem(AuthUser.KEYS.ROLE, this.role); localStorage.setItem(AuthUser.KEYS.ROLE, this.role);
localStorage.setItem(AuthUser.KEYS.DISPLAY_NSFW, JSON.stringify(this.displayNSFW); localStorage.setItem(AuthUser.KEYS.DISPLAY_NSFW, JSON.stringify(this.displayNSFW));
this.tokens.save(); this.tokens.save();
} }
} }

View File

@ -125,7 +125,7 @@ export class AuthService {
res.username = username; res.username = username;
return res; return res;
}) })
.flatMap(res => this.fetchUserInformations(res)) .flatMap(res => this.mergeUserInformations(res))
.map(res => this.handleLogin(res)) .map(res => this.handleLogin(res))
.catch((res) => this.restExtractor.handleError(res)); .catch((res) => this.restExtractor.handleError(res));
} }
@ -178,7 +178,23 @@ export class AuthService {
}); });
} }
private fetchUserInformations (obj: any) { refreshUserInformations() {
const obj = {
access_token: this.user.getAccessToken()
};
this.mergeUserInformations(obj)
.subscribe(
res => {
this.user.displayNSFW = res.displayNSFW;
this.user.role = res.role;
this.user.save();
}
);
}
private mergeUserInformations(obj: { access_token: string }) {
// Do not call authHttp here to avoid circular dependencies headaches // Do not call authHttp here to avoid circular dependencies headaches
const headers = new Headers(); const headers = new Headers();
@ -187,9 +203,13 @@ export class AuthService {
return this.http.get(AuthService.BASE_USER_INFORMATIONS_URL, { headers }) return this.http.get(AuthService.BASE_USER_INFORMATIONS_URL, { headers })
.map(res => res.json()) .map(res => res.json())
.map(res => { .map(res => {
obj.id = res.id; const newProperties = {
obj.role = res.role; id: res.id,
return obj; role: res.role,
displayNSFW: res.displayNSFW
};
return Object.assign(obj, newProperties);
} }
); );
} }
@ -198,13 +218,14 @@ export class AuthService {
const id = obj.id; const id = obj.id;
const username = obj.username; const username = obj.username;
const role = obj.role; const role = obj.role;
const displayNSFW = obj.displayNSFW;
const hashTokens = { const hashTokens = {
access_token: obj.access_token, access_token: obj.access_token,
token_type: obj.token_type, token_type: obj.token_type,
refresh_token: obj.refresh_token refresh_token: obj.refresh_token
}; };
this.user = new AuthUser({ id, username, role }, hashTokens); this.user = new AuthUser({ id, username, role, displayNSFW }, hashTokens);
this.user.save(); this.user.save();
this.setStatus(AuthStatus.LoggedIn); this.setStatus(AuthStatus.LoggedIn);

View File

@ -33,4 +33,12 @@ export class UserService {
.map(this.restExtractor.extractDataBool) .map(this.restExtractor.extractDataBool)
.catch((res) => this.restExtractor.handleError(res)); .catch((res) => this.restExtractor.handleError(res));
} }
updateDetails(details: { displayNSFW: boolean }) {
const url = UserService.BASE_USERS_URL + this.authService.getUser().id;
return this.authHttp.put(url, details)
.map(this.restExtractor.extractDataBool)
.catch((res) => this.restExtractor.handleError(res));
}
} }