import { DOCUMENT } from '@angular/common'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, OnDestroy, OnInit } from '@angular/core'; import { Title } from '@angular/platform-browser'; import { NavigationEnd, PRIMARY_OUTLET, Router } from '@angular/router'; import { InfoItem, User } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { ColorScheme } from '@ghostfolio/common/types'; import { DeviceDetectorService } from 'ngx-device-detector'; import { Subject } from 'rxjs'; import { filter, takeUntil } from 'rxjs/operators'; import { environment } from '../environments/environment'; import { DataService } from './services/data.service'; import { TokenStorageService } from './services/token-storage.service'; import { UserService } from './services/user/user.service'; @Component({ selector: 'gf-root', changeDetection: ChangeDetectionStrategy.OnPush, templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent implements OnDestroy, OnInit { public canCreateAccount: boolean; public currentRoute: string; public currentYear = new Date().getFullYear(); public deviceType: string; public info: InfoItem; public pageTitle: string; public user: User; public version = environment.version; private unsubscribeSubject = new Subject(); public constructor( private changeDetectorRef: ChangeDetectorRef, private dataService: DataService, private deviceService: DeviceDetectorService, @Inject(DOCUMENT) private document: Document, private router: Router, private title: Title, private tokenStorageService: TokenStorageService, private userService: UserService ) { this.initializeTheme(); this.user = undefined; } public ngOnInit() { this.deviceType = this.deviceService.getDeviceInfo().deviceType; this.router.events .pipe(filter((event) => event instanceof NavigationEnd)) .subscribe(() => { const urlTree = this.router.parseUrl(this.router.url); const urlSegmentGroup = urlTree.root.children[PRIMARY_OUTLET]; const urlSegments = urlSegmentGroup.segments; this.currentRoute = urlSegments[0].path; this.info = this.dataService.fetchInfo(); if (this.deviceType === 'mobile') { setTimeout(() => { const index = this.title.getTitle().indexOf('–'); const title = index === -1 ? '' : this.title.getTitle().substring(0, index).trim(); this.pageTitle = title.length <= 15 ? title : 'Ghostfolio'; this.changeDetectorRef.markForCheck(); }); } }); this.userService.stateChanged .pipe(takeUntil(this.unsubscribeSubject)) .subscribe((state) => { this.user = state.user; this.canCreateAccount = hasPermission( this.user?.permissions, permissions.createUserAccount ); this.initializeTheme(this.user?.settings.colorScheme); this.changeDetectorRef.markForCheck(); }); } public onCreateAccount() { this.tokenStorageService.signOut(); } public onSignOut() { this.tokenStorageService.signOut(); this.userService.remove(); document.location.href = `/${document.documentElement.lang}`; } public ngOnDestroy() { this.unsubscribeSubject.next(); this.unsubscribeSubject.complete(); } private initializeTheme(userPreferredColorScheme?: ColorScheme) { const isDarkTheme = userPreferredColorScheme ? userPreferredColorScheme === 'DARK' : window.matchMedia('(prefers-color-scheme: dark)').matches; this.toggleThemeStyleClass(isDarkTheme); window.matchMedia('(prefers-color-scheme: dark)').addListener((event) => { if (!this.user?.settings.colorScheme) { this.toggleThemeStyleClass(event.matches); } }); } private toggleThemeStyleClass(isDarkTheme: boolean) { if (isDarkTheme) { this.document.body.classList.add('is-dark-theme'); } else { this.document.body.classList.remove('is-dark-theme'); } } }