Browse Source

Fix rendering of currency and platform in dialogs and clean up observables (#202)

pull/205/head
Thomas 4 years ago
committed by GitHub
parent
commit
1135a5b335
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      CHANGELOG.md
  2. 5
      apps/client/src/app/app.component.ts
  3. 11
      apps/client/src/app/components/header/header.component.ts
  4. 10
      apps/client/src/app/components/performance-chart-dialog/performance-chart-dialog.component.ts
  5. 15
      apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts
  6. 5
      apps/client/src/app/components/position/position.component.ts
  7. 11
      apps/client/src/app/components/positions-table/positions-table.component.ts
  8. 9
      apps/client/src/app/components/transactions-table/transactions-table.component.ts
  9. 5
      apps/client/src/app/pages/about/about-page.component.ts
  10. 2
      apps/client/src/app/pages/account/account-page.component.ts
  11. 35
      apps/client/src/app/pages/accounts/accounts-page.component.ts
  12. 19
      apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts
  13. 19
      apps/client/src/app/pages/admin/admin-page.component.ts
  14. 17
      apps/client/src/app/pages/auth/auth-page.component.ts
  15. 14
      apps/client/src/app/pages/home/home-page.component.ts
  16. 6
      apps/client/src/app/pages/landing/landing-page.component.ts
  17. 4
      apps/client/src/app/pages/pricing/pricing-page.component.ts
  18. 6
      apps/client/src/app/pages/register/register-page.component.ts
  19. 4
      apps/client/src/app/pages/tools/report/report-page.component.ts
  20. 13
      apps/client/src/app/pages/transactions/create-or-update-transaction-dialog/create-or-update-transaction-dialog.component.ts
  21. 30
      apps/client/src/app/pages/transactions/transactions-page.component.ts
  22. 23
      apps/client/src/app/pages/webauthn/webauthn-page.component.ts
  23. 3
      apps/client/src/app/pages/zen/zen-page.component.ts

6
CHANGELOG.md

@ -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/), 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). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Fixed
- Fixed rendering of currency and platform in dialogs (account and transaction)
## 1.24.0 - 07.07.2021 ## 1.24.0 - 07.07.2021
### Added ### Added

5
apps/client/src/app/app.component.ts

@ -52,7 +52,10 @@ export class AppComponent implements OnDestroy, OnInit {
public ngOnInit() { public ngOnInit() {
this.deviceType = this.deviceService.getDeviceInfo().deviceType; this.deviceType = this.deviceService.getDeviceInfo().deviceType;
this.dataService.fetchInfo().subscribe((info) => { this.dataService
.fetchInfo()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((info) => {
this.info = info; this.info = info;
}); });

11
apps/client/src/app/components/header/header.component.ts

@ -51,6 +51,7 @@ export class HeaderComponent implements OnChanges {
) { ) {
this.impersonationStorageService this.impersonationStorageService
.onChangeHasImpersonation() .onChangeHasImpersonation()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((id) => { .subscribe((id) => {
this.impersonationId = id; this.impersonationId = id;
}); });
@ -98,7 +99,10 @@ export class HeaderComponent implements OnChanges {
width: '30rem' width: '30rem'
}); });
dialogRef.afterClosed().subscribe((data) => { dialogRef
.afterClosed()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((data) => {
if (data?.accessToken) { if (data?.accessToken) {
this.dataService this.dataService
.loginAnonymous(data?.accessToken) .loginAnonymous(data?.accessToken)
@ -125,4 +129,9 @@ export class HeaderComponent implements OnChanges {
this.router.navigate(['/']); this.router.navigate(['/']);
} }
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
} }

10
apps/client/src/app/components/performance-chart-dialog/performance-chart-dialog.component.ts

@ -7,6 +7,8 @@ import {
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
import { isToday, parse } from 'date-fns'; import { isToday, parse } from 'date-fns';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { LineChartItem } from '../line-chart/interfaces/line-chart.interface'; import { LineChartItem } from '../line-chart/interfaces/line-chart.interface';
import { PositionDetailDialogParams } from './interfaces/interfaces'; import { PositionDetailDialogParams } from './interfaces/interfaces';
@ -27,6 +29,8 @@ export class PerformanceChartDialog {
public historicalDataItems: LineChartItem[]; public historicalDataItems: LineChartItem[];
public title: string; public title: string;
private unsubscribeSubject = new Subject<void>();
public constructor( public constructor(
private changeDetectorRef: ChangeDetectorRef, private changeDetectorRef: ChangeDetectorRef,
private dataService: DataService, private dataService: DataService,
@ -35,6 +39,7 @@ export class PerformanceChartDialog {
) { ) {
this.dataService this.dataService
.fetchPositionDetail(this.benchmarkSymbol) .fetchPositionDetail(this.benchmarkSymbol)
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ currency, firstBuyDate, historicalData, marketPrice }) => { .subscribe(({ currency, firstBuyDate, historicalData, marketPrice }) => {
this.benchmarkDataItems = []; this.benchmarkDataItems = [];
this.currency = currency; this.currency = currency;
@ -84,4 +89,9 @@ export class PerformanceChartDialog {
public onClose(): void { public onClose(): void {
this.dialogRef.close(); this.dialogRef.close();
} }
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
} }

15
apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts

@ -2,11 +2,14 @@ import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
Inject Inject,
OnDestroy
} from '@angular/core'; } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
import { format, isSameMonth, isToday, parseISO } from 'date-fns'; import { format, isSameMonth, isToday, parseISO } from 'date-fns';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { LineChartItem } from '../../line-chart/interfaces/line-chart.interface'; import { LineChartItem } from '../../line-chart/interfaces/line-chart.interface';
import { PositionDetailDialogParams } from './interfaces/interfaces'; import { PositionDetailDialogParams } from './interfaces/interfaces';
@ -18,7 +21,7 @@ import { PositionDetailDialogParams } from './interfaces/interfaces';
templateUrl: 'position-detail-dialog.html', templateUrl: 'position-detail-dialog.html',
styleUrls: ['./position-detail-dialog.component.scss'] styleUrls: ['./position-detail-dialog.component.scss']
}) })
export class PositionDetailDialog { export class PositionDetailDialog implements OnDestroy {
public averagePrice: number; public averagePrice: number;
public benchmarkDataItems: LineChartItem[]; public benchmarkDataItems: LineChartItem[];
public currency: string; public currency: string;
@ -33,6 +36,8 @@ export class PositionDetailDialog {
public quantity: number; public quantity: number;
public transactionCount: number; public transactionCount: number;
private unsubscribeSubject = new Subject<void>();
public constructor( public constructor(
private changeDetectorRef: ChangeDetectorRef, private changeDetectorRef: ChangeDetectorRef,
private dataService: DataService, private dataService: DataService,
@ -41,6 +46,7 @@ export class PositionDetailDialog {
) { ) {
this.dataService this.dataService
.fetchPositionDetail(data.symbol) .fetchPositionDetail(data.symbol)
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe( .subscribe(
({ ({
averagePrice, averagePrice,
@ -135,4 +141,9 @@ export class PositionDetailDialog {
public onClose(): void { public onClose(): void {
this.dialogRef.close(); this.dialogRef.close();
} }
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
} }

5
apps/client/src/app/components/position/position.component.ts

@ -72,7 +72,10 @@ export class PositionComponent implements OnDestroy, OnInit {
width: this.deviceType === 'mobile' ? '100vw' : '50rem' width: this.deviceType === 'mobile' ? '100vw' : '50rem'
}); });
dialogRef.afterClosed().subscribe(() => { dialogRef
.afterClosed()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(() => {
this.router.navigate(['.'], { relativeTo: this.route }); this.router.navigate(['.'], { relativeTo: this.route });
}); });
} }

11
apps/client/src/app/components/positions-table/positions-table.component.ts

@ -4,6 +4,7 @@ import {
EventEmitter, EventEmitter,
Input, Input,
OnChanges, OnChanges,
OnDestroy,
OnInit, OnInit,
Output, Output,
ViewChild ViewChild
@ -26,7 +27,7 @@ import { PositionDetailDialog } from '../position/position-detail-dialog/positio
templateUrl: './positions-table.component.html', templateUrl: './positions-table.component.html',
styleUrls: ['./positions-table.component.scss'] styleUrls: ['./positions-table.component.scss']
}) })
export class PositionsTableComponent implements OnChanges, OnInit { export class PositionsTableComponent implements OnChanges, OnDestroy, OnInit {
@Input() baseCurrency: string; @Input() baseCurrency: string;
@Input() deviceType: string; @Input() deviceType: string;
@Input() locale: string; @Input() locale: string;
@ -38,7 +39,8 @@ export class PositionsTableComponent implements OnChanges, OnInit {
@ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort; @ViewChild(MatSort) sort: MatSort;
public dataSource: MatTableDataSource<PortfolioPosition> = new MatTableDataSource(); public dataSource: MatTableDataSource<PortfolioPosition> =
new MatTableDataSource();
public displayedColumns = []; public displayedColumns = [];
public isLoading = true; public isLoading = true;
public pageSize = 7; public pageSize = 7;
@ -133,7 +135,10 @@ export class PositionsTableComponent implements OnChanges, OnInit {
width: this.deviceType === 'mobile' ? '100vw' : '50rem' width: this.deviceType === 'mobile' ? '100vw' : '50rem'
}); });
dialogRef.afterClosed().subscribe(() => { dialogRef
.afterClosed()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(() => {
this.router.navigate(['.'], { relativeTo: this.route }); this.router.navigate(['.'], { relativeTo: this.route });
}); });
} }

9
apps/client/src/app/components/transactions-table/transactions-table.component.ts

@ -89,7 +89,9 @@ export class TransactionsTableComponent
} }
}); });
this.searchControl.valueChanges.subscribe((keyword) => { this.searchControl.valueChanges
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((keyword) => {
if (keyword) { if (keyword) {
const filterValue = keyword.toLowerCase(); const filterValue = keyword.toLowerCase();
this.filters$.next( this.filters$.next(
@ -223,7 +225,10 @@ export class TransactionsTableComponent
width: this.deviceType === 'mobile' ? '100vw' : '50rem' width: this.deviceType === 'mobile' ? '100vw' : '50rem'
}); });
dialogRef.afterClosed().subscribe(() => { dialogRef
.afterClosed()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(() => {
this.router.navigate(['.'], { relativeTo: this.route }); this.router.navigate(['.'], { relativeTo: this.route });
}); });
} }

5
apps/client/src/app/pages/about/about-page.component.ts

@ -1,4 +1,4 @@
import { ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
import { UserService } from '@ghostfolio/client/services/user/user.service'; import { UserService } from '@ghostfolio/client/services/user/user.service';
import { baseCurrency } from '@ghostfolio/common/config'; import { baseCurrency } from '@ghostfolio/common/config';
@ -15,7 +15,7 @@ import { environment } from '../../../environments/environment';
templateUrl: './about-page.html', templateUrl: './about-page.html',
styleUrls: ['./about-page.scss'] styleUrls: ['./about-page.scss']
}) })
export class AboutPageComponent implements OnInit { export class AboutPageComponent implements OnDestroy, OnInit {
public baseCurrency = baseCurrency; public baseCurrency = baseCurrency;
public hasPermissionForStatistics: boolean; public hasPermissionForStatistics: boolean;
public isLoggedIn: boolean; public isLoggedIn: boolean;
@ -41,6 +41,7 @@ export class AboutPageComponent implements OnInit {
public ngOnInit() { public ngOnInit() {
this.dataService this.dataService
.fetchInfo() .fetchInfo()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ globalPermissions, statistics }) => { .subscribe(({ globalPermissions, statistics }) => {
this.hasPermissionForStatistics = hasPermission( this.hasPermissionForStatistics = hasPermission(
globalPermissions, globalPermissions,

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

@ -166,6 +166,7 @@ export class AccountPageComponent implements OnDestroy, OnInit {
this.webAuthnService this.webAuthnService
.deregister() .deregister()
.pipe( .pipe(
takeUntil(this.unsubscribeSubject),
catchError(() => { catchError(() => {
this.update(); this.update();
@ -181,6 +182,7 @@ export class AccountPageComponent implements OnDestroy, OnInit {
this.webAuthnService this.webAuthnService
.register() .register()
.pipe( .pipe(
takeUntil(this.unsubscribeSubject),
catchError(() => { catchError(() => {
this.update(); this.update();

35
apps/client/src/app/pages/accounts/accounts-page.component.ts

@ -1,4 +1,4 @@
import { ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto'; import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto';
@ -20,7 +20,7 @@ import { CreateOrUpdateAccountDialog } from './create-or-update-account-dialog/c
templateUrl: './accounts-page.html', templateUrl: './accounts-page.html',
styleUrls: ['./accounts-page.scss'] styleUrls: ['./accounts-page.scss']
}) })
export class AccountsPageComponent implements OnInit { export class AccountsPageComponent implements OnDestroy, OnInit {
public accounts: AccountModel[]; public accounts: AccountModel[];
public deviceType: string; public deviceType: string;
public hasImpersonationId: boolean; public hasImpersonationId: boolean;
@ -71,6 +71,7 @@ export class AccountsPageComponent implements OnInit {
this.impersonationStorageService this.impersonationStorageService
.onChangeHasImpersonation() .onChangeHasImpersonation()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((aId) => { .subscribe((aId) => {
this.hasImpersonationId = !!aId; this.hasImpersonationId = !!aId;
}); });
@ -98,7 +99,10 @@ export class AccountsPageComponent implements OnInit {
} }
public fetchAccounts() { public fetchAccounts() {
this.dataService.fetchAccounts().subscribe((response) => { this.dataService
.fetchAccounts()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((response) => {
this.accounts = response; this.accounts = response;
if (this.accounts?.length <= 0) { if (this.accounts?.length <= 0) {
@ -110,7 +114,10 @@ export class AccountsPageComponent implements OnInit {
} }
public onDeleteAccount(aId: string) { public onDeleteAccount(aId: string) {
this.dataService.deleteAccount(aId).subscribe({ this.dataService
.deleteAccount(aId)
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe({
next: () => { next: () => {
this.fetchAccounts(); this.fetchAccounts();
} }
@ -146,11 +153,17 @@ export class AccountsPageComponent implements OnInit {
width: this.deviceType === 'mobile' ? '100vw' : '50rem' width: this.deviceType === 'mobile' ? '100vw' : '50rem'
}); });
dialogRef.afterClosed().subscribe((data: any) => { dialogRef
.afterClosed()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((data: any) => {
const account: UpdateAccountDto = data?.account; const account: UpdateAccountDto = data?.account;
if (account) { if (account) {
this.dataService.putAccount(account).subscribe({ this.dataService
.putAccount(account)
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe({
next: () => { next: () => {
this.fetchAccounts(); this.fetchAccounts();
} }
@ -181,11 +194,17 @@ export class AccountsPageComponent implements OnInit {
width: this.deviceType === 'mobile' ? '100vw' : '50rem' width: this.deviceType === 'mobile' ? '100vw' : '50rem'
}); });
dialogRef.afterClosed().subscribe((data: any) => { dialogRef
.afterClosed()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((data: any) => {
const account: CreateAccountDto = data?.account; const account: CreateAccountDto = data?.account;
if (account) { if (account) {
this.dataService.postAccount(account).subscribe({ this.dataService
.postAccount(account)
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe({
next: () => { next: () => {
this.fetchAccounts(); this.fetchAccounts();
} }

19
apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.component.ts

@ -1,7 +1,14 @@
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'; import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
Inject,
OnDestroy
} from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Currency } from '@prisma/client'; import { Currency } from '@prisma/client';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { DataService } from '../../../services/data.service'; import { DataService } from '../../../services/data.service';
import { CreateOrUpdateAccountDialogParams } from './interfaces/interfaces'; import { CreateOrUpdateAccountDialogParams } from './interfaces/interfaces';
@ -13,22 +20,28 @@ import { CreateOrUpdateAccountDialogParams } from './interfaces/interfaces';
styleUrls: ['./create-or-update-account-dialog.scss'], styleUrls: ['./create-or-update-account-dialog.scss'],
templateUrl: 'create-or-update-account-dialog.html' templateUrl: 'create-or-update-account-dialog.html'
}) })
export class CreateOrUpdateAccountDialog { export class CreateOrUpdateAccountDialog implements OnDestroy {
public currencies: Currency[] = []; public currencies: Currency[] = [];
public platforms: { id: string; name: string }[]; public platforms: { id: string; name: string }[];
private unsubscribeSubject = new Subject<void>(); private unsubscribeSubject = new Subject<void>();
public constructor( public constructor(
private changeDetectorRef: ChangeDetectorRef,
private dataService: DataService, private dataService: DataService,
public dialogRef: MatDialogRef<CreateOrUpdateAccountDialog>, public dialogRef: MatDialogRef<CreateOrUpdateAccountDialog>,
@Inject(MAT_DIALOG_DATA) public data: CreateOrUpdateAccountDialogParams @Inject(MAT_DIALOG_DATA) public data: CreateOrUpdateAccountDialogParams
) {} ) {}
ngOnInit() { ngOnInit() {
this.dataService.fetchInfo().subscribe(({ currencies, platforms }) => { this.dataService
.fetchInfo()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ currencies, platforms }) => {
this.currencies = currencies; this.currencies = currencies;
this.platforms = platforms; this.platforms = platforms;
this.changeDetectorRef.markForCheck();
}); });
} }

19
apps/client/src/app/pages/admin/admin-page.component.ts

@ -1,4 +1,4 @@
import { ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { AdminService } from '@ghostfolio/client/services/admin.service'; import { AdminService } from '@ghostfolio/client/services/admin.service';
import { CacheService } from '@ghostfolio/client/services/cache.service'; import { CacheService } from '@ghostfolio/client/services/cache.service';
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
@ -19,7 +19,7 @@ import { takeUntil } from 'rxjs/operators';
templateUrl: './admin-page.html', templateUrl: './admin-page.html',
styleUrls: ['./admin-page.scss'] styleUrls: ['./admin-page.scss']
}) })
export class AdminPageComponent implements OnInit { export class AdminPageComponent implements OnDestroy, OnInit {
public dataGatheringInProgress: boolean; public dataGatheringInProgress: boolean;
public defaultDateFormat = DEFAULT_DATE_FORMAT; public defaultDateFormat = DEFAULT_DATE_FORMAT;
public exchangeRates: { label1: string; label2: string; value: number }[]; public exchangeRates: { label1: string; label2: string; value: number }[];
@ -58,7 +58,10 @@ export class AdminPageComponent implements OnInit {
} }
public onFlushCache() { public onFlushCache() {
this.cacheService.flush().subscribe(() => { this.cacheService
.flush()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(() => {
setTimeout(() => { setTimeout(() => {
window.location.reload(); window.location.reload();
}, 300); }, 300);
@ -71,7 +74,10 @@ export class AdminPageComponent implements OnInit {
); );
if (confirmation === true) { if (confirmation === true) {
this.adminService.gatherMax().subscribe(() => { this.adminService
.gatherMax()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(() => {
setTimeout(() => { setTimeout(() => {
window.location.reload(); window.location.reload();
}, 300); }, 300);
@ -98,7 +104,10 @@ export class AdminPageComponent implements OnInit {
const confirmation = confirm('Do you really want to delete this user?'); const confirmation = confirm('Do you really want to delete this user?');
if (confirmation) { if (confirmation) {
this.dataService.deleteUser(aId).subscribe({ this.dataService
.deleteUser(aId)
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe({
next: () => { next: () => {
this.fetchAdminData(); this.fetchAdminData();
} }

17
apps/client/src/app/pages/auth/auth-page.component.ts

@ -1,17 +1,21 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { import {
STAY_SIGNED_IN, STAY_SIGNED_IN,
SettingsStorageService SettingsStorageService
} from '@ghostfolio/client/services/settings-storage.service'; } from '@ghostfolio/client/services/settings-storage.service';
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service'; import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({ @Component({
selector: 'gf-auth-page', selector: 'gf-auth-page',
templateUrl: './auth-page.html', templateUrl: './auth-page.html',
styleUrls: ['./auth-page.scss'] styleUrls: ['./auth-page.scss']
}) })
export class AuthPageComponent implements OnInit { export class AuthPageComponent implements OnDestroy, OnInit {
private unsubscribeSubject = new Subject<void>();
/** /**
* @constructor * @constructor
*/ */
@ -26,7 +30,9 @@ export class AuthPageComponent implements OnInit {
* Initializes the controller * Initializes the controller
*/ */
public ngOnInit() { public ngOnInit() {
this.route.params.subscribe((params) => { this.route.params
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((params) => {
const jwt = params['jwt']; const jwt = params['jwt'];
this.tokenStorageService.saveToken( this.tokenStorageService.saveToken(
jwt, jwt,
@ -36,4 +42,9 @@ export class AuthPageComponent implements OnInit {
this.router.navigate(['/']); this.router.navigate(['/']);
}); });
} }
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
} }

14
apps/client/src/app/pages/home/home-page.component.ts

@ -116,6 +116,7 @@ export class HomePageComponent implements OnDestroy, OnInit {
this.impersonationStorageService this.impersonationStorageService
.onChangeHasImpersonation() .onChangeHasImpersonation()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((aId) => { .subscribe((aId) => {
this.hasImpersonationId = !!aId; this.hasImpersonationId = !!aId;
}); });
@ -148,7 +149,10 @@ export class HomePageComponent implements OnDestroy, OnInit {
width: '50rem' width: '50rem'
}); });
dialogRef.afterClosed().subscribe(() => { dialogRef
.afterClosed()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(() => {
this.router.navigate(['.'], { relativeTo: this.route }); this.router.navigate(['.'], { relativeTo: this.route });
}); });
} }
@ -161,6 +165,7 @@ export class HomePageComponent implements OnDestroy, OnInit {
this.dataService this.dataService
.fetchChart({ range: this.dateRange }) .fetchChart({ range: this.dateRange })
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((chartData) => { .subscribe((chartData) => {
this.historicalDataItems = chartData.map((chartDataItem) => { this.historicalDataItems = chartData.map((chartDataItem) => {
return { return {
@ -174,6 +179,7 @@ export class HomePageComponent implements OnDestroy, OnInit {
this.dataService this.dataService
.fetchPortfolioPerformance({ range: this.dateRange }) .fetchPortfolioPerformance({ range: this.dateRange })
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((response) => { .subscribe((response) => {
this.performance = response; this.performance = response;
this.isLoadingPerformance = false; this.isLoadingPerformance = false;
@ -181,7 +187,10 @@ export class HomePageComponent implements OnDestroy, OnInit {
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
}); });
this.dataService.fetchPortfolioOverview().subscribe((response) => { this.dataService
.fetchPortfolioOverview()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((response) => {
this.overview = response; this.overview = response;
this.isLoadingOverview = false; this.isLoadingOverview = false;
@ -190,6 +199,7 @@ export class HomePageComponent implements OnDestroy, OnInit {
this.dataService this.dataService
.fetchPortfolioPositions({ range: this.dateRange }) .fetchPortfolioPositions({ range: this.dateRange })
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((response) => { .subscribe((response) => {
this.positions = response; this.positions = response;
this.hasPositions = this.hasPositions =

6
apps/client/src/app/pages/landing/landing-page.component.ts

@ -6,6 +6,7 @@ import { TokenStorageService } from '@ghostfolio/client/services/token-storage.s
import { WebAuthnService } from '@ghostfolio/client/services/web-authn.service'; import { WebAuthnService } from '@ghostfolio/client/services/web-authn.service';
import { format } from 'date-fns'; import { format } from 'date-fns';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({ @Component({
selector: 'gf-landing-page', selector: 'gf-landing-page',
@ -33,7 +34,10 @@ export class LandingPageComponent implements OnDestroy, OnInit {
* Initializes the controller * Initializes the controller
*/ */
public ngOnInit() { public ngOnInit() {
this.dataService.fetchInfo().subscribe(({ demoAuthToken }) => { this.dataService
.fetchInfo()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ demoAuthToken }) => {
this.demoAuthToken = demoAuthToken; this.demoAuthToken = demoAuthToken;
this.initializeLineChart(); this.initializeLineChart();

4
apps/client/src/app/pages/pricing/pricing-page.component.ts

@ -1,4 +1,4 @@
import { ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
import { UserService } from '@ghostfolio/client/services/user/user.service'; import { UserService } from '@ghostfolio/client/services/user/user.service';
import { baseCurrency } from '@ghostfolio/common/config'; import { baseCurrency } from '@ghostfolio/common/config';
@ -11,7 +11,7 @@ import { takeUntil } from 'rxjs/operators';
templateUrl: './pricing-page.html', templateUrl: './pricing-page.html',
styleUrls: ['./pricing-page.scss'] styleUrls: ['./pricing-page.scss']
}) })
export class PricingPageComponent implements OnInit { export class PricingPageComponent implements OnDestroy, OnInit {
public baseCurrency = baseCurrency; public baseCurrency = baseCurrency;
public coupon: number; public coupon: number;
public isLoggedIn: boolean; public isLoggedIn: boolean;

6
apps/client/src/app/pages/register/register-page.component.ts

@ -43,6 +43,7 @@ export class RegisterPageComponent implements OnDestroy, OnInit {
public ngOnInit() { public ngOnInit() {
this.dataService this.dataService
.fetchInfo() .fetchInfo()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ demoAuthToken, globalPermissions }) => { .subscribe(({ demoAuthToken, globalPermissions }) => {
this.demoAuthToken = demoAuthToken; this.demoAuthToken = demoAuthToken;
this.hasPermissionForSocialLogin = hasPermission( this.hasPermissionForSocialLogin = hasPermission(
@ -76,7 +77,10 @@ export class RegisterPageComponent implements OnDestroy, OnInit {
width: '30rem' width: '30rem'
}); });
dialogRef.afterClosed().subscribe((data) => { dialogRef
.afterClosed()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((data) => {
if (data?.authToken) { if (data?.authToken) {
this.tokenStorageService.saveToken(authToken, true); this.tokenStorageService.saveToken(authToken, true);

4
apps/client/src/app/pages/tools/report/report-page.component.ts

@ -1,4 +1,4 @@
import { ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { DataService } from '@ghostfolio/client/services/data.service'; import { DataService } from '@ghostfolio/client/services/data.service';
import { PortfolioReportRule } from '@ghostfolio/common/interfaces'; import { PortfolioReportRule } from '@ghostfolio/common/interfaces';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
@ -9,7 +9,7 @@ import { takeUntil } from 'rxjs/operators';
templateUrl: './report-page.html', templateUrl: './report-page.html',
styleUrls: ['./report-page.scss'] styleUrls: ['./report-page.scss']
}) })
export class ReportPageComponent implements OnInit { export class ReportPageComponent implements OnDestroy, OnInit {
public accountClusterRiskRules: PortfolioReportRule[]; public accountClusterRiskRules: PortfolioReportRule[];
public currencyClusterRiskRules: PortfolioReportRule[]; public currencyClusterRiskRules: PortfolioReportRule[];
public feeRules: PortfolioReportRule[]; public feeRules: PortfolioReportRule[];

13
apps/client/src/app/pages/transactions/create-or-update-transaction-dialog/create-or-update-transaction-dialog.component.ts

@ -2,7 +2,8 @@ import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
Inject Inject,
OnDestroy
} from '@angular/core'; } from '@angular/core';
import { FormControl, Validators } from '@angular/forms'; import { FormControl, Validators } from '@angular/forms';
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
@ -28,7 +29,7 @@ import { CreateOrUpdateTransactionDialogParams } from './interfaces/interfaces';
styleUrls: ['./create-or-update-transaction-dialog.scss'], styleUrls: ['./create-or-update-transaction-dialog.scss'],
templateUrl: 'create-or-update-transaction-dialog.html' templateUrl: 'create-or-update-transaction-dialog.html'
}) })
export class CreateOrUpdateTransactionDialog { export class CreateOrUpdateTransactionDialog implements OnDestroy {
public currencies: Currency[] = []; public currencies: Currency[] = [];
public currentMarketPrice = null; public currentMarketPrice = null;
public filteredLookupItems: Observable<LookupItem[]>; public filteredLookupItems: Observable<LookupItem[]>;
@ -49,9 +50,14 @@ export class CreateOrUpdateTransactionDialog {
) {} ) {}
ngOnInit() { ngOnInit() {
this.dataService.fetchInfo().subscribe(({ currencies, platforms }) => { this.dataService
.fetchInfo()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ currencies, platforms }) => {
this.currencies = currencies; this.currencies = currencies;
this.platforms = platforms; this.platforms = platforms;
this.changeDetectorRef.markForCheck();
}); });
this.filteredLookupItems = this.searchSymbolCtrl.valueChanges.pipe( this.filteredLookupItems = this.searchSymbolCtrl.valueChanges.pipe(
@ -73,6 +79,7 @@ export class CreateOrUpdateTransactionDialog {
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ marketPrice }) => { .subscribe(({ marketPrice }) => {
this.currentMarketPrice = marketPrice; this.currentMarketPrice = marketPrice;
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
}); });
} }

30
apps/client/src/app/pages/transactions/transactions-page.component.ts

@ -1,4 +1,4 @@
import { ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto'; import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
@ -20,7 +20,7 @@ import { CreateOrUpdateTransactionDialog } from './create-or-update-transaction-
templateUrl: './transactions-page.html', templateUrl: './transactions-page.html',
styleUrls: ['./transactions-page.scss'] styleUrls: ['./transactions-page.scss']
}) })
export class TransactionsPageComponent implements OnInit { export class TransactionsPageComponent implements OnDestroy, OnInit {
public deviceType: string; public deviceType: string;
public hasImpersonationId: boolean; public hasImpersonationId: boolean;
public hasPermissionToCreateOrder: boolean; public hasPermissionToCreateOrder: boolean;
@ -71,6 +71,7 @@ export class TransactionsPageComponent implements OnInit {
this.impersonationStorageService this.impersonationStorageService
.onChangeHasImpersonation() .onChangeHasImpersonation()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((aId) => { .subscribe((aId) => {
this.hasImpersonationId = !!aId; this.hasImpersonationId = !!aId;
}); });
@ -98,7 +99,10 @@ export class TransactionsPageComponent implements OnInit {
} }
public fetchOrders() { public fetchOrders() {
this.dataService.fetchOrders().subscribe((response) => { this.dataService
.fetchOrders()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((response) => {
this.transactions = response; this.transactions = response;
if (this.transactions?.length <= 0) { if (this.transactions?.length <= 0) {
@ -114,7 +118,10 @@ export class TransactionsPageComponent implements OnInit {
} }
public onDeleteTransaction(aId: string) { public onDeleteTransaction(aId: string) {
this.dataService.deleteOrder(aId).subscribe({ this.dataService
.deleteOrder(aId)
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe({
next: () => { next: () => {
this.fetchOrders(); this.fetchOrders();
} }
@ -159,11 +166,17 @@ export class TransactionsPageComponent implements OnInit {
width: this.deviceType === 'mobile' ? '100vw' : '50rem' width: this.deviceType === 'mobile' ? '100vw' : '50rem'
}); });
dialogRef.afterClosed().subscribe((data: any) => { dialogRef
.afterClosed()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((data: any) => {
const transaction: UpdateOrderDto = data?.transaction; const transaction: UpdateOrderDto = data?.transaction;
if (transaction) { if (transaction) {
this.dataService.putOrder(transaction).subscribe({ this.dataService
.putOrder(transaction)
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe({
next: () => { next: () => {
this.fetchOrders(); this.fetchOrders();
} }
@ -203,7 +216,10 @@ export class TransactionsPageComponent implements OnInit {
width: this.deviceType === 'mobile' ? '100vw' : '50rem' width: this.deviceType === 'mobile' ? '100vw' : '50rem'
}); });
dialogRef.afterClosed().subscribe((data: any) => { dialogRef
.afterClosed()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((data: any) => {
const transaction: CreateOrderDto = data?.transaction; const transaction: CreateOrderDto = data?.transaction;
if (transaction) { if (transaction) {

23
apps/client/src/app/pages/webauthn/webauthn-page.component.ts

@ -1,16 +1,20 @@
import { ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service'; import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';
import { WebAuthnService } from '@ghostfolio/client/services/web-authn.service'; import { WebAuthnService } from '@ghostfolio/client/services/web-authn.service';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({ @Component({
selector: 'gf-webauthn-page', selector: 'gf-webauthn-page',
templateUrl: './webauthn-page.html', templateUrl: './webauthn-page.html',
styleUrls: ['./webauthn-page.scss'] styleUrls: ['./webauthn-page.scss']
}) })
export class WebauthnPageComponent implements OnInit { export class WebauthnPageComponent implements OnDestroy, OnInit {
public hasError = false; public hasError = false;
private unsubscribeSubject = new Subject<void>();
constructor( constructor(
private changeDetectorRef: ChangeDetectorRef, private changeDetectorRef: ChangeDetectorRef,
private router: Router, private router: Router,
@ -23,7 +27,10 @@ export class WebauthnPageComponent implements OnInit {
} }
public deregisterDevice() { public deregisterDevice() {
this.webAuthnService.deregister().subscribe(() => { this.webAuthnService
.deregister()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(() => {
this.router.navigate(['/']); this.router.navigate(['/']);
}); });
} }
@ -31,7 +38,10 @@ export class WebauthnPageComponent implements OnInit {
public signIn() { public signIn() {
this.hasError = false; this.hasError = false;
this.webAuthnService.login().subscribe( this.webAuthnService
.login()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(
({ authToken }) => { ({ authToken }) => {
this.tokenStorageService.saveToken(authToken, false); this.tokenStorageService.saveToken(authToken, false);
this.router.navigate(['/']); this.router.navigate(['/']);
@ -43,4 +53,9 @@ export class WebauthnPageComponent implements OnInit {
} }
); );
} }
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
} }

3
apps/client/src/app/pages/zen/zen-page.component.ts

@ -61,6 +61,7 @@ export class ZenPageComponent implements OnDestroy, OnInit {
this.impersonationStorageService this.impersonationStorageService
.onChangeHasImpersonation() .onChangeHasImpersonation()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((aId) => { .subscribe((aId) => {
this.hasImpersonationId = !!aId; this.hasImpersonationId = !!aId;
}); });
@ -78,6 +79,7 @@ export class ZenPageComponent implements OnDestroy, OnInit {
this.dataService this.dataService
.fetchChart({ range: this.dateRange }) .fetchChart({ range: this.dateRange })
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((chartData) => { .subscribe((chartData) => {
this.historicalDataItems = chartData.map((chartDataItem) => { this.historicalDataItems = chartData.map((chartDataItem) => {
return { return {
@ -91,6 +93,7 @@ export class ZenPageComponent implements OnDestroy, OnInit {
this.dataService this.dataService
.fetchPortfolioPerformance({ range: this.dateRange }) .fetchPortfolioPerformance({ range: this.dateRange })
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((response) => { .subscribe((response) => {
this.performance = response; this.performance = response;
this.isLoadingPerformance = false; this.isLoadingPerformance = false;

Loading…
Cancel
Save