Browse Source

Improve usability by eliminating page reloads

pull/7518/head
Thomas Kaul 4 weeks ago
parent
commit
c93b3236ba
  1. 48
      apps/client/src/app/components/admin-market-data/admin-market-data.component.ts
  2. 17
      apps/client/src/app/components/admin-market-data/admin-market-data.service.ts
  3. 7
      apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts
  4. 23
      apps/client/src/app/components/user-account-membership/user-account-membership.component.ts

48
apps/client/src/app/components/admin-market-data/admin-market-data.component.ts

@ -43,6 +43,7 @@ import {
MatPaginatorModule, MatPaginatorModule,
PageEvent PageEvent
} from '@angular/material/paginator'; } from '@angular/material/paginator';
import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
import { import {
MatSort, MatSort,
MatSortModule, MatSortModule,
@ -64,6 +65,7 @@ import {
ellipsisVertical, ellipsisVertical,
trashOutline trashOutline
} from 'ionicons/icons'; } from 'ionicons/icons';
import ms from 'ms';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
@ -88,6 +90,7 @@ import { CreateAssetProfileDialogParams } from './create-asset-profile-dialog/in
MatCheckboxModule, MatCheckboxModule,
MatMenuModule, MatMenuModule,
MatPaginatorModule, MatPaginatorModule,
MatSnackBarModule,
MatSortModule, MatSortModule,
MatTableModule, MatTableModule,
NgxSkeletonLoaderModule, NgxSkeletonLoaderModule,
@ -177,6 +180,7 @@ export class GfAdminMarketDataComponent implements AfterViewInit, OnInit {
private readonly dialog = inject(MatDialog); private readonly dialog = inject(MatDialog);
private readonly route = inject(ActivatedRoute); private readonly route = inject(ActivatedRoute);
private readonly router = inject(Router); private readonly router = inject(Router);
private readonly snackBar = inject(MatSnackBar);
private readonly userService = inject(UserService); private readonly userService = inject(UserService);
public constructor() { public constructor() {
@ -285,15 +289,25 @@ export class GfAdminMarketDataComponent implements AfterViewInit, OnInit {
dataSource, dataSource,
symbol symbol
}: AssetProfileIdentifier) { }: AssetProfileIdentifier) {
this.adminMarketDataService.deleteAssetProfile({ dataSource, symbol }); this.adminMarketDataService
.deleteAssetProfile({ dataSource, symbol })
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => {
this.loadData();
});
} }
protected onDeleteAssetProfiles() { protected onDeleteAssetProfiles() {
this.adminMarketDataService.deleteAssetProfiles( this.adminMarketDataService
.deleteAssetProfiles(
this.selection.selected.map(({ dataSource, symbol }) => { this.selection.selected.map(({ dataSource, symbol }) => {
return { dataSource, symbol }; return { dataSource, symbol };
}) })
); )
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => {
this.loadData();
});
} }
protected onGatherMax() { protected onGatherMax() {
@ -301,9 +315,7 @@ export class GfAdminMarketDataComponent implements AfterViewInit, OnInit {
.gatherMax() .gatherMax()
.pipe(takeUntilDestroyed(this.destroyRef)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => { .subscribe(() => {
setTimeout(() => { this.notifyDataGatheringHasBeenStarted();
window.location.reload();
}, 300);
}); });
} }
@ -311,7 +323,9 @@ export class GfAdminMarketDataComponent implements AfterViewInit, OnInit {
this.adminService this.adminService
.gatherProfileData() .gatherProfileData()
.pipe(takeUntilDestroyed(this.destroyRef)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(); .subscribe(() => {
this.notifyDataGatheringHasBeenStarted();
});
} }
protected onGatherRecentMarketData() { protected onGatherRecentMarketData() {
@ -319,9 +333,7 @@ export class GfAdminMarketDataComponent implements AfterViewInit, OnInit {
.gatherRecentMarketData() .gatherRecentMarketData()
.pipe(takeUntilDestroyed(this.destroyRef)) .pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => { .subscribe(() => {
setTimeout(() => { this.notifyDataGatheringHasBeenStarted();
window.location.reload();
}, 300);
}); });
} }
@ -396,6 +408,16 @@ export class GfAdminMarketDataComponent implements AfterViewInit, OnInit {
}); });
} }
private notifyDataGatheringHasBeenStarted() {
this.snackBar.open(
'✅ ' + $localize`Data gathering has been started.`,
undefined,
{
duration: ms('3 seconds')
}
);
}
private openAssetProfileDialog({ private openAssetProfileDialog({
dataSource, dataSource,
symbol symbol
@ -431,6 +453,12 @@ export class GfAdminMarketDataComponent implements AfterViewInit, OnInit {
if (newAssetProfileIdentifier) { if (newAssetProfileIdentifier) {
this.onOpenAssetProfileDialog(newAssetProfileIdentifier); this.onOpenAssetProfileDialog(newAssetProfileIdentifier);
} else { } else {
this.loadData({
pageIndex: this.paginator().pageIndex,
sortColumn: this.sort().active,
sortDirection: this.sort().direction
});
this.router.navigate(['.'], { relativeTo: this.route }); this.router.navigate(['.'], { relativeTo: this.route });
} }
}); });

17
apps/client/src/app/components/admin-market-data/admin-market-data.service.ts

@ -4,7 +4,7 @@ import { NotificationService } from '@ghostfolio/ui/notifications';
import { AdminService } from '@ghostfolio/ui/services'; import { AdminService } from '@ghostfolio/ui/services';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { EMPTY, catchError, finalize, forkJoin } from 'rxjs'; import { EMPTY, Subject, catchError, finalize, forkJoin } from 'rxjs';
@Injectable() @Injectable()
export class AdminMarketDataService { export class AdminMarketDataService {
@ -14,25 +14,29 @@ export class AdminMarketDataService {
) {} ) {}
public deleteAssetProfile({ dataSource, symbol }: AssetProfileIdentifier) { public deleteAssetProfile({ dataSource, symbol }: AssetProfileIdentifier) {
const assetProfileDeleted = new Subject<void>();
this.notificationService.confirm({ this.notificationService.confirm({
confirmFn: () => { confirmFn: () => {
this.adminService this.adminService
.deleteProfileData({ dataSource, symbol }) .deleteProfileData({ dataSource, symbol })
.subscribe(() => { .subscribe(() => {
setTimeout(() => { assetProfileDeleted.next();
window.location.reload(); assetProfileDeleted.complete();
}, 300);
}); });
}, },
confirmType: ConfirmationDialogType.Warn, confirmType: ConfirmationDialogType.Warn,
title: $localize`Do you really want to delete this asset profile?` title: $localize`Do you really want to delete this asset profile?`
}); });
return assetProfileDeleted.asObservable();
} }
public deleteAssetProfiles( public deleteAssetProfiles(
aAssetProfileIdentifiers: AssetProfileIdentifier[] aAssetProfileIdentifiers: AssetProfileIdentifier[]
) { ) {
const assetProfileCount = aAssetProfileIdentifiers.length; const assetProfileCount = aAssetProfileIdentifiers.length;
const assetProfilesDeleted = new Subject<void>();
this.notificationService.confirm({ this.notificationService.confirm({
confirmFn: () => { confirmFn: () => {
@ -55,7 +59,8 @@ export class AdminMarketDataService {
return EMPTY; return EMPTY;
}), }),
finalize(() => { finalize(() => {
window.location.reload(); assetProfilesDeleted.next();
assetProfilesDeleted.complete();
}) })
) )
.subscribe(); .subscribe();
@ -66,5 +71,7 @@ export class AdminMarketDataService {
? $localize`Do you really want to delete this asset profile?` ? $localize`Do you really want to delete this asset profile?`
: $localize`Do you really want to delete these ${assetProfileCount}:count: asset profiles?` : $localize`Do you really want to delete these ${assetProfileCount}:count: asset profiles?`
}); });
return assetProfilesDeleted.asObservable();
} }
} }

7
apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts

@ -531,9 +531,12 @@ export class GfAssetProfileDialogComponent implements OnInit {
dataSource, dataSource,
symbol symbol
}: AssetProfileIdentifier) { }: AssetProfileIdentifier) {
this.adminMarketDataService.deleteAssetProfile({ dataSource, symbol }); this.adminMarketDataService
.deleteAssetProfile({ dataSource, symbol })
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => {
this.dialogRef.close(); this.dialogRef.close();
});
} }
protected onGatherProfileDataBySymbol({ protected onGatherProfileDataBySymbol({

23
apps/client/src/app/components/user-account-membership/user-account-membership.component.ts

@ -24,7 +24,7 @@ import { MatSnackBar } from '@angular/material/snack-bar';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import ms, { StringValue } from 'ms'; import ms, { StringValue } from 'ms';
import { EMPTY } from 'rxjs'; import { EMPTY } from 'rxjs';
import { catchError } from 'rxjs/operators'; import { catchError, switchMap } from 'rxjs/operators';
@Component({ @Component({
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
@ -184,30 +184,19 @@ export class GfUserAccountMembershipComponent {
return EMPTY; return EMPTY;
}), }),
switchMap(() => {
return this.userService.get(true);
}),
takeUntilDestroyed(this.destroyRef) takeUntilDestroyed(this.destroyRef)
) )
.subscribe(() => { .subscribe(() => {
const snackBarRef = this.snackBar.open( this.snackBar.open(
'✅ ' + $localize`Coupon code has been redeemed`, '✅ ' + $localize`Coupon code has been redeemed`,
$localize`Reload`, undefined,
{ {
duration: ms('3 seconds') duration: ms('3 seconds')
} }
); );
snackBarRef
.afterDismissed()
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => {
window.location.reload();
});
snackBarRef
.onAction()
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => {
window.location.reload();
});
}); });
} }
}, },

Loading…
Cancel
Save