Skip to content

Commit

Permalink
feat: resource state management (DEV-3358) (#1496)
Browse files Browse the repository at this point in the history
Co-authored-by: Julien Schneider <julien.schneider.1991@gmail.com>
  • Loading branch information
irmastnt and derschnee68 committed Mar 4, 2024
1 parent 5cc4577 commit 48b06bc
Show file tree
Hide file tree
Showing 10 changed files with 235 additions and 65 deletions.
Expand Up @@ -26,7 +26,6 @@ import {
} from '../../../../main/action/confirmation-dialog/confirmation-dialog.component';
import { BaseValueDirective } from '../../../../main/directive/base-value.directive';
import { PropertyInfoValues } from '../../properties/properties.component';
import { UserService } from '../../services/user.service';
import {
DeletedEventValue,
EmitEvent,
Expand Down Expand Up @@ -68,68 +67,45 @@ export class DisplayEditComponent implements OnInit {
@ViewChild('displayVal') displayValueComponent: BaseValueDirective;

@Input() displayValue: ReadValue;

@Input() propArray: PropertyInfoValues[];

@Input() parentResource: ReadResource;

@Input() configuration?: object;

@Input() canDelete: boolean;

@Input() cantDeleteReason: string;

@Input() projectStatus: boolean;

@Input() valueUuidToHighlight: string;
@Input() user: ReadUser;

@Output() referredResourceClicked: EventEmitter<ReadLinkValue> = new EventEmitter<ReadLinkValue>();

@Output() referredResourceHovered: EventEmitter<ReadLinkValue> = new EventEmitter<ReadLinkValue>();

constants = Constants;

mode: 'read' | 'update' | 'create' | 'search';

canModify: boolean;

editModeActive = false;

submittingValue = false;

shouldShowCommentToggle: boolean;

// type of given displayValue
// or knora-api-js-lib class representing the value
valueTypeOrClass: string;

// indicates if value can be edited
readOnlyValue: boolean;

// indicates if the action bubble with the CRUD buttons should be shown
showActionBubble = false;

// string used as class name to add add to value-component element on hover
valueHovered: boolean;

dateDisplayOptions: 'era' | 'calendar' | 'all';

showDateLabels = false;

textArea = false;

dateFormat: string;

user: ReadUser;

ontoIri: string;

constructor(
@Inject(DspApiConnectionToken)
private _dspApiConnection: KnoraApiConnection,
private _valueOperationEventService: ValueOperationEventService,
private _dialog: MatDialog,
private _userService: UserService,
private _valueService: ValueService,
private _cd: ChangeDetectorRef
) {}
Expand Down Expand Up @@ -168,13 +144,6 @@ export class DisplayEditComponent implements OnInit {
}

this.readOnlyValue = this._valueService.isReadOnly(this.valueTypeOrClass, this.displayValue, resPropDef[0]);

// prevent getting info about system user (standoff link values are managed by the system)
if (this.displayValue.attachedToUser !== 'http://www.knora.org/ontology/knora-admin#SystemUser') {
this._userService.getUser(this.displayValue.attachedToUser).subscribe(user => {
this.user = user.user;
});
}
}

getTooltipText(): string {
Expand Down
@@ -1,5 +1,5 @@
<!-- toolbar -->
<div class="toolbar" *ngIf="project" [class.deleted]="deletedResource || resource.res.isDeleted">
<div class="toolbar" *ngIf="project$ | async" [class.deleted]="deletedResource || resource.res.isDeleted">
<!-- resource info -->
<h3 class="label mat-headline-6">{{resource.res.label}} <span *ngIf="deletedResource">(deleted)</span></h3>

Expand Down Expand Up @@ -78,7 +78,7 @@ <h3 class="label mat-headline-6">{{resource.res.label}} <span *ngIf="deletedReso
<!-- more menu with: delete, erase resource -->
<button
color="primary"
*ngIf="userCanEdit && project?.status"
*ngIf="userCanEdit && (project$ | async)?.status"
mat-icon-button
class="more-menu"
matTooltip="More"
Expand Down Expand Up @@ -120,7 +120,7 @@ <h3 class="label mat-headline-6">{{resource.res.label}} <span *ngIf="deletedReso
</div>

<!-- additional line with project and user information -->
<div class="infobar mat-caption" *ngIf="project && user">
<div class="infobar mat-caption" *ngIf="(project$ | async) as project">
<span *ngIf="displayProjectInfo"
>This resource {{ resource.res.isDeleted ? 'belonged' : 'belongs' }} to the project
<span class="project link" (click)="openProject(project)" (mouseover)="previewProject()">
Expand All @@ -140,7 +140,7 @@ <h3 class="label mat-headline-6">{{resource.res.label}} <span *ngIf="deletedReso
<div class="properties-container">
<div class="properties" *ngIf="resource.resProps.length > 0; else noProperties">
<!-- list of properties -->
<div *ngFor="let prop of resource.resProps; let last = last">
<div *ngFor="let prop of resource.resProps; let last = last; trackBy: trackByPropertyInfoFn">
<!-- show property; all in case of showAll === true or only the ones with prop.values -->
<div
*ngIf="(showAllProps || ( prop.values && prop.values.length > 0 ) || (prop.propDef.id === hasIncomingLinkIri && numberOffAllIncomingLinkRes > 0)) && (
Expand Down Expand Up @@ -170,7 +170,7 @@ <h3 class="label mat-headline-6">{{resource.res.label}} <span *ngIf="deletedReso
</div>
<div class="property-value">
<!-- the value(s) of the property -->
<div *ngFor="let val of prop.values">
<div *ngFor="let val of prop.values; trackBy: trackByValuesFn">
<app-display-edit
*ngIf="val"
#displayEdit
Expand All @@ -179,14 +179,15 @@ <h3 class="label mat-headline-6">{{resource.res.label}} <span *ngIf="deletedReso
[propArray]="resource.resProps"
[canDelete]="deleteValueIsAllowed(prop)"
[cantDeleteReason]="cantDeleteReason"
[projectStatus]="project?.status"
[projectStatus]="(project$ | async)?.status"
[user]="user"
[valueUuidToHighlight]="valueUuidToHighlight"
(referredResourceClicked)="openResource($event)"
(referredResourceHovered)="previewResource()">
</app-display-edit>
</div>
<!-- Add value form -->
<div *ngIf="addValueFormIsVisible && propID === prop.propDef.id && project?.status">
<div *ngIf="addValueFormIsVisible && propID === prop.propDef.id && (project$ | async)?.status">
<app-add-value
#addValue
class="add-value"
Expand All @@ -196,7 +197,7 @@ <h3 class="label mat-headline-6">{{resource.res.label}} <span *ngIf="deletedReso
</app-add-value>
</div>
<!-- Add button -->
<div *ngIf="addValueIsAllowed(prop) && project?.status">
<div *ngIf="addValueIsAllowed(prop) && (project$ | async)?.status">
<button
mat-icon-button
type="button"
Expand All @@ -213,7 +214,7 @@ <h3 class="label mat-headline-6">{{resource.res.label}} <span *ngIf="deletedReso
<!-- the value(s) of the incoming links -->
<a
class="link link-value"
*ngFor="let inRes of displayedIncomingLinkResources"
*ngFor="let inRes of displayedIncomingLinkResources; trackBy: trackByFn"
(click)="openResource(inRes.id)"
>{{inRes.resourceClassLabel}}: <strong>{{inRes.label}}</strong></a
>
Expand Down
Expand Up @@ -34,9 +34,7 @@ import {
ResourcePropertyDefinition,
UpdateResourceMetadata,
UpdateResourceMetadataResponse,
UserResponse,
} from '@dasch-swiss/dsp-js';
import { ProjectApiService } from '@dasch-swiss/vre/shared/app-api';
import { DspApiConnectionToken } from '@dasch-swiss/vre/shared/app-config';
import {
Events as CommsEvents,
Expand All @@ -47,15 +45,20 @@ import {
SortingService,
} from '@dasch-swiss/vre/shared/app-helper-services';
import { NotificationService } from '@dasch-swiss/vre/shared/app-notification';
import { LoadClassItemsCountAction } from '@dasch-swiss/vre/shared/app-state';
import { Store } from '@ngxs/store';
import { Observable, Subscription, forkJoin } from 'rxjs';
import {
GetAttachedProjectAction,
GetAttachedUserAction,
LoadClassItemsCountAction,
ResourceSelectors,
} from '@dasch-swiss/vre/shared/app-state';
import { Actions, Store, ofActionSuccessful } from '@ngxs/store';
import { Observable, Subject, Subscription, forkJoin } from 'rxjs';
import { map, take, takeUntil, takeWhile } from 'rxjs/operators';
import { ConfirmationWithComment, DialogComponent } from '../../../main/dialog/dialog.component';
import { DspResource } from '../dsp-resource';
import { RepresentationConstants } from '../representation/file-representation';
import { IncomingService } from '../services/incoming.service';
import { ResourceService } from '../services/resource.service';
import { UserService } from '../services/user.service';
import {
AddedEventValue,
DeletedEventValue,
Expand All @@ -79,6 +82,8 @@ export interface PropertyInfoValues {
styleUrls: ['./properties.component.scss'],
})
export class PropertiesComponent implements OnInit, OnChanges, OnDestroy {
private ngUnsubscribe: Subject<void> = new Subject<void>();

/**
* input `resource` of properties component:
* complete information about the current resource
Expand Down Expand Up @@ -154,9 +159,15 @@ export class PropertiesComponent implements OnInit, OnChanges, OnDestroy {
displayedIncomingLinkResources: ReadResource[] = [];
hasIncomingLinkIri = Constants.HasIncomingLinkValue;

project: ReadProject;
user: ReadUser;
project$ = this._store.select(ResourceSelectors.attachedProjects).pipe(
takeWhile(attachedProjects => this.resource !== undefined && attachedProjects[this.resource.res.id] !== undefined),
takeUntil(this.ngUnsubscribe),
map(attachedProjects =>
attachedProjects[this.resource.res.id].value.find(u => u.id === this.resource.res.attachedToProject)
)
);

user: ReadUser;
pageEvent: PageEvent;
loading = false;

Expand All @@ -165,19 +176,18 @@ export class PropertiesComponent implements OnInit, OnChanges, OnDestroy {
constructor(
@Inject(DspApiConnectionToken)
private _dspApiConnection: KnoraApiConnection,
private _projectApiService: ProjectApiService,
private _dialog: MatDialog,
private _incomingService: IncomingService,
private _notification: NotificationService,
private _resourceService: ResourceService,
private _userService: UserService,
private _valueOperationEventService: ValueOperationEventService,
private _valueService: ValueService,
private _componentCommsService: ComponentCommunicationEventService,
private _sortingService: SortingService,
private _cd: ChangeDetectorRef,
private _store: Store,
private _ontologyService: OntologyService
private _ontologyService: OntologyService,
private _actions$: Actions
) {}

ngOnInit(): void {
Expand Down Expand Up @@ -241,18 +251,21 @@ export class PropertiesComponent implements OnInit, OnChanges, OnDestroy {
} else {
localStorage.setItem('showAllProps', JSON.stringify(this.showAllProps));
}

this._store.dispatch([
new GetAttachedUserAction(this.resource.res.id, this.resource.res.attachedToUser),
new GetAttachedProjectAction(this.resource.res.id, this.resource.res.attachedToProject),
]);
this._actions$
.pipe(ofActionSuccessful(GetAttachedUserAction))
.pipe(take(1))
.subscribe(() => {
const attachedUsers = this._store.selectSnapshot(ResourceSelectors.attachedUsers);
this.user = attachedUsers[this.resource.res.id].value.find(u => u.id === this.resource.res.attachedToUser);
});
}

ngOnChanges(): void {
this._projectApiService.get(this.resource.res.attachedToProject).subscribe(response => {
this.project = response.project;
});

// get user information
this._userService.getUser(this.resource.res.attachedToUser).subscribe((response: UserResponse) => {
this.user = response.user;
});

this._getAllIncomingLinkRes();
}

Expand All @@ -263,6 +276,10 @@ export class PropertiesComponent implements OnInit, OnChanges, OnDestroy {
}
}

trackByFn = (index: number, item: ReadResource) => `${index}-${item.id}`;
trackByValuesFn = (index: number, item: any) => `${index}-${item}`;
trackByPropertyInfoFn = (index: number, item: PropertyInfoValues) => `${index}-${item.propDef.id}`;

/**
* opens project
* @param project
Expand Down Expand Up @@ -547,7 +564,9 @@ export class PropertiesComponent implements OnInit, OnChanges, OnDestroy {
// display notification and mark resource as 'erased'
this._notification.openSnackBar(`${response.result}: ${this.resource.res.label}`);
this.deletedResource = true;
const ontologyIri = this._ontologyService.getOntologyIriFromRoute(this.project?.shortcode);
const attachedProject = this._store.selectSnapshot(ResourceSelectors.attachedProjects);
const project = attachedProject[this.resource.res.id].value.find(u => u.id === this.resource.res.attachedToProject);
const ontologyIri = this._ontologyService.getOntologyIriFromRoute(project?.shortcode);
const classId = this.resource.res.entityInfo.classes[this.resource.res.type]?.id;
this._store.dispatch(new LoadClassItemsCountAction(ontologyIri, classId));
this._componentCommsService.emit(new EmitEvent(CommsEvents.resourceDeleted));
Expand Down
4 changes: 4 additions & 0 deletions libs/vre/shared/app-state/src/index.ts
Expand Up @@ -3,24 +3,28 @@ export * from './lib/lists/lists.selectors';
export * from './lib/ontologies/ontologies.selectors';
export * from './lib/ontology-class/ontology-class.selectors';
export * from './lib/projects/projects.selectors';
export * from './lib/resource/resource.selectors';
export * from './lib/user/user.selectors';

export * from './lib/lists/lists.actions';
export * from './lib/ontologies/ontologies.actions';
export * from './lib/ontology-class/ontology-class.actions';
export * from './lib/projects/projects.actions';
export * from './lib/resource/resource.actions';
export * from './lib/user/user.actions';

export * from './lib/lists/lists.state-model';
export * from './lib/ontologies/ontologies.state-model';
export * from './lib/ontology-class/ontology-class.state-model';
export * from './lib/projects/projects.state-model';
export * from './lib/resource/resource.state-model';
export * from './lib/user/user.state-model';

export * from './lib/lists/lists.state';
export * from './lib/ontologies/ontologies.state';
export * from './lib/ontology-class/ontology-class.state';
export * from './lib/projects/projects.state';
export * from './lib/resource/resource.state';
export * from './lib/user/user.state';

export * from './lib/model-interfaces';
Expand Down
17 changes: 16 additions & 1 deletion libs/vre/shared/app-state/src/lib/model-interfaces.ts
@@ -1,4 +1,12 @@
import { ClassDefinition, IHasProperty, OntologyMetadata, PropertyDefinition, ReadOntology } from '@dasch-swiss/dsp-js';
import {
ClassDefinition,
IHasProperty,
OntologyMetadata,
PropertyDefinition,
ReadOntology,
ReadProject,
ReadUser,
} from '@dasch-swiss/dsp-js';
import { PropertyInfoObject } from '@dasch-swiss/vre/shared/app-helper-services';

export interface IKeyValuePairs<T> {
Expand Down Expand Up @@ -45,3 +53,10 @@ export interface IClassItemsKeyValuePairs {
classItemsCount: number;
};
}

export interface IResourceKeyValuePairs {
[key: string]: {
attachedUsers: ReadUser[];
attachedProjects: ReadProject[];
};
}
18 changes: 18 additions & 0 deletions libs/vre/shared/app-state/src/lib/resource/resource.actions.ts
@@ -0,0 +1,18 @@
export class GetAttachedUserAction {
static readonly type = '[Resource] Get Attached User';

constructor(
public resourceIri: string,
public identifier: string,
public idType: 'iri' | 'username' | 'email' = 'iri'
) {}
}

export class GetAttachedProjectAction {
static readonly type = '[Resource] Get Attached Project';

constructor(
public resourceIri: string,
public projectIri: string
) {}
}

0 comments on commit 48b06bc

Please sign in to comment.