diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c6c921b40..7e35621f0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Improved the usability of the activities page by eliminating the reload on canceling the import activities dialog - Improved the accessibility of the _Buy me a coffee_ button on the about page - Improved the accessibility of the entity logo component - Improved the performance of the historical market data table of the admin control panel by loading only the latest market price per asset profile diff --git a/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts b/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts index dbeedff322..b52339ddfa 100644 --- a/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts +++ b/apps/client/src/app/pages/portfolio/activities/activities-page.component.ts @@ -34,7 +34,10 @@ import { format, parseISO } from 'date-fns'; import { DeviceDetectorService } from 'ngx-device-detector'; import { GfImportActivitiesDialogComponent } from './import-activities-dialog/import-activities-dialog.component'; -import { ImportActivitiesDialogParams } from './import-activities-dialog/interfaces/interfaces'; +import { + ImportActivitiesDialogParams, + ImportActivitiesDialogResult +} from './import-activities-dialog/interfaces/interfaces'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -217,7 +220,8 @@ export class GfActivitiesPageComponent implements OnInit { protected onImport() { const dialogRef = this.dialog.open< GfImportActivitiesDialogComponent, - ImportActivitiesDialogParams + ImportActivitiesDialogParams, + ImportActivitiesDialogResult >(GfImportActivitiesDialogComponent, { data: { deviceType: this.deviceType, @@ -230,22 +234,25 @@ export class GfActivitiesPageComponent implements OnInit { dialogRef .afterClosed() .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe(() => { - this.userService - .get(true) - .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe(); + .subscribe(({ withRefresh } = { withRefresh: false }) => { + if (withRefresh) { + this.userService + .get(true) + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe(); - this.fetchActivities(); + this.fetchActivities(); - this.changeDetectorRef.markForCheck(); + this.changeDetectorRef.markForCheck(); + } }); } protected onImportDividends() { const dialogRef = this.dialog.open< GfImportActivitiesDialogComponent, - ImportActivitiesDialogParams + ImportActivitiesDialogParams, + ImportActivitiesDialogResult >(GfImportActivitiesDialogComponent, { data: { activityTypes: ['DIVIDEND'], @@ -259,15 +266,17 @@ export class GfActivitiesPageComponent implements OnInit { dialogRef .afterClosed() .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe(() => { - this.userService - .get(true) - .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe(); + .subscribe(({ withRefresh } = { withRefresh: false }) => { + if (withRefresh) { + this.userService + .get(true) + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe(); - this.fetchActivities(); + this.fetchActivities(); - this.changeDetectorRef.markForCheck(); + this.changeDetectorRef.markForCheck(); + } }); } diff --git a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts index 43a28d8c54..6ebcc4f2ad 100644 --- a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts +++ b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.component.ts @@ -1,5 +1,6 @@ import { GfFileDropDirective } from '@ghostfolio/client/directives/file-drop/file-drop.directive'; import { ImportActivitiesService } from '@ghostfolio/client/services/import-activities.service'; +import { UserService } from '@ghostfolio/client/services/user/user.service'; import { AI_ACTIVITIES_FILE_MAXIMUM_SIZE, AI_MAX_ACTIVITIES, @@ -76,7 +77,10 @@ import ms from 'ms'; import { from, switchMap } from 'rxjs'; import { ImportStep } from './enums/import-step'; -import { ImportActivitiesDialogParams } from './interfaces/interfaces'; +import { + ImportActivitiesDialogParams, + ImportActivitiesDialogResult +} from './interfaces/interfaces'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -155,6 +159,7 @@ export class GfImportActivitiesDialogComponent { private accounts: CreateAccountWithBalancesDto[] = []; private activities: Activity[] = []; private assetProfiles: CreateAssetProfileWithMarketDataDto[] = []; + private hasChangedUserSettings = false; private platforms: CreatePlatformDto[] = []; private tags: CreateTagDto[] = []; @@ -162,15 +167,29 @@ export class GfImportActivitiesDialogComponent { private readonly dataService = inject(DataService); private readonly destroyRef = inject(DestroyRef); private readonly dialogRef = - inject>(MatDialogRef); + inject< + MatDialogRef< + GfImportActivitiesDialogComponent, + ImportActivitiesDialogResult + > + >(MatDialogRef); private readonly importActivitiesService = inject(ImportActivitiesService); private readonly snackBar = inject(MatSnackBar); + private readonly userService = inject(UserService); public constructor() { addIcons({ cloudUploadOutline, informationCircleOutline, warningOutline }); } public ngOnInit() { + // No takeUntilDestroyed(), the component is already destroyed here + // The opener refreshes the user if withRefresh is true + this.dialogRef.afterClosed().subscribe((result) => { + if (this.hasChangedUserSettings && !result?.withRefresh) { + this.userService.get(true).subscribe(); + } + }); + if ( this.data?.activityTypes?.length === 1 && this.data?.activityTypes?.[0] === 'DIVIDEND' @@ -215,7 +234,7 @@ export class GfImportActivitiesDialogComponent { } protected onCancel() { - this.dialogRef.close(); + this.dialogRef.close({ withRefresh: false }); } protected async onImportActivities() { @@ -248,7 +267,7 @@ export class GfImportActivitiesDialogComponent { } ); } finally { - this.dialogRef.close(); + this.dialogRef.close({ withRefresh: true }); } } @@ -267,6 +286,7 @@ export class GfImportActivitiesDialogComponent { } protected onGhostfolioAiChange({ checked }: MatCheckboxChange) { + this.hasChangedUserSettings = true; this.isGhostfolioAiEnabled = checked; this.dataService diff --git a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/interfaces/interfaces.ts b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/interfaces/interfaces.ts index 39749b1222..4261468237 100644 --- a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/interfaces/interfaces.ts +++ b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/interfaces/interfaces.ts @@ -6,3 +6,7 @@ export interface ImportActivitiesDialogParams { deviceType: string; user: User; } + +export interface ImportActivitiesDialogResult { + withRefresh: boolean; +}