Fix rendering of currency and platform in dialogs and clean up observables (#202)
This commit is contained in:
parent
d9ea255c17
commit
1135a5b335
@ -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
|
||||||
|
@ -52,9 +52,12 @@ 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
|
||||||
this.info = info;
|
.fetchInfo()
|
||||||
});
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
|
.subscribe((info) => {
|
||||||
|
this.info = info;
|
||||||
|
});
|
||||||
|
|
||||||
this.router.events
|
this.router.events
|
||||||
.pipe(filter((event) => event instanceof NavigationEnd))
|
.pipe(filter((event) => event instanceof NavigationEnd))
|
||||||
|
@ -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,23 +99,26 @@ export class HeaderComponent implements OnChanges {
|
|||||||
width: '30rem'
|
width: '30rem'
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe((data) => {
|
dialogRef
|
||||||
if (data?.accessToken) {
|
.afterClosed()
|
||||||
this.dataService
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.loginAnonymous(data?.accessToken)
|
.subscribe((data) => {
|
||||||
.pipe(
|
if (data?.accessToken) {
|
||||||
catchError(() => {
|
this.dataService
|
||||||
alert('Oops! Incorrect Security Token.');
|
.loginAnonymous(data?.accessToken)
|
||||||
|
.pipe(
|
||||||
|
catchError(() => {
|
||||||
|
alert('Oops! Incorrect Security Token.');
|
||||||
|
|
||||||
return EMPTY;
|
return EMPTY;
|
||||||
}),
|
}),
|
||||||
takeUntil(this.unsubscribeSubject)
|
takeUntil(this.unsubscribeSubject)
|
||||||
)
|
)
|
||||||
.subscribe(({ authToken }) => {
|
.subscribe(({ authToken }) => {
|
||||||
this.setToken(authToken);
|
this.setToken(authToken);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public setToken(aToken: string) {
|
public setToken(aToken: string) {
|
||||||
@ -125,4 +129,9 @@ export class HeaderComponent implements OnChanges {
|
|||||||
|
|
||||||
this.router.navigate(['/']);
|
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 { 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,8 +72,11 @@ export class PositionComponent implements OnDestroy, OnInit {
|
|||||||
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe(() => {
|
dialogRef
|
||||||
this.router.navigate(['.'], { relativeTo: this.route });
|
.afterClosed()
|
||||||
});
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
|
.subscribe(() => {
|
||||||
|
this.router.navigate(['.'], { relativeTo: this.route });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,9 +135,12 @@ export class PositionsTableComponent implements OnChanges, OnInit {
|
|||||||
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe(() => {
|
dialogRef
|
||||||
this.router.navigate(['.'], { relativeTo: this.route });
|
.afterClosed()
|
||||||
});
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
|
.subscribe(() => {
|
||||||
|
this.router.navigate(['.'], { relativeTo: this.route });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnDestroy() {
|
public ngOnDestroy() {
|
||||||
|
@ -89,18 +89,20 @@ export class TransactionsTableComponent
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.searchControl.valueChanges.subscribe((keyword) => {
|
this.searchControl.valueChanges
|
||||||
if (keyword) {
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
const filterValue = keyword.toLowerCase();
|
.subscribe((keyword) => {
|
||||||
this.filters$.next(
|
if (keyword) {
|
||||||
this.allFilters.filter(
|
const filterValue = keyword.toLowerCase();
|
||||||
(filter) => filter.toLowerCase().indexOf(filterValue) === 0
|
this.filters$.next(
|
||||||
)
|
this.allFilters.filter(
|
||||||
);
|
(filter) => filter.toLowerCase().indexOf(filterValue) === 0
|
||||||
} else {
|
)
|
||||||
this.filters$.next(this.allFilters);
|
);
|
||||||
}
|
} else {
|
||||||
});
|
this.filters$.next(this.allFilters);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public addKeyword({ input, value }: MatChipInputEvent): void {
|
public addKeyword({ input, value }: MatChipInputEvent): void {
|
||||||
@ -223,9 +225,12 @@ export class TransactionsTableComponent
|
|||||||
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe(() => {
|
dialogRef
|
||||||
this.router.navigate(['.'], { relativeTo: this.route });
|
.afterClosed()
|
||||||
});
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
|
.subscribe(() => {
|
||||||
|
this.router.navigate(['.'], { relativeTo: this.route });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnDestroy() {
|
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 { 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,
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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,23 +99,29 @@ export class AccountsPageComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public fetchAccounts() {
|
public fetchAccounts() {
|
||||||
this.dataService.fetchAccounts().subscribe((response) => {
|
this.dataService
|
||||||
this.accounts = response;
|
.fetchAccounts()
|
||||||
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
|
.subscribe((response) => {
|
||||||
|
this.accounts = response;
|
||||||
|
|
||||||
if (this.accounts?.length <= 0) {
|
if (this.accounts?.length <= 0) {
|
||||||
this.router.navigate([], { queryParams: { createDialog: true } });
|
this.router.navigate([], { queryParams: { createDialog: true } });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.changeDetectorRef.markForCheck();
|
this.changeDetectorRef.markForCheck();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public onDeleteAccount(aId: string) {
|
public onDeleteAccount(aId: string) {
|
||||||
this.dataService.deleteAccount(aId).subscribe({
|
this.dataService
|
||||||
next: () => {
|
.deleteAccount(aId)
|
||||||
this.fetchAccounts();
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
}
|
.subscribe({
|
||||||
});
|
next: () => {
|
||||||
|
this.fetchAccounts();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public onUpdateAccount(aAccount: AccountModel) {
|
public onUpdateAccount(aAccount: AccountModel) {
|
||||||
@ -146,19 +153,25 @@ export class AccountsPageComponent implements OnInit {
|
|||||||
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe((data: any) => {
|
dialogRef
|
||||||
const account: UpdateAccountDto = data?.account;
|
.afterClosed()
|
||||||
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
|
.subscribe((data: any) => {
|
||||||
|
const account: UpdateAccountDto = data?.account;
|
||||||
|
|
||||||
if (account) {
|
if (account) {
|
||||||
this.dataService.putAccount(account).subscribe({
|
this.dataService
|
||||||
next: () => {
|
.putAccount(account)
|
||||||
this.fetchAccounts();
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
}
|
.subscribe({
|
||||||
});
|
next: () => {
|
||||||
}
|
this.fetchAccounts();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.router.navigate(['.'], { relativeTo: this.route });
|
this.router.navigate(['.'], { relativeTo: this.route });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnDestroy() {
|
public ngOnDestroy() {
|
||||||
@ -181,18 +194,24 @@ export class AccountsPageComponent implements OnInit {
|
|||||||
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe((data: any) => {
|
dialogRef
|
||||||
const account: CreateAccountDto = data?.account;
|
.afterClosed()
|
||||||
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
|
.subscribe((data: any) => {
|
||||||
|
const account: CreateAccountDto = data?.account;
|
||||||
|
|
||||||
if (account) {
|
if (account) {
|
||||||
this.dataService.postAccount(account).subscribe({
|
this.dataService
|
||||||
next: () => {
|
.postAccount(account)
|
||||||
this.fetchAccounts();
|
.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 { 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,23 +20,29 @@ 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
|
||||||
this.currencies = currencies;
|
.fetchInfo()
|
||||||
this.platforms = platforms;
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
});
|
.subscribe(({ currencies, platforms }) => {
|
||||||
|
this.currencies = currencies;
|
||||||
|
this.platforms = platforms;
|
||||||
|
|
||||||
|
this.changeDetectorRef.markForCheck();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public onCancel(): void {
|
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 { 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,11 +58,14 @@ export class AdminPageComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onFlushCache() {
|
public onFlushCache() {
|
||||||
this.cacheService.flush().subscribe(() => {
|
this.cacheService
|
||||||
setTimeout(() => {
|
.flush()
|
||||||
window.location.reload();
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
}, 300);
|
.subscribe(() => {
|
||||||
});
|
setTimeout(() => {
|
||||||
|
window.location.reload();
|
||||||
|
}, 300);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public onGatherMax() {
|
public onGatherMax() {
|
||||||
@ -71,11 +74,14 @@ export class AdminPageComponent implements OnInit {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (confirmation === true) {
|
if (confirmation === true) {
|
||||||
this.adminService.gatherMax().subscribe(() => {
|
this.adminService
|
||||||
setTimeout(() => {
|
.gatherMax()
|
||||||
window.location.reload();
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
}, 300);
|
.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?');
|
const confirmation = confirm('Do you really want to delete this user?');
|
||||||
|
|
||||||
if (confirmation) {
|
if (confirmation) {
|
||||||
this.dataService.deleteUser(aId).subscribe({
|
this.dataService
|
||||||
next: () => {
|
.deleteUser(aId)
|
||||||
this.fetchAdminData();
|
.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 { 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,14 +30,21 @@ export class AuthPageComponent implements OnInit {
|
|||||||
* Initializes the controller
|
* Initializes the controller
|
||||||
*/
|
*/
|
||||||
public ngOnInit() {
|
public ngOnInit() {
|
||||||
this.route.params.subscribe((params) => {
|
this.route.params
|
||||||
const jwt = params['jwt'];
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
this.tokenStorageService.saveToken(
|
.subscribe((params) => {
|
||||||
jwt,
|
const jwt = params['jwt'];
|
||||||
this.settingsStorageService.getSetting(STAY_SIGNED_IN) === 'true'
|
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
|
this.impersonationStorageService
|
||||||
.onChangeHasImpersonation()
|
.onChangeHasImpersonation()
|
||||||
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe((aId) => {
|
.subscribe((aId) => {
|
||||||
this.hasImpersonationId = !!aId;
|
this.hasImpersonationId = !!aId;
|
||||||
});
|
});
|
||||||
@ -148,9 +149,12 @@ export class HomePageComponent implements OnDestroy, OnInit {
|
|||||||
width: '50rem'
|
width: '50rem'
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe(() => {
|
dialogRef
|
||||||
this.router.navigate(['.'], { relativeTo: this.route });
|
.afterClosed()
|
||||||
});
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
|
.subscribe(() => {
|
||||||
|
this.router.navigate(['.'], { relativeTo: this.route });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private update() {
|
private update() {
|
||||||
@ -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,15 +187,19 @@ export class HomePageComponent implements OnDestroy, OnInit {
|
|||||||
this.changeDetectorRef.markForCheck();
|
this.changeDetectorRef.markForCheck();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.dataService.fetchPortfolioOverview().subscribe((response) => {
|
this.dataService
|
||||||
this.overview = response;
|
.fetchPortfolioOverview()
|
||||||
this.isLoadingOverview = false;
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
|
.subscribe((response) => {
|
||||||
|
this.overview = response;
|
||||||
|
this.isLoadingOverview = false;
|
||||||
|
|
||||||
this.changeDetectorRef.markForCheck();
|
this.changeDetectorRef.markForCheck();
|
||||||
});
|
});
|
||||||
|
|
||||||
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,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,13 +34,16 @@ export class LandingPageComponent implements OnDestroy, OnInit {
|
|||||||
* Initializes the controller
|
* Initializes the controller
|
||||||
*/
|
*/
|
||||||
public ngOnInit() {
|
public ngOnInit() {
|
||||||
this.dataService.fetchInfo().subscribe(({ demoAuthToken }) => {
|
this.dataService
|
||||||
this.demoAuthToken = demoAuthToken;
|
.fetchInfo()
|
||||||
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
|
.subscribe(({ demoAuthToken }) => {
|
||||||
|
this.demoAuthToken = demoAuthToken;
|
||||||
|
|
||||||
this.initializeLineChart();
|
this.initializeLineChart();
|
||||||
|
|
||||||
this.changeDetectorRef.markForCheck();
|
this.changeDetectorRef.markForCheck();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public initializeLineChart() {
|
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 { 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;
|
||||||
|
@ -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,13 +77,16 @@ export class RegisterPageComponent implements OnDestroy, OnInit {
|
|||||||
width: '30rem'
|
width: '30rem'
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe((data) => {
|
dialogRef
|
||||||
if (data?.authToken) {
|
.afterClosed()
|
||||||
this.tokenStorageService.saveToken(authToken, true);
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
|
.subscribe((data) => {
|
||||||
|
if (data?.authToken) {
|
||||||
|
this.tokenStorageService.saveToken(authToken, true);
|
||||||
|
|
||||||
this.router.navigate(['/']);
|
this.router.navigate(['/']);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnDestroy() {
|
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 { 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[];
|
||||||
|
@ -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,10 +50,15 @@ export class CreateOrUpdateTransactionDialog {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.dataService.fetchInfo().subscribe(({ currencies, platforms }) => {
|
this.dataService
|
||||||
this.currencies = currencies;
|
.fetchInfo()
|
||||||
this.platforms = platforms;
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
});
|
.subscribe(({ currencies, platforms }) => {
|
||||||
|
this.currencies = currencies;
|
||||||
|
this.platforms = platforms;
|
||||||
|
|
||||||
|
this.changeDetectorRef.markForCheck();
|
||||||
|
});
|
||||||
|
|
||||||
this.filteredLookupItems = this.searchSymbolCtrl.valueChanges.pipe(
|
this.filteredLookupItems = this.searchSymbolCtrl.valueChanges.pipe(
|
||||||
startWith(''),
|
startWith(''),
|
||||||
@ -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();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -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,15 +99,18 @@ export class TransactionsPageComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public fetchOrders() {
|
public fetchOrders() {
|
||||||
this.dataService.fetchOrders().subscribe((response) => {
|
this.dataService
|
||||||
this.transactions = response;
|
.fetchOrders()
|
||||||
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
|
.subscribe((response) => {
|
||||||
|
this.transactions = response;
|
||||||
|
|
||||||
if (this.transactions?.length <= 0) {
|
if (this.transactions?.length <= 0) {
|
||||||
this.router.navigate([], { queryParams: { createDialog: true } });
|
this.router.navigate([], { queryParams: { createDialog: true } });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.changeDetectorRef.markForCheck();
|
this.changeDetectorRef.markForCheck();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public onCloneTransaction(aTransaction: OrderModel) {
|
public onCloneTransaction(aTransaction: OrderModel) {
|
||||||
@ -114,11 +118,14 @@ export class TransactionsPageComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onDeleteTransaction(aId: string) {
|
public onDeleteTransaction(aId: string) {
|
||||||
this.dataService.deleteOrder(aId).subscribe({
|
this.dataService
|
||||||
next: () => {
|
.deleteOrder(aId)
|
||||||
this.fetchOrders();
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
}
|
.subscribe({
|
||||||
});
|
next: () => {
|
||||||
|
this.fetchOrders();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public onUpdateTransaction(aTransaction: OrderModel) {
|
public onUpdateTransaction(aTransaction: OrderModel) {
|
||||||
@ -159,19 +166,25 @@ export class TransactionsPageComponent implements OnInit {
|
|||||||
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe((data: any) => {
|
dialogRef
|
||||||
const transaction: UpdateOrderDto = data?.transaction;
|
.afterClosed()
|
||||||
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
|
.subscribe((data: any) => {
|
||||||
|
const transaction: UpdateOrderDto = data?.transaction;
|
||||||
|
|
||||||
if (transaction) {
|
if (transaction) {
|
||||||
this.dataService.putOrder(transaction).subscribe({
|
this.dataService
|
||||||
next: () => {
|
.putOrder(transaction)
|
||||||
this.fetchOrders();
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
}
|
.subscribe({
|
||||||
});
|
next: () => {
|
||||||
}
|
this.fetchOrders();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.router.navigate(['.'], { relativeTo: this.route });
|
this.router.navigate(['.'], { relativeTo: this.route });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnDestroy() {
|
public ngOnDestroy() {
|
||||||
@ -203,18 +216,21 @@ export class TransactionsPageComponent implements OnInit {
|
|||||||
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe((data: any) => {
|
dialogRef
|
||||||
const transaction: CreateOrderDto = data?.transaction;
|
.afterClosed()
|
||||||
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
|
.subscribe((data: any) => {
|
||||||
|
const transaction: CreateOrderDto = data?.transaction;
|
||||||
|
|
||||||
if (transaction) {
|
if (transaction) {
|
||||||
this.dataService.postOrder(transaction).subscribe({
|
this.dataService.postOrder(transaction).subscribe({
|
||||||
next: () => {
|
next: () => {
|
||||||
this.fetchOrders();
|
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 { 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,24 +27,35 @@ export class WebauthnPageComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public deregisterDevice() {
|
public deregisterDevice() {
|
||||||
this.webAuthnService.deregister().subscribe(() => {
|
this.webAuthnService
|
||||||
this.router.navigate(['/']);
|
.deregister()
|
||||||
});
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
|
.subscribe(() => {
|
||||||
|
this.router.navigate(['/']);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public signIn() {
|
public signIn() {
|
||||||
this.hasError = false;
|
this.hasError = false;
|
||||||
|
|
||||||
this.webAuthnService.login().subscribe(
|
this.webAuthnService
|
||||||
({ authToken }) => {
|
.login()
|
||||||
this.tokenStorageService.saveToken(authToken, false);
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
this.router.navigate(['/']);
|
.subscribe(
|
||||||
},
|
({ authToken }) => {
|
||||||
(error) => {
|
this.tokenStorageService.saveToken(authToken, false);
|
||||||
console.error(error);
|
this.router.navigate(['/']);
|
||||||
this.hasError = true;
|
},
|
||||||
this.changeDetectorRef.markForCheck();
|
(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
|
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…
x
Reference in New Issue
Block a user