Feature/refactor user service as observable store (#117)
* Implement user service as observable store * Clean up tokenStorageService usage * Update changelog
This commit is contained in:
parent
ced4519412
commit
0d6fe4a232
@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Changed
|
||||
|
||||
- Introduced a user service implemented as an observable store (single source of truth for state)
|
||||
|
||||
## 1.7.0 - 22.05.2021
|
||||
|
||||
### Changed
|
||||
|
@ -4,6 +4,7 @@
|
||||
[currentRoute]="currentRoute"
|
||||
[info]="info"
|
||||
[user]="user"
|
||||
(signOut)="onSignOut()"
|
||||
></gf-header>
|
||||
</header>
|
||||
|
||||
|
@ -17,6 +17,7 @@ import { filter, takeUntil } from 'rxjs/operators';
|
||||
import { environment } from '../environments/environment';
|
||||
import { DataService } from './services/data.service';
|
||||
import { TokenStorageService } from './services/token-storage.service';
|
||||
import { UserService } from './services/user/user.service';
|
||||
|
||||
@Component({
|
||||
selector: 'gf-root',
|
||||
@ -30,7 +31,6 @@ export class AppComponent implements OnDestroy, OnInit {
|
||||
public currentYear = new Date().getFullYear();
|
||||
public deviceType: string;
|
||||
public info: InfoItem;
|
||||
public isLoggedIn = false;
|
||||
public user: User;
|
||||
public version = environment.version;
|
||||
|
||||
@ -42,7 +42,8 @@ export class AppComponent implements OnDestroy, OnInit {
|
||||
private deviceService: DeviceDetectorService,
|
||||
private materialCssVarsService: MaterialCssVarsService,
|
||||
private router: Router,
|
||||
private tokenStorageService: TokenStorageService
|
||||
private tokenStorageService: TokenStorageService,
|
||||
private userService: UserService
|
||||
) {
|
||||
this.initializeTheme();
|
||||
this.user = undefined;
|
||||
@ -64,26 +65,22 @@ export class AppComponent implements OnDestroy, OnInit {
|
||||
this.currentRoute = urlSegments[0].path;
|
||||
});
|
||||
|
||||
this.tokenStorageService
|
||||
.onChangeHasToken()
|
||||
this.userService.stateChanged
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {
|
||||
this.isLoggedIn = !!this.tokenStorageService.getToken();
|
||||
.subscribe((state) => {
|
||||
if (state?.user) {
|
||||
this.user = state.user;
|
||||
|
||||
if (this.isLoggedIn) {
|
||||
this.dataService.fetchUser().subscribe((user) => {
|
||||
this.user = user;
|
||||
|
||||
this.canCreateAccount = hasPermission(
|
||||
this.user.permissions,
|
||||
permissions.createUserAccount
|
||||
);
|
||||
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
} else {
|
||||
this.canCreateAccount = hasPermission(
|
||||
this.user.permissions,
|
||||
permissions.createUserAccount
|
||||
);
|
||||
} else if (!this.tokenStorageService.getToken()) {
|
||||
// User has not been logged in
|
||||
this.user = null;
|
||||
}
|
||||
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
@ -92,6 +89,13 @@ export class AppComponent implements OnDestroy, OnInit {
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
public onSignOut() {
|
||||
this.tokenStorageService.signOut();
|
||||
this.userService.remove();
|
||||
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
public ngOnDestroy() {
|
||||
this.unsubscribeSubject.next();
|
||||
this.unsubscribeSubject.complete();
|
||||
|
@ -1,8 +1,10 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnChanges
|
||||
OnChanges,
|
||||
Output
|
||||
} from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { Router } from '@angular/router';
|
||||
@ -26,6 +28,8 @@ export class HeaderComponent implements OnChanges {
|
||||
@Input() info: InfoItem;
|
||||
@Input() user: User;
|
||||
|
||||
@Output() signOut = new EventEmitter<void>();
|
||||
|
||||
public hasPermissionForSocialLogin: boolean;
|
||||
public hasPermissionForSubscription: boolean;
|
||||
public hasPermissionToAccessAdminControl: boolean;
|
||||
@ -75,8 +79,7 @@ export class HeaderComponent implements OnChanges {
|
||||
}
|
||||
|
||||
public onSignOut() {
|
||||
this.tokenStorageService.signOut();
|
||||
window.location.reload();
|
||||
this.signOut.next();
|
||||
}
|
||||
|
||||
public openLoginDialog(): void {
|
||||
|
@ -11,13 +11,15 @@ import { catchError } from 'rxjs/operators';
|
||||
|
||||
import { DataService } from '../services/data.service';
|
||||
import { SettingsStorageService } from '../services/settings-storage.service';
|
||||
import { UserService } from '../services/user/user.service';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AuthGuard implements CanActivate {
|
||||
constructor(
|
||||
private dataService: DataService,
|
||||
private router: Router,
|
||||
private settingsStorageService: SettingsStorageService
|
||||
private settingsStorageService: SettingsStorageService,
|
||||
private userService: UserService
|
||||
) {}
|
||||
|
||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
@ -29,8 +31,8 @@ export class AuthGuard implements CanActivate {
|
||||
}
|
||||
|
||||
return new Promise<boolean>((resolve) => {
|
||||
this.dataService
|
||||
.fetchUser()
|
||||
this.userService
|
||||
.get()
|
||||
.pipe(
|
||||
catchError(() => {
|
||||
if (state.url !== '/start') {
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';
|
||||
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||
import { baseCurrency } from '@ghostfolio/common/config';
|
||||
import { User } from '@ghostfolio/common/interfaces';
|
||||
import { Subject } from 'rxjs';
|
||||
@ -27,27 +26,22 @@ export class AboutPageComponent implements OnInit {
|
||||
*/
|
||||
public constructor(
|
||||
private cd: ChangeDetectorRef,
|
||||
private dataService: DataService,
|
||||
private tokenStorageService: TokenStorageService
|
||||
private userService: UserService
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Initializes the controller
|
||||
*/
|
||||
public ngOnInit() {
|
||||
this.isLoggedIn = !!this.tokenStorageService.getToken();
|
||||
this.userService.stateChanged
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe((state) => {
|
||||
if (state?.user) {
|
||||
this.user = state.user;
|
||||
|
||||
if (this.isLoggedIn)
|
||||
this.tokenStorageService
|
||||
.onChangeHasToken()
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {
|
||||
this.dataService.fetchUser().subscribe((user) => {
|
||||
this.user = user;
|
||||
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
});
|
||||
this.cd.markForCheck();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnDestroy() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';
|
||||
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||
import { DEFAULT_DATE_FORMAT } from '@ghostfolio/common/config';
|
||||
import { Access, User } from '@ghostfolio/common/interfaces';
|
||||
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
||||
@ -30,7 +30,7 @@ export class AccountPageComponent implements OnDestroy, OnInit {
|
||||
public constructor(
|
||||
private cd: ChangeDetectorRef,
|
||||
private dataService: DataService,
|
||||
private tokenStorageService: TokenStorageService
|
||||
private userService: UserService
|
||||
) {
|
||||
this.dataService
|
||||
.fetchInfo()
|
||||
@ -44,12 +44,11 @@ export class AccountPageComponent implements OnDestroy, OnInit {
|
||||
);
|
||||
});
|
||||
|
||||
this.tokenStorageService
|
||||
.onChangeHasToken()
|
||||
this.userService.stateChanged
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {
|
||||
this.dataService.fetchUser().subscribe((user) => {
|
||||
this.user = user;
|
||||
.subscribe((state) => {
|
||||
if (state?.user) {
|
||||
this.user = state.user;
|
||||
|
||||
this.hasPermissionToUpdateUserSettings = hasPermission(
|
||||
this.user.permissions,
|
||||
@ -57,7 +56,7 @@ export class AccountPageComponent implements OnDestroy, OnInit {
|
||||
);
|
||||
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -78,11 +77,16 @@ export class AccountPageComponent implements OnDestroy, OnInit {
|
||||
})
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {
|
||||
this.dataService.fetchUser().subscribe((user) => {
|
||||
this.user = user;
|
||||
this.userService.remove();
|
||||
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
this.userService
|
||||
.get()
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe((user) => {
|
||||
this.user = user;
|
||||
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto
|
||||
import { UpdateAccountDto } from '@ghostfolio/api/app/account/update-account.dto';
|
||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||
import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service';
|
||||
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';
|
||||
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||
import { User } from '@ghostfolio/common/interfaces';
|
||||
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
||||
import { Account as AccountModel, AccountType } from '@prisma/client';
|
||||
@ -42,7 +42,7 @@ export class AccountsPageComponent implements OnInit {
|
||||
private impersonationStorageService: ImpersonationStorageService,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private tokenStorageService: TokenStorageService
|
||||
private userService: UserService
|
||||
) {
|
||||
this.routeQueryParams = route.queryParams
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
@ -75,23 +75,23 @@ export class AccountsPageComponent implements OnInit {
|
||||
this.hasImpersonationId = !!aId;
|
||||
});
|
||||
|
||||
this.tokenStorageService
|
||||
.onChangeHasToken()
|
||||
this.userService.stateChanged
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {
|
||||
this.dataService.fetchUser().subscribe((user) => {
|
||||
this.user = user;
|
||||
.subscribe((state) => {
|
||||
if (state?.user) {
|
||||
this.user = state.user;
|
||||
|
||||
this.hasPermissionToCreateAccount = hasPermission(
|
||||
user.permissions,
|
||||
this.user.permissions,
|
||||
permissions.createAccount
|
||||
);
|
||||
this.hasPermissionToDeleteAccount = hasPermission(
|
||||
user.permissions,
|
||||
this.user.permissions,
|
||||
permissions.deleteAccount
|
||||
);
|
||||
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.fetchAccounts();
|
||||
|
@ -2,7 +2,7 @@ import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||
import { AdminService } from '@ghostfolio/client/services/admin.service';
|
||||
import { CacheService } from '@ghostfolio/client/services/cache.service';
|
||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';
|
||||
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||
import { DEFAULT_DATE_FORMAT } from '@ghostfolio/common/config';
|
||||
import { AdminData, User } from '@ghostfolio/common/interfaces';
|
||||
import { formatDistanceToNow, isValid, parseISO, sub } from 'date-fns';
|
||||
@ -34,7 +34,7 @@ export class AdminPageComponent implements OnInit {
|
||||
private cacheService: CacheService,
|
||||
private cd: ChangeDetectorRef,
|
||||
private dataService: DataService,
|
||||
private tokenStorageService: TokenStorageService
|
||||
private userService: UserService
|
||||
) {}
|
||||
|
||||
/**
|
||||
@ -43,13 +43,12 @@ export class AdminPageComponent implements OnInit {
|
||||
public ngOnInit() {
|
||||
this.fetchAdminData();
|
||||
|
||||
this.tokenStorageService
|
||||
.onChangeHasToken()
|
||||
this.userService.stateChanged
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {
|
||||
this.dataService.fetchUser().subscribe((user) => {
|
||||
this.user = user;
|
||||
});
|
||||
.subscribe((state) => {
|
||||
if (state?.user) {
|
||||
this.user = state.user;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { ToggleOption } from '@ghostfolio/client/components/toggle/interfaces/toggle-option.type';
|
||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||
import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service';
|
||||
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';
|
||||
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||
import {
|
||||
PortfolioItem,
|
||||
PortfolioPosition,
|
||||
@ -44,7 +44,7 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
|
||||
private dataService: DataService,
|
||||
private deviceService: DeviceDetectorService,
|
||||
private impersonationStorageService: ImpersonationStorageService,
|
||||
private tokenStorageService: TokenStorageService
|
||||
private userService: UserService
|
||||
) {}
|
||||
|
||||
/**
|
||||
@ -79,15 +79,14 @@ export class AnalysisPageComponent implements OnDestroy, OnInit {
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
|
||||
this.tokenStorageService
|
||||
.onChangeHasToken()
|
||||
this.userService.stateChanged
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {
|
||||
this.dataService.fetchUser().subscribe((user) => {
|
||||
this.user = user;
|
||||
.subscribe((state) => {
|
||||
if (state?.user) {
|
||||
this.user = state.user;
|
||||
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
RANGE,
|
||||
SettingsStorageService
|
||||
} from '@ghostfolio/client/services/settings-storage.service';
|
||||
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';
|
||||
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||
import {
|
||||
PortfolioOverview,
|
||||
PortfolioPerformance,
|
||||
@ -66,7 +66,7 @@ export class HomePageComponent implements OnDestroy, OnInit {
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private settingsStorageService: SettingsStorageService,
|
||||
private tokenStorageService: TokenStorageService
|
||||
private userService: UserService
|
||||
) {
|
||||
this.routeQueryParams = this.route.queryParams
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
@ -76,14 +76,14 @@ export class HomePageComponent implements OnDestroy, OnInit {
|
||||
}
|
||||
});
|
||||
|
||||
this.tokenStorageService
|
||||
.onChangeHasToken()
|
||||
this.userService.stateChanged
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {
|
||||
this.dataService.fetchUser().subscribe((user) => {
|
||||
this.user = user;
|
||||
.subscribe((state) => {
|
||||
if (state?.user) {
|
||||
this.user = state.user;
|
||||
|
||||
this.hasPermissionToAccessFearAndGreedIndex = hasPermission(
|
||||
user.permissions,
|
||||
this.user.permissions,
|
||||
permissions.accessFearAndGreedIndex
|
||||
);
|
||||
|
||||
@ -99,12 +99,12 @@ export class HomePageComponent implements OnDestroy, OnInit {
|
||||
}
|
||||
|
||||
this.hasPermissionToReadForeignPortfolio = hasPermission(
|
||||
user.permissions,
|
||||
this.user.permissions,
|
||||
permissions.readForeignPortfolio
|
||||
);
|
||||
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';
|
||||
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||
import { baseCurrency } from '@ghostfolio/common/config';
|
||||
import { User } from '@ghostfolio/common/interfaces';
|
||||
import { Subject } from 'rxjs';
|
||||
@ -23,27 +22,22 @@ export class PricingPageComponent implements OnInit {
|
||||
*/
|
||||
public constructor(
|
||||
private cd: ChangeDetectorRef,
|
||||
private dataService: DataService,
|
||||
private tokenStorageService: TokenStorageService
|
||||
private userService: UserService
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Initializes the controller
|
||||
*/
|
||||
public ngOnInit() {
|
||||
this.isLoggedIn = !!this.tokenStorageService.getToken();
|
||||
this.userService.stateChanged
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe((state) => {
|
||||
if (state?.user) {
|
||||
this.user = state.user;
|
||||
|
||||
if (this.isLoggedIn)
|
||||
this.tokenStorageService
|
||||
.onChangeHasToken()
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {
|
||||
this.dataService.fetchUser().subscribe((user) => {
|
||||
this.user = user;
|
||||
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
});
|
||||
this.cd.markForCheck();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnDestroy() {
|
||||
|
@ -5,7 +5,7 @@ import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
|
||||
import { UpdateOrderDto } from '@ghostfolio/api/app/order/update-order.dto';
|
||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||
import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service';
|
||||
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';
|
||||
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||
import { User } from '@ghostfolio/common/interfaces';
|
||||
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
||||
import { Order as OrderModel } from '@prisma/client';
|
||||
@ -42,7 +42,7 @@ export class TransactionsPageComponent implements OnInit {
|
||||
private impersonationStorageService: ImpersonationStorageService,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private tokenStorageService: TokenStorageService
|
||||
private userService: UserService
|
||||
) {
|
||||
this.routeQueryParams = route.queryParams
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
@ -75,23 +75,23 @@ export class TransactionsPageComponent implements OnInit {
|
||||
this.hasImpersonationId = !!aId;
|
||||
});
|
||||
|
||||
this.tokenStorageService
|
||||
.onChangeHasToken()
|
||||
this.userService.stateChanged
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {
|
||||
this.dataService.fetchUser().subscribe((user) => {
|
||||
this.user = user;
|
||||
.subscribe((state) => {
|
||||
if (state?.user) {
|
||||
this.user = state.user;
|
||||
|
||||
this.hasPermissionToCreateOrder = hasPermission(
|
||||
user.permissions,
|
||||
this.user.permissions,
|
||||
permissions.createOrder
|
||||
);
|
||||
this.hasPermissionToDeleteOrder = hasPermission(
|
||||
user.permissions,
|
||||
this.user.permissions,
|
||||
permissions.deleteOrder
|
||||
);
|
||||
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.fetchOrders();
|
||||
|
@ -2,7 +2,7 @@ import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { LineChartItem } from '@ghostfolio/client/components/line-chart/interfaces/line-chart.interface';
|
||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||
import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service';
|
||||
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';
|
||||
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||
import { PortfolioPerformance, User } from '@ghostfolio/common/interfaces';
|
||||
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
||||
import { DateRange } from '@ghostfolio/common/types';
|
||||
@ -35,22 +35,21 @@ export class ZenPageComponent implements OnDestroy, OnInit {
|
||||
private dataService: DataService,
|
||||
private deviceService: DeviceDetectorService,
|
||||
private impersonationStorageService: ImpersonationStorageService,
|
||||
private tokenStorageService: TokenStorageService
|
||||
private userService: UserService
|
||||
) {
|
||||
this.tokenStorageService
|
||||
.onChangeHasToken()
|
||||
this.userService.stateChanged
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe(() => {
|
||||
this.dataService.fetchUser().subscribe((user) => {
|
||||
this.user = user;
|
||||
.subscribe((state) => {
|
||||
if (state?.user) {
|
||||
this.user = state.user;
|
||||
|
||||
this.hasPermissionToReadForeignPortfolio = hasPermission(
|
||||
user.permissions,
|
||||
this.user.permissions,
|
||||
permissions.readForeignPortfolio
|
||||
);
|
||||
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -74,13 +74,6 @@ export class DataService {
|
||||
}
|
||||
|
||||
public fetchInfo() {
|
||||
/*
|
||||
if (this.info) {
|
||||
// TODO: Cache info
|
||||
return of(this.info);
|
||||
}
|
||||
*/
|
||||
|
||||
return this.http.get<InfoItem>('/api/info').pipe(
|
||||
map((data) => {
|
||||
if (
|
||||
@ -154,10 +147,6 @@ export class DataService {
|
||||
);
|
||||
}
|
||||
|
||||
public fetchUser() {
|
||||
return this.http.get<User>('/api/user');
|
||||
}
|
||||
|
||||
public loginAnonymous(accessToken: string) {
|
||||
return this.http.get<any>(`/api/auth/anonymous/${accessToken}`);
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
const TOKEN_KEY = 'auth-token';
|
||||
|
||||
@ -7,23 +6,15 @@ const TOKEN_KEY = 'auth-token';
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class TokenStorageService {
|
||||
private hasTokenChangeSubject = new BehaviorSubject<void>(null);
|
||||
|
||||
public constructor() {}
|
||||
|
||||
public getToken(): string {
|
||||
return window.localStorage.getItem(TOKEN_KEY);
|
||||
}
|
||||
|
||||
public onChangeHasToken() {
|
||||
return this.hasTokenChangeSubject.asObservable();
|
||||
}
|
||||
|
||||
public saveToken(token: string): void {
|
||||
window.localStorage.removeItem(TOKEN_KEY);
|
||||
window.localStorage.setItem(TOKEN_KEY, token);
|
||||
|
||||
this.hasTokenChangeSubject.next();
|
||||
}
|
||||
|
||||
public signOut(): void {
|
||||
@ -34,7 +25,5 @@ export class TokenStorageService {
|
||||
if (utmSource) {
|
||||
window.localStorage.setItem('utm_source', utmSource);
|
||||
}
|
||||
|
||||
this.hasTokenChangeSubject.next();
|
||||
}
|
||||
}
|
||||
|
4
apps/client/src/app/services/user/user-store.actions.ts
Normal file
4
apps/client/src/app/services/user/user-store.actions.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export enum UserStoreActions {
|
||||
GetUser = 'GET_USER',
|
||||
RemoveUser = 'REMOVE_USER'
|
||||
}
|
5
apps/client/src/app/services/user/user-store.state.ts
Normal file
5
apps/client/src/app/services/user/user-store.state.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { User } from '@ghostfolio/common/interfaces';
|
||||
|
||||
export interface UserStoreState {
|
||||
user: User;
|
||||
}
|
56
apps/client/src/app/services/user/user.service.ts
Normal file
56
apps/client/src/app/services/user/user.service.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ObservableStore } from '@codewithdan/observable-store';
|
||||
import { User } from '@ghostfolio/common/interfaces';
|
||||
import { of } from 'rxjs';
|
||||
import { throwError } from 'rxjs';
|
||||
import { catchError, map } from 'rxjs/operators';
|
||||
|
||||
import { UserStoreActions } from './user-store.actions';
|
||||
import { UserStoreState } from './user-store.state';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class UserService extends ObservableStore<UserStoreState> {
|
||||
public constructor(private http: HttpClient) {
|
||||
super({ trackStateHistory: true });
|
||||
|
||||
this.setState({ user: undefined }, 'INIT_STATE');
|
||||
}
|
||||
|
||||
public get() {
|
||||
const state = this.getState();
|
||||
|
||||
if (state?.user) {
|
||||
// Get from cache
|
||||
return of(state.user);
|
||||
} else {
|
||||
// Get from endpoint
|
||||
return this.fetchUser().pipe(catchError(this.handleError));
|
||||
}
|
||||
}
|
||||
|
||||
public remove() {
|
||||
this.setState({ user: null }, UserStoreActions.RemoveUser);
|
||||
}
|
||||
|
||||
private fetchUser() {
|
||||
return this.http.get<User>('/api/user').pipe(
|
||||
map((user) => {
|
||||
this.setState({ user }, UserStoreActions.GetUser);
|
||||
return user;
|
||||
}),
|
||||
catchError(this.handleError)
|
||||
);
|
||||
}
|
||||
|
||||
private handleError(error: any) {
|
||||
if (error.error instanceof Error) {
|
||||
const errMessage = error.error.message;
|
||||
return throwError(errMessage);
|
||||
}
|
||||
|
||||
return throwError(error || 'Server error');
|
||||
}
|
||||
}
|
@ -55,6 +55,7 @@
|
||||
"@angular/platform-browser": "11.2.4",
|
||||
"@angular/platform-browser-dynamic": "11.2.4",
|
||||
"@angular/router": "11.2.4",
|
||||
"@codewithdan/observable-store": "2.2.11",
|
||||
"@nestjs/common": "7.6.5",
|
||||
"@nestjs/config": "0.6.1",
|
||||
"@nestjs/core": "7.6.5",
|
||||
|
@ -1396,6 +1396,11 @@
|
||||
exec-sh "^0.3.2"
|
||||
minimist "^1.2.0"
|
||||
|
||||
"@codewithdan/observable-store@2.2.11":
|
||||
version "2.2.11"
|
||||
resolved "https://registry.yarnpkg.com/@codewithdan/observable-store/-/observable-store-2.2.11.tgz#f5a168e86a2fa185a50ca40a1e838aa5e5fb007d"
|
||||
integrity sha512-6CfqLJUqV0SwS4yE+9vciqxHUJ6CqIptSXXzFw80MonCDoVJvCJ/xhKfs7VZqJ4jDtEu/7ILvovFtZdLg9fiAg==
|
||||
|
||||
"@ctrl/tinycolor@^2.6.0":
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-2.6.1.tgz#0e78cc836a1fd997a9a22fa1c26c555411882160"
|
||||
|
Loading…
x
Reference in New Issue
Block a user