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

pull/205/head
Thomas 3 years ago committed by GitHub
parent d9ea255c17
commit 1135a5b335
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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
### Fixed
- Fixed rendering of currency and platform in dialogs (account and transaction)
## 1.24.0 - 07.07.2021
### Added

@ -52,9 +52,12 @@ export class AppComponent implements OnDestroy, OnInit {
public ngOnInit() {
this.deviceType = this.deviceService.getDeviceInfo().deviceType;
this.dataService.fetchInfo().subscribe((info) => {
this.info = info;
});
this.dataService
.fetchInfo()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((info) => {
this.info = info;
});
this.router.events
.pipe(filter((event) => event instanceof NavigationEnd))

@ -51,6 +51,7 @@ export class HeaderComponent implements OnChanges {
) {
this.impersonationStorageService
.onChangeHasImpersonation()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((id) => {
this.impersonationId = id;
});
@ -98,23 +99,26 @@ export class HeaderComponent implements OnChanges {
width: '30rem'
});
dialogRef.afterClosed().subscribe((data) => {
if (data?.accessToken) {
this.dataService
.loginAnonymous(data?.accessToken)
.pipe(
catchError(() => {
alert('Oops! Incorrect Security Token.');
return EMPTY;
}),
takeUntil(this.unsubscribeSubject)
)
.subscribe(({ authToken }) => {
this.setToken(authToken);
});
}
});
dialogRef
.afterClosed()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((data) => {
if (data?.accessToken) {
this.dataService
.loginAnonymous(data?.accessToken)
.pipe(
catchError(() => {
alert('Oops! Incorrect Security Token.');
return EMPTY;
}),
takeUntil(this.unsubscribeSubject)
)
.subscribe(({ authToken }) => {
this.setToken(authToken);
});
}
});
}
public setToken(aToken: string) {
@ -125,4 +129,9 @@ export class HeaderComponent implements OnChanges {
this.router.navigate(['/']);
}
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
}

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

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

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

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

@ -89,18 +89,20 @@ export class TransactionsTableComponent
}
});
this.searchControl.valueChanges.subscribe((keyword) => {
if (keyword) {
const filterValue = keyword.toLowerCase();
this.filters$.next(
this.allFilters.filter(
(filter) => filter.toLowerCase().indexOf(filterValue) === 0
)
);
} else {
this.filters$.next(this.allFilters);
}
});
this.searchControl.valueChanges
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((keyword) => {
if (keyword) {
const filterValue = keyword.toLowerCase();
this.filters$.next(
this.allFilters.filter(
(filter) => filter.toLowerCase().indexOf(filterValue) === 0
)
);
} else {
this.filters$.next(this.allFilters);
}
});
}
public addKeyword({ input, value }: MatChipInputEvent): void {
@ -223,9 +225,12 @@ export class TransactionsTableComponent
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
});
dialogRef.afterClosed().subscribe(() => {
this.router.navigate(['.'], { relativeTo: this.route });
});
dialogRef
.afterClosed()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(() => {
this.router.navigate(['.'], { relativeTo: this.route });
});
}
public ngOnDestroy() {

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

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

@ -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 { ActivatedRoute, Router } from '@angular/router';
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',
styleUrls: ['./accounts-page.scss']
})
export class AccountsPageComponent implements OnInit {
export class AccountsPageComponent implements OnDestroy, OnInit {
public accounts: AccountModel[];
public deviceType: string;
public hasImpersonationId: boolean;
@ -71,6 +71,7 @@ export class AccountsPageComponent implements OnInit {
this.impersonationStorageService
.onChangeHasImpersonation()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((aId) => {
this.hasImpersonationId = !!aId;
});
@ -98,23 +99,29 @@ export class AccountsPageComponent implements OnInit {
}
public fetchAccounts() {
this.dataService.fetchAccounts().subscribe((response) => {
this.accounts = response;
this.dataService
.fetchAccounts()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((response) => {
this.accounts = response;
if (this.accounts?.length <= 0) {
this.router.navigate([], { queryParams: { createDialog: true } });
}
if (this.accounts?.length <= 0) {
this.router.navigate([], { queryParams: { createDialog: true } });
}
this.changeDetectorRef.markForCheck();
});
this.changeDetectorRef.markForCheck();
});
}
public onDeleteAccount(aId: string) {
this.dataService.deleteAccount(aId).subscribe({
next: () => {
this.fetchAccounts();
}
});
this.dataService
.deleteAccount(aId)
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe({
next: () => {
this.fetchAccounts();
}
});
}
public onUpdateAccount(aAccount: AccountModel) {
@ -146,19 +153,25 @@ export class AccountsPageComponent implements OnInit {
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
});
dialogRef.afterClosed().subscribe((data: any) => {
const account: UpdateAccountDto = data?.account;
if (account) {
this.dataService.putAccount(account).subscribe({
next: () => {
this.fetchAccounts();
}
});
}
dialogRef
.afterClosed()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((data: any) => {
const account: UpdateAccountDto = data?.account;
if (account) {
this.dataService
.putAccount(account)
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe({
next: () => {
this.fetchAccounts();
}
});
}
this.router.navigate(['.'], { relativeTo: this.route });
});
this.router.navigate(['.'], { relativeTo: this.route });
});
}
public ngOnDestroy() {
@ -181,18 +194,24 @@ export class AccountsPageComponent implements OnInit {
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
});
dialogRef.afterClosed().subscribe((data: any) => {
const account: CreateAccountDto = data?.account;
if (account) {
this.dataService.postAccount(account).subscribe({
next: () => {
this.fetchAccounts();
}
});
}
dialogRef
.afterClosed()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((data: any) => {
const account: CreateAccountDto = data?.account;
if (account) {
this.dataService
.postAccount(account)
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe({
next: () => {
this.fetchAccounts();
}
});
}
this.router.navigate(['.'], { relativeTo: this.route });
});
this.router.navigate(['.'], { relativeTo: this.route });
});
}
}

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

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

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

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

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

@ -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 { UserService } from '@ghostfolio/client/services/user/user.service';
import { baseCurrency } from '@ghostfolio/common/config';
@ -11,7 +11,7 @@ import { takeUntil } from 'rxjs/operators';
templateUrl: './pricing-page.html',
styleUrls: ['./pricing-page.scss']
})
export class PricingPageComponent implements OnInit {
export class PricingPageComponent implements OnDestroy, OnInit {
public baseCurrency = baseCurrency;
public coupon: number;
public isLoggedIn: boolean;

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

@ -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 { PortfolioReportRule } from '@ghostfolio/common/interfaces';
import { Subject } from 'rxjs';
@ -9,7 +9,7 @@ import { takeUntil } from 'rxjs/operators';
templateUrl: './report-page.html',
styleUrls: ['./report-page.scss']
})
export class ReportPageComponent implements OnInit {
export class ReportPageComponent implements OnDestroy, OnInit {
public accountClusterRiskRules: PortfolioReportRule[];
public currencyClusterRiskRules: PortfolioReportRule[];
public feeRules: PortfolioReportRule[];

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

@ -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 { ActivatedRoute, Router } from '@angular/router';
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',
styleUrls: ['./transactions-page.scss']
})
export class TransactionsPageComponent implements OnInit {
export class TransactionsPageComponent implements OnDestroy, OnInit {
public deviceType: string;
public hasImpersonationId: boolean;
public hasPermissionToCreateOrder: boolean;
@ -71,6 +71,7 @@ export class TransactionsPageComponent implements OnInit {
this.impersonationStorageService
.onChangeHasImpersonation()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((aId) => {
this.hasImpersonationId = !!aId;
});
@ -98,15 +99,18 @@ export class TransactionsPageComponent implements OnInit {
}
public fetchOrders() {
this.dataService.fetchOrders().subscribe((response) => {
this.transactions = response;
this.dataService
.fetchOrders()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((response) => {
this.transactions = response;
if (this.transactions?.length <= 0) {
this.router.navigate([], { queryParams: { createDialog: true } });
}
if (this.transactions?.length <= 0) {
this.router.navigate([], { queryParams: { createDialog: true } });
}
this.changeDetectorRef.markForCheck();
});
this.changeDetectorRef.markForCheck();
});
}
public onCloneTransaction(aTransaction: OrderModel) {
@ -114,11 +118,14 @@ export class TransactionsPageComponent implements OnInit {
}
public onDeleteTransaction(aId: string) {
this.dataService.deleteOrder(aId).subscribe({
next: () => {
this.fetchOrders();
}
});
this.dataService
.deleteOrder(aId)
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe({
next: () => {
this.fetchOrders();
}
});
}
public onUpdateTransaction(aTransaction: OrderModel) {
@ -159,19 +166,25 @@ export class TransactionsPageComponent implements OnInit {
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
});
dialogRef.afterClosed().subscribe((data: any) => {
const transaction: UpdateOrderDto = data?.transaction;
if (transaction) {
this.dataService.putOrder(transaction).subscribe({
next: () => {
this.fetchOrders();
}
});
}
dialogRef
.afterClosed()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((data: any) => {
const transaction: UpdateOrderDto = data?.transaction;
if (transaction) {
this.dataService
.putOrder(transaction)
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe({
next: () => {
this.fetchOrders();
}
});
}
this.router.navigate(['.'], { relativeTo: this.route });
});
this.router.navigate(['.'], { relativeTo: this.route });
});
}
public ngOnDestroy() {
@ -203,18 +216,21 @@ export class TransactionsPageComponent implements OnInit {
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
});
dialogRef.afterClosed().subscribe((data: any) => {
const transaction: CreateOrderDto = data?.transaction;
if (transaction) {
this.dataService.postOrder(transaction).subscribe({
next: () => {
this.fetchOrders();
}
});
}
dialogRef
.afterClosed()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe((data: any) => {
const transaction: CreateOrderDto = data?.transaction;
if (transaction) {
this.dataService.postOrder(transaction).subscribe({
next: () => {
this.fetchOrders();
}
});
}
this.router.navigate(['.'], { relativeTo: this.route });
});
this.router.navigate(['.'], { relativeTo: this.route });
});
}
}

@ -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 { TokenStorageService } from '@ghostfolio/client/services/token-storage.service';
import { WebAuthnService } from '@ghostfolio/client/services/web-authn.service';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'gf-webauthn-page',
templateUrl: './webauthn-page.html',
styleUrls: ['./webauthn-page.scss']
})
export class WebauthnPageComponent implements OnInit {
export class WebauthnPageComponent implements OnDestroy, OnInit {
public hasError = false;
private unsubscribeSubject = new Subject<void>();
constructor(
private changeDetectorRef: ChangeDetectorRef,
private router: Router,
@ -23,24 +27,35 @@ export class WebauthnPageComponent implements OnInit {
}
public deregisterDevice() {
this.webAuthnService.deregister().subscribe(() => {
this.router.navigate(['/']);
});
this.webAuthnService
.deregister()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(() => {
this.router.navigate(['/']);
});
}
public signIn() {
this.hasError = false;
this.webAuthnService.login().subscribe(
({ authToken }) => {
this.tokenStorageService.saveToken(authToken, false);
this.router.navigate(['/']);
},
(error) => {
console.error(error);
this.hasError = true;
this.changeDetectorRef.markForCheck();
}
);
this.webAuthnService
.login()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(
({ authToken }) => {
this.tokenStorageService.saveToken(authToken, false);
this.router.navigate(['/']);
},
(error) => {
console.error(error);
this.hasError = true;
this.changeDetectorRef.markForCheck();
}
);
}
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
}

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

Loading…
Cancel
Save