Browse Source

Task/improve type safety in home summary component (#6926)

* fix(client): resolve errors

* feat(client): replace constructor based DI with inject function

* feat(client): enforce encapsulation

* fix(client): remove dead code

* feat(client): replace deprecated getDeviceInfo

* feat(client): convert isLoading to signal

* feat(client): convert hasImpersonationId to signal

* feat(client): convert summary to signal

* feat(client): convert user and hasPermissionToUpdateUserSettings to signals

* feat(client): implement OnPush change detection strategy

* fix(client): remove nested subscription
pull/6928/head
Kenrick Tandrian 2 days ago
committed by GitHub
parent
commit
2ba7e46579
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 98
      apps/client/src/app/components/home-summary/home-summary.component.ts
  2. 18
      apps/client/src/app/components/home-summary/home-summary.html

98
apps/client/src/app/components/home-summary/home-summary.component.ts

@ -1,27 +1,27 @@
import { GfPortfolioSummaryComponent } from '@ghostfolio/client/components/portfolio-summary/portfolio-summary.component';
import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service';
import { UserService } from '@ghostfolio/client/services/user/user.service';
import {
InfoItem,
PortfolioSummary,
User
} from '@ghostfolio/common/interfaces';
import { PortfolioSummary, User } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { DataService } from '@ghostfolio/ui/services';
import {
ChangeDetectorRef,
ChangeDetectionStrategy,
Component,
computed,
CUSTOM_ELEMENTS_SCHEMA,
DestroyRef,
OnInit
inject,
OnInit,
signal
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatCardModule } from '@angular/material/card';
import { MatSnackBarRef, TextOnlySnackBar } from '@angular/material/snack-bar';
import { DeviceDetectorService } from 'ngx-device-detector';
import { switchMap } from 'rxjs';
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [GfPortfolioSummaryComponent, MatCardModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
selector: 'gf-home-summary',
@ -29,87 +29,75 @@ import { DeviceDetectorService } from 'ngx-device-detector';
templateUrl: './home-summary.html'
})
export class GfHomeSummaryComponent implements OnInit {
public deviceType: string;
public hasImpersonationId: boolean;
public hasPermissionForSubscription: boolean;
public hasPermissionToUpdateUserSettings: boolean;
public info: InfoItem;
public isLoading = true;
public snackBarRef: MatSnackBarRef<TextOnlySnackBar>;
public summary: PortfolioSummary;
public user: User;
protected readonly hasImpersonationId = signal<boolean>(false);
protected readonly isLoading = signal(true);
protected readonly summary = signal<PortfolioSummary | undefined>(undefined);
protected readonly user = signal<User | undefined>(undefined);
protected readonly deviceType = computed(
() => this.deviceDetectorService.deviceInfo().deviceType
);
protected readonly hasPermissionToUpdateUserSettings = computed(() => {
const user = this.user();
public constructor(
private changeDetectorRef: ChangeDetectorRef,
private dataService: DataService,
private destroyRef: DestroyRef,
private deviceDetectorService: DeviceDetectorService,
private impersonationStorageService: ImpersonationStorageService,
private userService: UserService
) {
this.info = this.dataService.fetchInfo();
return user
? hasPermission(user.permissions, permissions.updateUserSettings)
: false;
});
this.hasPermissionForSubscription = hasPermission(
this.info?.globalPermissions,
permissions.enableSubscription
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 constructor() {
this.userService.stateChanged
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((state) => {
if (state?.user) {
this.user = state.user;
this.hasPermissionToUpdateUserSettings = hasPermission(
this.user.permissions,
permissions.updateUserSettings
);
this.user.set(state.user);
this.update();
}
});
}
public ngOnInit() {
this.deviceType = this.deviceDetectorService.getDeviceInfo().deviceType;
this.impersonationStorageService
.onChangeHasImpersonation()
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((impersonationId) => {
this.hasImpersonationId = !!impersonationId;
this.hasImpersonationId.set(!!impersonationId);
});
}
public onChangeEmergencyFund(emergencyFund: number) {
protected onChangeEmergencyFund(emergencyFund: number) {
this.dataService
.putUserSetting({ emergencyFund })
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => {
this.userService
.get(true)
.pipe(takeUntilDestroyed(this.destroyRef))
.pipe(
switchMap(() => this.userService.get(true)),
takeUntilDestroyed(this.destroyRef)
)
.subscribe((user) => {
this.user = user;
this.changeDetectorRef.markForCheck();
});
this.user.set(user);
});
}
private update() {
this.isLoading = true;
this.isLoading.set(true);
this.dataService
.fetchPortfolioDetails()
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(({ summary }) => {
this.summary = summary;
this.isLoading = false;
if (summary) {
this.summary.set(summary);
}
this.changeDetectorRef.markForCheck();
this.isLoading.set(false);
});
this.changeDetectorRef.markForCheck();
}
}

18
apps/client/src/app/components/home-summary/home-summary.html

@ -5,17 +5,17 @@
<mat-card appearance="outlined">
<mat-card-content>
<gf-portfolio-summary
[baseCurrency]="user?.settings?.baseCurrency"
[deviceType]="deviceType"
[hasImpersonationId]="hasImpersonationId"
[baseCurrency]="user()?.settings?.baseCurrency"
[deviceType]="deviceType()"
[hasImpersonationId]="hasImpersonationId()"
[hasPermissionToUpdateUserSettings]="
!hasImpersonationId && hasPermissionToUpdateUserSettings
!hasImpersonationId() && hasPermissionToUpdateUserSettings()
"
[isLoading]="isLoading"
[language]="user?.settings?.language"
[locale]="user?.settings?.locale"
[summary]="summary"
[user]="user"
[isLoading]="isLoading()"
[language]="user()?.settings?.language"
[locale]="user()?.settings?.locale"
[summary]="summary()"
[user]="user()"
(emergencyFundChanged)="onChangeEmergencyFund($event)"
/>
</mat-card-content>

Loading…
Cancel
Save