You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

270 lines
7.9 KiB

import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service';
import { UserService } from '@ghostfolio/client/services/user/user.service';
import { SubscriptionType } from '@ghostfolio/common/enums';
import {
FireCalculationCompleteEvent,
FireWealth,
User
} from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { GfFireCalculatorComponent } from '@ghostfolio/ui/fire-calculator';
import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator';
import { DataService } from '@ghostfolio/ui/services';
import { GfValueComponent } from '@ghostfolio/ui/value';
import { CommonModule, NgStyle } from '@angular/common';
import {
ChangeDetectorRef,
Component,
computed,
DestroyRef,
inject,
OnInit
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { FormControl } from '@angular/forms';
import { Big } from 'big.js';
import { DeviceDetectorService } from 'ngx-device-detector';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
@Component({
imports: [
CommonModule,
FormsModule,
GfFireCalculatorComponent,
GfPremiumIndicatorComponent,
GfValueComponent,
NgStyle,
NgxSkeletonLoaderModule,
ReactiveFormsModule
],
selector: 'gf-fire-page',
styleUrls: ['./fire-page.scss'],
templateUrl: './fire-page.html'
})
export class GfFirePageComponent implements OnInit {
protected readonly deviceType = computed(
() => this.deviceDetectorService.deviceInfo().deviceType
);
protected fireWealth: FireWealth;
protected hasImpersonationId: boolean;
protected hasPermissionToUpdateUserSettings: boolean;
protected isLoading = false;
protected retirementDate: Date;
protected readonly safeWithdrawalRateControl = new FormControl<
number | undefined
>(undefined);
protected readonly safeWithdrawalRateOptions = [
0.025, 0.03, 0.035, 0.04, 0.045
] as const;
protected user: User;
protected withdrawalRatePerMonth: Big;
protected withdrawalRatePerMonthProjected: Big;
protected withdrawalRatePerYear: Big;
protected withdrawalRatePerYearProjected: Big;
private projectedTotalAmount: number;
private readonly changeDetectorRef = inject(ChangeDetectorRef);
private readonly dataService = inject(DataService);
private readonly destroyRef = inject(DestroyRef);
private readonly deviceDetectorService = inject(DeviceDetectorService);
private readonly impersonationStorageService = inject(
ImpersonationStorageService
);
private readonly userService = inject(UserService);
public ngOnInit() {
this.isLoading = true;
this.dataService
.fetchPortfolioDetails()
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(({ summary }) => {
this.fireWealth = {
today: {
valueInBaseCurrency: summary?.fireWealth
? summary.fireWealth.today.valueInBaseCurrency
: 0
}
};
if (this.user.subscription?.type === SubscriptionType.Basic) {
this.fireWealth = {
today: {
valueInBaseCurrency: 10000
}
};
}
this.calculateWithdrawalRates();
this.changeDetectorRef.markForCheck();
});
this.impersonationStorageService
.onChangeHasImpersonation()
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((impersonationId) => {
this.hasImpersonationId = !!impersonationId;
});
this.safeWithdrawalRateControl.valueChanges
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((value) => {
this.updateSafeWithdrawalRate(Number(value));
});
this.userService.stateChanged
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((state) => {
if (state?.user) {
this.user = state.user;
this.hasPermissionToUpdateUserSettings =
this.user.subscription?.type === SubscriptionType.Basic
? false
: hasPermission(
this.user.permissions,
permissions.updateUserSettings
);
this.safeWithdrawalRateControl.setValue(
this.user.settings.safeWithdrawalRate,
{ emitEvent: false }
);
this.calculateWithdrawalRates();
this.changeDetectorRef.markForCheck();
}
});
}
protected onAnnualInterestRateChange(annualInterestRate: number) {
this.dataService
.putUserSetting({ annualInterestRate })
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => {
this.userService
.get(true)
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((user) => {
this.user = user;
this.changeDetectorRef.markForCheck();
});
});
}
protected onCalculationComplete({
projectedTotalAmount,
retirementDate
}: FireCalculationCompleteEvent) {
this.projectedTotalAmount = projectedTotalAmount;
this.retirementDate = retirementDate;
this.calculateWithdrawalRatesProjected();
this.isLoading = false;
}
protected onProjectedTotalAmountChange(projectedTotalAmount: number) {
this.dataService
.putUserSetting({
projectedTotalAmount,
retirementDate: null
})
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => {
this.userService
.get(true)
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((user) => {
this.user = user;
this.changeDetectorRef.markForCheck();
});
});
}
protected onRetirementDateChange(retirementDate: Date) {
this.dataService
.putUserSetting({
projectedTotalAmount: null,
retirementDate: retirementDate.toISOString()
})
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => {
this.userService
.get(true)
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((user) => {
this.user = user;
this.changeDetectorRef.markForCheck();
});
});
}
protected onSavingsRateChange(savingsRate: number) {
this.dataService
.putUserSetting({ savingsRate })
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => {
this.userService
.get(true)
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((user) => {
this.user = user;
this.changeDetectorRef.markForCheck();
});
});
}
private calculateWithdrawalRates() {
if (this.fireWealth && this.user?.settings?.safeWithdrawalRate) {
this.withdrawalRatePerYear = new Big(
this.fireWealth.today.valueInBaseCurrency
).mul(this.user.settings.safeWithdrawalRate);
this.withdrawalRatePerMonth = this.withdrawalRatePerYear.div(12);
}
}
private calculateWithdrawalRatesProjected() {
if (
this.fireWealth &&
this.projectedTotalAmount &&
this.user?.settings?.safeWithdrawalRate
) {
this.withdrawalRatePerYearProjected = new Big(
this.projectedTotalAmount
).mul(this.user.settings.safeWithdrawalRate);
this.withdrawalRatePerMonthProjected =
this.withdrawalRatePerYearProjected.div(12);
}
}
private updateSafeWithdrawalRate(safeWithdrawalRate: number) {
this.dataService
.putUserSetting({ safeWithdrawalRate })
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => {
this.userService
.get(true)
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((user) => {
this.user = user;
this.calculateWithdrawalRates();
this.calculateWithdrawalRatesProjected();
this.changeDetectorRef.markForCheck();
});
});
}
}