From 7c1312477dcf5ca89e248f9415a060c2d3cb8f70 Mon Sep 17 00:00:00 2001 From: Thomas <4159106+dtslvr@users.noreply.github.com> Date: Thu, 20 May 2021 21:41:53 +0200 Subject: [PATCH] Start with implementation --- apps/client/src/app/app-routing.module.ts | 5 + .../pages/account/account-page.component.ts | 15 +++ .../src/app/pages/account/account-page.html | 16 ++- .../src/app/pages/home/home-page.component.ts | 10 +- .../src/app/pages/home/home-page.module.ts | 2 - .../app/pages/zen/zen-page-routing.module.ts | 15 +++ .../src/app/pages/zen/zen-page.component.ts | 104 ++++++++++++++++++ apps/client/src/app/pages/zen/zen-page.html | 25 +++++ .../src/app/pages/zen/zen-page.module.ts | 23 ++++ apps/client/src/app/pages/zen/zen-page.scss | 39 +++++++ .../lib/interfaces/user-settings.interface.ts | 1 + 11 files changed, 247 insertions(+), 8 deletions(-) create mode 100644 apps/client/src/app/pages/zen/zen-page-routing.module.ts create mode 100644 apps/client/src/app/pages/zen/zen-page.component.ts create mode 100644 apps/client/src/app/pages/zen/zen-page.html create mode 100644 apps/client/src/app/pages/zen/zen-page.module.ts create mode 100644 apps/client/src/app/pages/zen/zen-page.scss diff --git a/apps/client/src/app/app-routing.module.ts b/apps/client/src/app/app-routing.module.ts index 8c81bd1bd..e26e0dc9f 100644 --- a/apps/client/src/app/app-routing.module.ts +++ b/apps/client/src/app/app-routing.module.ts @@ -78,6 +78,11 @@ const routes: Routes = [ (m) => m.TransactionsPageModule ) }, + { + path: 'zen', + loadChildren: () => + import('./pages/zen/zen-page.module').then((m) => m.ZenPageModule) + }, { // wildcard, if requested url doesn't match any paths for routes defined // earlier diff --git a/apps/client/src/app/pages/account/account-page.component.ts b/apps/client/src/app/pages/account/account-page.component.ts index 957b4007c..a710ca2a5 100644 --- a/apps/client/src/app/pages/account/account-page.component.ts +++ b/apps/client/src/app/pages/account/account-page.component.ts @@ -51,6 +51,8 @@ export class AccountPageComponent implements OnDestroy, OnInit { this.dataService.fetchUser().subscribe((user) => { this.user = user; + this.user.settings.mode = 'ZEN'; + this.hasPermissionToUpdateUserSettings = hasPermission( this.user.permissions, permissions.updateUserSettings @@ -81,6 +83,19 @@ export class AccountPageComponent implements OnDestroy, OnInit { }); } + public onChangeMode({ value: mode }: { value: Currency }) { + /*this.dataService + .putUserSettings({ currency }) + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(() => { + this.dataService.fetchUser().subscribe((user) => { + this.user = user; + + this.cd.markForCheck(); + }); + });*/ + } + public ngOnDestroy() { this.unsubscribeSubject.next(); this.unsubscribeSubject.complete(); diff --git a/apps/client/src/app/pages/account/account-page.html b/apps/client/src/app/pages/account/account-page.html index e5bad92db..e3b6f830a 100644 --- a/apps/client/src/app/pages/account/account-page.html +++ b/apps/client/src/app/pages/account/account-page.html @@ -30,7 +30,7 @@
Settings
-
+ Base Currency
+
+ + Mode + + Default + Zen + + +
diff --git a/apps/client/src/app/pages/home/home-page.component.ts b/apps/client/src/app/pages/home/home-page.component.ts index ef2932096..515d1e63b 100644 --- a/apps/client/src/app/pages/home/home-page.component.ts +++ b/apps/client/src/app/pages/home/home-page.component.ts @@ -132,6 +132,11 @@ export class HomePageComponent implements OnDestroy, OnInit { this.update(); } + public ngOnDestroy() { + this.unsubscribeSubject.next(); + this.unsubscribeSubject.complete(); + } + private openDialog(): void { const dialogRef = this.dialog.open(PerformanceChartDialog, { autoFocus: false, @@ -195,9 +200,4 @@ export class HomePageComponent implements OnDestroy, OnInit { this.cd.markForCheck(); } - - public ngOnDestroy() { - this.unsubscribeSubject.next(); - this.unsubscribeSubject.complete(); - } } diff --git a/apps/client/src/app/pages/home/home-page.module.ts b/apps/client/src/app/pages/home/home-page.module.ts index 9f3c495e2..3306228dd 100644 --- a/apps/client/src/app/pages/home/home-page.module.ts +++ b/apps/client/src/app/pages/home/home-page.module.ts @@ -1,6 +1,5 @@ import { CommonModule } from '@angular/common'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; -import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { RouterModule } from '@angular/router'; import { GfLineChartModule } from '@ghostfolio/client/components/line-chart/line-chart.module'; @@ -27,7 +26,6 @@ import { HomePageComponent } from './home-page.component'; GfPositionsModule, GfToggleModule, HomePageRoutingModule, - MatButtonModule, MatCardModule, RouterModule ], diff --git a/apps/client/src/app/pages/zen/zen-page-routing.module.ts b/apps/client/src/app/pages/zen/zen-page-routing.module.ts new file mode 100644 index 000000000..59d4cb062 --- /dev/null +++ b/apps/client/src/app/pages/zen/zen-page-routing.module.ts @@ -0,0 +1,15 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { AuthGuard } from '@ghostfolio/client/core/auth.guard'; + +import { ZenPageComponent } from './zen-page.component'; + +const routes: Routes = [ + { path: '', component: ZenPageComponent, canActivate: [AuthGuard] } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class ZenPageRoutingModule {} diff --git a/apps/client/src/app/pages/zen/zen-page.component.ts b/apps/client/src/app/pages/zen/zen-page.component.ts new file mode 100644 index 000000000..1fc5e65ee --- /dev/null +++ b/apps/client/src/app/pages/zen/zen-page.component.ts @@ -0,0 +1,104 @@ +import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; +import { LineChartItem } from '@ghostfolio/client/components/line-chart/interfaces/line-chart.interface'; +import { DataService } from '@ghostfolio/client/services/data.service'; +import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; +import { TokenStorageService } from '@ghostfolio/client/services/token-storage.service'; +import { PortfolioPerformance, User } from '@ghostfolio/common/interfaces'; +import { hasPermission, permissions } from '@ghostfolio/common/permissions'; +import { DateRange } from '@ghostfolio/common/types'; +import { DeviceDetectorService } from 'ngx-device-detector'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; + +@Component({ + selector: 'gf-zen-page', + templateUrl: './zen-page.html', + styleUrls: ['./zen-page.scss'] +}) +export class ZenPageComponent implements OnDestroy, OnInit { + public dateRange: DateRange = 'max'; + public deviceType: string; + public hasImpersonationId: boolean; + public hasPermissionToReadForeignPortfolio: boolean; + public historicalDataItems: LineChartItem[]; + public isLoadingPerformance = true; + public performance: PortfolioPerformance; + public user: User; + + private unsubscribeSubject = new Subject(); + + /** + * @constructor + */ + public constructor( + private cd: ChangeDetectorRef, + private dataService: DataService, + private deviceService: DeviceDetectorService, + private impersonationStorageService: ImpersonationStorageService, + private tokenStorageService: TokenStorageService + ) { + this.tokenStorageService + .onChangeHasToken() + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(() => { + this.dataService.fetchUser().subscribe((user) => { + this.user = user; + + this.hasPermissionToReadForeignPortfolio = hasPermission( + user.permissions, + permissions.readForeignPortfolio + ); + + this.cd.markForCheck(); + }); + }); + } + + /** + * Initializes the controller + */ + public ngOnInit() { + this.deviceType = this.deviceService.getDeviceInfo().deviceType; + + this.impersonationStorageService + .onChangeHasImpersonation() + .subscribe((aId) => { + this.hasImpersonationId = !!aId; + }); + + this.update(); + } + + public ngOnDestroy() { + this.unsubscribeSubject.next(); + this.unsubscribeSubject.complete(); + } + + private update() { + this.isLoadingPerformance = true; + + this.dataService + .fetchChart({ range: this.dateRange }) + .subscribe((chartData) => { + this.historicalDataItems = chartData.map((chartDataItem) => { + return { + date: chartDataItem.date, + value: chartDataItem.value + }; + }); + + this.cd.markForCheck(); + }); + + this.dataService + .fetchPortfolioPerformance({ range: this.dateRange }) + .subscribe((response) => { + this.performance = response; + this.isLoadingPerformance = false; + + this.cd.markForCheck(); + }); + + this.cd.markForCheck(); + } +} diff --git a/apps/client/src/app/pages/zen/zen-page.html b/apps/client/src/app/pages/zen/zen-page.html new file mode 100644 index 000000000..a3e16b732 --- /dev/null +++ b/apps/client/src/app/pages/zen/zen-page.html @@ -0,0 +1,25 @@ +
+
+
+ +
+
+
+
+ +
+
+
diff --git a/apps/client/src/app/pages/zen/zen-page.module.ts b/apps/client/src/app/pages/zen/zen-page.module.ts new file mode 100644 index 000000000..117f69136 --- /dev/null +++ b/apps/client/src/app/pages/zen/zen-page.module.ts @@ -0,0 +1,23 @@ +import { CommonModule } from '@angular/common'; +import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; +import { MatCardModule } from '@angular/material/card'; +import { GfLineChartModule } from '@ghostfolio/client/components/line-chart/line-chart.module'; +import { GfPortfolioPerformanceSummaryModule } from '@ghostfolio/client/components/portfolio-performance-summary/portfolio-performance-summary.module'; + +import { ZenPageRoutingModule } from './zen-page-routing.module'; +import { ZenPageComponent } from './zen-page.component'; + +@NgModule({ + declarations: [ZenPageComponent], + exports: [], + imports: [ + CommonModule, + GfLineChartModule, + GfPortfolioPerformanceSummaryModule, + MatCardModule, + ZenPageRoutingModule + ], + providers: [], + schemas: [CUSTOM_ELEMENTS_SCHEMA] +}) +export class ZenPageModule {} diff --git a/apps/client/src/app/pages/zen/zen-page.scss b/apps/client/src/app/pages/zen/zen-page.scss new file mode 100644 index 000000000..d3be6211f --- /dev/null +++ b/apps/client/src/app/pages/zen/zen-page.scss @@ -0,0 +1,39 @@ +:host { + color: rgb(var(--dark-primary-text)); + display: block; + + .chart-container { + aspect-ratio: 16 / 9; + cursor: pointer; + margin-top: -1rem; + max-height: 50vh; + + // Fallback for aspect-ratio (using padding hack) + @supports not (aspect-ratio: 16 / 9) { + &::before { + float: left; + padding-top: 56.25%; + content: ''; + } + + &::after { + display: block; + content: ''; + clear: both; + } + } + + gf-line-chart { + bottom: 0; + left: 0; + position: absolute; + right: 0; + top: 0; + z-index: -1; + } + } +} + +:host-context(.is-dark-theme) { + color: rgb(var(--light-primary-text)); +} diff --git a/libs/common/src/lib/interfaces/user-settings.interface.ts b/libs/common/src/lib/interfaces/user-settings.interface.ts index 182845b71..e025e0832 100644 --- a/libs/common/src/lib/interfaces/user-settings.interface.ts +++ b/libs/common/src/lib/interfaces/user-settings.interface.ts @@ -3,4 +3,5 @@ import { Currency } from '@prisma/client'; export interface UserSettings { baseCurrency: Currency; locale: string; + mode: 'DEFAULT' | 'ZEN'; }