Browse Source

Refactor AuthGuard, persist displayMode in user settings

pull/110/head
Thomas 4 years ago
parent
commit
bc2e905258
  1. 7
      apps/api/src/app/user/update-user-settings.dto.ts
  2. 3
      apps/api/src/app/user/user.controller.ts
  3. 16
      apps/api/src/app/user/user.service.ts
  4. 2
      apps/api/src/services/configuration.service.ts
  5. 6
      apps/client/src/app/components/header/header.component.html
  6. 62
      apps/client/src/app/core/auth.guard.ts
  7. 1
      apps/client/src/app/core/http-response.interceptor.ts
  8. 24
      apps/client/src/app/pages/account/account-page.component.ts
  9. 16
      apps/client/src/app/pages/account/account-page.html
  10. 3
      apps/client/src/app/pages/zen/zen-page.scss
  11. 2
      libs/common/src/lib/config.ts
  12. 4
      libs/common/src/lib/interfaces/user-settings.interface.ts
  13. 14
      prisma/schema.prisma

7
apps/api/src/app/user/update-user-settings.dto.ts

@ -1,7 +1,10 @@
import { Currency } from '@prisma/client';
import { Currency, DisplayMode } from '@prisma/client';
import { IsString } from 'class-validator';
export class UpdateUserSettingsDto {
@IsString()
currency: Currency;
baseCurrency: Currency;
@IsString()
displayMode: DisplayMode;
}

3
apps/api/src/app/user/user.controller.ts

@ -93,7 +93,8 @@ export class UserController {
}
return await this.userService.updateUserSettings({
currency: data.currency,
currency: data.baseCurrency,
displayMode: data.displayMode,
userId: this.request.user.id
});
}

16
apps/api/src/app/user/user.service.ts

@ -5,7 +5,7 @@ import { resetHours } from '@ghostfolio/common/helper';
import { User as IUser, UserWithSettings } from '@ghostfolio/common/interfaces';
import { getPermissions, permissions } from '@ghostfolio/common/permissions';
import { Injectable } from '@nestjs/common';
import { Currency, Prisma, Provider, User } from '@prisma/client';
import { Currency, DisplayMode, Prisma, Provider, User } from '@prisma/client';
import { add } from 'date-fns';
const crypto = require('crypto');
@ -52,8 +52,9 @@ export class UserService {
accounts: Account,
permissions: currentPermissions,
settings: {
baseCurrency: Settings?.currency || UserService.DEFAULT_CURRENCY,
locale
locale,
baseCurrency: Settings?.currency ?? UserService.DEFAULT_CURRENCY,
displayMode: Settings.displayMode ?? DisplayMode.DEFAULT
},
subscription: {
expiresAt: resetHours(add(new Date(), { days: 7 })),
@ -79,6 +80,7 @@ export class UserService {
// Set default settings if needed
user.Settings = {
currency: UserService.DEFAULT_CURRENCY,
displayMode: DisplayMode.DEFAULT,
updatedAt: new Date(),
userId: user?.id
};
@ -187,14 +189,17 @@ export class UserService {
public async updateUserSettings({
currency,
displayMode,
userId
}: {
currency: Currency;
currency?: Currency;
displayMode?: DisplayMode;
userId: string;
}) {
await this.prisma.settings.upsert({
create: {
currency,
displayMode,
User: {
connect: {
id: userId
@ -202,7 +207,8 @@ export class UserService {
}
},
update: {
currency
currency,
displayMode
},
where: {
userId: userId

2
apps/api/src/services/configuration.service.ts

@ -1,8 +1,8 @@
import { Injectable } from '@nestjs/common';
import { DataSource } from '@prisma/client';
import { bool, cleanEnv, json, num, port, str } from 'envalid';
import { Environment } from './interfaces/environment.interface';
import { DataSource } from '.prisma/client';
@Injectable()
export class ConfigurationService {

6
apps/client/src/app/components/header/header.component.html

@ -8,11 +8,14 @@
class="d-none d-sm-block"
i18n
mat-flat-button
[color]="currentRoute === 'home' ? 'primary' : null"
[color]="
currentRoute === 'home' || currentRoute === 'zen' ? 'primary' : null
"
[routerLink]="['/']"
>Overview</a
>
<a
*ngIf="user?.settings?.displayMode === 'DEFAULT'"
class="d-none d-sm-block mx-1"
i18n
mat-flat-button
@ -21,6 +24,7 @@
>Analysis</a
>
<a
*ngIf="user?.settings?.displayMode === 'DEFAULT'"
class="d-none d-sm-block mx-1"
i18n
mat-flat-button

62
apps/client/src/app/core/auth.guard.ts

@ -5,16 +5,19 @@ import {
Router,
RouterStateSnapshot
} from '@angular/router';
import { DisplayMode } from '@prisma/client';
import { EMPTY } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { DataService } from '../services/data.service';
import { SettingsStorageService } from '../services/settings-storage.service';
import { TokenStorageService } from '../services/token-storage.service';
@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
constructor(
private dataService: DataService,
private router: Router,
private settingsStorageService: SettingsStorageService,
private tokenStorageService: TokenStorageService
private settingsStorageService: SettingsStorageService
) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
@ -25,23 +28,46 @@ export class AuthGuard implements CanActivate {
);
}
const isLoggedIn = !!this.tokenStorageService.getToken();
return new Promise<boolean>((resolve) => {
this.dataService
.fetchUser()
.pipe(
catchError(() => {
if (state.url !== '/start') {
this.router.navigate(['/start']);
resolve(false);
return EMPTY;
}
if (isLoggedIn) {
if (state.url === '/start') {
this.router.navigate(['/home']);
return false;
}
resolve(true);
return EMPTY;
})
)
.subscribe((user) => {
if (
state.url === '/home' &&
user.settings.displayMode === DisplayMode.ZEN
) {
this.router.navigate(['/zen']);
resolve(false);
} else if (state.url === '/start') {
if (user.settings.displayMode === DisplayMode.ZEN) {
this.router.navigate(['/zen']);
} else {
this.router.navigate(['/home']);
}
return true;
}
// Not logged in
if (state.url !== '/start') {
this.router.navigate(['/start']);
return false;
}
resolve(false);
} else if (
state.url === '/zen' &&
user.settings.displayMode === DisplayMode.DEFAULT
) {
this.router.navigate(['/home']);
resolve(false);
}
return true;
resolve(true);
});
});
}
}

1
apps/client/src/app/core/http-response.interceptor.ts

@ -79,7 +79,6 @@ export class HttpResponseInterceptor implements HttpInterceptor {
}
} else if (error.status === StatusCodes.UNAUTHORIZED) {
this.tokenStorageService.signOut();
this.router.navigate(['start']);
}
return throwError('');

24
apps/client/src/app/pages/account/account-page.component.ts

@ -51,8 +51,6 @@ export class AccountPageComponent implements OnDestroy, OnInit {
this.dataService.fetchUser().subscribe((user) => {
this.user = user;
this.user.settings.mode = 'ZEN';
this.hasPermissionToUpdateUserSettings = hasPermission(
this.user.permissions,
permissions.updateUserSettings
@ -70,9 +68,14 @@ export class AccountPageComponent implements OnDestroy, OnInit {
this.update();
}
public onChangeBaseCurrency({ value: currency }: { value: Currency }) {
public onChangeUserSettings(aKey: string, aValue: string) {
const settings = { ...this.user.settings, [aKey]: aValue };
this.dataService
.putUserSettings({ currency })
.putUserSettings({
baseCurrency: settings?.baseCurrency,
displayMode: settings?.displayMode
})
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(() => {
this.dataService.fetchUser().subscribe((user) => {
@ -83,19 +86,6 @@ export class AccountPageComponent implements OnDestroy, OnInit {
});
}
public onChangeMode({ value: mode }: { value: Currency }) {
/*this.dataService
.putUserSettings({ currency })
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(() => {
this.dataService.fetchUser().subscribe((user) => {
this.user = user;
this.cd.markForCheck();
});
});*/
}
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();

16
apps/client/src/app/pages/account/account-page.html

@ -30,14 +30,14 @@
<div class="d-flex mt-4 py-1">
<div class="pt-4 w-50" i18n>Settings</div>
<div class="w-50">
<form #changeBaseCurrencyForm="ngForm">
<mat-form-field appearance="outline" class="w-100">
<form #changeUserSettingsForm="ngForm">
<mat-form-field appearance="outline" class="mb-3 w-100">
<mat-label i18n>Base Currency</mat-label>
<mat-select
name="baseCurrency"
[disabled]="!hasPermissionToUpdateUserSettings"
[value]="user.settings.baseCurrency"
(selectionChange)="onChangeBaseCurrency($event)"
(selectionChange)="onChangeUserSettings('baseCurrency', $event.value)"
>
<mat-option
*ngFor="let currency of currencies"
@ -46,15 +46,13 @@
>
</mat-select>
</mat-form-field>
</form>
<form #changeModeForm="ngForm">
<mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Mode</mat-label>
<mat-label i18n>Display Mode</mat-label>
<mat-select
name="baseCurrency"
name="displayMode"
[disabled]="!hasPermissionToUpdateUserSettings"
[value]="user.settings.mode"
(selectionChange)="onChangeMode($event)"
[value]="user.settings.displayMode"
(selectionChange)="onChangeUserSettings('displayMode', $event.value)"
>
<mat-option value="DEFAULT">Default</mat-option>
<mat-option value="ZEN">Zen</mat-option>

3
apps/client/src/app/pages/zen/zen-page.scss

@ -4,8 +4,7 @@
.chart-container {
aspect-ratio: 16 / 9;
cursor: pointer;
margin-top: -1rem;
margin-top: 3rem;
max-height: 50vh;
// Fallback for aspect-ratio (using padding hack)

2
libs/common/src/lib/config.ts

@ -1,4 +1,4 @@
import { Currency } from '.prisma/client';
import { Currency } from '@prisma/client';
export const baseCurrency = Currency.CHF;

4
libs/common/src/lib/interfaces/user-settings.interface.ts

@ -1,7 +1,7 @@
import { Currency } from '@prisma/client';
import { Currency, DisplayMode } from '@prisma/client';
export interface UserSettings {
baseCurrency: Currency;
displayMode: DisplayMode;
locale: string;
mode: 'DEFAULT' | 'ZEN';
}

14
prisma/schema.prisma

@ -92,10 +92,11 @@ model Property {
}
model Settings {
currency Currency
updatedAt DateTime @updatedAt
User User @relation(fields: [userId], references: [id])
userId String @id
currency Currency?
displayMode DisplayMode?
updatedAt DateTime @updatedAt
User User @relation(fields: [userId], references: [id])
userId String @id
}
model User {
@ -133,6 +134,11 @@ enum DataSource {
YAHOO
}
enum DisplayMode {
DEFAULT
ZEN
}
enum Provider {
ANONYMOUS
GOOGLE

Loading…
Cancel
Save