Skip to content

Commit

Permalink
feature: introduced ontology class item count from state
Browse files Browse the repository at this point in the history
  • Loading branch information
irmastnt committed Jan 17, 2024
1 parent ec8898e commit 32e85c6
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 85 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,17 @@
<div class="entry-container">
<mat-icon>{{icon}}</mat-icon>
<ngx-skeleton-loader
*ngIf="results === undefined"
*ngIf="(results$ | async) === undefined"
count="1"
appearance="line"
[theme]="{
'margin-bottom': 0,
'vertical-align': 'middle'
}">
</ngx-skeleton-loader>
<div *ngIf="results !== undefined" class="entry">{{results | i18nPlural: itemPluralMapping['entry']}}</div>
<div *ngIf="(results$ | async) !== undefined" class="entry">
{{results$ | async | i18nPlural: itemPluralMapping['entry']}}
</div>
</div>
</div>
<div class="icon link" *ngIf="projectMember" [routerLink]="getAddClassInstanceLink()">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,23 @@ import {
ChangeDetectorRef,
Component,
ElementRef,
Inject,
Input,
OnDestroy,
OnInit,
ViewChild,
} from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import {
ClassDefinition,
KnoraApiConnection,
CountQueryResponse,
ApiResponseError,
Constants,
} from '@dasch-swiss/dsp-js';
import { DspApiConnectionToken, RouteConstants } from '@dasch-swiss/vre/shared/app-config';
import { AppErrorHandler } from '@dasch-swiss/vre/shared/app-error-handler';
import { ClassDefinition, Constants } from '@dasch-swiss/dsp-js';
import { RouteConstants } from '@dasch-swiss/vre/shared/app-config';
import { OntologyService } from '@dasch-swiss/vre/shared/app-helper-services';
import { Subscription } from 'rxjs';
import {
IClassItemsKeyValuePairs,
LoadClassItemsCountAction,
OntologyClassSelectors,
} from '@dasch-swiss/vre/shared/app-state';
import { Actions, Select, Store, ofActionSuccessful } from '@ngxs/store';
import { Observable, Subject, Subscription, combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';
import {
ComponentCommunicationEventService,
EmitEvent,
Expand All @@ -35,15 +34,20 @@ import {
styleUrls: ['./ontology-class-item.component.scss'],
})
export class OntologyClassItemComponent implements OnInit, AfterViewInit, OnDestroy {
destroyed: Subject<void> = new Subject<void>();

@Input() resClass: ClassDefinition;

@Input() projectMember: boolean;

@ViewChild('resClassLabel') resClassLabel: ElementRef;

gravsearch: string;

results: number;
get results$(): Observable<number> {
return combineLatest([
this._store.select(OntologyClassSelectors.classItems),
this._store.select(OntologyClassSelectors.isLoading),
]).pipe(map(([classItems]) => classItems[this.resClass.id]?.classItemsCount));
}

classLink: string;

Expand All @@ -62,39 +66,27 @@ export class OntologyClassItemComponent implements OnInit, AfterViewInit, OnDest
},
};

@Select(OntologyClassSelectors.classItems) classItems$: Observable<IClassItemsKeyValuePairs>;

constructor(
@Inject(DspApiConnectionToken)
private _dspApiConnection: KnoraApiConnection,
private _errorHandler: AppErrorHandler,
private _route: ActivatedRoute,
private _componentCommsService: ComponentCommunicationEventService,
private _cdr: ChangeDetectorRef
) {}
private _cdr: ChangeDetectorRef,
private _store: Store,
private _actions$: Actions,
private _cd: ChangeDetectorRef
) {
this._actions$.pipe(ofActionSuccessful(LoadClassItemsCountAction)).subscribe(() => {
this._cd.markForCheck();
});
}

ngOnInit(): void {
const uuid = this._route.snapshot.params.uuid;
const splitIri = this.resClass.id.split('#');
const ontologyName = OntologyService.getOntologyName(splitIri[0]);
this.classLink = `${RouteConstants.projectRelative}/${uuid}/${RouteConstants.ontology}/${ontologyName}/${splitIri[1]}`;

this.gravsearch = this._setGravsearch(this.resClass.id);

// get number of resource instances
this._getSearchCount();

this.icon = this._getIcon();

this.componentCommsSubscriptions.push(
this._componentCommsService.on(Events.resourceDeleted, () => {
this._getSearchCount();
})
);

this.componentCommsSubscriptions.push(
this._componentCommsService.on(Events.resourceCreated, () => {
this._getSearchCount();
})
);
}

ngAfterViewInit(): void {
Expand All @@ -104,6 +96,8 @@ export class OntologyClassItemComponent implements OnInit, AfterViewInit, OnDest

ngOnDestroy(): void {
this.componentCommsSubscriptions.forEach(sub => sub.unsubscribe());
this.destroyed.next();
this.destroyed.complete();
}

selectItem() {
Expand All @@ -116,24 +110,6 @@ export class OntologyClassItemComponent implements OnInit, AfterViewInit, OnDest
}
}

private _setGravsearch(iri: string): string {
return `
PREFIX knora-api: <http://api.knora.org/ontology/knora-api/v2#>
CONSTRUCT {
?mainRes knora-api:isMainResource true .
} WHERE {
?mainRes a knora-api:Resource .
?mainRes a <${iri}> .
}
OFFSET 0`;
}

/**
* return the correct mat-icon depending on the subclass of the resource
*
Expand All @@ -158,18 +134,6 @@ export class OntologyClassItemComponent implements OnInit, AfterViewInit, OnDest
}
}

private _getSearchCount() {
this._dspApiConnection.v2.search.doExtendedSearchCountQuery(this.gravsearch).subscribe(
(res: CountQueryResponse) => {
this.results = res.numberOfResults;
this._cdr.markForCheck();
},
(error: ApiResponseError) => {
this._errorHandler.showMessage(error);
}
);
}

getAddClassInstanceLink(): string {
return `${this.classLink}/${RouteConstants.addClassInstance}`;
}
Expand Down
25 changes: 20 additions & 5 deletions apps/dsp-app/src/app/project/project-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export class ProjectBase implements OnInit, OnDestroy {
@Select(ProjectsSelectors.isCurrentProjectMember) isProjectMember$: Observable<boolean>;
@Select(ProjectsSelectors.currentProject) project$: Observable<ReadProject>;
@Select(ProjectsSelectors.isProjectsLoading) isProjectsLoading$: Observable<boolean>;
@Select(ProjectsSelectors.isMembershipLoading) isMembershipLoading$: Observable<boolean>;
@Select(ProjectsSelectors.projectMembers) projectMembers$: Observable<IKeyValuePairs<ReadUser>>;

constructor(
Expand All @@ -65,7 +66,8 @@ export class ProjectBase implements OnInit, OnDestroy {
}

ngOnInit(): void {
this.loadProject();
this._loadMembership();
this._loadProject();
}

ngOnDestroy(): void {
Expand All @@ -82,29 +84,42 @@ export class ProjectBase implements OnInit, OnDestroy {
return projects.find(x => x.id.split('/').pop() === this.projectUuid);
}

private loadProject(): void {
private _loadProject(): void {
this.isProjectsLoading$
.pipe(takeWhile(isLoading => isLoading === false && this.projectUuid !== undefined))
.pipe(take(1))
.subscribe({
next: () => {
const projectMembers = this._store.selectSnapshot(ProjectsSelectors.projectMembers)[this.projectIri];
this.project = this._store.selectSnapshot(ProjectsSelectors.currentProject);
if (!this.project || this.project.id !== this.projectIri || !projectMembers) {
if (!this.project || this.project.id !== this.projectIri) {
// get current project data, project members and project groups
// and set the project state here
this._actions$
.pipe(ofActionSuccessful(LoadProjectsAction))
.pipe(take(1))
.subscribe(() => this.setProjectData());
this._store.dispatch([new LoadProjectsAction(), new LoadProjectMembershipAction(this.projectUuid)]);
this._store.dispatch([new LoadProjectsAction()]);
} else if (!this.isOntologiesAvailable()) {
this._store.dispatch(new LoadProjectOntologiesAction(this.projectUuid));
}
},
});
}

private _loadMembership(): void {
this.isMembershipLoading$
.pipe(takeWhile(isMembershipLoading => isMembershipLoading === false && this.projectUuid !== undefined))
.pipe(take(1))
.subscribe({
next: () => {
const projectMembers = this._store.selectSnapshot(ProjectsSelectors.projectMembers)[this.projectIri];
if (!projectMembers) {
this._store.dispatch(new LoadProjectMembershipAction(this.projectUuid));
}
},
});
}

private setProjectData(): void {
this.project = this._store.selectSnapshot(ProjectsSelectors.currentProject);
if (!this.project) {
Expand Down
19 changes: 16 additions & 3 deletions libs/vre/shared/app-state/src/lib/ontologies/ontologies.state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { of } from 'rxjs';
import { map, take, tap } from 'rxjs/operators';
import { LoadListsInProjectAction } from '../lists/lists.actions';
import { IProjectOntologiesKeyValuePairs, OntologyProperties } from '../model-interfaces';
import { LoadClassItemsCountAction } from '../ontology-class/ontology-class.actions';
import {
ClearCurrentOntologyAction,
ClearOntologiesAction,
Expand Down Expand Up @@ -146,10 +147,22 @@ export class OntologiesState {
this._actions$
.pipe(ofActionSuccessful(LoadOntologyAction))
.pipe(take(1))
.subscribe(() =>
.subscribe(() => {
// last action dispatched
ctx.dispatch(new LoadListsInProjectAction(projectIri))
)
ctx.dispatch(new LoadListsInProjectAction(projectIri));
ontoMeta.ontologies.forEach(onto => {
const readOntology = ctx
.getState()
.projectOntologies[projectIri].readOntologies.find(o => o.id === onto.id);
if (readOntology) {
ctx.dispatch(
getAllEntityDefinitionsAsArray(readOntology.classes).map(
resClass => new LoadClassItemsCountAction(onto.id, resClass.id)
)
);
}
});
})
);
},
error: (error: ApiResponseError) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Inject, Injectable } from '@angular/core';
import { ApiResponseError, CountQueryResponse, KnoraApiConnection } from '@dasch-swiss/dsp-js';
import { DspApiConnectionToken } from '@dasch-swiss/vre/shared/app-config';
import { Action, Actions, State, StateContext } from '@ngxs/store';
import { Action, State, StateContext } from '@ngxs/store';
import { finalize, map, take, tap } from 'rxjs/operators';
import { LoadClassItemsCountAction } from './ontology-class.actions';
import { OntologyClassStateModel } from './ontology-class.state-model';
Expand All @@ -19,8 +19,7 @@ const defaults: OntologyClassStateModel = <OntologyClassStateModel>{
export class OntologyClassState {
constructor(
@Inject(DspApiConnectionToken)
private _dspApiConnection: KnoraApiConnection,
private _actions$: Actions
private _dspApiConnection: KnoraApiConnection
) {}

@Action(LoadClassItemsCountAction)
Expand All @@ -44,16 +43,15 @@ export class OntologyClassState {
classItems[resClassId].classItemsCount = countQueryResponse.numberOfResults;
}

ctx.patchState({
ctx.setState({
...ctx.getState(),
classItems,
});
},
error: (error: ApiResponseError) => {
ctx.patchState({ isLoading: false });
},
}),
finalize(() => ctx.patchState({ isLoading: false }))
finalize(() => {
ctx.patchState({ isLoading: false });
})
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ export class ProjectsSelectors {
return state.isLoading;
}

@Selector([ProjectsState])
static isMembershipLoading(state: ProjectsStateModel): boolean {
return state.isMembershipLoading;
}

@Selector([ProjectsState])
static hasLoadingErrors(state: ProjectsStateModel): boolean {
return state.hasLoadingErrors;
Expand Down

0 comments on commit 32e85c6

Please sign in to comment.