Browse Source

Task/align activity dialog host with account dialog host (#7472)

Align with account dialog host
pull/7573/head
Thomas Kaul 10 hours ago
committed by GitHub
parent
commit
ac082b362c
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 4
      apps/client/src/app/pages/portfolio/activities/activities-page.component.ts
  2. 152
      apps/client/src/app/pages/portfolio/activities/activity-dialog-host/activity-dialog-host.component.ts

4
apps/client/src/app/pages/portfolio/activities/activities-page.component.ts

@ -331,7 +331,9 @@ export class GfActivitiesPageComponent implements OnInit {
this.hasPermissionToCreateActivity = this.hasPermissionToCreateActivity =
!this.hasImpersonationId && !this.hasImpersonationId &&
hasPermission(this.user.permissions, permissions.createActivity); hasPermission(this.user.permissions, permissions.createActivity) &&
!this.user.settings?.isRestrictedView;
this.hasPermissionToDeleteActivity = this.hasPermissionToDeleteActivity =
!this.hasImpersonationId && !this.hasImpersonationId &&
hasPermission(this.user.permissions, permissions.deleteActivity); hasPermission(this.user.permissions, permissions.deleteActivity);

152
apps/client/src/app/pages/portfolio/activities/activity-dialog-host/activity-dialog-host.component.ts

@ -1,6 +1,8 @@
import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service';
import { UserService } from '@ghostfolio/client/services/user/user.service'; import { UserService } from '@ghostfolio/client/services/user/user.service';
import { CreateOrderDto, UpdateOrderDto } from '@ghostfolio/common/dtos'; import { CreateOrderDto, UpdateOrderDto } from '@ghostfolio/common/dtos';
import { Activity, User } from '@ghostfolio/common/interfaces'; import { Activity, User } from '@ghostfolio/common/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { internalRoutes } from '@ghostfolio/common/routes/routes'; import { internalRoutes } from '@ghostfolio/common/routes/routes';
import { DataService } from '@ghostfolio/ui/services'; import { DataService } from '@ghostfolio/ui/services';
@ -16,8 +18,15 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatDialog, MatDialogRef } from '@angular/material/dialog'; import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
import { Observable, of } from 'rxjs'; import { EMPTY, Observable, of, Subject } from 'rxjs';
import { map, switchMap } from 'rxjs/operators'; import {
catchError,
distinctUntilChanged,
map,
switchMap,
takeUntil,
tap
} from 'rxjs/operators';
import { GfCreateOrUpdateActivityDialogComponent } from '../create-or-update-activity-dialog/create-or-update-activity-dialog.component'; import { GfCreateOrUpdateActivityDialogComponent } from '../create-or-update-activity-dialog/create-or-update-activity-dialog.component';
import { CreateOrUpdateActivityDialogParams } from '../create-or-update-activity-dialog/interfaces/interfaces'; import { CreateOrUpdateActivityDialogParams } from '../create-or-update-activity-dialog/interfaces/interfaces';
@ -31,72 +40,102 @@ import { ActivityDialogMode } from './types/activity-dialog-mode.type';
export class GfActivityDialogHostComponent implements OnDestroy, OnInit { export class GfActivityDialogHostComponent implements OnDestroy, OnInit {
private dialogRef: MatDialogRef<GfCreateOrUpdateActivityDialogComponent>; private dialogRef: MatDialogRef<GfCreateOrUpdateActivityDialogComponent>;
private readonly dialogClosed = new Subject<void>();
private readonly dataService = inject(DataService); private readonly dataService = inject(DataService);
private readonly destroyRef = inject(DestroyRef); private readonly destroyRef = inject(DestroyRef);
private readonly deviceDetectorService = inject(DeviceDetectorService); private readonly deviceDetectorService = inject(DeviceDetectorService);
private readonly dialog = inject(MatDialog); private readonly dialog = inject(MatDialog);
private readonly impersonationStorageService = inject(
ImpersonationStorageService
);
private readonly route = inject(ActivatedRoute); private readonly route = inject(ActivatedRoute);
private readonly router = inject(Router); private readonly router = inject(Router);
private readonly userService = inject(UserService); private readonly userService = inject(UserService);
public ngOnInit() { public ngOnInit() {
const mode = this.route.snapshot.data.mode as ActivityDialogMode; const mode = this.route.snapshot.data.mode as ActivityDialogMode;
const activityId = this.route.snapshot.paramMap.get('activityId');
const activity$: Observable<Activity | undefined> = activityId
? this.dataService.fetchActivity(activityId)
: of(undefined);
this.userService // The router reuses this component when only the activity id changes, so
.get() // the parameters are observed instead of read from the snapshot once
this.route.paramMap
.pipe( .pipe(
switchMap((user) => { map((paramMap) => {
return activity$.pipe( return paramMap.get('activityId');
map((activity) => { }),
return { activity, user }; distinctUntilChanged(),
tap(() => {
this.closeDialog();
}),
switchMap((activityId) => {
const activity$: Observable<Activity | undefined> = activityId
? this.dataService.fetchActivity(activityId)
: of(undefined);
return this.userService.get().pipe(
switchMap((user) => {
return activity$.pipe(
map((activity) => {
return { activity, user };
})
);
}),
catchError(() => {
this.navigateBack();
return EMPTY;
}) })
); );
}), }),
takeUntilDestroyed(this.destroyRef) takeUntilDestroyed(this.destroyRef)
) )
.subscribe({ .subscribe(({ activity, user }) => {
error: () => { if (mode === 'update') {
this.navigateBack(); if (
}, !activity ||
next: ({ activity, user }) => { !hasPermission(user?.permissions, permissions.updateActivity) ||
if (mode === 'update') { this.isReadOnlyMode(user)
if (!activity) { ) {
this.navigateBack(); this.navigateBack();
return;
}
this.openDialog({ activity, user, isUpdate: true });
return; return;
} }
if (mode === 'clone' && !activity) { this.openDialog({ activity, user, isUpdate: true });
this.navigateBack();
return; return;
} }
if (mode === 'clone' && !activity) {
this.navigateBack();
this.openDialog({ return;
user,
activity: {
...activity,
accountId: activity?.accountId,
assetProfile: activity?.assetProfile ?? null,
date: new Date(),
fee: 0,
id: null,
type: activity?.type ?? 'BUY',
unitPrice: null
},
isUpdate: false
});
} }
// Cloning creates a new activity as well
if (
!hasPermission(user?.permissions, permissions.createActivity) ||
this.isReadOnlyMode(user)
) {
this.navigateBack();
return;
}
this.openDialog({
user,
activity: {
...activity,
accountId: activity?.accountId,
assetProfile: activity?.assetProfile ?? null,
date: new Date(),
fee: 0,
id: null,
type: activity?.type ?? 'BUY',
unitPrice: null
},
isUpdate: false
});
}); });
} }
@ -105,6 +144,23 @@ export class GfActivityDialogHostComponent implements OnDestroy, OnInit {
// be closed explicitly when leaving the route (for example via the browser // be closed explicitly when leaving the route (for example via the browser
// navigation) // navigation)
this.dialogRef?.close(); this.dialogRef?.close();
this.dialogClosed.complete();
}
private closeDialog() {
// Tear down the subscription of the dialog which is about to be replaced,
// so that its result is not mistaken for the user closing it
this.dialogClosed.next();
this.dialogRef?.close();
}
private isReadOnlyMode(user: User) {
return (
!!this.impersonationStorageService.getId() ||
!!user?.settings?.isRestrictedView
);
} }
private navigateBack() { private navigateBack() {
@ -124,7 +180,7 @@ export class GfActivityDialogHostComponent implements OnDestroy, OnInit {
}) { }) {
const deviceType = this.deviceDetectorService.getDeviceInfo().deviceType; const deviceType = this.deviceDetectorService.getDeviceInfo().deviceType;
this.dialogRef = this.dialog.open< const dialogRef = this.dialog.open<
GfCreateOrUpdateActivityDialogComponent, GfCreateOrUpdateActivityDialogComponent,
CreateOrUpdateActivityDialogParams CreateOrUpdateActivityDialogParams
>(GfCreateOrUpdateActivityDialogComponent, { >(GfCreateOrUpdateActivityDialogComponent, {
@ -137,9 +193,11 @@ export class GfActivityDialogHostComponent implements OnDestroy, OnInit {
width: deviceType === 'mobile' ? '100vw' : '50rem' width: deviceType === 'mobile' ? '100vw' : '50rem'
}); });
this.dialogRef this.dialogRef = dialogRef;
dialogRef
.afterClosed() .afterClosed()
.pipe(takeUntilDestroyed(this.destroyRef)) .pipe(takeUntil(this.dialogClosed), takeUntilDestroyed(this.destroyRef))
.subscribe((result: CreateOrderDto | UpdateOrderDto | null) => { .subscribe((result: CreateOrderDto | UpdateOrderDto | null) => {
if (!result) { if (!result) {
this.navigateBack(); this.navigateBack();

Loading…
Cancel
Save