From a9c5c20c5aa810d516eb28d87ee1f217336fe90a Mon Sep 17 00:00:00 2001 From: Marie Verdonck Date: Fri, 16 Jun 2023 11:45:26 +0200 Subject: [PATCH 01/47] 102415: Provide all external sources of correct entity type in edit relationships ({item-page}/edit/relationship) --- .../dynamic-lookup-relation-modal.component.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.ts index 69c2ac3b7f..95acd0f41d 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.ts @@ -30,6 +30,10 @@ import { getAllSucceededRemoteDataPayload } from '../../../../../core/shared/ope import { followLink } from '../../../../utils/follow-link-config.model'; import { RelationshipType } from '../../../../../core/shared/item-relationships/relationship-type.model'; import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; +import { FindListOptions } from '../../../../../core/data/request.models'; +import { RequestParam } from '../../../../../core/cache/models/request-param.model'; +import { getFirstSucceededRemoteDataPayload } from '../../../../../core/shared/operators'; +import { PaginatedList } from '../../../../../core/data/paginated-list.model'; @Component({ selector: 'ds-dynamic-lookup-relation-modal', @@ -202,6 +206,19 @@ export class DsDynamicLookupRelationModalComponent implements OnInit, OnDestroy ).pipe( getAllSucceededRemoteDataPayload() ); + } else { + const findListOptions = Object.assign({}, new FindListOptions(), { + elementsPerPage: 5, + currentPage: 1, + searchParams: [ + new RequestParam('entityType', this.relationshipOptions.relationshipType) + ] + }); + this.externalSourcesRD$ = this.externalSourceService.searchBy('findByEntityType', findListOptions, + true, true, followLink('entityTypes')) + .pipe(getFirstSucceededRemoteDataPayload(), map((r: PaginatedList) => { + return r.page; + })); } this.setTotals(); From cf9179d800dbaab74fd48b15bde4840cc1155f57 Mon Sep 17 00:00:00 2001 From: Marie Verdonck Date: Mon, 19 Jun 2023 16:29:15 +0200 Subject: [PATCH 02/47] 102415: Fix for import modal i18n labels --- .../dynamic-lookup-relation-modal.component.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.ts index 95acd0f41d..38b547d4a9 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.ts @@ -178,6 +178,7 @@ export class DsDynamicLookupRelationModalComponent implements OnInit, OnDestroy if (!!this.currentItemIsLeftItem$) { this.currentItemIsLeftItem$.subscribe((isLeft) => { this.isLeft = isLeft; + this.label = this.relationshipType.leftwardType; }); } From 91b4d3dcd10cbd8248a41505eac96586707bac2b Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Fri, 10 Nov 2023 16:13:20 +0100 Subject: [PATCH 03/47] [DURACOM-204][#2622] Makes forgot-password link removable --- .../data/feature-authorization/feature-id.ts | 1 + .../password/log-in-password.component.html | 16 ++++++------ .../password/log-in-password.component.ts | 26 ++++++++++++++++--- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/app/core/data/feature-authorization/feature-id.ts b/src/app/core/data/feature-authorization/feature-id.ts index 8fef45a953..bd1f65b72d 100644 --- a/src/app/core/data/feature-authorization/feature-id.ts +++ b/src/app/core/data/feature-authorization/feature-id.ts @@ -34,4 +34,5 @@ export enum FeatureID { CanEditItem = 'canEditItem', CanRegisterDOI = 'canRegisterDOI', CanSubscribe = 'canSubscribeDso', + EPersonForgotPassword = 'epersonForgotPassword', } diff --git a/src/app/shared/log-in/methods/password/log-in-password.component.html b/src/app/shared/log-in/methods/password/log-in-password.component.html index 60477d141d..52f28e7190 100644 --- a/src/app/shared/log-in/methods/password/log-in-password.component.html +++ b/src/app/shared/log-in/methods/password/log-in-password.component.html @@ -29,11 +29,11 @@ [disabled]="!form.valid"> {{"login.form.submit" | translate}} - + + + diff --git a/src/app/shared/log-in/methods/password/log-in-password.component.ts b/src/app/shared/log-in/methods/password/log-in-password.component.ts index 6132394019..1f96c4c0d8 100644 --- a/src/app/shared/log-in/methods/password/log-in-password.component.ts +++ b/src/app/shared/log-in/methods/password/log-in-password.component.ts @@ -1,9 +1,9 @@ -import { map } from 'rxjs/operators'; +import { combineLatest, Observable, shareReplay } from 'rxjs'; +import { filter, map } from 'rxjs/operators'; import { Component, Inject, OnInit } from '@angular/core'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { select, Store } from '@ngrx/store'; -import { Observable } from 'rxjs'; import { AuthenticateAction, ResetAuthenticationMessagesAction } from '../../../../core/auth/auth.actions'; import { getAuthenticationError, getAuthenticationInfo, } from '../../../../core/auth/selectors'; @@ -73,6 +73,17 @@ export class LogInPasswordComponent implements OnInit { */ public canRegister$: Observable; + /** + * Whether or not the current user (or anonymous) is authorized to register an account + */ + canForgot$: Observable; + + /** + * Shows the divider only if contains at least one link to show + */ + canShowDivider$: Observable; + + constructor( @Inject('authMethodProvider') public injectedAuthMethodModel: AuthMethod, @Inject('isStandalonePage') public isStandalonePage: boolean, @@ -114,8 +125,15 @@ export class LogInPasswordComponent implements OnInit { return message; }) ); - - this.canRegister$ = this.authorizationService.isAuthorized(FeatureID.EPersonRegistration); + + this.canRegister$ = this.authorizationService.isAuthorized(FeatureID.EPersonRegistration).pipe(shareReplay(1)); + this.canForgot$ = this.authorizationService.isAuthorized(FeatureID.EPersonForgotPassword).pipe(shareReplay(1)); + this.canShowDivider$ = + combineLatest([this.canRegister$, this.canForgot$]) + .pipe( + map(([canRegister, canForgot]) => canRegister || canForgot), + filter(Boolean) + ); } getRegisterRoute() { From 2429c3660b8ae370a8da945a9c3483b185b201e9 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Fri, 10 Nov 2023 16:49:06 +0100 Subject: [PATCH 04/47] [DURACOM-204][#2622] Makes forgot-password link removable --- .../log-in/methods/password/log-in-password.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/shared/log-in/methods/password/log-in-password.component.ts b/src/app/shared/log-in/methods/password/log-in-password.component.ts index 1f96c4c0d8..008eb11eef 100644 --- a/src/app/shared/log-in/methods/password/log-in-password.component.ts +++ b/src/app/shared/log-in/methods/password/log-in-password.component.ts @@ -64,7 +64,7 @@ export class LogInPasswordComponent implements OnInit { /** * The authentication form. - * @type {FormGroup} + * @type {UntypedFormGroup} */ public form: UntypedFormGroup; @@ -125,7 +125,7 @@ export class LogInPasswordComponent implements OnInit { return message; }) ); - + this.canRegister$ = this.authorizationService.isAuthorized(FeatureID.EPersonRegistration).pipe(shareReplay(1)); this.canForgot$ = this.authorizationService.isAuthorized(FeatureID.EPersonForgotPassword).pipe(shareReplay(1)); this.canShowDivider$ = From 98241d8925cbc394c78feb124cdea476c67da4be Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Fri, 8 Dec 2023 21:54:32 +0300 Subject: [PATCH 05/47] src/assets/i18n: change "controller" to "reviewer" Reviewer is a less obscure term for what this actually is in most cases. --- src/assets/i18n/ar.json5 | 4 ++-- src/assets/i18n/bn.json5 | 2 +- src/assets/i18n/ca.json5 | 2 +- src/assets/i18n/cs.json5 | 4 ++-- src/assets/i18n/de.json5 | 2 +- src/assets/i18n/en.json5 | 4 ++-- src/assets/i18n/es.json5 | 2 +- src/assets/i18n/fi.json5 | 2 +- src/assets/i18n/fr.json5 | 2 +- src/assets/i18n/gd.json5 | 2 +- src/assets/i18n/hu.json5 | 2 +- src/assets/i18n/it.json5 | 2 +- src/assets/i18n/ja.json5 | 4 ++-- src/assets/i18n/kk.json5 | 2 +- src/assets/i18n/lv.json5 | 2 +- src/assets/i18n/nl.json5 | 2 +- src/assets/i18n/pt-BR.json5 | 2 +- src/assets/i18n/pt-PT.json5 | 2 +- src/assets/i18n/sv.json5 | 2 +- src/assets/i18n/sw.json5 | 4 ++-- src/assets/i18n/tr.json5 | 2 +- src/assets/i18n/uk.json5 | 2 +- 22 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/assets/i18n/ar.json5 b/src/assets/i18n/ar.json5 index 3069104dd9..617d2a93de 100644 --- a/src/assets/i18n/ar.json5 +++ b/src/assets/i18n/ar.json5 @@ -4284,9 +4284,9 @@ // TODO New key - Add a translation "mydspace.status.mydspaceValidation": "Validation", - // "mydspace.status.mydspaceWaitingController": "Waiting for controller", + // "mydspace.status.mydspaceWaitingController": "Waiting for reviewer", // TODO New key - Add a translation - "mydspace.status.mydspaceWaitingController": "Waiting for controller", + "mydspace.status.mydspaceWaitingController": "Waiting for reviewer", // "mydspace.status.mydspaceWorkflow": "Workflow", // TODO New key - Add a translation diff --git a/src/assets/i18n/bn.json5 b/src/assets/i18n/bn.json5 index c70cc6f459..c9c5ba2640 100644 --- a/src/assets/i18n/bn.json5 +++ b/src/assets/i18n/bn.json5 @@ -3886,7 +3886,7 @@ // "mydspace.status.mydspaceValidation": "Validation", "mydspace.status.mydspaceValidation": "বৈধতা", - // "mydspace.status.mydspaceWaitingController": "Waiting for controller", + // "mydspace.status.mydspaceWaitingController": "Waiting for reviewer", "mydspace.status.mydspaceWaitingController": "নিয়ামক জন্য অপেক্ষা করছে", // "mydspace.status.mydspaceWorkflow": "Workflow", diff --git a/src/assets/i18n/ca.json5 b/src/assets/i18n/ca.json5 index ad8fe49424..db624f2c3f 100644 --- a/src/assets/i18n/ca.json5 +++ b/src/assets/i18n/ca.json5 @@ -4196,7 +4196,7 @@ // "mydspace.status.mydspaceValidation": "Validation", "mydspace.status.mydspaceValidation": "Validació", - // "mydspace.status.mydspaceWaitingController": "Waiting for controller", + // "mydspace.status.mydspaceWaitingController": "Waiting for reviewer", "mydspace.status.mydspaceWaitingController": "Esperant el controlador", // "mydspace.status.mydspaceWorkflow": "Workflow", diff --git a/src/assets/i18n/cs.json5 b/src/assets/i18n/cs.json5 index 7f9583a50e..9816571e05 100644 --- a/src/assets/i18n/cs.json5 +++ b/src/assets/i18n/cs.json5 @@ -4195,9 +4195,9 @@ // TODO New key - Add a translation "mydspace.status.mydspaceValidation": "Validation", - // "mydspace.status.mydspaceWaitingController": "Waiting for controller", + // "mydspace.status.mydspaceWaitingController": "Waiting for reviewer", // TODO New key - Add a translation - "mydspace.status.mydspaceWaitingController": "Waiting for controller", + "mydspace.status.mydspaceWaitingController": "Waiting for reviewer", // "mydspace.status.mydspaceWorkflow": "Workflow", // TODO New key - Add a translation diff --git a/src/assets/i18n/de.json5 b/src/assets/i18n/de.json5 index c185a13432..f77d1a21fd 100644 --- a/src/assets/i18n/de.json5 +++ b/src/assets/i18n/de.json5 @@ -3491,7 +3491,7 @@ // "mydspace.status.mydspaceValidation": "Validation", "mydspace.status.mydspaceValidation": "Validierung", - // "mydspace.status.mydspaceWaitingController": "Waiting for controller", + // "mydspace.status.mydspaceWaitingController": "Waiting for reviewer", "mydspace.status.mydspaceWaitingController": "Warten auf die Überprüfung", // "mydspace.status.mydspaceWorkflow": "Workflow", diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 643a3ce0d1..78d034c417 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -3042,7 +3042,7 @@ "mydspace.status.mydspaceValidation": "Validation", - "mydspace.status.mydspaceWaitingController": "Waiting for controller", + "mydspace.status.mydspaceWaitingController": "Waiting for reviewer", "mydspace.status.mydspaceWorkflow": "Workflow", @@ -3864,7 +3864,7 @@ "search.filters.namedresourcetype.Validation": "Validation", - "search.filters.namedresourcetype.Waiting for Controller": "Waiting for Controller", + "search.filters.namedresourcetype.Waiting for Controller": "Waiting for reviewer", "search.filters.namedresourcetype.Workflow": "Workflow", diff --git a/src/assets/i18n/es.json5 b/src/assets/i18n/es.json5 index 0d5b27473c..dc4e3afcc7 100644 --- a/src/assets/i18n/es.json5 +++ b/src/assets/i18n/es.json5 @@ -4524,7 +4524,7 @@ // "mydspace.status.mydspaceValidation": "Validation", "mydspace.status.mydspaceValidation": "Validación", - // "mydspace.status.mydspaceWaitingController": "Waiting for controller", + // "mydspace.status.mydspaceWaitingController": "Waiting for reviewer", "mydspace.status.mydspaceWaitingController": "Esperando al controlador", // "mydspace.status.mydspaceWorkflow": "Workflow", diff --git a/src/assets/i18n/fi.json5 b/src/assets/i18n/fi.json5 index ede41ffb0c..914dfa8f45 100644 --- a/src/assets/i18n/fi.json5 +++ b/src/assets/i18n/fi.json5 @@ -4479,7 +4479,7 @@ // "mydspace.status.mydspaceValidation": "Validation", "mydspace.status.mydspaceValidation": "Validointi", - // "mydspace.status.mydspaceWaitingController": "Waiting for controller", + // "mydspace.status.mydspaceWaitingController": "Waiting for reviewer", "mydspace.status.mydspaceWaitingController": "Odottaa tarkastajaa", // "mydspace.status.mydspaceWorkflow": "Workflow", diff --git a/src/assets/i18n/fr.json5 b/src/assets/i18n/fr.json5 index 699ca5cc27..80566e589e 100644 --- a/src/assets/i18n/fr.json5 +++ b/src/assets/i18n/fr.json5 @@ -3828,7 +3828,7 @@ // "mydspace.status.mydspaceValidation": "Validation", "mydspace.status.mydspaceValidation": "En cours de validation", - // "mydspace.status.mydspaceWaitingController": "Waiting for controller", + // "mydspace.status.mydspaceWaitingController": "Waiting for reviewer", "mydspace.status.mydspaceWaitingController": "En attente d'assignation", // "mydspace.status.mydspaceWorkflow": "Workflow", diff --git a/src/assets/i18n/gd.json5 b/src/assets/i18n/gd.json5 index 55a53bc6f1..929ab87d10 100644 --- a/src/assets/i18n/gd.json5 +++ b/src/assets/i18n/gd.json5 @@ -3873,7 +3873,7 @@ // "mydspace.status.mydspaceValidation": "Validation", "mydspace.status.mydspaceValidation": "Dearbhadh", - // "mydspace.status.mydspaceWaitingController": "Waiting for controller", + // "mydspace.status.mydspaceWaitingController": "Waiting for reviewer", "mydspace.status.mydspaceWaitingController": "A' feitheamh riaghladair", // "mydspace.status.mydspaceWorkflow": "Workflow", diff --git a/src/assets/i18n/hu.json5 b/src/assets/i18n/hu.json5 index d186f6435a..afbfa25a13 100644 --- a/src/assets/i18n/hu.json5 +++ b/src/assets/i18n/hu.json5 @@ -4989,7 +4989,7 @@ // "mydspace.status.mydspaceValidation": "Validation", "mydspace.status.mydspaceValidation": "Érvényesítés", - // "mydspace.status.mydspaceWaitingController": "Waiting for controller", + // "mydspace.status.mydspaceWaitingController": "Waiting for reviewer", "mydspace.status.mydspaceWaitingController": "Várakozás a kontrollerre", // "mydspace.status.mydspaceWorkflow": "Workflow", diff --git a/src/assets/i18n/it.json5 b/src/assets/i18n/it.json5 index 7f410ce0b1..ad2478ae61 100644 --- a/src/assets/i18n/it.json5 +++ b/src/assets/i18n/it.json5 @@ -4570,7 +4570,7 @@ // "mydspace.status.mydspaceValidation": "Validation", "mydspace.status.mydspaceValidation": "Convalida", - // "mydspace.status.mydspaceWaitingController": "Waiting for controller", + // "mydspace.status.mydspaceWaitingController": "Waiting for reviewer", "mydspace.status.mydspaceWaitingController": "In attesa del controllo", // "mydspace.status.mydspaceWorkflow": "Workflow", diff --git a/src/assets/i18n/ja.json5 b/src/assets/i18n/ja.json5 index da2385fd62..03323eb7ef 100644 --- a/src/assets/i18n/ja.json5 +++ b/src/assets/i18n/ja.json5 @@ -4284,9 +4284,9 @@ // TODO New key - Add a translation "mydspace.status.mydspaceValidation": "Validation", - // "mydspace.status.mydspaceWaitingController": "Waiting for controller", + // "mydspace.status.mydspaceWaitingController": "Waiting for reviewer", // TODO New key - Add a translation - "mydspace.status.mydspaceWaitingController": "Waiting for controller", + "mydspace.status.mydspaceWaitingController": "Waiting for reviewer", // "mydspace.status.mydspaceWorkflow": "Workflow", // TODO New key - Add a translation diff --git a/src/assets/i18n/kk.json5 b/src/assets/i18n/kk.json5 index d23dc23c47..dbaa8078e2 100644 --- a/src/assets/i18n/kk.json5 +++ b/src/assets/i18n/kk.json5 @@ -4145,7 +4145,7 @@ // "mydspace.status.mydspaceValidation": "Validation", "mydspace.status.mydspaceValidation": "Валидация", - // "mydspace.status.mydspaceWaitingController": "Waiting for controller", + // "mydspace.status.mydspaceWaitingController": "Waiting for reviewer", "mydspace.status.mydspaceWaitingController": "Контроллерді күтуде", // "mydspace.status.mydspaceWorkflow": "Workflow", diff --git a/src/assets/i18n/lv.json5 b/src/assets/i18n/lv.json5 index 81e2383a1f..3ceaac7fea 100644 --- a/src/assets/i18n/lv.json5 +++ b/src/assets/i18n/lv.json5 @@ -3498,7 +3498,7 @@ // "mydspace.status.mydspaceValidation": "Validation", "mydspace.status.mydspaceValidation": "Validācija", - // "mydspace.status.mydspaceWaitingController": "Waiting for controller", + // "mydspace.status.mydspaceWaitingController": "Waiting for reviewer", "mydspace.status.mydspaceWaitingController": "Gaida kontrolieri", // "mydspace.status.mydspaceWorkflow": "Workflow", diff --git a/src/assets/i18n/nl.json5 b/src/assets/i18n/nl.json5 index 280a87b96f..fc52543c38 100644 --- a/src/assets/i18n/nl.json5 +++ b/src/assets/i18n/nl.json5 @@ -3770,7 +3770,7 @@ // "mydspace.status.mydspaceValidation": "Validation", "mydspace.status.mydspaceValidation": "Validatie", - // "mydspace.status.mydspaceWaitingController": "Waiting for controller", + // "mydspace.status.mydspaceWaitingController": "Waiting for reviewer", "mydspace.status.mydspaceWaitingController": "Wachten op controlleur", // "mydspace.status.mydspaceWorkflow": "Workflow", diff --git a/src/assets/i18n/pt-BR.json5 b/src/assets/i18n/pt-BR.json5 index ce35f1ec05..5061c5a0e9 100644 --- a/src/assets/i18n/pt-BR.json5 +++ b/src/assets/i18n/pt-BR.json5 @@ -4533,7 +4533,7 @@ // "mydspace.status.mydspaceValidation": "Validation", "mydspace.status.mydspaceValidation": "Validação", - // "mydspace.status.mydspaceWaitingController": "Waiting for controller", + // "mydspace.status.mydspaceWaitingController": "Waiting for reviewer", "mydspace.status.mydspaceWaitingController": "Esperando pelo controlador", // "mydspace.status.mydspaceWorkflow": "Workflow", diff --git a/src/assets/i18n/pt-PT.json5 b/src/assets/i18n/pt-PT.json5 index faa027705e..328cb20810 100644 --- a/src/assets/i18n/pt-PT.json5 +++ b/src/assets/i18n/pt-PT.json5 @@ -4478,7 +4478,7 @@ // "mydspace.status.mydspaceValidation": "Validation", "mydspace.status.mydspaceValidation": "Em validação", - // "mydspace.status.mydspaceWaitingController": "Waiting for controller", + // "mydspace.status.mydspaceWaitingController": "Waiting for reviewer", "mydspace.status.mydspaceWaitingController": "Aguarda validador", // "mydspace.status.mydspaceWorkflow": "Workflow", diff --git a/src/assets/i18n/sv.json5 b/src/assets/i18n/sv.json5 index 4e3576ccfc..d2fe72536c 100644 --- a/src/assets/i18n/sv.json5 +++ b/src/assets/i18n/sv.json5 @@ -3940,7 +3940,7 @@ // "mydspace.status.mydspaceValidation": "Validation", "mydspace.status.mydspaceValidation": "Validering", - // "mydspace.status.mydspaceWaitingController": "Waiting for controller", + // "mydspace.status.mydspaceWaitingController": "Waiting for reviewer", "mydspace.status.mydspaceWaitingController": "Väntar på kontrollant", // "mydspace.status.mydspaceWorkflow": "Workflow", diff --git a/src/assets/i18n/sw.json5 b/src/assets/i18n/sw.json5 index a470ee4b58..d2d663cdc5 100644 --- a/src/assets/i18n/sw.json5 +++ b/src/assets/i18n/sw.json5 @@ -4284,9 +4284,9 @@ // TODO New key - Add a translation "mydspace.status.mydspaceValidation": "Validation", - // "mydspace.status.mydspaceWaitingController": "Waiting for controller", + // "mydspace.status.mydspaceWaitingController": "Waiting for reviewer", // TODO New key - Add a translation - "mydspace.status.mydspaceWaitingController": "Waiting for controller", + "mydspace.status.mydspaceWaitingController": "Waiting for reviewer", // "mydspace.status.mydspaceWorkflow": "Workflow", // TODO New key - Add a translation diff --git a/src/assets/i18n/tr.json5 b/src/assets/i18n/tr.json5 index 153eaa1281..75bc5c9a36 100644 --- a/src/assets/i18n/tr.json5 +++ b/src/assets/i18n/tr.json5 @@ -3263,7 +3263,7 @@ // "mydspace.status.mydspaceValidation": "Validation", "mydspace.status.mydspaceValidation": "Doğrulama", - // "mydspace.status.mydspaceWaitingController": "Waiting for controller", + // "mydspace.status.mydspaceWaitingController": "Waiting for reviewer", "mydspace.status.mydspaceWaitingController": "Kontrolör bekleniyor", // "mydspace.status.mydspaceWorkflow": "Workflow", diff --git a/src/assets/i18n/uk.json5 b/src/assets/i18n/uk.json5 index 7df55fa236..fae770b6bd 100644 --- a/src/assets/i18n/uk.json5 +++ b/src/assets/i18n/uk.json5 @@ -3389,7 +3389,7 @@ // "mydspace.status.mydspaceValidation": "Validation", "mydspace.status.mydspaceValidation": "Перевірка", - // "mydspace.status.mydspaceWaitingController": "Waiting for controller", + // "mydspace.status.mydspaceWaitingController": "Waiting for reviewer", "mydspace.status.mydspaceWaitingController": "Чекаємо контролера", // "mydspace.status.mydspaceWorkflow": "Workflow", From f78f4b45fcb82654c2cca08cccb254f414a3244f Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Fri, 8 Dec 2023 22:02:19 +0300 Subject: [PATCH 06/47] src/assets/i18n/en.json5: minor updates for consistency We should be capitalizing acronyms and project-specific nouns like MyDSpace consistently in our interface. --- src/assets/i18n/en.json5 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 78d034c417..b3e1759c1b 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -590,9 +590,9 @@ "admin.metadata-import.page.error.addFile": "Select file first!", - "admin.metadata-import.page.error.addFileUrl": "Insert file url first!", + "admin.metadata-import.page.error.addFileUrl": "Insert file URL first!", - "admin.batch-import.page.error.addFile": "Select Zip file first!", + "admin.batch-import.page.error.addFile": "Select ZIP file first!", "admin.metadata-import.page.toggle.upload": "Upload", @@ -3028,9 +3028,9 @@ "mydspace.results.no-title": "No title", - "mydspace.results.no-uri": "No Uri", + "mydspace.results.no-uri": "No URI", - "mydspace.search-form.placeholder": "Search in mydspace...", + "mydspace.search-form.placeholder": "Search in MyDSpace...", "mydspace.show.workflow": "Workflow tasks", From d6c46847c2340c2e2755a81b0909e30a25052ea5 Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Fri, 8 Dec 2023 22:03:11 +0300 Subject: [PATCH 07/47] src/assets/i18n/en.json5: minor changes for consistency Fix some random capitalizations and strange wording. --- src/assets/i18n/en.json5 | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index b3e1759c1b..7bf16c4ee8 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -4,15 +4,15 @@ "401.link.home-page": "Take me to the home page", - "401.unauthorized": "unauthorized", + "401.unauthorized": "Unauthorized", "403.help": "You don't have permission to access this page. You can use the button below to get back to the home page.", "403.link.home-page": "Take me to the home page", - "403.forbidden": "forbidden", + "403.forbidden": "Forbidden", - "500.page-internal-server-error": "Service Unavailable", + "500.page-internal-server-error": "Service unavailable", "500.help": "The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.", @@ -22,15 +22,15 @@ "404.link.home-page": "Take me to the home page", - "404.page-not-found": "page not found", + "404.page-not-found": "Page not found", - "error-page.description.401": "unauthorized", + "error-page.description.401": "Unauthorized", - "error-page.description.403": "forbidden", + "error-page.description.403": "Forbidden", - "error-page.description.500": "Service Unavailable", + "error-page.description.500": "Service unavailable", - "error-page.description.404": "page not found", + "error-page.description.404": "Page not found", "error-page.orcid.generic-error": "An error occurred during login via ORCID. Make sure you have shared your ORCID account email address with DSpace. If the error persists, contact the administrator", @@ -58,7 +58,7 @@ "admin.registries.bitstream-formats.create.failure.head": "Failure", - "admin.registries.bitstream-formats.create.head": "Create Bitstream format", + "admin.registries.bitstream-formats.create.head": "Create bitstream format", "admin.registries.bitstream-formats.create.new": "Add a new bitstream format", @@ -300,7 +300,7 @@ "admin.access-control.epeople.form.email": "E-mail", - "admin.access-control.epeople.form.emailHint": "Must be valid e-mail address", + "admin.access-control.epeople.form.emailHint": "Must be a valid e-mail address", "admin.access-control.epeople.form.canLogIn": "Can log in", @@ -1192,9 +1192,9 @@ "community.edit.logo.label": "Community logo", - "community.edit.logo.notifications.add.error": "Uploading Community logo failed. Please verify the content before retrying.", + "community.edit.logo.notifications.add.error": "Uploading community logo failed. Please verify the content before retrying.", - "community.edit.logo.notifications.add.success": "Upload Community logo successful.", + "community.edit.logo.notifications.add.success": "Upload community logo successful.", "community.edit.logo.notifications.delete.success.title": "Logo deleted", @@ -1202,13 +1202,13 @@ "community.edit.logo.notifications.delete.error.title": "Error deleting logo", - "community.edit.logo.upload": "Drop a Community Logo to upload", + "community.edit.logo.upload": "Drop a community logo to upload", "community.edit.notifications.success": "Successfully edited the Community", "community.edit.notifications.unauthorized": "You do not have privileges to make this change", - "community.edit.notifications.error": "An error occured while editing the Community", + "community.edit.notifications.error": "An error occured while editing the community", "community.edit.return": "Back", @@ -1602,9 +1602,9 @@ "error.validation.required": "This field is required", - "error.validation.NotValidEmail": "This E-mail is not a valid email", + "error.validation.NotValidEmail": "This is not a valid e-mail", - "error.validation.emailTaken": "This E-mail is already taken", + "error.validation.emailTaken": "This e-mail is already taken", "error.validation.groupExists": "This group already exists", @@ -3034,7 +3034,7 @@ "mydspace.show.workflow": "Workflow tasks", - "mydspace.show.workspace": "Your Submissions", + "mydspace.show.workspace": "Your submissions", "mydspace.show.supervisedWorkspace": "Supervised items", @@ -3070,7 +3070,7 @@ "nav.login": "Log In", - "nav.user-profile-menu-and-logout": "User profile menu and Log Out", + "nav.user-profile-menu-and-logout": "User profile menu and log out", "nav.logout": "Log Out", From 2327513dd02db70c8a3e75388ef91028b341ba0d Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Sat, 10 Jun 2023 15:12:22 +0200 Subject: [PATCH 08/47] Created AbstractComponentLoaderComponent to load components dynamically --- .../abstract-component-loader.component.html | 1 + .../abstract-component-loader.component.ts | 113 ++++++++++++++++++ .../dynamic-component-loader.directive.ts | 16 +++ src/app/shared/shared.module.ts | 2 + 4 files changed, 132 insertions(+) create mode 100644 src/app/shared/abstract-component-loader/abstract-component-loader.component.html create mode 100644 src/app/shared/abstract-component-loader/abstract-component-loader.component.ts create mode 100644 src/app/shared/abstract-component-loader/dynamic-component-loader.directive.ts diff --git a/src/app/shared/abstract-component-loader/abstract-component-loader.component.html b/src/app/shared/abstract-component-loader/abstract-component-loader.component.html new file mode 100644 index 0000000000..2035dbadd0 --- /dev/null +++ b/src/app/shared/abstract-component-loader/abstract-component-loader.component.html @@ -0,0 +1 @@ + diff --git a/src/app/shared/abstract-component-loader/abstract-component-loader.component.ts b/src/app/shared/abstract-component-loader/abstract-component-loader.component.ts new file mode 100644 index 0000000000..6f934f5b4b --- /dev/null +++ b/src/app/shared/abstract-component-loader/abstract-component-loader.component.ts @@ -0,0 +1,113 @@ +import { Component, ComponentRef, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewChild, ViewContainerRef } from '@angular/core'; +import { Context } from '../../core/shared/context.model'; +import { ThemeService } from '../theme-support/theme.service'; +import { GenericConstructor } from '../../core/shared/generic-constructor'; +import { hasNoValue, hasValue, isNotEmpty } from '../empty.util'; +import { Subscription } from 'rxjs'; +import { DynamicComponentLoaderDirective } from './dynamic-component-loader.directive'; + +@Component({ + selector: 'ds-abstract-component-loader', + templateUrl: './abstract-component-loader.component.html', +}) +export abstract class AbstractComponentLoaderComponent implements OnInit, OnChanges, OnDestroy { + + /** + * The optional context + */ + @Input() context: Context; + + /** + * Directive to determine where the dynamic child component is located + */ + @ViewChild(DynamicComponentLoaderDirective, { static: true }) componentDirective: DynamicComponentLoaderDirective; + + /** + * The reference to the dynamic component + */ + protected compRef: ComponentRef; + + /** + * Array to track all subscriptions and unsubscribe them onDestroy + */ + protected subs: Subscription[] = []; + + protected inAndOutputNames: (keyof this)[] = [ + 'context', + ]; + + constructor( + protected themeService: ThemeService, + ) { + } + + /** + * Set up the dynamic child component + */ + ngOnInit(): void { + this.instantiateComponent(); + } + + /** + * Whenever the inputs change, update the inputs of the dynamic component + */ + ngOnChanges(changes: SimpleChanges): void { + if (hasNoValue(this.compRef)) { + // sometimes the component has not been initialized yet, so it first needs to be initialized + // before being called again + this.instantiateComponent(changes); + } else { + // if an input or output has changed + if (this.inAndOutputNames.some((name: any) => hasValue(changes[name]))) { + this.connectInputsAndOutputs(); + if (this.compRef?.instance && 'ngOnChanges' in this.compRef.instance) { + (this.compRef.instance as any).ngOnChanges(changes); + } + } + } + } + + ngOnDestroy(): void { + this.subs + .filter((subscription: Subscription) => hasValue(subscription)) + .forEach((subscription: Subscription) => subscription.unsubscribe()); + } + + public instantiateComponent(changes?: SimpleChanges): void { + const component: GenericConstructor = this.getComponent(); + + const viewContainerRef: ViewContainerRef = this.componentDirective.viewContainerRef; + viewContainerRef.clear(); + + this.compRef = viewContainerRef.createComponent( + component, { + index: 0, + injector: undefined, + }, + ); + + if (hasValue(changes)) { + this.ngOnChanges(changes); + } else { + this.connectInputsAndOutputs(); + } + } + + /** + * Fetch the component depending on the item's entity type, metadata representation type and context + */ + public abstract getComponent(): GenericConstructor; + + /** + * Connect the in and outputs of this component to the dynamic component, + * to ensure they're in sync + */ + protected connectInputsAndOutputs(): void { + if (isNotEmpty(this.inAndOutputNames) && hasValue(this.compRef) && hasValue(this.compRef.instance)) { + this.inAndOutputNames.filter((name: any) => this[name] !== undefined).forEach((name: any) => { + this.compRef.instance[name] = this[name]; + }); + } + } + +} diff --git a/src/app/shared/abstract-component-loader/dynamic-component-loader.directive.ts b/src/app/shared/abstract-component-loader/dynamic-component-loader.directive.ts new file mode 100644 index 0000000000..8c77df1cdb --- /dev/null +++ b/src/app/shared/abstract-component-loader/dynamic-component-loader.directive.ts @@ -0,0 +1,16 @@ +import { Directive, ViewContainerRef } from '@angular/core'; + +/** + * Directive used as a hook to know where to inject the dynamic loaded component + */ +@Directive({ + selector: '[dsDynamicComponentLoader]' +}) +export class DynamicComponentLoaderDirective { + + constructor( + public viewContainerRef: ViewContainerRef, + ) { + } + +} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 0f7871f7f9..2f9d3317fb 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -284,6 +284,7 @@ import { } from '../item-page/simple/field-components/specific-field/title/themed-item-page-field.component'; import { BitstreamListItemComponent } from './object-list/bitstream-list-item/bitstream-list-item.component'; import { NgxPaginationModule } from 'ngx-pagination'; +import { DynamicComponentLoaderDirective } from './abstract-component-loader/dynamic-component-loader.directive'; const MODULES = [ CommonModule, @@ -491,6 +492,7 @@ const DIRECTIVES = [ MetadataFieldValidator, HoverClassDirective, ContextHelpDirective, + DynamicComponentLoaderDirective, ]; @NgModule({ From fb7afaddd047c73ebf4cc97db70236e56f1a8f96 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Sat, 10 Jun 2023 15:45:59 +0200 Subject: [PATCH 09/47] Make ListableObjectComponentLoaderComponent extend AbstractComponentLoaderComponent --- ...-search-result-grid-element.component.html | 2 +- ...in-search-result-grid-element.component.ts | 6 +- ...admin-workflow-grid-element.component.html | 2 +- ...in-workflow-grid-element.component.spec.ts | 12 +- ...t-admin-workflow-grid-element.component.ts | 6 +- ...admin-workflow-grid-element.component.html | 2 +- ...in-workflow-grid-element.component.spec.ts | 12 +- ...t-admin-workflow-grid-element.component.ts | 8 +- ...ble-object-component-loader.component.html | 1 - ...-object-component-loader.component.spec.ts | 22 +-- ...table-object-component-loader.component.ts | 135 +++--------------- .../listable-object.directive.ts | 11 -- src/app/shared/shared.module.ts | 2 - 13 files changed, 59 insertions(+), 162 deletions(-) delete mode 100644 src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.html delete mode 100644 src/app/shared/object-collection/shared/listable-object/listable-object.directive.ts diff --git a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.html b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.html index c4b737849b..639c47f7f8 100644 --- a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.html +++ b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.html @@ -1,4 +1,4 @@ - +
diff --git a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.ts b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.ts index dab6694f36..b6271b5ad5 100644 --- a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.ts +++ b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.ts @@ -11,7 +11,7 @@ import { SearchResultGridElementComponent } from '../../../../../shared/object-g import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service'; import { BitstreamDataService } from '../../../../../core/data/bitstream-data.service'; import { GenericConstructor } from '../../../../../core/shared/generic-constructor'; -import { ListableObjectDirective } from '../../../../../shared/object-collection/shared/listable-object/listable-object.directive'; +import { DynamicComponentLoaderDirective } from '../../../../../shared/abstract-component-loader/dynamic-component-loader.directive'; import { ThemeService } from '../../../../../shared/theme-support/theme.service'; import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; @@ -25,7 +25,7 @@ import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service * The component for displaying a list element for an item search result on the admin search page */ export class ItemAdminSearchResultGridElementComponent extends SearchResultGridElementComponent implements OnInit { - @ViewChild(ListableObjectDirective, { static: true }) listableObjectDirective: ListableObjectDirective; + @ViewChild(DynamicComponentLoaderDirective, { static: true }) dynamicComponentLoaderDirective: DynamicComponentLoaderDirective; @ViewChild('badges', { static: true }) badges: ElementRef; @ViewChild('buttons', { static: true }) buttons: ElementRef; @@ -46,7 +46,7 @@ export class ItemAdminSearchResultGridElementComponent extends SearchResultGridE super.ngOnInit(); const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.getComponent()); - const viewContainerRef = this.listableObjectDirective.viewContainerRef; + const viewContainerRef = this.dynamicComponentLoaderDirective.viewContainerRef; viewContainerRef.clear(); const componentRef = viewContainerRef.createComponent( diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.html b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.html index 87bae0c261..c5c2a5331a 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.html +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.html @@ -1,4 +1,4 @@ - +
diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.spec.ts b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.spec.ts index 8035c53547..b02fa476ea 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.spec.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.spec.ts @@ -18,8 +18,8 @@ import { ItemGridElementComponent } from '../../../../../shared/object-grid/item-grid-element/item-types/item/item-grid-element.component'; import { - ListableObjectDirective -} from '../../../../../shared/object-collection/shared/listable-object/listable-object.directive'; + DynamicComponentLoaderDirective +} from '../../../../../shared/abstract-component-loader/dynamic-component-loader.directive'; import { WorkflowItemSearchResult } from '../../../../../shared/object-collection/shared/workflow-item-search-result.model'; @@ -38,7 +38,7 @@ describe('WorkflowItemSearchResultAdminWorkflowGridElementComponent', () => { let itemRD$; let linkService; let object; - let themeService; + let themeService: ThemeService; function init() { itemRD$ = createSuccessfulRemoteDataObject$(new Item()); @@ -55,7 +55,11 @@ describe('WorkflowItemSearchResultAdminWorkflowGridElementComponent', () => { init(); TestBed.configureTestingModule( { - declarations: [WorkflowItemSearchResultAdminWorkflowGridElementComponent, ItemGridElementComponent, ListableObjectDirective], + declarations: [ + WorkflowItemSearchResultAdminWorkflowGridElementComponent, + ItemGridElementComponent, + DynamicComponentLoaderDirective, + ], imports: [ NoopAnimationsModule, TranslateModule.forRoot(), diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.ts b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.ts index fd9d21e227..d4a69b829d 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.ts @@ -10,7 +10,7 @@ import { SearchResultGridElementComponent } from '../../../../../shared/object-g import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service'; import { BitstreamDataService } from '../../../../../core/data/bitstream-data.service'; import { GenericConstructor } from '../../../../../core/shared/generic-constructor'; -import { ListableObjectDirective } from '../../../../../shared/object-collection/shared/listable-object/listable-object.directive'; +import { DynamicComponentLoaderDirective } from '../../../../../shared/abstract-component-loader/dynamic-component-loader.directive'; import { WorkflowItem } from '../../../../../core/submission/models/workflowitem.model'; import { Observable } from 'rxjs'; import { LinkService } from '../../../../../core/cache/builders/link.service'; @@ -38,7 +38,7 @@ export class WorkflowItemSearchResultAdminWorkflowGridElementComponent extends S /** * Directive used to render the dynamic component in */ - @ViewChild(ListableObjectDirective, { static: true }) listableObjectDirective: ListableObjectDirective; + @ViewChild(DynamicComponentLoaderDirective, { static: true }) dynamicComponentLoaderDirective: DynamicComponentLoaderDirective; /** * The html child that contains the badges html @@ -77,7 +77,7 @@ export class WorkflowItemSearchResultAdminWorkflowGridElementComponent extends S this.item$.pipe(take(1)).subscribe((item: Item) => { const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.getComponent(item)); - const viewContainerRef = this.listableObjectDirective.viewContainerRef; + const viewContainerRef = this.dynamicComponentLoaderDirective.viewContainerRef; viewContainerRef.clear(); const componentRef = viewContainerRef.createComponent( diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workspace-item/workspace-item-search-result-admin-workflow-grid-element.component.html b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workspace-item/workspace-item-search-result-admin-workflow-grid-element.component.html index 767ad79786..f78a0a3ca4 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workspace-item/workspace-item-search-result-admin-workflow-grid-element.component.html +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workspace-item/workspace-item-search-result-admin-workflow-grid-element.component.html @@ -1,4 +1,4 @@ - +
diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workspace-item/workspace-item-search-result-admin-workflow-grid-element.component.spec.ts b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workspace-item/workspace-item-search-result-admin-workflow-grid-element.component.spec.ts index b9e752c104..d023e57709 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workspace-item/workspace-item-search-result-admin-workflow-grid-element.component.spec.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workspace-item/workspace-item-search-result-admin-workflow-grid-element.component.spec.ts @@ -20,8 +20,8 @@ import { ItemGridElementComponent } from '../../../../../shared/object-grid/item-grid-element/item-types/item/item-grid-element.component'; import { - ListableObjectDirective -} from '../../../../../shared/object-collection/shared/listable-object/listable-object.directive'; + DynamicComponentLoaderDirective +} from '../../../../../shared/abstract-component-loader/dynamic-component-loader.directive'; import { WorkflowItemSearchResult } from '../../../../../shared/object-collection/shared/workflow-item-search-result.model'; @@ -45,7 +45,7 @@ describe('WorkspaceItemSearchResultAdminWorkflowGridElementComponent', () => { let itemRD$; let linkService; let object; - let themeService; + let themeService: ThemeService; let supervisionOrderDataService; function init() { @@ -67,7 +67,11 @@ describe('WorkspaceItemSearchResultAdminWorkflowGridElementComponent', () => { init(); TestBed.configureTestingModule( { - declarations: [WorkspaceItemSearchResultAdminWorkflowGridElementComponent, ItemGridElementComponent, ListableObjectDirective], + declarations: [ + WorkspaceItemSearchResultAdminWorkflowGridElementComponent, + ItemGridElementComponent, + DynamicComponentLoaderDirective, + ], imports: [ NoopAnimationsModule, TranslateModule.forRoot(), diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workspace-item/workspace-item-search-result-admin-workflow-grid-element.component.ts b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workspace-item/workspace-item-search-result-admin-workflow-grid-element.component.ts index d6f39e79fe..f74d8b3e5c 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workspace-item/workspace-item-search-result-admin-workflow-grid-element.component.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workspace-item/workspace-item-search-result-admin-workflow-grid-element.component.ts @@ -16,9 +16,7 @@ import { import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service'; import { BitstreamDataService } from '../../../../../core/data/bitstream-data.service'; import { GenericConstructor } from '../../../../../core/shared/generic-constructor'; -import { - ListableObjectDirective -} from '../../../../../shared/object-collection/shared/listable-object/listable-object.directive'; +import { DynamicComponentLoaderDirective } from '../../../../../shared/abstract-component-loader/dynamic-component-loader.directive'; import { WorkspaceItem } from '../../../../../core/submission/models/workspaceitem.model'; import { LinkService } from '../../../../../core/cache/builders/link.service'; import { followLink } from '../../../../../shared/utils/follow-link-config.model'; @@ -67,7 +65,7 @@ export class WorkspaceItemSearchResultAdminWorkflowGridElementComponent extends /** * Directive used to render the dynamic component in */ - @ViewChild(ListableObjectDirective, { static: true }) listableObjectDirective: ListableObjectDirective; + @ViewChild(DynamicComponentLoaderDirective, { static: true }) dynamicComponentLoaderDirective: DynamicComponentLoaderDirective; /** * The html child that contains the badges html @@ -102,7 +100,7 @@ export class WorkspaceItemSearchResultAdminWorkflowGridElementComponent extends this.item$.pipe(take(1)).subscribe((item: Item) => { const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.getComponent(item)); - const viewContainerRef = this.listableObjectDirective.viewContainerRef; + const viewContainerRef = this.dynamicComponentLoaderDirective.viewContainerRef; viewContainerRef.clear(); const componentRef = viewContainerRef.createComponent( diff --git a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.html b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.html deleted file mode 100644 index 58561f0277..0000000000 --- a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.spec.ts b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.spec.ts index e893fe807b..7b9d010e39 100644 --- a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.spec.ts +++ b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.spec.ts @@ -8,7 +8,7 @@ import { ViewMode } from '../../../../core/shared/view-mode.model'; import { ItemListElementComponent } from '../../../object-list/item-list-element/item-types/item/item-list-element.component'; -import { ListableObjectDirective } from './listable-object.directive'; +import { DynamicComponentLoaderDirective } from '../../../abstract-component-loader/dynamic-component-loader.directive'; import { TranslateModule } from '@ngx-translate/core'; import { By } from '@angular/platform-browser'; import { provideMockStore } from '@ngrx/store/testing'; @@ -36,7 +36,7 @@ describe('ListableObjectComponentLoaderComponent', () => { }); TestBed.configureTestingModule({ imports: [TranslateModule.forRoot()], - declarations: [ListableObjectComponentLoaderComponent, ItemListElementComponent, ListableObjectDirective], + declarations: [ListableObjectComponentLoaderComponent, ItemListElementComponent, DynamicComponentLoaderDirective], schemas: [NO_ERRORS_SCHEMA], providers: [ provideMockStore({}), @@ -65,7 +65,7 @@ describe('ListableObjectComponentLoaderComponent', () => { describe('When the component is rendered', () => { it('should call the getListableObjectComponent function with the right types, view mode and context', () => { - expect(comp.getComponent).toHaveBeenCalledWith([testType], testViewMode, testContext); + expect(comp.getComponent).toHaveBeenCalled(); }); it('should connectInputsAndOutputs of loaded component', () => { @@ -78,29 +78,29 @@ describe('ListableObjectComponentLoaderComponent', () => { let reloadedObject: any; beforeEach(() => { - spyOn((comp as any), 'instantiateComponent').and.returnValue(null); - spyOn((comp as any).contentChange, 'emit').and.returnValue(null); + spyOn(comp, 'instantiateComponent').and.returnValue(null); + spyOn(comp.contentChange, 'emit').and.returnValue(null); listableComponent = fixture.debugElement.query(By.css('ds-item-list-element')).componentInstance; reloadedObject = 'object'; }); it('should re-instantiate the listable component', fakeAsync(() => { - expect((comp as any).instantiateComponent).not.toHaveBeenCalled(); + expect(comp.instantiateComponent).not.toHaveBeenCalled(); - (listableComponent as any).reloadedObject.emit(reloadedObject); + listableComponent.reloadedObject.emit(reloadedObject); tick(200); - expect((comp as any).instantiateComponent).toHaveBeenCalledWith(reloadedObject, undefined); + expect(comp.instantiateComponent).toHaveBeenCalledWith(undefined); })); it('should re-emit it as a contentChange', fakeAsync(() => { - expect((comp as any).contentChange.emit).not.toHaveBeenCalled(); + expect(comp.contentChange.emit).not.toHaveBeenCalled(); - (listableComponent as any).reloadedObject.emit(reloadedObject); + listableComponent.reloadedObject.emit(reloadedObject); tick(200); - expect((comp as any).contentChange.emit).toHaveBeenCalledWith(reloadedObject); + expect(comp.contentChange.emit).toHaveBeenCalledWith(reloadedObject); })); }); diff --git a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts index 7a3cc42bf5..f62ee44a1c 100644 --- a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts +++ b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts @@ -1,40 +1,26 @@ -import { - ChangeDetectorRef, - Component, - ComponentRef, - EventEmitter, - Input, - OnChanges, - OnDestroy, - OnInit, - Output, - SimpleChanges, - ViewChild -} from '@angular/core'; - -import { Subscription, combineLatest, of as observableOf, Observable } from 'rxjs'; +import { ChangeDetectorRef, Component, EventEmitter, Input, Output, SimpleChanges } from '@angular/core'; +import { combineLatest, Observable, of as observableOf } from 'rxjs'; import { take } from 'rxjs/operators'; - import { ListableObject } from '../listable-object.model'; import { ViewMode } from '../../../../core/shared/view-mode.model'; -import { Context } from '../../../../core/shared/context.model'; +import { Context } from 'src/app/core/shared/context.model'; import { getListableObjectComponent } from './listable-object.decorator'; import { GenericConstructor } from '../../../../core/shared/generic-constructor'; -import { ListableObjectDirective } from './listable-object.directive'; import { CollectionElementLinkType } from '../../collection-element-link.type'; -import { hasValue, isNotEmpty, hasNoValue } from '../../../empty.util'; import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; -import { ThemeService } from '../../../theme-support/theme.service'; +import { AbstractComponentLoaderComponent } from '../../../abstract-component-loader/abstract-component-loader.component'; +import { ThemeService } from 'src/app/shared/theme-support/theme.service'; @Component({ selector: 'ds-listable-object-component-loader', styleUrls: ['./listable-object-component-loader.component.scss'], - templateUrl: './listable-object-component-loader.component.html' + templateUrl: '../../../abstract-component-loader/abstract-component-loader.component.html', }) /** * Component for determining what component to use depending on the item's entity type (dspace.entity.type) */ -export class ListableObjectComponentLoaderComponent implements OnInit, OnChanges, OnDestroy { +export class ListableObjectComponentLoaderComponent extends AbstractComponentLoaderComponent { + /** * The item or metadata to determine the component for */ @@ -80,99 +66,36 @@ export class ListableObjectComponentLoaderComponent implements OnInit, OnChanges */ @Input() value: string; - /** - * Directive hook used to place the dynamic child component - */ - @ViewChild(ListableObjectDirective, { static: true }) listableObjectDirective: ListableObjectDirective; - /** * Emit when the listable object has been reloaded. */ @Output() contentChange = new EventEmitter(); - /** - * Array to track all subscriptions and unsubscribe them onDestroy - * @type {Array} - */ - protected subs: Subscription[] = []; - - /** - * The reference to the dynamic component - */ - protected compRef: ComponentRef; - /** * The list of input and output names for the dynamic component */ - protected inAndOutputNames: string[] = [ + protected inAndOutputNames: (keyof this)[] = [ + ...this.inAndOutputNames, 'object', 'index', 'linkType', 'listID', 'showLabel', 'showThumbnails', - 'context', 'viewMode', 'value', - 'hideBadges', 'contentChange', ]; - constructor(private cdr: ChangeDetectorRef, private themeService: ThemeService) { + constructor( + protected themeService: ThemeService, + protected cdr: ChangeDetectorRef, + ) { + super(themeService); } - /** - * Setup the dynamic child component - */ - ngOnInit(): void { - this.instantiateComponent(this.object); - } - - /** - * Whenever the inputs change, update the inputs of the dynamic component - */ - ngOnChanges(changes: SimpleChanges): void { - if (hasNoValue(this.compRef)) { - // sometimes the component has not been initialized yet, so it first needs to be initialized - // before being called again - this.instantiateComponent(this.object, changes); - } else { - // if an input or output has changed - if (this.inAndOutputNames.some((name: any) => hasValue(changes[name]))) { - this.connectInputsAndOutputs(); - if (this.compRef?.instance && 'ngOnChanges' in this.compRef.instance) { - (this.compRef.instance as any).ngOnChanges(changes); - } - } - } - } - - ngOnDestroy() { - this.subs - .filter((subscription) => hasValue(subscription)) - .forEach((subscription) => subscription.unsubscribe()); - } - - private instantiateComponent(object: ListableObject, changes?: SimpleChanges): void { - - const component = this.getComponent(object.getRenderTypes(), this.viewMode, this.context); - - const viewContainerRef = this.listableObjectDirective.viewContainerRef; - viewContainerRef.clear(); - - this.compRef = viewContainerRef.createComponent( - component, { - index: 0, - injector: undefined - } - ); - - if (hasValue(changes)) { - this.ngOnChanges(changes); - } else { - this.connectInputsAndOutputs(); - } - + public instantiateComponent(changes?: SimpleChanges): void { + super.instantiateComponent(changes); if ((this.compRef.instance as any).reloadedObject) { combineLatest([ observableOf(changes), @@ -181,7 +104,7 @@ export class ListableObjectComponentLoaderComponent implements OnInit, OnChanges if (reloadedObject) { this.compRef.destroy(); this.object = reloadedObject; - this.instantiateComponent(reloadedObject, simpleChanges); + this.instantiateComponent(simpleChanges); this.cdr.detectChanges(); this.contentChange.emit(reloadedObject); } @@ -189,26 +112,8 @@ export class ListableObjectComponentLoaderComponent implements OnInit, OnChanges } } - /** - * Fetch the component depending on the item's entity type, view mode and context - * @returns {GenericConstructor} - */ - getComponent(renderTypes: (string | GenericConstructor)[], - viewMode: ViewMode, - context: Context): GenericConstructor { - return getListableObjectComponent(renderTypes, viewMode, context, this.themeService.getThemeName()); - } - - /** - * Connect the in and outputs of this component to the dynamic component, - * to ensure they're in sync - */ - protected connectInputsAndOutputs(): void { - if (isNotEmpty(this.inAndOutputNames) && hasValue(this.compRef) && hasValue(this.compRef.instance)) { - this.inAndOutputNames.filter((name: any) => this[name] !== undefined).forEach((name: any) => { - this.compRef.instance[name] = this[name]; - }); - } + public getComponent(): GenericConstructor { + return getListableObjectComponent(this.object.getRenderTypes(), this.viewMode, this.context, this.themeService.getThemeName()); } } diff --git a/src/app/shared/object-collection/shared/listable-object/listable-object.directive.ts b/src/app/shared/object-collection/shared/listable-object/listable-object.directive.ts deleted file mode 100644 index 93c06961f4..0000000000 --- a/src/app/shared/object-collection/shared/listable-object/listable-object.directive.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Directive, ViewContainerRef } from '@angular/core'; - -@Directive({ - selector: '[dsListableObject]', -}) -/** - * Directive used as a hook to know where to inject the dynamic listable object component - */ -export class ListableObjectDirective { - constructor(public viewContainerRef: ViewContainerRef) { } -} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 2f9d3317fb..f185847596 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -177,7 +177,6 @@ import { import { ItemSearchResultListElementComponent } from './object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component'; -import { ListableObjectDirective } from './object-collection/shared/listable-object/listable-object.directive'; import { ItemMetadataRepresentationListElementComponent } from './object-list/metadata-representation-list-element/item/item-metadata-representation-list-element.component'; @@ -484,7 +483,6 @@ const DIRECTIVES = [ AutoFocusDirective, RoleDirective, MetadataRepresentationDirective, - ListableObjectDirective, ClaimedTaskActionsDirective, FileValueAccessorDirective, FileValidator, From fe60adb47f47f304433599e97df9f6b50320ab70 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Sat, 10 Jun 2023 15:58:31 +0200 Subject: [PATCH 10/47] Make ClaimedTaskActionsLoaderComponent extend AbstractComponentLoaderComponent --- ...claimed-task-actions-loader.component.html | 1 - ...imed-task-actions-loader.component.spec.ts | 22 +++-- .../claimed-task-actions-loader.component.ts | 98 +++---------------- .../claimed-task-actions.directive.ts | 11 --- src/app/shared/shared.module.ts | 2 - 5 files changed, 27 insertions(+), 107 deletions(-) delete mode 100644 src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.html delete mode 100644 src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions.directive.ts diff --git a/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.html b/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.html deleted file mode 100644 index 364443c47f..0000000000 --- a/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.spec.ts b/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.spec.ts index 95e31f5523..e48983d449 100644 --- a/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.spec.ts +++ b/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.spec.ts @@ -1,7 +1,7 @@ import { ClaimedTaskActionsLoaderComponent } from './claimed-task-actions-loader.component'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core'; -import { ClaimedTaskActionsDirective } from './claimed-task-actions.directive'; +import { DynamicComponentLoaderDirective } from '../../../abstract-component-loader/dynamic-component-loader.directive'; import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model'; import { TranslateModule } from '@ngx-translate/core'; import { ClaimedTaskActionsEditMetadataComponent } from '../edit-metadata/claimed-task-actions-edit-metadata.component'; @@ -17,6 +17,8 @@ import { getMockSearchService } from '../../../mocks/search-service.mock'; import { getMockRequestService } from '../../../mocks/request.service.mock'; import { Item } from '../../../../core/shared/item.model'; import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model'; +import { ThemeService } from 'src/app/shared/theme-support/theme.service'; +import { getMockThemeService } from '../../../mocks/theme-service.mock'; const searchService = getMockSearchService(); @@ -25,6 +27,7 @@ const requestService = getMockRequestService(); describe('ClaimedTaskActionsLoaderComponent', () => { let comp: ClaimedTaskActionsLoaderComponent; let fixture: ComponentFixture; + let themeService: ThemeService; const option = 'test_option'; const object = Object.assign(new ClaimedTask(), { id: 'claimed-task-1' }); @@ -61,9 +64,15 @@ describe('ClaimedTaskActionsLoaderComponent', () => { const workflowitem = Object.assign(new WorkflowItem(), { id: '333' }); beforeEach(waitForAsync(() => { + themeService = getMockThemeService('dspace'); + TestBed.configureTestingModule({ imports: [TranslateModule.forRoot()], - declarations: [ClaimedTaskActionsLoaderComponent, ClaimedTaskActionsEditMetadataComponent, ClaimedTaskActionsDirective], + declarations: [ + ClaimedTaskActionsLoaderComponent, + ClaimedTaskActionsEditMetadataComponent, + DynamicComponentLoaderDirective, + ], schemas: [NO_ERRORS_SCHEMA], providers: [ { provide: ClaimedTaskDataService, useValue: {} }, @@ -72,7 +81,8 @@ describe('ClaimedTaskActionsLoaderComponent', () => { { provide: Router, useValue: new RouterStub() }, { provide: SearchService, useValue: searchService }, { provide: RequestService, useValue: requestService }, - { provide: PoolTaskDataService, useValue: {} } + { provide: PoolTaskDataService, useValue: {} }, + { provide: ThemeService, useValue: themeService }, ] }).overrideComponent(ClaimedTaskActionsLoaderComponent, { set: { @@ -89,14 +99,14 @@ describe('ClaimedTaskActionsLoaderComponent', () => { comp.object = object; comp.option = option; comp.workflowitem = workflowitem; - spyOn(comp, 'getComponentByWorkflowTaskOption').and.returnValue(ClaimedTaskActionsEditMetadataComponent); + spyOn(comp, 'getComponent').and.returnValue(ClaimedTaskActionsEditMetadataComponent); fixture.detectChanges(); })); describe('When the component is rendered', () => { - it('should call the getComponentByWorkflowTaskOption function with the right option', () => { - expect(comp.getComponentByWorkflowTaskOption).toHaveBeenCalledWith(option); + it('should call the getComponent function', () => { + expect(comp.getComponent).toHaveBeenCalled(); }); }); }); diff --git a/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts b/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts index c0dc1cad02..75b392fe51 100644 --- a/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts @@ -1,33 +1,22 @@ -import { - Component, - ComponentFactoryResolver, - EventEmitter, - Input, - OnInit, - Output, - ViewChild, - OnChanges, - SimpleChanges, - ComponentRef, -} from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnInit, Output, } from '@angular/core'; import { getComponentByWorkflowTaskOption } from './claimed-task-actions-decorator'; import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model'; -import { ClaimedTaskActionsDirective } from './claimed-task-actions.directive'; -import { hasValue, isNotEmpty, hasNoValue } from '../../../empty.util'; import { MyDSpaceActionsResult } from '../../mydspace-actions'; import { Item } from '../../../../core/shared/item.model'; import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model'; import { ClaimedTaskActionsAbstractComponent } from '../abstract/claimed-task-actions-abstract.component'; +import { AbstractComponentLoaderComponent } from '../../../abstract-component-loader/abstract-component-loader.component'; +import { GenericConstructor } from '../../../../core/shared/generic-constructor'; @Component({ selector: 'ds-claimed-task-actions-loader', - templateUrl: './claimed-task-actions-loader.component.html' + templateUrl: '../../../abstract-component-loader/abstract-component-loader.component.html', }) /** * Component for loading a ClaimedTaskAction component depending on the "option" input * Passes on the ClaimedTask to the component and subscribes to the processCompleted output */ -export class ClaimedTaskActionsLoaderComponent implements OnInit, OnChanges { +export class ClaimedTaskActionsLoaderComponent extends AbstractComponentLoaderComponent implements OnInit, OnChanges { /** * The item object that belonging to the ClaimedTask object */ @@ -54,85 +43,20 @@ export class ClaimedTaskActionsLoaderComponent implements OnInit, OnChanges { */ @Output() processCompleted = new EventEmitter(); - /** - * Directive to determine where the dynamic child component is located - */ - @ViewChild(ClaimedTaskActionsDirective, {static: true}) claimedTaskActionsDirective: ClaimedTaskActionsDirective; - - /** - * The reference to the dynamic component - */ - protected compRef: ComponentRef; - /** * The list of input and output names for the dynamic component */ - protected inAndOutputNames: (keyof ClaimedTaskActionsAbstractComponent & keyof this)[] = [ + protected inAndOutputNames: (keyof this)[] = [ + ...this.inAndOutputNames, + 'item', 'object', 'option', + 'workflowitem', 'processCompleted', ]; - constructor(private componentFactoryResolver: ComponentFactoryResolver) { + public getComponent(): GenericConstructor { + return getComponentByWorkflowTaskOption(this.option); } - /** - * Fetch, create and initialize the relevant component - */ - ngOnInit(): void { - this.instantiateComponent(); - } - - /** - * Whenever the inputs change, update the inputs of the dynamic component - */ - ngOnChanges(changes: SimpleChanges): void { - if (hasNoValue(this.compRef)) { - // sometimes the component has not been initialized yet, so it first needs to be initialized - // before being called again - this.instantiateComponent(changes); - } else { - // if an input or output has changed - if (this.inAndOutputNames.some((name: any) => hasValue(changes[name]))) { - this.connectInputsAndOutputs(); - if (this.compRef?.instance && 'ngOnChanges' in this.compRef.instance) { - (this.compRef.instance as any).ngOnChanges(changes); - } - } - } - } - - private instantiateComponent(changes?: SimpleChanges): void { - const comp = this.getComponentByWorkflowTaskOption(this.option); - if (hasValue(comp)) { - const componentFactory = this.componentFactoryResolver.resolveComponentFactory(comp); - - const viewContainerRef = this.claimedTaskActionsDirective.viewContainerRef; - viewContainerRef.clear(); - - this.compRef = viewContainerRef.createComponent(componentFactory); - - if (hasValue(changes)) { - this.ngOnChanges(changes); - } else { - this.connectInputsAndOutputs(); - } - } - } - - getComponentByWorkflowTaskOption(option: string) { - return getComponentByWorkflowTaskOption(option); - } - - /** - * Connect the in and outputs of this component to the dynamic component, - * to ensure they're in sync - */ - protected connectInputsAndOutputs(): void { - if (isNotEmpty(this.inAndOutputNames) && hasValue(this.compRef) && hasValue(this.compRef.instance)) { - this.inAndOutputNames.filter((name: any) => this[name] !== undefined).forEach((name: any) => { - this.compRef.instance[name] = this[name]; - }); - } - } } diff --git a/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions.directive.ts b/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions.directive.ts deleted file mode 100644 index a4a55b541b..0000000000 --- a/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions.directive.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Directive, ViewContainerRef } from '@angular/core'; - -@Directive({ - selector: '[dsClaimedTaskActions]', -}) -/** - * Directive used as a hook to know where to inject the dynamic Claimed Task Actions component - */ -export class ClaimedTaskActionsDirective { - constructor(public viewContainerRef: ViewContainerRef) { } -} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index f185847596..c00b09e20f 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -197,7 +197,6 @@ import { FileValueAccessorDirective } from './utils/file-value-accessor.directiv import { ModifyItemOverviewComponent } from '../item-page/edit-item-page/modify-item-overview/modify-item-overview.component'; -import { ClaimedTaskActionsDirective } from './mydspace-actions/claimed-task/switcher/claimed-task-actions.directive'; import { ImpersonateNavbarComponent } from './impersonate-navbar/impersonate-navbar.component'; import { NgForTrackByIdDirective } from './ng-for-track-by-id.directive'; import { FileDownloadLinkComponent } from './file-download-link/file-download-link.component'; @@ -483,7 +482,6 @@ const DIRECTIVES = [ AutoFocusDirective, RoleDirective, MetadataRepresentationDirective, - ClaimedTaskActionsDirective, FileValueAccessorDirective, FileValidator, NgForTrackByIdDirective, From 774784a9b949943c8bdd87ead3dff280acac115e Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Sat, 10 Jun 2023 16:03:07 +0200 Subject: [PATCH 11/47] Make AdvancedWorkflowActionsLoaderComponent extend AbstractComponentLoaderComponent --- ...ced-workflow-actions-loader.component.html | 1 - ...ced-workflow-actions-loader.component.scss | 0 ...-workflow-actions-loader.component.spec.ts | 17 ++++++--- ...anced-workflow-actions-loader.component.ts | 38 ++++++++----------- .../advanced-workflow-actions.directive.ts | 16 -------- .../workflowitems-edit-page.module.ts | 4 -- 6 files changed, 27 insertions(+), 49 deletions(-) delete mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.html delete mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.scss delete mode 100644 src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions.directive.ts diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.html b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.html deleted file mode 100644 index 0904d0fcde..0000000000 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.scss b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.scss deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.spec.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.spec.ts index 2c12b07589..3188d00891 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.spec.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.spec.ts @@ -3,12 +3,14 @@ import { AdvancedWorkflowActionsLoaderComponent } from './advanced-workflow-acti import { Router } from '@angular/router'; import { RouterStub } from '../../../shared/testing/router.stub'; import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { AdvancedWorkflowActionsDirective } from './advanced-workflow-actions.directive'; +import { DynamicComponentLoaderDirective } from '../../../shared/abstract-component-loader/dynamic-component-loader.directive'; import { rendersAdvancedWorkflowTaskOption } from '../../../shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-decorator'; import { By } from '@angular/platform-browser'; import { PAGE_NOT_FOUND_PATH } from '../../../app-routing-paths'; +import { ThemeService } from 'src/app/shared/theme-support/theme.service'; +import { getMockThemeService } from 'src/app/shared/mocks/theme-service.mock'; const ADVANCED_WORKFLOW_ACTION_TEST = 'testaction'; @@ -17,17 +19,20 @@ describe('AdvancedWorkflowActionsLoaderComponent', () => { let fixture: ComponentFixture; let router: RouterStub; + let themeService: ThemeService; beforeEach(async () => { router = new RouterStub(); + themeService = getMockThemeService(); await TestBed.configureTestingModule({ declarations: [ - AdvancedWorkflowActionsDirective, + DynamicComponentLoaderDirective, AdvancedWorkflowActionsLoaderComponent, ], providers: [ { provide: Router, useValue: router }, + { provide: ThemeService, useValue: themeService }, ], }).overrideComponent(AdvancedWorkflowActionsLoaderComponent, { set: { @@ -50,24 +55,24 @@ describe('AdvancedWorkflowActionsLoaderComponent', () => { describe('When the component is rendered', () => { it('should display the AdvancedWorkflowActionTestComponent when the type has been defined in a rendersAdvancedWorkflowTaskOption', () => { - spyOn(component, 'getComponentByWorkflowTaskOption').and.returnValue(AdvancedWorkflowActionTestComponent); + spyOn(component, 'getComponent').and.returnValue(AdvancedWorkflowActionTestComponent); component.ngOnInit(); fixture.detectChanges(); - expect(component.getComponentByWorkflowTaskOption).toHaveBeenCalledWith(ADVANCED_WORKFLOW_ACTION_TEST); + expect(component.getComponent).toHaveBeenCalled(); expect(fixture.debugElement.query(By.css('#AdvancedWorkflowActionsLoaderComponent'))).not.toBeNull(); expect(router.navigate).not.toHaveBeenCalled(); }); it('should redirect to page not found when the type has not been defined in a rendersAdvancedWorkflowTaskOption', () => { - spyOn(component, 'getComponentByWorkflowTaskOption').and.returnValue(undefined); + spyOn(component, 'getComponent').and.returnValue(undefined); component.type = 'nonexistingaction'; component.ngOnInit(); fixture.detectChanges(); - expect(component.getComponentByWorkflowTaskOption).toHaveBeenCalledWith('nonexistingaction'); + expect(component.getComponent).toHaveBeenCalled(); expect(router.navigate).toHaveBeenCalledWith([PAGE_NOT_FOUND_PATH]); }); }); diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.ts index 32f14c015d..10cae7ec7d 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.ts @@ -1,21 +1,22 @@ -import { Component, Input, ViewChild, ComponentFactoryResolver, OnInit } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; import { hasValue } from '../../../shared/empty.util'; import { getAdvancedComponentByWorkflowTaskOption } from '../../../shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-decorator'; -import { AdvancedWorkflowActionsDirective } from './advanced-workflow-actions.directive'; import { Router } from '@angular/router'; import { PAGE_NOT_FOUND_PATH } from '../../../app-routing-paths'; +import { GenericConstructor } from '../../../core/shared/generic-constructor'; +import { AbstractComponentLoaderComponent } from 'src/app/shared/abstract-component-loader/abstract-component-loader.component'; +import { ThemeService } from '../../../shared/theme-support/theme.service'; /** * Component for loading a {@link AdvancedWorkflowActionComponent} depending on the "{@link type}" input */ @Component({ selector: 'ds-advanced-workflow-actions-loader', - templateUrl: './advanced-workflow-actions-loader.component.html', - styleUrls: ['./advanced-workflow-actions-loader.component.scss'], + templateUrl: '../../../shared/abstract-component-loader/abstract-component-loader.component.html', }) -export class AdvancedWorkflowActionsLoaderComponent implements OnInit { +export class AdvancedWorkflowActionsLoaderComponent extends AbstractComponentLoaderComponent implements OnInit { /** * The name of the type to render @@ -23,35 +24,28 @@ export class AdvancedWorkflowActionsLoaderComponent implements OnInit { */ @Input() type: string; - /** - * Directive to determine where the dynamic child component is located - */ - @ViewChild(AdvancedWorkflowActionsDirective, { static: true }) claimedTaskActionsDirective: AdvancedWorkflowActionsDirective; + protected inAndOutputNames: (keyof this)[] = [ + ...this.inAndOutputNames, + 'type', + ]; constructor( - private componentFactoryResolver: ComponentFactoryResolver, + protected themeService: ThemeService, private router: Router, ) { + super(themeService); } - /** - * Fetch, create and initialize the relevant component - */ ngOnInit(): void { - const comp = this.getComponentByWorkflowTaskOption(this.type); - if (hasValue(comp)) { - const componentFactory = this.componentFactoryResolver.resolveComponentFactory(comp); - - const viewContainerRef = this.claimedTaskActionsDirective.viewContainerRef; - viewContainerRef.clear(); - viewContainerRef.createComponent(componentFactory); + if (hasValue(this.getComponent())) { + super.ngOnInit(); } else { void this.router.navigate([PAGE_NOT_FOUND_PATH]); } } - getComponentByWorkflowTaskOption(type: string): any { - return getAdvancedComponentByWorkflowTaskOption(type); + public getComponent(): GenericConstructor { + return getAdvancedComponentByWorkflowTaskOption(this.type); } } diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions.directive.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions.directive.ts deleted file mode 100644 index e569f6cc6f..0000000000 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions.directive.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Directive, ViewContainerRef } from '@angular/core'; - -@Directive({ - selector: '[dsAdvancedWorkflowActions]', -}) -/** - * Directive used as a hook to know where to inject the dynamic Advanced Claimed Task Actions component - */ -export class AdvancedWorkflowActionsDirective { - - constructor( - public viewContainerRef: ViewContainerRef, - ) { - } - -} diff --git a/src/app/workflowitems-edit-page/workflowitems-edit-page.module.ts b/src/app/workflowitems-edit-page/workflowitems-edit-page.module.ts index cf998c5274..c2bd3d5ad7 100644 --- a/src/app/workflowitems-edit-page/workflowitems-edit-page.module.ts +++ b/src/app/workflowitems-edit-page/workflowitems-edit-page.module.ts @@ -23,9 +23,6 @@ import { import { AdvancedWorkflowActionPageComponent } from './advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component'; -import { - AdvancedWorkflowActionsDirective -} from './advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions.directive'; import { AccessControlModule } from '../access-control/access-control.module'; import { ReviewersListComponent @@ -54,7 +51,6 @@ import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; AdvancedWorkflowActionRatingComponent, AdvancedWorkflowActionSelectReviewerComponent, AdvancedWorkflowActionPageComponent, - AdvancedWorkflowActionsDirective, ReviewersListComponent, ] }) From 2bae174350689887de4b856ab3ebae20b2389586 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Sat, 10 Jun 2023 16:14:54 +0200 Subject: [PATCH 12/47] Make MetadataRepresentationLoaderComponent extend AbstractComponentLoaderComponent --- ...adata-representation-loader.component.html | 1 - ...ta-representation-loader.component.spec.ts | 18 ++-- ...etadata-representation-loader.component.ts | 99 ++----------------- .../metadata-representation.directive.ts | 11 --- src/app/shared/shared.module.ts | 2 - 5 files changed, 20 insertions(+), 111 deletions(-) delete mode 100644 src/app/shared/metadata-representation/metadata-representation-loader.component.html delete mode 100644 src/app/shared/metadata-representation/metadata-representation.directive.ts diff --git a/src/app/shared/metadata-representation/metadata-representation-loader.component.html b/src/app/shared/metadata-representation/metadata-representation-loader.component.html deleted file mode 100644 index 3979c238ad..0000000000 --- a/src/app/shared/metadata-representation/metadata-representation-loader.component.html +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/app/shared/metadata-representation/metadata-representation-loader.component.spec.ts b/src/app/shared/metadata-representation/metadata-representation-loader.component.spec.ts index 7edf1a700e..c9bec402d1 100644 --- a/src/app/shared/metadata-representation/metadata-representation-loader.component.spec.ts +++ b/src/app/shared/metadata-representation/metadata-representation-loader.component.spec.ts @@ -6,10 +6,11 @@ import { MetadataRepresentationType } from '../../core/shared/metadata-representation/metadata-representation.model'; import { MetadataRepresentationLoaderComponent } from './metadata-representation-loader.component'; -import { MetadataRepresentationDirective } from './metadata-representation.directive'; +import { DynamicComponentLoaderDirective } from '../abstract-component-loader/dynamic-component-loader.directive'; import { METADATA_REPRESENTATION_COMPONENT_FACTORY } from './metadata-representation.decorator'; import { ThemeService } from '../theme-support/theme.service'; import { PlainTextMetadataListElementComponent } from '../object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component'; +import { getMockThemeService } from '../mocks/theme-service.mock'; const testType = 'TestType'; const testContext = Context.Search; @@ -36,12 +37,14 @@ describe('MetadataRepresentationLoaderComponent', () => { const themeName = 'test-theme'; beforeEach(waitForAsync(() => { - themeService = jasmine.createSpyObj('themeService', { - getThemeName: themeName, - }); + themeService = getMockThemeService(themeName); TestBed.configureTestingModule({ imports: [], - declarations: [MetadataRepresentationLoaderComponent, PlainTextMetadataListElementComponent, MetadataRepresentationDirective], + declarations: [ + MetadataRepresentationLoaderComponent, + PlainTextMetadataListElementComponent, + DynamicComponentLoaderDirective, + ], schemas: [NO_ERRORS_SCHEMA], providers: [ { @@ -64,6 +67,7 @@ describe('MetadataRepresentationLoaderComponent', () => { beforeEach(waitForAsync(() => { fixture = TestBed.createComponent(MetadataRepresentationLoaderComponent); comp = fixture.componentInstance; + spyOn(comp, 'getComponent').and.callThrough(); comp.mdRepresentation = new TestType(); comp.context = testContext; @@ -71,8 +75,8 @@ describe('MetadataRepresentationLoaderComponent', () => { })); describe('When the component is rendered', () => { - it('should call the getMetadataRepresentationComponent function with the right entity type, representation type and context', () => { - expect((comp as any).getMetadataRepresentationComponent).toHaveBeenCalledWith(testType, testRepresentationType, testContext, themeName); + it('should call the getComponent function', () => { + expect(comp.getComponent).toHaveBeenCalled(); }); }); }); diff --git a/src/app/shared/metadata-representation/metadata-representation-loader.component.ts b/src/app/shared/metadata-representation/metadata-representation-loader.component.ts index 42ee093278..83542512ed 100644 --- a/src/app/shared/metadata-representation/metadata-representation-loader.component.ts +++ b/src/app/shared/metadata-representation/metadata-representation-loader.component.ts @@ -1,4 +1,4 @@ -import { Component, ComponentFactoryResolver, Inject, Input, OnInit, ViewChild, OnChanges, SimpleChanges, ComponentRef, ViewContainerRef, ComponentFactory } from '@angular/core'; +import { Component, Inject, Input } from '@angular/core'; import { MetadataRepresentation, MetadataRepresentationType @@ -7,118 +7,37 @@ import { METADATA_REPRESENTATION_COMPONENT_FACTORY } from './metadata-representa import { Context } from '../../core/shared/context.model'; import { GenericConstructor } from '../../core/shared/generic-constructor'; import { MetadataRepresentationListElementComponent } from '../object-list/metadata-representation-list-element/metadata-representation-list-element.component'; -import { MetadataRepresentationDirective } from './metadata-representation.directive'; -import { hasValue, isNotEmpty, hasNoValue } from '../empty.util'; import { ThemeService } from '../theme-support/theme.service'; +import { AbstractComponentLoaderComponent } from '../abstract-component-loader/abstract-component-loader.component'; @Component({ selector: 'ds-metadata-representation-loader', - templateUrl: './metadata-representation-loader.component.html' + templateUrl: '../abstract-component-loader/abstract-component-loader.component.html', }) /** * Component for determining what component to use depending on the item's entity type (dspace.entity.type), its metadata representation and, optionally, its context */ -export class MetadataRepresentationLoaderComponent implements OnInit, OnChanges { +export class MetadataRepresentationLoaderComponent extends AbstractComponentLoaderComponent { /** * The item or metadata to determine the component for */ - private _mdRepresentation: MetadataRepresentation; - get mdRepresentation(): MetadataRepresentation { - return this._mdRepresentation; - } - @Input() set mdRepresentation(nextValue: MetadataRepresentation) { - this._mdRepresentation = nextValue; - if (hasValue(this.compRef?.instance)) { - this.compRef.instance.mdRepresentation = nextValue; - } - } - - /** - * The optional context - */ - @Input() context: Context; - - /** - * Directive to determine where the dynamic child component is located - */ - @ViewChild(MetadataRepresentationDirective, {static: true}) mdRepDirective: MetadataRepresentationDirective; - - /** - * The reference to the dynamic component - */ - protected compRef: ComponentRef; + @Input() mdRepresentation: MetadataRepresentation; protected inAndOutputNames: (keyof this)[] = [ - 'context', + ...this.inAndOutputNames, 'mdRepresentation', ]; constructor( - private componentFactoryResolver: ComponentFactoryResolver, - private themeService: ThemeService, + protected themeService: ThemeService, @Inject(METADATA_REPRESENTATION_COMPONENT_FACTORY) private getMetadataRepresentationComponent: (entityType: string, mdRepresentationType: MetadataRepresentationType, context: Context, theme: string) => GenericConstructor, ) { + super(themeService); } - /** - * Set up the dynamic child component - */ - ngOnInit(): void { - this.instantiateComponent(); - } - - /** - * Whenever the inputs change, update the inputs of the dynamic component - */ - ngOnChanges(changes: SimpleChanges): void { - if (hasNoValue(this.compRef)) { - // sometimes the component has not been initialized yet, so it first needs to be initialized - // before being called again - this.instantiateComponent(changes); - } else { - // if an input or output has changed - if (this.inAndOutputNames.some((name: any) => hasValue(changes[name]))) { - this.connectInputsAndOutputs(); - if (this.compRef?.instance && 'ngOnChanges' in this.compRef.instance) { - (this.compRef.instance as any).ngOnChanges(changes); - } - } - } - } - - private instantiateComponent(changes?: SimpleChanges): void { - const componentFactory: ComponentFactory = this.componentFactoryResolver.resolveComponentFactory(this.getComponent()); - - const viewContainerRef: ViewContainerRef = this.mdRepDirective.viewContainerRef; - viewContainerRef.clear(); - - this.compRef = viewContainerRef.createComponent(componentFactory); - - if (hasValue(changes)) { - this.ngOnChanges(changes); - } else { - this.connectInputsAndOutputs(); - } - } - - /** - * Fetch the component depending on the item's entity type, metadata representation type and context - * @returns {string} - */ - private getComponent(): GenericConstructor { + public getComponent(): GenericConstructor { return this.getMetadataRepresentationComponent(this.mdRepresentation.itemType, this.mdRepresentation.representationType, this.context, this.themeService.getThemeName()); } - /** - * Connect the in and outputs of this component to the dynamic component, - * to ensure they're in sync - */ - protected connectInputsAndOutputs(): void { - if (isNotEmpty(this.inAndOutputNames) && hasValue(this.compRef) && hasValue(this.compRef.instance)) { - this.inAndOutputNames.filter((name: any) => this[name] !== undefined).forEach((name: any) => { - this.compRef.instance[name] = this[name]; - }); - } - } } diff --git a/src/app/shared/metadata-representation/metadata-representation.directive.ts b/src/app/shared/metadata-representation/metadata-representation.directive.ts deleted file mode 100644 index 9ff0573baf..0000000000 --- a/src/app/shared/metadata-representation/metadata-representation.directive.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Directive, ViewContainerRef } from '@angular/core'; - -@Directive({ - selector: '[dsMetadataRepresentation]', -}) -/** - * Directive used as a hook to know where to inject the dynamic metadata representation component - */ -export class MetadataRepresentationDirective { - constructor(public viewContainerRef: ViewContainerRef) { } -} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index c00b09e20f..bce7282e8b 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -170,7 +170,6 @@ import { AccessStatusBadgeComponent } from './object-collection/shared/badges/ac import { MetadataRepresentationLoaderComponent } from './metadata-representation/metadata-representation-loader.component'; -import { MetadataRepresentationDirective } from './metadata-representation/metadata-representation.directive'; import { ListableObjectComponentLoaderComponent } from './object-collection/shared/listable-object/listable-object-component-loader.component'; @@ -481,7 +480,6 @@ const DIRECTIVES = [ InListValidator, AutoFocusDirective, RoleDirective, - MetadataRepresentationDirective, FileValueAccessorDirective, FileValidator, NgForTrackByIdDirective, From 26e0bc81c7a58548d8557a3a672891b107f250d5 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Sat, 9 Dec 2023 22:11:50 +0100 Subject: [PATCH 13/47] Removed Themed components from PR 1842 because those should be themed using the decorator rendersBrowseBy(browseType, themeName) and fixed the components in the custom theme folder - Removed ngOnDestroy from BrowseByTitlePageComponent because super.ngOnDestroy already included the unsubscribe functionality - Removed BrowseBySwitcherComponent since a themed version of that isn't really useful --- .../browse-by-date-page.component.ts | 6 ++-- .../themed-browse-by-date-page.component.ts | 29 ------------------- .../browse-by-metadata-page.component.ts | 2 ++ ...hemed-browse-by-metadata-page.component.ts | 29 ------------------- .../browse-by-switcher/browse-by-decorator.ts | 3 +- .../themed-browse-by-switcher.component.ts | 28 ------------------ .../browse-by-taxonomy-page.component.ts | 3 +- ...hemed-browse-by-taxonomy-page.component.ts | 28 ------------------ .../browse-by-title-page.component.ts | 11 +++---- .../themed-browse-by-title-page.component.ts | 29 ------------------- src/app/browse-by/browse-by.module.ts | 9 ------ .../browse-by-date-page.component.ts | 7 ++--- .../browse-by-metadata-page.component.ts | 7 ++--- .../browse-by-switcher.component.ts | 15 ---------- .../browse-by-taxonomy-page.component.ts | 5 ++-- .../browse-by-title-page.component.ts | 7 ++--- src/themes/custom/lazy-theme.module.ts | 2 -- 17 files changed, 22 insertions(+), 198 deletions(-) delete mode 100644 src/app/browse-by/browse-by-date-page/themed-browse-by-date-page.component.ts delete mode 100644 src/app/browse-by/browse-by-metadata-page/themed-browse-by-metadata-page.component.ts delete mode 100644 src/app/browse-by/browse-by-switcher/themed-browse-by-switcher.component.ts delete mode 100644 src/app/browse-by/browse-by-taxonomy-page/themed-browse-by-taxonomy-page.component.ts delete mode 100644 src/app/browse-by/browse-by-title-page/themed-browse-by-title-page.component.ts delete mode 100644 src/themes/custom/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts diff --git a/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts b/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts index 7074190e1e..b106803351 100644 --- a/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts +++ b/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectorRef, Component, Inject } from '@angular/core'; +import { ChangeDetectorRef, Component, Inject, OnInit } from '@angular/core'; import { BrowseByMetadataPageComponent, browseParamsToOptions, @@ -19,6 +19,7 @@ import { APP_CONFIG, AppConfig } from '../../../config/app-config.interface'; import { RemoteData } from '../../core/data/remote-data'; import { Item } from '../../core/shared/item.model'; import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; +import { rendersBrowseBy, BrowseByDataType } from '../browse-by-switcher/browse-by-decorator'; @Component({ selector: 'ds-browse-by-date-page', @@ -30,7 +31,8 @@ import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; * A metadata definition (a.k.a. browse id) is a short term used to describe one or multiple metadata fields. * An example would be 'dateissued' for 'dc.date.issued' */ -export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent { +@rendersBrowseBy(BrowseByDataType.Date) +export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent implements OnInit { /** * The default metadata keys to use for determining the lower limit of the StartsWith dropdown options diff --git a/src/app/browse-by/browse-by-date-page/themed-browse-by-date-page.component.ts b/src/app/browse-by/browse-by-date-page/themed-browse-by-date-page.component.ts deleted file mode 100644 index 8eeae0c5de..0000000000 --- a/src/app/browse-by/browse-by-date-page/themed-browse-by-date-page.component.ts +++ /dev/null @@ -1,29 +0,0 @@ -import {Component} from '@angular/core'; -import { ThemedComponent } from '../../shared/theme-support/themed.component'; -import { BrowseByDatePageComponent } from './browse-by-date-page.component'; -import {BrowseByDataType, rendersBrowseBy} from '../browse-by-switcher/browse-by-decorator'; - -/** - * Themed wrapper for BrowseByDatePageComponent - * */ -@Component({ - selector: 'ds-themed-browse-by-metadata-page', - styleUrls: [], - templateUrl: '../../shared/theme-support/themed.component.html', -}) - -@rendersBrowseBy(BrowseByDataType.Date) -export class ThemedBrowseByDatePageComponent - extends ThemedComponent { - protected getComponentName(): string { - return 'BrowseByDatePageComponent'; - } - - protected importThemedComponent(themeName: string): Promise { - return import(`../../../themes/${themeName}/app/browse-by/browse-by-date-page/browse-by-date-page.component`); - } - - protected importUnthemedComponent(): Promise { - return import(`./browse-by-date-page.component`); - } -} diff --git a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts index 113bc67c92..28d57ce3a4 100644 --- a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts +++ b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts @@ -22,6 +22,7 @@ import { Collection } from '../../core/shared/collection.model'; import { Community } from '../../core/shared/community.model'; import { APP_CONFIG, AppConfig } from '../../../config/app-config.interface'; import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; +import { rendersBrowseBy, BrowseByDataType } from '../browse-by-switcher/browse-by-decorator'; export const BBM_PAGINATION_ID = 'bbm'; @@ -36,6 +37,7 @@ export const BBM_PAGINATION_ID = 'bbm'; * or multiple metadata fields. An example would be 'author' for * 'dc.contributor.*' */ +@rendersBrowseBy(BrowseByDataType.Metadata) export class BrowseByMetadataPageComponent implements OnInit, OnDestroy { /** diff --git a/src/app/browse-by/browse-by-metadata-page/themed-browse-by-metadata-page.component.ts b/src/app/browse-by/browse-by-metadata-page/themed-browse-by-metadata-page.component.ts deleted file mode 100644 index b0679258e9..0000000000 --- a/src/app/browse-by/browse-by-metadata-page/themed-browse-by-metadata-page.component.ts +++ /dev/null @@ -1,29 +0,0 @@ -import {Component} from '@angular/core'; -import { ThemedComponent } from '../../shared/theme-support/themed.component'; -import { BrowseByMetadataPageComponent } from './browse-by-metadata-page.component'; -import {BrowseByDataType, rendersBrowseBy} from '../browse-by-switcher/browse-by-decorator'; - -/** - * Themed wrapper for BrowseByMetadataPageComponent - **/ -@Component({ - selector: 'ds-themed-browse-by-metadata-page', - styleUrls: [], - templateUrl: '../../shared/theme-support/themed.component.html', -}) - -@rendersBrowseBy(BrowseByDataType.Metadata) -export class ThemedBrowseByMetadataPageComponent - extends ThemedComponent { - protected getComponentName(): string { - return 'BrowseByMetadataPageComponent'; - } - - protected importThemedComponent(themeName: string): Promise { - return import(`../../../themes/${themeName}/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component`); - } - - protected importUnthemedComponent(): Promise { - return import(`./browse-by-metadata-page.component`); - } -} diff --git a/src/app/browse-by/browse-by-switcher/browse-by-decorator.ts b/src/app/browse-by/browse-by-switcher/browse-by-decorator.ts index b59a46cae1..11ff1cec28 100644 --- a/src/app/browse-by/browse-by-switcher/browse-by-decorator.ts +++ b/src/app/browse-by/browse-by-switcher/browse-by-decorator.ts @@ -9,7 +9,8 @@ import { export enum BrowseByDataType { Title = 'title', Metadata = 'text', - Date = 'date' + Date = 'date', + Hierarchy = 'hierarchy', } export const DEFAULT_BROWSE_BY_TYPE = BrowseByDataType.Metadata; diff --git a/src/app/browse-by/browse-by-switcher/themed-browse-by-switcher.component.ts b/src/app/browse-by/browse-by-switcher/themed-browse-by-switcher.component.ts deleted file mode 100644 index 0187d4e3c5..0000000000 --- a/src/app/browse-by/browse-by-switcher/themed-browse-by-switcher.component.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Component } from '@angular/core'; - -import { ThemedComponent } from '../../shared/theme-support/themed.component'; -import { BrowseBySwitcherComponent } from './browse-by-switcher.component'; - -/** - * Themed wrapper for BrowseBySwitcherComponent - */ -@Component({ - selector: 'ds-themed-browse-by-switcher', - styleUrls: [], - templateUrl: '../../shared/theme-support/themed.component.html' -}) -export class ThemedBrowseBySwitcherComponent extends ThemedComponent { - protected getComponentName(): string { - return 'BrowseBySwitcherComponent'; - } - - protected importThemedComponent(themeName: string): Promise { - return import(`../../../themes/${themeName}/app/browse-by/browse-by-switcher/browse-by-switcher.component`); - } - - protected importUnthemedComponent(): Promise { - return import(`./browse-by-switcher.component`); - } - - -} diff --git a/src/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts b/src/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts index cf6345bf39..3536d92e47 100644 --- a/src/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts +++ b/src/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts @@ -5,7 +5,7 @@ import { ActivatedRoute } from '@angular/router'; import { Observable, Subscription } from 'rxjs'; import { BrowseDefinition } from '../../core/shared/browse-definition.model'; import { GenericConstructor } from '../../core/shared/generic-constructor'; -import { BROWSE_BY_COMPONENT_FACTORY } from '../browse-by-switcher/browse-by-decorator'; +import { BROWSE_BY_COMPONENT_FACTORY, rendersBrowseBy, BrowseByDataType } from '../browse-by-switcher/browse-by-decorator'; import { map } from 'rxjs/operators'; import { ThemeService } from 'src/app/shared/theme-support/theme.service'; import { HierarchicalBrowseDefinition } from '../../core/shared/hierarchical-browse-definition.model'; @@ -18,6 +18,7 @@ import { HierarchicalBrowseDefinition } from '../../core/shared/hierarchical-bro /** * Component for browsing items by metadata in a hierarchical controlled vocabulary */ +@rendersBrowseBy(BrowseByDataType.Hierarchy) export class BrowseByTaxonomyPageComponent implements OnInit, OnDestroy { /** diff --git a/src/app/browse-by/browse-by-taxonomy-page/themed-browse-by-taxonomy-page.component.ts b/src/app/browse-by/browse-by-taxonomy-page/themed-browse-by-taxonomy-page.component.ts deleted file mode 100644 index 212044b853..0000000000 --- a/src/app/browse-by/browse-by-taxonomy-page/themed-browse-by-taxonomy-page.component.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Component } from '@angular/core'; -import { ThemedComponent } from '../../shared/theme-support/themed.component'; -import { rendersBrowseBy } from '../browse-by-switcher/browse-by-decorator'; -import { BrowseByTaxonomyPageComponent } from './browse-by-taxonomy-page.component'; - -@Component({ - selector: 'ds-themed-browse-by-taxonomy-page', - templateUrl: '../../shared/theme-support/themed.component.html', - styleUrls: [] -}) -/** - * Themed wrapper for BrowseByTaxonomyPageComponent - */ -@rendersBrowseBy('hierarchy') -export class ThemedBrowseByTaxonomyPageComponent extends ThemedComponent{ - - protected getComponentName(): string { - return 'BrowseByTaxonomyPageComponent'; - } - - protected importThemedComponent(themeName: string): Promise { - return import(`../../../themes/${themeName}/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component`); - } - - protected importUnthemedComponent(): Promise { - return import(`./browse-by-taxonomy-page.component`); - } -} diff --git a/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts b/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts index 58df79ebe8..cec30712da 100644 --- a/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts +++ b/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts @@ -1,7 +1,6 @@ import { combineLatest as observableCombineLatest } from 'rxjs'; -import { Component, Inject } from '@angular/core'; +import { Component, Inject, OnInit } from '@angular/core'; import { ActivatedRoute, Params, Router } from '@angular/router'; -import { hasValue } from '../../shared/empty.util'; import { BrowseByMetadataPageComponent, browseParamsToOptions, getBrowseSearchOptions @@ -14,6 +13,7 @@ import { map } from 'rxjs/operators'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { AppConfig, APP_CONFIG } from '../../../config/app-config.interface'; import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; +import { rendersBrowseBy, BrowseByDataType } from '../browse-by-switcher/browse-by-decorator'; @Component({ selector: 'ds-browse-by-title-page', @@ -23,7 +23,8 @@ import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; /** * Component for browsing items by title (dc.title) */ -export class BrowseByTitlePageComponent extends BrowseByMetadataPageComponent { +@rendersBrowseBy(BrowseByDataType.Title) +export class BrowseByTitlePageComponent extends BrowseByMetadataPageComponent implements OnInit { public constructor(protected route: ActivatedRoute, protected browseService: BrowseService, @@ -57,8 +58,4 @@ export class BrowseByTitlePageComponent extends BrowseByMetadataPageComponent { this.updateStartsWithTextOptions(); } - ngOnDestroy(): void { - this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe()); - } - } diff --git a/src/app/browse-by/browse-by-title-page/themed-browse-by-title-page.component.ts b/src/app/browse-by/browse-by-title-page/themed-browse-by-title-page.component.ts deleted file mode 100644 index 4a1bcc0bc1..0000000000 --- a/src/app/browse-by/browse-by-title-page/themed-browse-by-title-page.component.ts +++ /dev/null @@ -1,29 +0,0 @@ -import {Component} from '@angular/core'; -import { ThemedComponent } from '../../shared/theme-support/themed.component'; -import { BrowseByTitlePageComponent } from './browse-by-title-page.component'; -import {BrowseByDataType, rendersBrowseBy} from '../browse-by-switcher/browse-by-decorator'; - -/** - * Themed wrapper for BrowseByTitlePageComponent - */ -@Component({ - selector: 'ds-themed-browse-by-title-page', - styleUrls: [], - templateUrl: '../../shared/theme-support/themed.component.html', -}) - -@rendersBrowseBy(BrowseByDataType.Title) -export class ThemedBrowseByTitlePageComponent - extends ThemedComponent { - protected getComponentName(): string { - return 'BrowseByTitlePageComponent'; - } - - protected importThemedComponent(themeName: string): Promise { - return import(`../../../themes/${themeName}/app/browse-by/browse-by-title-page/browse-by-title-page.component`); - } - - protected importUnthemedComponent(): Promise { - return import(`./browse-by-title-page.component`); - } -} diff --git a/src/app/browse-by/browse-by.module.ts b/src/app/browse-by/browse-by.module.ts index c0e2d3f9ff..bc7f6a159a 100644 --- a/src/app/browse-by/browse-by.module.ts +++ b/src/app/browse-by/browse-by.module.ts @@ -7,10 +7,6 @@ import { BrowseBySwitcherComponent } from './browse-by-switcher/browse-by-switch import { BrowseByTaxonomyPageComponent } from './browse-by-taxonomy-page/browse-by-taxonomy-page.component'; import { ThemedBrowseBySwitcherComponent } from './browse-by-switcher/themed-browse-by-switcher.component'; import { ComcolModule } from '../shared/comcol/comcol.module'; -import { ThemedBrowseByMetadataPageComponent } from './browse-by-metadata-page/themed-browse-by-metadata-page.component'; -import { ThemedBrowseByDatePageComponent } from './browse-by-date-page/themed-browse-by-date-page.component'; -import { ThemedBrowseByTitlePageComponent } from './browse-by-title-page/themed-browse-by-title-page.component'; -import { ThemedBrowseByTaxonomyPageComponent } from './browse-by-taxonomy-page/themed-browse-by-taxonomy-page.component'; import { SharedBrowseByModule } from '../shared/browse-by/shared-browse-by.module'; import { DsoPageModule } from '../shared/dso-page/dso-page.module'; import { FormModule } from '../shared/form/form.module'; @@ -21,11 +17,6 @@ const ENTRY_COMPONENTS = [ BrowseByMetadataPageComponent, BrowseByDatePageComponent, BrowseByTaxonomyPageComponent, - - ThemedBrowseByMetadataPageComponent, - ThemedBrowseByDatePageComponent, - ThemedBrowseByTitlePageComponent, - ThemedBrowseByTaxonomyPageComponent, ]; @NgModule({ diff --git a/src/themes/custom/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts b/src/themes/custom/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts index 9fcf773350..589effde60 100644 --- a/src/themes/custom/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts +++ b/src/themes/custom/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts @@ -1,5 +1,6 @@ import { Component } from '@angular/core'; import { BrowseByDatePageComponent as BaseComponent } from '../../../../../app/browse-by/browse-by-date-page/browse-by-date-page.component'; +import { rendersBrowseBy, BrowseByDataType } from '../../../../../app/browse-by/browse-by-switcher/browse-by-decorator'; @Component({ selector: 'ds-browse-by-date-page', @@ -8,10 +9,6 @@ import { BrowseByDatePageComponent as BaseComponent } from '../../../../../app/b // templateUrl: './browse-by-date-page.component.html' templateUrl: '../../../../../app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html' }) - -/** - * Component for determining what Browse-By component to use depending on the metadata (browse ID) provided - */ - +@rendersBrowseBy(BrowseByDataType.Date, 'custom') export class BrowseByDatePageComponent extends BaseComponent { } diff --git a/src/themes/custom/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts b/src/themes/custom/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts index 9434ca936d..d2ee7ad694 100644 --- a/src/themes/custom/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts +++ b/src/themes/custom/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts @@ -1,5 +1,6 @@ import { Component } from '@angular/core'; import { BrowseByMetadataPageComponent as BaseComponent } from '../../../../../app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component'; +import { rendersBrowseBy, BrowseByDataType } from '../../../../../app/browse-by/browse-by-switcher/browse-by-decorator'; @Component({ selector: 'ds-browse-by-metadata-page', @@ -8,10 +9,6 @@ import { BrowseByMetadataPageComponent as BaseComponent } from '../../../../../a // templateUrl: './browse-by-metadata-page.component.html' templateUrl: '../../../../../app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html' }) - -/** - * Component for determining what Browse-By component to use depending on the metadata (browse ID) provided - */ - +@rendersBrowseBy(BrowseByDataType.Metadata, 'custom') export class BrowseByMetadataPageComponent extends BaseComponent { } diff --git a/src/themes/custom/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts b/src/themes/custom/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts deleted file mode 100644 index e65997eaf4..0000000000 --- a/src/themes/custom/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Component } from '@angular/core'; -import { BrowseBySwitcherComponent as BaseComponent } from '../../../../../app/browse-by/browse-by-switcher/browse-by-switcher.component'; - -@Component({ - selector: 'ds-browse-by-switcher', - // styleUrls: ['./browse-by-switcher.component.scss'], - // templateUrl: './browse-by-switcher.component.html' - templateUrl: '../../../../../app/browse-by/browse-by-switcher/browse-by-switcher.component.html' -}) - -/** - * Component for determining what Browse-By component to use depending on the metadata (browse ID) provided - */ -export class BrowseBySwitcherComponent extends BaseComponent {} - diff --git a/src/themes/custom/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts b/src/themes/custom/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts index 34d80a0cb8..459dc54d7e 100644 --- a/src/themes/custom/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts +++ b/src/themes/custom/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts @@ -1,5 +1,6 @@ import { Component } from '@angular/core'; import { BrowseByTaxonomyPageComponent as BaseComponent } from '../../../../../app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component'; +import { rendersBrowseBy, BrowseByDataType } from '../../../../../app/browse-by/browse-by-switcher/browse-by-decorator'; @Component({ selector: 'ds-browse-by-taxonomy-page', @@ -8,8 +9,6 @@ import { BrowseByTaxonomyPageComponent as BaseComponent } from '../../../../../a // styleUrls: ['./browse-by-taxonomy-page.component.scss'], styleUrls: ['../../../../../app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.scss'], }) -/** - * Component for browsing items by metadata in a hierarchical controlled vocabulary - */ +@rendersBrowseBy(BrowseByDataType.Hierarchy, 'custom') export class BrowseByTaxonomyPageComponent extends BaseComponent { } diff --git a/src/themes/custom/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts b/src/themes/custom/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts index ed96a81110..136970b38a 100644 --- a/src/themes/custom/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts +++ b/src/themes/custom/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts @@ -1,5 +1,6 @@ import { Component } from '@angular/core'; import { BrowseByTitlePageComponent as BaseComponent } from '../../../../../app/browse-by/browse-by-title-page/browse-by-title-page.component'; +import { rendersBrowseBy, BrowseByDataType } from '../../../../../app/browse-by/browse-by-switcher/browse-by-decorator'; @Component({ selector: 'ds-browse-by-title-page', @@ -8,10 +9,6 @@ import { BrowseByTitlePageComponent as BaseComponent } from '../../../../../app/ // templateUrl: './browse-by-title-page.component.html' templateUrl: '../../../../../app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html' }) - -/** - * Component for determining what Browse-By component to use depending on the metadata (browse ID) provided - */ - +@rendersBrowseBy(BrowseByDataType.Title, 'custom') export class BrowseByTitlePageComponent extends BaseComponent { } diff --git a/src/themes/custom/lazy-theme.module.ts b/src/themes/custom/lazy-theme.module.ts index edb3f5478c..e102089361 100644 --- a/src/themes/custom/lazy-theme.module.ts +++ b/src/themes/custom/lazy-theme.module.ts @@ -46,7 +46,6 @@ import { RootModule } from '../../app/root.module'; import { FileSectionComponent } from './app/item-page/simple/field-components/file-section/file-section.component'; import { HomePageComponent } from './app/home-page/home-page.component'; import { RootComponent } from './app/root/root.component'; -import { BrowseBySwitcherComponent } from './app/browse-by/browse-by-switcher/browse-by-switcher.component'; import { CommunityListPageComponent } from './app/community-list-page/community-list-page.component'; import { SearchPageComponent } from './app/search-page/search-page.component'; import { ConfigurationSearchPageComponent } from './app/search-page/configuration-search-page.component'; @@ -161,7 +160,6 @@ const DECLARATIONS = [ FileSectionComponent, HomePageComponent, RootComponent, - BrowseBySwitcherComponent, CommunityListPageComponent, SearchPageComponent, ConfigurationSearchPageComponent, From 14d42b0d93a18e129c0cb63af695592e825e7b74 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Sun, 10 Dec 2023 22:19:57 +0100 Subject: [PATCH 14/47] Fixed getComponent not triggering again when it's input values change by creating inputNamesDependentForComponent Also simplified the way @Input() values are passed to their child component and how ngOnChanges is called by using setInput instead --- .../abstract-component-loader.component.ts | 61 ++++++++++++++----- ...etadata-representation-loader.component.ts | 4 +- .../claimed-task-actions-loader.component.ts | 12 ++-- ...table-object-component-loader.component.ts | 26 ++++---- ...anced-workflow-actions-loader.component.ts | 4 +- 5 files changed, 71 insertions(+), 36 deletions(-) diff --git a/src/app/shared/abstract-component-loader/abstract-component-loader.component.ts b/src/app/shared/abstract-component-loader/abstract-component-loader.component.ts index 6f934f5b4b..46074f7a6b 100644 --- a/src/app/shared/abstract-component-loader/abstract-component-loader.component.ts +++ b/src/app/shared/abstract-component-loader/abstract-component-loader.component.ts @@ -32,10 +32,22 @@ export abstract class AbstractComponentLoaderComponent implements OnInit, OnC */ protected subs: Subscription[] = []; - protected inAndOutputNames: (keyof this)[] = [ + /** + * The @{@link Input}() that are used to find the matching component using {@link getComponent}. When the value of + * one of these @{@link Input}() change this loader needs to retrieve the best matching component again using the + * {@link getComponent} method. + */ + protected inputNamesDependentForComponent: (keyof this & string)[] = [ 'context', ]; + protected inputNames: (keyof this & string)[] = [ + 'context', + ]; + + protected outputNames: (keyof this & string)[] = [ + ]; + constructor( protected themeService: ThemeService, ) { @@ -45,7 +57,9 @@ export abstract class AbstractComponentLoaderComponent implements OnInit, OnC * Set up the dynamic child component */ ngOnInit(): void { - this.instantiateComponent(); + if (hasNoValue(this.compRef)) { + this.instantiateComponent(); + } } /** @@ -55,14 +69,14 @@ export abstract class AbstractComponentLoaderComponent implements OnInit, OnC if (hasNoValue(this.compRef)) { // sometimes the component has not been initialized yet, so it first needs to be initialized // before being called again - this.instantiateComponent(changes); + this.instantiateComponent(); } else { - // if an input or output has changed - if (this.inAndOutputNames.some((name: any) => hasValue(changes[name]))) { + if (this.inputNamesDependentForComponent.some((name: any) => hasValue(changes[name]) && changes[name].previousValue !== changes[name].currentValue)) { + // Recreate the component when the @Input()s used by getComponent() aren't up-to-date anymore + this.destroyComponentInstance(); + this.instantiateComponent(); + } else { this.connectInputsAndOutputs(); - if (this.compRef?.instance && 'ngOnChanges' in this.compRef.instance) { - (this.compRef.instance as any).ngOnChanges(changes); - } } } } @@ -73,7 +87,10 @@ export abstract class AbstractComponentLoaderComponent implements OnInit, OnC .forEach((subscription: Subscription) => subscription.unsubscribe()); } - public instantiateComponent(changes?: SimpleChanges): void { + /** + * Creates the component and connects the @Input() & @Output() from the ThemedComponent to its child Component. + */ + public instantiateComponent(): void { const component: GenericConstructor = this.getComponent(); const viewContainerRef: ViewContainerRef = this.componentDirective.viewContainerRef; @@ -86,10 +103,16 @@ export abstract class AbstractComponentLoaderComponent implements OnInit, OnC }, ); - if (hasValue(changes)) { - this.ngOnChanges(changes); - } else { - this.connectInputsAndOutputs(); + this.connectInputsAndOutputs(); + } + + /** + * Destroys the themed component and calls it's `ngOnDestroy` + */ + public destroyComponentInstance(): void { + if (hasValue(this.compRef)) { + this.compRef.destroy(); + this.compRef = null; } } @@ -99,12 +122,18 @@ export abstract class AbstractComponentLoaderComponent implements OnInit, OnC public abstract getComponent(): GenericConstructor; /** - * Connect the in and outputs of this component to the dynamic component, + * Connect the inputs and outputs of this component to the dynamic component, * to ensure they're in sync */ protected connectInputsAndOutputs(): void { - if (isNotEmpty(this.inAndOutputNames) && hasValue(this.compRef) && hasValue(this.compRef.instance)) { - this.inAndOutputNames.filter((name: any) => this[name] !== undefined).forEach((name: any) => { + if (isNotEmpty(this.inputNames) && hasValue(this.compRef) && hasValue(this.compRef.instance)) { + this.inputNames.filter((name: string) => this[name] !== undefined).filter((name: string) => this[name] !== this.compRef.instance[name]).forEach((name: string) => { + // Using setInput will automatically trigger the ngOnChanges + this.compRef.setInput(name, this[name]); + }); + } + if (isNotEmpty(this.outputNames) && hasValue(this.compRef) && hasValue(this.compRef.instance)) { + this.outputNames.filter((name: string) => this[name] !== undefined).filter((name: string) => this[name] !== this.compRef.instance[name]).forEach((name: string) => { this.compRef.instance[name] = this[name]; }); } diff --git a/src/app/shared/metadata-representation/metadata-representation-loader.component.ts b/src/app/shared/metadata-representation/metadata-representation-loader.component.ts index 83542512ed..4b63a31a2b 100644 --- a/src/app/shared/metadata-representation/metadata-representation-loader.component.ts +++ b/src/app/shared/metadata-representation/metadata-representation-loader.component.ts @@ -24,8 +24,8 @@ export class MetadataRepresentationLoaderComponent extends AbstractComponentLoad */ @Input() mdRepresentation: MetadataRepresentation; - protected inAndOutputNames: (keyof this)[] = [ - ...this.inAndOutputNames, + protected inputNames: (keyof this & string)[] = [ + ...this.inputNames, 'mdRepresentation', ]; diff --git a/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts b/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts index 75b392fe51..fb39347d63 100644 --- a/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, OnChanges, OnInit, Output, } from '@angular/core'; +import { Component, EventEmitter, Input, Output, } from '@angular/core'; import { getComponentByWorkflowTaskOption } from './claimed-task-actions-decorator'; import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model'; import { MyDSpaceActionsResult } from '../../mydspace-actions'; @@ -16,7 +16,7 @@ import { GenericConstructor } from '../../../../core/shared/generic-constructor' * Component for loading a ClaimedTaskAction component depending on the "option" input * Passes on the ClaimedTask to the component and subscribes to the processCompleted output */ -export class ClaimedTaskActionsLoaderComponent extends AbstractComponentLoaderComponent implements OnInit, OnChanges { +export class ClaimedTaskActionsLoaderComponent extends AbstractComponentLoaderComponent { /** * The item object that belonging to the ClaimedTask object */ @@ -46,12 +46,16 @@ export class ClaimedTaskActionsLoaderComponent extends AbstractComponentLoaderCo /** * The list of input and output names for the dynamic component */ - protected inAndOutputNames: (keyof this)[] = [ - ...this.inAndOutputNames, + protected inputNames: (keyof this & string)[] = [ + ...this.inputNames, 'item', 'object', 'option', 'workflowitem', + ]; + + protected outputNames: (keyof this & string)[] = [ + ...this.outputNames, 'processCompleted', ]; diff --git a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts index f62ee44a1c..a83cdfb6b2 100644 --- a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts +++ b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts @@ -1,5 +1,4 @@ -import { ChangeDetectorRef, Component, EventEmitter, Input, Output, SimpleChanges } from '@angular/core'; -import { combineLatest, Observable, of as observableOf } from 'rxjs'; +import { ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core'; import { take } from 'rxjs/operators'; import { ListableObject } from '../listable-object.model'; import { ViewMode } from '../../../../core/shared/view-mode.model'; @@ -74,8 +73,8 @@ export class ListableObjectComponentLoaderComponent extends AbstractComponentLoa /** * The list of input and output names for the dynamic component */ - protected inAndOutputNames: (keyof this)[] = [ - ...this.inAndOutputNames, + protected inputNames: (keyof this & string)[] = [ + ...this.inputNames, 'object', 'index', 'linkType', @@ -84,6 +83,10 @@ export class ListableObjectComponentLoaderComponent extends AbstractComponentLoa 'showThumbnails', 'viewMode', 'value', + ]; + + protected outputNames: (keyof this & string)[] = [ + ...this.outputNames, 'contentChange', ]; @@ -94,17 +97,16 @@ export class ListableObjectComponentLoaderComponent extends AbstractComponentLoa super(themeService); } - public instantiateComponent(changes?: SimpleChanges): void { - super.instantiateComponent(changes); + public instantiateComponent(): void { + super.instantiateComponent(); if ((this.compRef.instance as any).reloadedObject) { - combineLatest([ - observableOf(changes), - (this.compRef.instance as any).reloadedObject.pipe(take(1)) as Observable, - ]).subscribe(([simpleChanges, reloadedObject]: [SimpleChanges, DSpaceObject]) => { + (this.compRef.instance as any).reloadedObject.pipe( + take(1), + ).subscribe((reloadedObject: DSpaceObject) => { if (reloadedObject) { - this.compRef.destroy(); + this.destroyComponentInstance(); this.object = reloadedObject; - this.instantiateComponent(simpleChanges); + this.instantiateComponent(); this.cdr.detectChanges(); this.contentChange.emit(reloadedObject); } diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.ts index 10cae7ec7d..1db49b97e8 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.ts @@ -24,8 +24,8 @@ export class AdvancedWorkflowActionsLoaderComponent extends AbstractComponentLoa */ @Input() type: string; - protected inAndOutputNames: (keyof this)[] = [ - ...this.inAndOutputNames, + protected inputNames: (keyof this & string)[] = [ + ...this.inputNames, 'type', ]; From 7b8962a7cde749074df3c47e93e780fb21c895ca Mon Sep 17 00:00:00 2001 From: Thomas Misilo Date: Mon, 11 Dec 2023 13:16:46 -0600 Subject: [PATCH 15/47] Update "E-mail" to be "Email" for consistency --- .../privacy-content/privacy-content.component.html | 2 +- src/assets/i18n/en.json5 | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/app/info/privacy/privacy-content/privacy-content.component.html b/src/app/info/privacy/privacy-content/privacy-content.component.html index f29a786e8b..003e6b0d51 100644 --- a/src/app/info/privacy/privacy-content/privacy-content.component.html +++ b/src/app/info/privacy/privacy-content/privacy-content.component.html @@ -22,7 +22,7 @@

Information we collect about you and how we collect it

We collect several types of information from and about users of our Website, including information:

    -
  • by which you may be personally identified, such as name, e-mail address, telephone number, or any other identifier by which you may be contacted online or offline ("personal information"); and/or
  • +
  • by which you may be personally identified, such as name, email address, telephone number, or any other identifier by which you may be contacted online or offline ("personal information"); and/or
  • about your internet connection, the equipment you use to access our Website and usage details.

We collect this information:

diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 7bf16c4ee8..515e0338f1 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -266,7 +266,7 @@ "admin.access-control.epeople.search.scope.metadata": "Metadata", - "admin.access-control.epeople.search.scope.email": "E-mail (exact)", + "admin.access-control.epeople.search.scope.email": "Email (exact)", "admin.access-control.epeople.search.button": "Search", @@ -278,7 +278,7 @@ "admin.access-control.epeople.table.name": "Name", - "admin.access-control.epeople.table.email": "E-mail (exact)", + "admin.access-control.epeople.table.email": "Email (exact)", "admin.access-control.epeople.table.edit": "Edit", @@ -298,9 +298,9 @@ "admin.access-control.epeople.form.lastName": "Last name", - "admin.access-control.epeople.form.email": "E-mail", + "admin.access-control.epeople.form.email": "Email", - "admin.access-control.epeople.form.emailHint": "Must be a valid e-mail address", + "admin.access-control.epeople.form.emailHint": "Must be a valid email address", "admin.access-control.epeople.form.canLogIn": "Can log in", @@ -750,7 +750,7 @@ "bitstream-request-a-copy.name.error": "The name is required", - "bitstream-request-a-copy.email.label": "Your e-mail address *", + "bitstream-request-a-copy.email.label": "Your email address *", "bitstream-request-a-copy.email.hint": "This email address is used for sending the file.", @@ -1602,9 +1602,9 @@ "error.validation.required": "This field is required", - "error.validation.NotValidEmail": "This is not a valid e-mail", + "error.validation.NotValidEmail": "This is not a valid email", - "error.validation.emailTaken": "This e-mail is already taken", + "error.validation.emailTaken": "This email is already taken", "error.validation.groupExists": "This group already exists", From a83d69ee0eaf9233398d64344367ba5cc41a289b Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Mon, 11 Dec 2023 01:56:25 +0100 Subject: [PATCH 16/47] Created BrowseByPageComponent that uses the new refactored BrowseBySwitcherComponent extending AbstractComponentLoaderComponent - Added the context to the rendersBrowseBy decorator - Created AbstractBrowseByTypeComponent that is extended by all the browse type sections --- .../abstract-browse-by-type.component.ts | 32 +++++++++ .../browse-by-metadata-page.component.ts | 14 ++-- src/app/browse-by/browse-by-page.module.ts | 16 ++++- .../browse-by-page.component.html | 2 + .../browse-by-page.component.scss} | 0 .../browse-by-page.component.spec.ts | 69 +++++++++++++++++++ .../browse-by-page.component.ts | 31 +++++++++ src/app/browse-by/browse-by-routing.module.ts | 4 +- .../browse-by-switcher/browse-by-decorator.ts | 38 ++++++---- .../browse-by-switcher.component.html | 1 - .../browse-by-switcher.component.spec.ts | 61 +++++++++------- .../browse-by-switcher.component.ts | 45 +++++------- .../browse-by-taxonomy-page.component.ts | 35 ++++------ src/app/browse-by/browse-by.module.ts | 11 +-- .../core/shared/browse-definition.model.ts | 3 +- .../shared/flat-browse-definition.model.ts | 3 +- .../hierarchical-browse-definition.model.ts | 5 +- .../value-list-browse-definition.model.ts | 3 +- .../abstract-component-loader.component.ts | 2 +- ...-object-component-loader.component.spec.ts | 2 +- .../browse-by-date-page.component.ts | 3 +- .../browse-by-metadata-page.component.ts | 3 +- .../browse-by-switcher.component.scss | 0 .../browse-by-taxonomy-page.component.ts | 3 +- .../browse-by-title-page.component.ts | 3 +- 25 files changed, 271 insertions(+), 118 deletions(-) create mode 100644 src/app/browse-by/abstract-browse-by-type.component.ts create mode 100644 src/app/browse-by/browse-by-page/browse-by-page.component.html rename src/{themes/custom/app/browse-by/browse-by-switcher/browse-by-switcher.component.html => app/browse-by/browse-by-page/browse-by-page.component.scss} (100%) create mode 100644 src/app/browse-by/browse-by-page/browse-by-page.component.spec.ts create mode 100644 src/app/browse-by/browse-by-page/browse-by-page.component.ts delete mode 100644 src/app/browse-by/browse-by-switcher/browse-by-switcher.component.html delete mode 100644 src/themes/custom/app/browse-by/browse-by-switcher/browse-by-switcher.component.scss diff --git a/src/app/browse-by/abstract-browse-by-type.component.ts b/src/app/browse-by/abstract-browse-by-type.component.ts new file mode 100644 index 0000000000..246e59a010 --- /dev/null +++ b/src/app/browse-by/abstract-browse-by-type.component.ts @@ -0,0 +1,32 @@ +import { Component, Input, OnDestroy } from '@angular/core'; +import { BrowseByDataType } from './browse-by-switcher/browse-by-decorator'; +import { Context } from '../core/shared/context.model'; +import { Subscription } from 'rxjs'; +import { hasValue } from '../shared/empty.util'; + +@Component({ + selector: 'ds-abstract-browse-by-type', + template: '', +}) +export abstract class AbstractBrowseByTypeComponent implements OnDestroy { + + /** + * The optional context + */ + @Input() context: Context; + + /** + * The {@link BrowseByDataType} of this Component + */ + @Input() browseByType: BrowseByDataType; + + /** + * List of subscriptions + */ + subs: Subscription[] = []; + + ngOnDestroy(): void { + this.subs.filter((sub: Subscription) => hasValue(sub)).forEach((sub: Subscription) => sub.unsubscribe()); + } + +} diff --git a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts index 28d57ce3a4..225b9b503c 100644 --- a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts +++ b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts @@ -1,4 +1,4 @@ -import { combineLatest as observableCombineLatest, Observable, Subscription } from 'rxjs'; +import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; import { Component, Inject, OnInit, OnDestroy } from '@angular/core'; import { RemoteData } from '../../core/data/remote-data'; import { PaginatedList } from '../../core/data/paginated-list.model'; @@ -23,6 +23,7 @@ import { Community } from '../../core/shared/community.model'; import { APP_CONFIG, AppConfig } from '../../../config/app-config.interface'; import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; import { rendersBrowseBy, BrowseByDataType } from '../browse-by-switcher/browse-by-decorator'; +import { AbstractBrowseByTypeComponent } from '../abstract-browse-by-type.component'; export const BBM_PAGINATION_ID = 'bbm'; @@ -38,7 +39,7 @@ export const BBM_PAGINATION_ID = 'bbm'; * 'dc.contributor.*' */ @rendersBrowseBy(BrowseByDataType.Metadata) -export class BrowseByMetadataPageComponent implements OnInit, OnDestroy { +export class BrowseByMetadataPageComponent extends AbstractBrowseByTypeComponent implements OnInit, OnDestroy { /** * The list of browse-entries to display @@ -75,11 +76,6 @@ export class BrowseByMetadataPageComponent implements OnInit, OnDestroy { */ currentSort$: Observable; - /** - * List of subscriptions - */ - subs: Subscription[] = []; - /** * The default browse id to resort to when none is provided */ @@ -132,7 +128,7 @@ export class BrowseByMetadataPageComponent implements OnInit, OnDestroy { @Inject(APP_CONFIG) public appConfig: AppConfig, public dsoNameService: DSONameService, ) { - + super(); this.fetchThumbnails = this.appConfig.browseBy.showThumbnails; this.paginationConfig = Object.assign(new PaginationComponentOptions(), { id: BBM_PAGINATION_ID, @@ -276,7 +272,7 @@ export class BrowseByMetadataPageComponent implements OnInit, OnDestroy { } ngOnDestroy(): void { - this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe()); + super.ngOnDestroy(); this.paginationService.clearPagination(this.paginationConfig.id); } diff --git a/src/app/browse-by/browse-by-page.module.ts b/src/app/browse-by/browse-by-page.module.ts index 554a6c4f46..8a010f7105 100644 --- a/src/app/browse-by/browse-by-page.module.ts +++ b/src/app/browse-by/browse-by-page.module.ts @@ -5,12 +5,19 @@ import { ItemDataService } from '../core/data/item-data.service'; import { BrowseService } from '../core/browse/browse.service'; import { BrowseByGuard } from './browse-by-guard'; import { SharedBrowseByModule } from '../shared/browse-by/shared-browse-by.module'; +import { BrowseByPageComponent } from './browse-by-page/browse-by-page.component'; +import { SharedModule } from '../shared/shared.module'; + +const DECLARATIONS = [ + BrowseByPageComponent, +]; @NgModule({ imports: [ SharedBrowseByModule, BrowseByRoutingModule, - BrowseByModule.withEntryComponents(), + BrowseByModule, + SharedModule, ], providers: [ ItemDataService, @@ -18,8 +25,11 @@ import { SharedBrowseByModule } from '../shared/browse-by/shared-browse-by.modul BrowseByGuard, ], declarations: [ - - ] + ...DECLARATIONS, + ], + exports: [ + ...DECLARATIONS, + ], }) export class BrowseByPageModule { diff --git a/src/app/browse-by/browse-by-page/browse-by-page.component.html b/src/app/browse-by/browse-by-page/browse-by-page.component.html new file mode 100644 index 0000000000..b7b109643b --- /dev/null +++ b/src/app/browse-by/browse-by-page/browse-by-page.component.html @@ -0,0 +1,2 @@ + + diff --git a/src/themes/custom/app/browse-by/browse-by-switcher/browse-by-switcher.component.html b/src/app/browse-by/browse-by-page/browse-by-page.component.scss similarity index 100% rename from src/themes/custom/app/browse-by/browse-by-switcher/browse-by-switcher.component.html rename to src/app/browse-by/browse-by-page/browse-by-page.component.scss diff --git a/src/app/browse-by/browse-by-page/browse-by-page.component.spec.ts b/src/app/browse-by/browse-by-page/browse-by-page.component.spec.ts new file mode 100644 index 0000000000..d60ca02afa --- /dev/null +++ b/src/app/browse-by/browse-by-page/browse-by-page.component.spec.ts @@ -0,0 +1,69 @@ +// eslint-disable-next-line max-classes-per-file +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { BrowseByPageComponent } from './browse-by-page.component'; +import { BrowseBySwitcherComponent } from '../browse-by-switcher/browse-by-switcher.component'; +import { DynamicComponentLoaderDirective } from '../../shared/abstract-component-loader/dynamic-component-loader.directive'; +import { ActivatedRoute } from '@angular/router'; +import { ActivatedRouteStub } from '../../shared/testing/active-router.stub'; +import { getMockThemeService } from '../../shared/mocks/theme-service.mock'; +import { ThemeService } from '../../shared/theme-support/theme.service'; +import { rendersBrowseBy, BrowseByDataType } from '../browse-by-switcher/browse-by-decorator'; +import { Component } from '@angular/core'; +import { AbstractBrowseByTypeComponent } from '../abstract-browse-by-type.component'; +import { BrowseDefinition } from '../../core/shared/browse-definition.model'; +import { By } from '@angular/platform-browser'; + +@rendersBrowseBy('BrowseByPageComponent' as BrowseByDataType) +@Component({ + // eslint-disable-next-line @angular-eslint/component-selector + selector: '', + template: '', +}) +class BrowseByTestComponent extends AbstractBrowseByTypeComponent { +} + +class TestBrowseByPageBrowseDefinition extends BrowseDefinition { + getRenderType(): BrowseByDataType { + return 'BrowseByPageComponent' as BrowseByDataType; + } +} + +describe('BrowseByPageComponent', () => { + let component: BrowseByPageComponent; + let fixture: ComponentFixture; + + let activatedRoute: ActivatedRouteStub; + let themeService: ThemeService; + + beforeEach(async () => { + activatedRoute = new ActivatedRouteStub(); + themeService = getMockThemeService(); + + await TestBed.configureTestingModule({ + declarations: [ + BrowseByPageComponent, + BrowseBySwitcherComponent, + DynamicComponentLoaderDirective, + ], + providers: [ + BrowseByTestComponent, + { provide: ActivatedRoute, useValue: activatedRoute }, + { provide: ThemeService, useValue: themeService }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(BrowseByPageComponent); + component = fixture.componentInstance; + }); + + it('should create the correct browse section based on the route browseDefinition', () => { + activatedRoute.testData = { + browseDefinition: new TestBrowseByPageBrowseDefinition(), + }; + + fixture.detectChanges(); + + expect(component).toBeTruthy(); + expect(fixture.debugElement.query(By.css('#BrowseByTestComponent'))).not.toBeNull(); + }); +}); diff --git a/src/app/browse-by/browse-by-page/browse-by-page.component.ts b/src/app/browse-by/browse-by-page/browse-by-page.component.ts new file mode 100644 index 0000000000..ad26133485 --- /dev/null +++ b/src/app/browse-by/browse-by-page/browse-by-page.component.ts @@ -0,0 +1,31 @@ +import { Component, OnInit } from '@angular/core'; +import { map } from 'rxjs/operators'; +import { BrowseDefinition } from '../../core/shared/browse-definition.model'; +import { ActivatedRoute } from '@angular/router'; +import { Observable } from 'rxjs'; +import { BrowseByDataType } from '../browse-by-switcher/browse-by-decorator'; + +@Component({ + selector: 'ds-browse-by-page', + templateUrl: './browse-by-page.component.html', + styleUrls: ['./browse-by-page.component.scss'], +}) +export class BrowseByPageComponent implements OnInit { + + browseByType$: Observable; + + constructor( + protected route: ActivatedRoute, + ) { + } + + /** + * Fetch the correct browse-by component by using the relevant config from the route data + */ + ngOnInit(): void { + this.browseByType$ = this.route.data.pipe( + map((data: { browseDefinition: BrowseDefinition }) => data.browseDefinition.getRenderType()), + ); + } + +} diff --git a/src/app/browse-by/browse-by-routing.module.ts b/src/app/browse-by/browse-by-routing.module.ts index bb67dc65ae..f07df26b32 100644 --- a/src/app/browse-by/browse-by-routing.module.ts +++ b/src/app/browse-by/browse-by-routing.module.ts @@ -3,7 +3,7 @@ import { NgModule } from '@angular/core'; import { BrowseByGuard } from './browse-by-guard'; import { BrowseByDSOBreadcrumbResolver } from './browse-by-dso-breadcrumb.resolver'; import { BrowseByI18nBreadcrumbResolver } from './browse-by-i18n-breadcrumb.resolver'; -import { ThemedBrowseBySwitcherComponent } from './browse-by-switcher/themed-browse-by-switcher.component'; +import { BrowseByPageComponent } from './browse-by-page/browse-by-page.component'; import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; @NgModule({ @@ -18,7 +18,7 @@ import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; children: [ { path: ':id', - component: ThemedBrowseBySwitcherComponent, + component: BrowseByPageComponent, canActivate: [BrowseByGuard], resolve: { breadcrumb: BrowseByI18nBreadcrumbResolver }, data: { title: 'browse.title.page', breadcrumbKey: 'browse.metadata' } diff --git a/src/app/browse-by/browse-by-switcher/browse-by-decorator.ts b/src/app/browse-by/browse-by-switcher/browse-by-decorator.ts index 11ff1cec28..6891fba003 100644 --- a/src/app/browse-by/browse-by-switcher/browse-by-decorator.ts +++ b/src/app/browse-by/browse-by-switcher/browse-by-decorator.ts @@ -1,10 +1,9 @@ import { hasNoValue } from '../../shared/empty.util'; import { InjectionToken } from '@angular/core'; +import { DEFAULT_THEME, resolveTheme } from '../../shared/object-collection/shared/listable-object/listable-object.decorator'; +import { AbstractBrowseByTypeComponent } from '../abstract-browse-by-type.component'; +import { Context } from '../../core/shared/context.model'; import { GenericConstructor } from '../../core/shared/generic-constructor'; -import { - DEFAULT_THEME, - resolveTheme -} from '../../shared/object-collection/shared/listable-object/listable-object.decorator'; export enum BrowseByDataType { Title = 'title', @@ -14,28 +13,36 @@ export enum BrowseByDataType { } export const DEFAULT_BROWSE_BY_TYPE = BrowseByDataType.Metadata; +export const DEFAULT_BROWSE_BY_CONTEXT = Context.Any; -export const BROWSE_BY_COMPONENT_FACTORY = new InjectionToken<(browseByType, theme) => GenericConstructor>('getComponentByBrowseByType', { +export const BROWSE_BY_COMPONENT_FACTORY = new InjectionToken<(browseByType: BrowseByDataType, context: Context, theme: string) => GenericConstructor>('getComponentByBrowseByType', { providedIn: 'root', factory: () => getComponentByBrowseByType }); -const map = new Map(); +const map: Map>>> = new Map(); /** * Decorator used for rendering Browse-By pages by type * @param browseByType The type of page + * @param context The optional context for the component * @param theme The optional theme for the component */ -export function rendersBrowseBy(browseByType: string, theme = DEFAULT_THEME) { +export function rendersBrowseBy(browseByType: BrowseByDataType, context = DEFAULT_BROWSE_BY_CONTEXT, theme = DEFAULT_THEME) { return function decorator(component: any) { + if (hasNoValue(browseByType)) { + return; + } if (hasNoValue(map.get(browseByType))) { map.set(browseByType, new Map()); } - if (hasNoValue(map.get(browseByType).get(theme))) { - map.get(browseByType).set(theme, component); + if (hasNoValue(map.get(browseByType).get(context))) { + map.get(browseByType).set(context, new Map()); + } + if (hasNoValue(map.get(browseByType).get(context).get(theme))) { + map.get(browseByType).get(context).set(theme, component); } else { - throw new Error(`There can't be more than one component to render Browse-By of type "${browseByType}" and theme "${theme}"`); + throw new Error(`There can't be more than one component to render Browse-By of type "${browseByType}", context "${context}" and theme "${theme}"`); } }; } @@ -43,12 +50,17 @@ export function rendersBrowseBy(browseByType: string, theme = DEFAULT_THEME) { /** * Get the component used for rendering a Browse-By page by type * @param browseByType The type of page + * @param context The context to match * @param theme the theme to match */ -export function getComponentByBrowseByType(browseByType, theme) { - let themeMap = map.get(browseByType); +export function getComponentByBrowseByType(browseByType: BrowseByDataType, context: Context, theme: string): GenericConstructor { + let contextMap: Map>> = map.get(browseByType); + if (hasNoValue(contextMap)) { + contextMap = map.get(DEFAULT_BROWSE_BY_TYPE); + } + let themeMap: Map> = contextMap.get(context); if (hasNoValue(themeMap)) { - themeMap = map.get(DEFAULT_BROWSE_BY_TYPE); + themeMap = contextMap.get(DEFAULT_BROWSE_BY_CONTEXT); } const comp = resolveTheme(themeMap, theme); if (hasNoValue(comp)) { diff --git a/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.html b/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.html deleted file mode 100644 index afe79cf2b1..0000000000 --- a/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.spec.ts b/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.spec.ts index c13405dd4d..5812a54269 100644 --- a/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.spec.ts +++ b/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.spec.ts @@ -1,13 +1,23 @@ import { BrowseBySwitcherComponent } from './browse-by-switcher.component'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { BROWSE_BY_COMPONENT_FACTORY, BrowseByDataType } from './browse-by-decorator'; -import { BehaviorSubject } from 'rxjs'; +import { SimpleChange, Component } from '@angular/core'; +import { BrowseByDataType, rendersBrowseBy } from './browse-by-decorator'; import { ThemeService } from '../../shared/theme-support/theme.service'; import { FlatBrowseDefinition } from '../../core/shared/flat-browse-definition.model'; import { ValueListBrowseDefinition } from '../../core/shared/value-list-browse-definition.model'; import { NonHierarchicalBrowseDefinition } from '../../core/shared/non-hierarchical-browse-definition'; +import { getMockThemeService } from '../../shared/mocks/theme-service.mock'; +import { DynamicComponentLoaderDirective } from '../../shared/abstract-component-loader/dynamic-component-loader.directive'; +import { AbstractBrowseByTypeComponent } from '../abstract-browse-by-type.component'; + +@rendersBrowseBy('BrowseBySwitcherComponent' as BrowseByDataType) +@Component({ + // eslint-disable-next-line @angular-eslint/component-selector + selector: '', + template: '', +}) +class BrowseByTestComponent extends AbstractBrowseByTypeComponent { +} describe('BrowseBySwitcherComponent', () => { let comp: BrowseBySwitcherComponent; @@ -41,46 +51,45 @@ describe('BrowseBySwitcherComponent', () => { ), ]; - const data = new BehaviorSubject(createDataWithBrowseDefinition(new FlatBrowseDefinition())); - - const activatedRouteStub = { - data - }; - let themeService: ThemeService; - let themeName: string; + const themeName = 'dspace'; beforeEach(waitForAsync(() => { - themeName = 'dspace'; - themeService = jasmine.createSpyObj('themeService', { - getThemeName: themeName, - }); + themeService = getMockThemeService(themeName); - TestBed.configureTestingModule({ - declarations: [BrowseBySwitcherComponent], - providers: [ - { provide: ActivatedRoute, useValue: activatedRouteStub }, - { provide: ThemeService, useValue: themeService }, - { provide: BROWSE_BY_COMPONENT_FACTORY, useValue: jasmine.createSpy('getComponentByBrowseByType').and.returnValue(null) } + void TestBed.configureTestingModule({ + declarations: [ + BrowseBySwitcherComponent, + DynamicComponentLoaderDirective, + ], + providers: [ + BrowseByTestComponent, + { provide: ThemeService, useValue: themeService }, ], - schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); })); beforeEach(waitForAsync(() => { fixture = TestBed.createComponent(BrowseBySwitcherComponent); comp = fixture.componentInstance; + spyOn(comp, 'getComponent').and.returnValue(BrowseByTestComponent); + spyOn(comp, 'connectInputsAndOutputs').and.callThrough(); })); types.forEach((type: NonHierarchicalBrowseDefinition) => { describe(`when switching to a browse-by page for "${type.id}"`, () => { - beforeEach(() => { - data.next(createDataWithBrowseDefinition(type)); + beforeEach(async () => { + comp.browseByType = type.dataType; + comp.ngOnChanges({ + browseByType: new SimpleChange(undefined, type.dataType, true), + }); fixture.detectChanges(); + await fixture.whenStable(); }); - it(`should call getComponentByBrowseByType with type "${type.dataType}"`, () => { - expect((comp as any).getComponentByBrowseByType).toHaveBeenCalledWith(type.dataType, themeName); + it(`should call getComponent with type "${type.dataType}"`, () => { + expect(comp.getComponent).toHaveBeenCalled(); + expect(comp.connectInputsAndOutputs).toHaveBeenCalled(); }); }); }); diff --git a/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts b/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts index 35e4edf900..881df71311 100644 --- a/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts +++ b/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts @@ -1,38 +1,29 @@ -import { Component, Inject, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; -import { BROWSE_BY_COMPONENT_FACTORY } from './browse-by-decorator'; +import { Component, Input } from '@angular/core'; +import { getComponentByBrowseByType, BrowseByDataType } from './browse-by-decorator'; import { GenericConstructor } from '../../core/shared/generic-constructor'; -import { BrowseDefinition } from '../../core/shared/browse-definition.model'; -import { ThemeService } from '../../shared/theme-support/theme.service'; +import { AbstractComponentLoaderComponent } from '../../shared/abstract-component-loader/abstract-component-loader.component'; +import { AbstractBrowseByTypeComponent } from '../abstract-browse-by-type.component'; @Component({ selector: 'ds-browse-by-switcher', - templateUrl: './browse-by-switcher.component.html' + templateUrl: '../../shared/abstract-component-loader/abstract-component-loader.component.html' }) -/** - * Component for determining what Browse-By component to use depending on the metadata (browse ID) provided - */ -export class BrowseBySwitcherComponent implements OnInit { +export class BrowseBySwitcherComponent extends AbstractComponentLoaderComponent { - /** - * Resolved browse-by component - */ - browseByComponent: Observable; + @Input() browseByType: BrowseByDataType; - public constructor(protected route: ActivatedRoute, - protected themeService: ThemeService, - @Inject(BROWSE_BY_COMPONENT_FACTORY) private getComponentByBrowseByType: (browseByType, theme) => GenericConstructor) { - } + protected inputNamesDependentForComponent: (keyof this & string)[] = [ + ...this.inputNamesDependentForComponent, + 'browseByType', + ]; - /** - * Fetch the correct browse-by component by using the relevant config from the route data - */ - ngOnInit(): void { - this.browseByComponent = this.route.data.pipe( - map((data: { browseDefinition: BrowseDefinition }) => this.getComponentByBrowseByType(data.browseDefinition.getRenderType(), this.themeService.getThemeName())) - ); + protected inputNames: (keyof this & string)[] = [ + ...this.inputNames, + 'browseByType', + ]; + + public getComponent(): GenericConstructor { + return getComponentByBrowseByType(this.browseByType, this.context, this.themeService.getThemeName()); } } diff --git a/src/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts b/src/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts index 3536d92e47..f3c2b8b6b0 100644 --- a/src/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts +++ b/src/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts @@ -1,14 +1,13 @@ -import { Component, OnInit, Inject, OnDestroy } from '@angular/core'; +import { Component, OnInit, OnDestroy } from '@angular/core'; import { VocabularyOptions } from '../../core/submission/vocabularies/models/vocabulary-options.model'; import { VocabularyEntryDetail } from '../../core/submission/vocabularies/models/vocabulary-entry-detail.model'; import { ActivatedRoute } from '@angular/router'; -import { Observable, Subscription } from 'rxjs'; +import { Observable } from 'rxjs'; import { BrowseDefinition } from '../../core/shared/browse-definition.model'; -import { GenericConstructor } from '../../core/shared/generic-constructor'; -import { BROWSE_BY_COMPONENT_FACTORY, rendersBrowseBy, BrowseByDataType } from '../browse-by-switcher/browse-by-decorator'; +import { rendersBrowseBy, BrowseByDataType } from '../browse-by-switcher/browse-by-decorator'; import { map } from 'rxjs/operators'; -import { ThemeService } from 'src/app/shared/theme-support/theme.service'; import { HierarchicalBrowseDefinition } from '../../core/shared/hierarchical-browse-definition.model'; +import { AbstractBrowseByTypeComponent } from '../abstract-browse-by-type.component'; @Component({ selector: 'ds-browse-by-taxonomy-page', @@ -19,7 +18,7 @@ import { HierarchicalBrowseDefinition } from '../../core/shared/hierarchical-bro * Component for browsing items by metadata in a hierarchical controlled vocabulary */ @rendersBrowseBy(BrowseByDataType.Hierarchy) -export class BrowseByTaxonomyPageComponent implements OnInit, OnDestroy { +export class BrowseByTaxonomyPageComponent extends AbstractBrowseByTypeComponent implements OnInit, OnDestroy { /** * The {@link VocabularyOptions} object @@ -52,28 +51,23 @@ export class BrowseByTaxonomyPageComponent implements OnInit, OnDestroy { queryParams: any; /** - * Resolved browse-by component + * Resolved browse-by definition */ - browseByComponent: Observable; + browseDefinition$: Observable; - /** - * Subscriptions to track - */ - browseByComponentSubs: Subscription[] = []; - - public constructor( protected route: ActivatedRoute, - protected themeService: ThemeService, - @Inject(BROWSE_BY_COMPONENT_FACTORY) private getComponentByBrowseByType: (browseByType, theme) => GenericConstructor) { + public constructor( + protected route: ActivatedRoute, + ) { + super(); } ngOnInit(): void { - this.browseByComponent = this.route.data.pipe( + this.browseDefinition$ = this.route.data.pipe( map((data: { browseDefinition: BrowseDefinition }) => { - this.getComponentByBrowseByType(data.browseDefinition.getRenderType(), this.themeService.getThemeName()); return data.browseDefinition; }) ); - this.browseByComponentSubs.push(this.browseByComponent.subscribe((browseDefinition: HierarchicalBrowseDefinition) => { + this.subs.push(this.browseDefinition$.subscribe((browseDefinition: HierarchicalBrowseDefinition) => { this.facetType = browseDefinition.facetType; this.vocabularyName = browseDefinition.vocabulary; this.vocabularyOptions = { name: this.vocabularyName, closed: true }; @@ -113,7 +107,4 @@ export class BrowseByTaxonomyPageComponent implements OnInit, OnDestroy { }; } - ngOnDestroy(): void { - this.browseByComponentSubs.forEach((sub: Subscription) => sub.unsubscribe()); - } } diff --git a/src/app/browse-by/browse-by.module.ts b/src/app/browse-by/browse-by.module.ts index bc7f6a159a..e8d05dc15a 100644 --- a/src/app/browse-by/browse-by.module.ts +++ b/src/app/browse-by/browse-by.module.ts @@ -5,12 +5,15 @@ import { BrowseByMetadataPageComponent } from './browse-by-metadata-page/browse- import { BrowseByDatePageComponent } from './browse-by-date-page/browse-by-date-page.component'; import { BrowseBySwitcherComponent } from './browse-by-switcher/browse-by-switcher.component'; import { BrowseByTaxonomyPageComponent } from './browse-by-taxonomy-page/browse-by-taxonomy-page.component'; -import { ThemedBrowseBySwitcherComponent } from './browse-by-switcher/themed-browse-by-switcher.component'; import { ComcolModule } from '../shared/comcol/comcol.module'; import { SharedBrowseByModule } from '../shared/browse-by/shared-browse-by.module'; import { DsoPageModule } from '../shared/dso-page/dso-page.module'; import { FormModule } from '../shared/form/form.module'; +const DECLARATIONS = [ + BrowseBySwitcherComponent, +]; + const ENTRY_COMPONENTS = [ // put only entry components that use custom decorator BrowseByTitlePageComponent, @@ -28,12 +31,12 @@ const ENTRY_COMPONENTS = [ FormModule, ], declarations: [ - BrowseBySwitcherComponent, - ThemedBrowseBySwitcherComponent, + ...DECLARATIONS, ...ENTRY_COMPONENTS ], exports: [ - BrowseBySwitcherComponent + ...DECLARATIONS, + ...ENTRY_COMPONENTS, ] }) export class BrowseByModule { diff --git a/src/app/core/shared/browse-definition.model.ts b/src/app/core/shared/browse-definition.model.ts index a5bed53c9f..8a1ce71b55 100644 --- a/src/app/core/shared/browse-definition.model.ts +++ b/src/app/core/shared/browse-definition.model.ts @@ -1,5 +1,6 @@ import { autoserialize } from 'cerialize'; import { CacheableObject } from '../cache/cacheable-object.model'; +import { BrowseByDataType } from '../../browse-by/browse-by-switcher/browse-by-decorator'; /** * Base class for BrowseDefinition models @@ -12,5 +13,5 @@ export abstract class BrowseDefinition extends CacheableObject { /** * Get the render type of the BrowseDefinition model */ - abstract getRenderType(): string; + abstract getRenderType(): BrowseByDataType; } diff --git a/src/app/core/shared/flat-browse-definition.model.ts b/src/app/core/shared/flat-browse-definition.model.ts index 086fca891b..49e58e1f20 100644 --- a/src/app/core/shared/flat-browse-definition.model.ts +++ b/src/app/core/shared/flat-browse-definition.model.ts @@ -5,6 +5,7 @@ import { FLAT_BROWSE_DEFINITION } from './flat-browse-definition.resource-type'; import { ResourceType } from './resource-type'; import { NonHierarchicalBrowseDefinition } from './non-hierarchical-browse-definition'; import { HALLink } from './hal-link.model'; +import { BrowseByDataType } from '../../browse-by/browse-by-switcher/browse-by-decorator'; /** * BrowseDefinition model for browses of type 'flatBrowse' @@ -30,7 +31,7 @@ export class FlatBrowseDefinition extends NonHierarchicalBrowseDefinition { items: HALLink; }; - getRenderType(): string { + getRenderType(): BrowseByDataType { return this.dataType; } } diff --git a/src/app/core/shared/hierarchical-browse-definition.model.ts b/src/app/core/shared/hierarchical-browse-definition.model.ts index d561fff643..cf8d7a9ed9 100644 --- a/src/app/core/shared/hierarchical-browse-definition.model.ts +++ b/src/app/core/shared/hierarchical-browse-definition.model.ts @@ -5,6 +5,7 @@ import { HIERARCHICAL_BROWSE_DEFINITION } from './hierarchical-browse-definition import { HALLink } from './hal-link.model'; import { ResourceType } from './resource-type'; import { BrowseDefinition } from './browse-definition.model'; +import { BrowseByDataType } from '../../browse-by/browse-by-switcher/browse-by-decorator'; /** * BrowseDefinition model for browses of type 'hierarchicalBrowse' @@ -39,7 +40,7 @@ export class HierarchicalBrowseDefinition extends BrowseDefinition { vocabulary: HALLink; }; - getRenderType(): string { - return 'hierarchy'; + getRenderType(): BrowseByDataType { + return BrowseByDataType.Hierarchy; } } diff --git a/src/app/core/shared/value-list-browse-definition.model.ts b/src/app/core/shared/value-list-browse-definition.model.ts index 3378263962..e22742bb78 100644 --- a/src/app/core/shared/value-list-browse-definition.model.ts +++ b/src/app/core/shared/value-list-browse-definition.model.ts @@ -5,6 +5,7 @@ import { VALUE_LIST_BROWSE_DEFINITION } from './value-list-browse-definition.res import { ResourceType } from './resource-type'; import { NonHierarchicalBrowseDefinition } from './non-hierarchical-browse-definition'; import { HALLink } from './hal-link.model'; +import { BrowseByDataType } from '../../browse-by/browse-by-switcher/browse-by-decorator'; /** * BrowseDefinition model for browses of type 'valueList' @@ -30,7 +31,7 @@ export class ValueListBrowseDefinition extends NonHierarchicalBrowseDefinition { entries: HALLink; }; - getRenderType(): string { + getRenderType(): BrowseByDataType { return this.dataType; } } diff --git a/src/app/shared/abstract-component-loader/abstract-component-loader.component.ts b/src/app/shared/abstract-component-loader/abstract-component-loader.component.ts index 46074f7a6b..12f0f02821 100644 --- a/src/app/shared/abstract-component-loader/abstract-component-loader.component.ts +++ b/src/app/shared/abstract-component-loader/abstract-component-loader.component.ts @@ -125,7 +125,7 @@ export abstract class AbstractComponentLoaderComponent implements OnInit, OnC * Connect the inputs and outputs of this component to the dynamic component, * to ensure they're in sync */ - protected connectInputsAndOutputs(): void { + public connectInputsAndOutputs(): void { if (isNotEmpty(this.inputNames) && hasValue(this.compRef) && hasValue(this.compRef.instance)) { this.inputNames.filter((name: string) => this[name] !== undefined).filter((name: string) => this[name] !== this.compRef.instance[name]).forEach((name: string) => { // Using setInput will automatically trigger the ngOnChanges diff --git a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.spec.ts b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.spec.ts index 7b9d010e39..3b97e2a86e 100644 --- a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.spec.ts +++ b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.spec.ts @@ -91,7 +91,7 @@ describe('ListableObjectComponentLoaderComponent', () => { listableComponent.reloadedObject.emit(reloadedObject); tick(200); - expect(comp.instantiateComponent).toHaveBeenCalledWith(undefined); + expect(comp.instantiateComponent).toHaveBeenCalledWith(); })); it('should re-emit it as a contentChange', fakeAsync(() => { diff --git a/src/themes/custom/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts b/src/themes/custom/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts index 589effde60..fcbe15cfae 100644 --- a/src/themes/custom/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts +++ b/src/themes/custom/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts @@ -1,6 +1,7 @@ import { Component } from '@angular/core'; import { BrowseByDatePageComponent as BaseComponent } from '../../../../../app/browse-by/browse-by-date-page/browse-by-date-page.component'; import { rendersBrowseBy, BrowseByDataType } from '../../../../../app/browse-by/browse-by-switcher/browse-by-decorator'; +import { Context } from '../../../../../app/core/shared/context.model'; @Component({ selector: 'ds-browse-by-date-page', @@ -9,6 +10,6 @@ import { rendersBrowseBy, BrowseByDataType } from '../../../../../app/browse-by/ // templateUrl: './browse-by-date-page.component.html' templateUrl: '../../../../../app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html' }) -@rendersBrowseBy(BrowseByDataType.Date, 'custom') +@rendersBrowseBy(BrowseByDataType.Date, Context.Any, 'custom') export class BrowseByDatePageComponent extends BaseComponent { } diff --git a/src/themes/custom/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts b/src/themes/custom/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts index d2ee7ad694..533dca29ca 100644 --- a/src/themes/custom/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts +++ b/src/themes/custom/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts @@ -1,6 +1,7 @@ import { Component } from '@angular/core'; import { BrowseByMetadataPageComponent as BaseComponent } from '../../../../../app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component'; import { rendersBrowseBy, BrowseByDataType } from '../../../../../app/browse-by/browse-by-switcher/browse-by-decorator'; +import { Context } from '../../../../../app/core/shared/context.model'; @Component({ selector: 'ds-browse-by-metadata-page', @@ -9,6 +10,6 @@ import { rendersBrowseBy, BrowseByDataType } from '../../../../../app/browse-by/ // templateUrl: './browse-by-metadata-page.component.html' templateUrl: '../../../../../app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html' }) -@rendersBrowseBy(BrowseByDataType.Metadata, 'custom') +@rendersBrowseBy(BrowseByDataType.Metadata, Context.Any, 'custom') export class BrowseByMetadataPageComponent extends BaseComponent { } diff --git a/src/themes/custom/app/browse-by/browse-by-switcher/browse-by-switcher.component.scss b/src/themes/custom/app/browse-by/browse-by-switcher/browse-by-switcher.component.scss deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/themes/custom/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts b/src/themes/custom/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts index 459dc54d7e..afc0292bef 100644 --- a/src/themes/custom/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts +++ b/src/themes/custom/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts @@ -1,6 +1,7 @@ import { Component } from '@angular/core'; import { BrowseByTaxonomyPageComponent as BaseComponent } from '../../../../../app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component'; import { rendersBrowseBy, BrowseByDataType } from '../../../../../app/browse-by/browse-by-switcher/browse-by-decorator'; +import { Context } from '../../../../../app/core/shared/context.model'; @Component({ selector: 'ds-browse-by-taxonomy-page', @@ -9,6 +10,6 @@ import { rendersBrowseBy, BrowseByDataType } from '../../../../../app/browse-by/ // styleUrls: ['./browse-by-taxonomy-page.component.scss'], styleUrls: ['../../../../../app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.scss'], }) -@rendersBrowseBy(BrowseByDataType.Hierarchy, 'custom') +@rendersBrowseBy(BrowseByDataType.Hierarchy, Context.Any, 'custom') export class BrowseByTaxonomyPageComponent extends BaseComponent { } diff --git a/src/themes/custom/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts b/src/themes/custom/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts index 136970b38a..5424293529 100644 --- a/src/themes/custom/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts +++ b/src/themes/custom/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts @@ -1,6 +1,7 @@ import { Component } from '@angular/core'; import { BrowseByTitlePageComponent as BaseComponent } from '../../../../../app/browse-by/browse-by-title-page/browse-by-title-page.component'; import { rendersBrowseBy, BrowseByDataType } from '../../../../../app/browse-by/browse-by-switcher/browse-by-decorator'; +import { Context } from '../../../../../app/core/shared/context.model'; @Component({ selector: 'ds-browse-by-title-page', @@ -9,6 +10,6 @@ import { rendersBrowseBy, BrowseByDataType } from '../../../../../app/browse-by/ // templateUrl: './browse-by-title-page.component.html' templateUrl: '../../../../../app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html' }) -@rendersBrowseBy(BrowseByDataType.Title, 'custom') +@rendersBrowseBy(BrowseByDataType.Title, Context.Any, 'custom') export class BrowseByTitlePageComponent extends BaseComponent { } From e6bf2f0ca71adf02dca64a7a1a07de46c309a8d1 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Tue, 12 Dec 2023 00:34:12 +0100 Subject: [PATCH 17/47] Fixed bug in BrowseService where findListByHref was called with null instead of undefined, which prevented its default values from being used --- src/app/core/browse/browse.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/core/browse/browse.service.ts b/src/app/core/browse/browse.service.ts index b210b34949..58bbc0b870 100644 --- a/src/app/core/browse/browse.service.ts +++ b/src/app/core/browse/browse.service.ts @@ -105,7 +105,7 @@ export class BrowseService { }) ); if (options.fetchThumbnail ) { - return this.hrefOnlyDataService.findListByHref(href$, {}, null, null, ...BROWSE_LINKS_TO_FOLLOW); + return this.hrefOnlyDataService.findListByHref(href$, {}, undefined, undefined, ...BROWSE_LINKS_TO_FOLLOW); } return this.hrefOnlyDataService.findListByHref(href$); } @@ -153,7 +153,7 @@ export class BrowseService { }), ); if (options.fetchThumbnail) { - return this.hrefOnlyDataService.findListByHref(href$, {}, null, null, ...BROWSE_LINKS_TO_FOLLOW); + return this.hrefOnlyDataService.findListByHref(href$, {}, undefined, undefined, ...BROWSE_LINKS_TO_FOLLOW); } return this.hrefOnlyDataService.findListByHref(href$); } From a5b7e2a40fc7039789ba5e937ce505b43c27c33a Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Tue, 12 Dec 2023 01:20:41 +0100 Subject: [PATCH 18/47] Fixed circular dependency by extracting BrowseByDataType to a separate file --- .../browse-by/abstract-browse-by-type.component.ts | 2 +- .../browse-by-date-page.component.ts | 3 ++- src/app/browse-by/browse-by-guard.spec.ts | 2 +- .../browse-by-metadata-page.component.ts | 3 ++- .../browse-by-page.component.spec.ts | 3 ++- .../browse-by-page/browse-by-page.component.ts | 2 +- .../browse-by-switcher/browse-by-data-type.ts | 6 ++++++ .../browse-by-switcher/browse-by-decorator.spec.ts | 3 ++- .../browse-by-switcher/browse-by-decorator.ts | 14 +------------- .../browse-by-switcher.component.spec.ts | 3 ++- .../browse-by-switcher.component.ts | 3 ++- .../browse-by-taxonomy-page.component.ts | 3 ++- .../browse-by-title-page.component.ts | 3 ++- src/app/core/shared/browse-definition.model.ts | 2 +- .../core/shared/flat-browse-definition.model.ts | 2 +- .../shared/hierarchical-browse-definition.model.ts | 2 +- .../shared/non-hierarchical-browse-definition.ts | 2 +- .../shared/value-list-browse-definition.model.ts | 2 +- src/app/navbar/navbar.component.spec.ts | 2 +- .../browse-by-date-page.component.ts | 3 ++- .../browse-by-metadata-page.component.ts | 3 ++- .../browse-by-taxonomy-page.component.ts | 3 ++- .../browse-by-title-page.component.ts | 3 ++- 23 files changed, 40 insertions(+), 34 deletions(-) create mode 100644 src/app/browse-by/browse-by-switcher/browse-by-data-type.ts diff --git a/src/app/browse-by/abstract-browse-by-type.component.ts b/src/app/browse-by/abstract-browse-by-type.component.ts index 246e59a010..5bd39cfc50 100644 --- a/src/app/browse-by/abstract-browse-by-type.component.ts +++ b/src/app/browse-by/abstract-browse-by-type.component.ts @@ -1,5 +1,5 @@ import { Component, Input, OnDestroy } from '@angular/core'; -import { BrowseByDataType } from './browse-by-switcher/browse-by-decorator'; +import { BrowseByDataType } from './browse-by-switcher/browse-by-data-type'; import { Context } from '../core/shared/context.model'; import { Subscription } from 'rxjs'; import { hasValue } from '../shared/empty.util'; diff --git a/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts b/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts index b106803351..2fb9725bda 100644 --- a/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts +++ b/src/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts @@ -19,7 +19,8 @@ import { APP_CONFIG, AppConfig } from '../../../config/app-config.interface'; import { RemoteData } from '../../core/data/remote-data'; import { Item } from '../../core/shared/item.model'; import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; -import { rendersBrowseBy, BrowseByDataType } from '../browse-by-switcher/browse-by-decorator'; +import { rendersBrowseBy } from '../browse-by-switcher/browse-by-decorator'; +import { BrowseByDataType } from '../browse-by-switcher/browse-by-data-type'; @Component({ selector: 'ds-browse-by-date-page', diff --git a/src/app/browse-by/browse-by-guard.spec.ts b/src/app/browse-by/browse-by-guard.spec.ts index aac5ba2723..c7d3e1e0c0 100644 --- a/src/app/browse-by/browse-by-guard.spec.ts +++ b/src/app/browse-by/browse-by-guard.spec.ts @@ -2,7 +2,7 @@ import { first } from 'rxjs/operators'; import { BrowseByGuard } from './browse-by-guard'; import { of as observableOf } from 'rxjs'; import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils'; -import { BrowseByDataType } from './browse-by-switcher/browse-by-decorator'; +import { BrowseByDataType } from './browse-by-switcher/browse-by-data-type'; import { ValueListBrowseDefinition } from '../core/shared/value-list-browse-definition.model'; import { DSONameServiceMock } from '../shared/mocks/dso-name.service.mock'; import { DSONameService } from '../core/breadcrumbs/dso-name.service'; diff --git a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts index 225b9b503c..4c897b40f8 100644 --- a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts +++ b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts @@ -22,8 +22,9 @@ import { Collection } from '../../core/shared/collection.model'; import { Community } from '../../core/shared/community.model'; import { APP_CONFIG, AppConfig } from '../../../config/app-config.interface'; import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; -import { rendersBrowseBy, BrowseByDataType } from '../browse-by-switcher/browse-by-decorator'; +import { rendersBrowseBy } from '../browse-by-switcher/browse-by-decorator'; import { AbstractBrowseByTypeComponent } from '../abstract-browse-by-type.component'; +import { BrowseByDataType } from '../browse-by-switcher/browse-by-data-type'; export const BBM_PAGINATION_ID = 'bbm'; diff --git a/src/app/browse-by/browse-by-page/browse-by-page.component.spec.ts b/src/app/browse-by/browse-by-page/browse-by-page.component.spec.ts index d60ca02afa..3f9271a67d 100644 --- a/src/app/browse-by/browse-by-page/browse-by-page.component.spec.ts +++ b/src/app/browse-by/browse-by-page/browse-by-page.component.spec.ts @@ -7,11 +7,12 @@ import { ActivatedRoute } from '@angular/router'; import { ActivatedRouteStub } from '../../shared/testing/active-router.stub'; import { getMockThemeService } from '../../shared/mocks/theme-service.mock'; import { ThemeService } from '../../shared/theme-support/theme.service'; -import { rendersBrowseBy, BrowseByDataType } from '../browse-by-switcher/browse-by-decorator'; +import { rendersBrowseBy } from '../browse-by-switcher/browse-by-decorator'; import { Component } from '@angular/core'; import { AbstractBrowseByTypeComponent } from '../abstract-browse-by-type.component'; import { BrowseDefinition } from '../../core/shared/browse-definition.model'; import { By } from '@angular/platform-browser'; +import { BrowseByDataType } from '../browse-by-switcher/browse-by-data-type'; @rendersBrowseBy('BrowseByPageComponent' as BrowseByDataType) @Component({ diff --git a/src/app/browse-by/browse-by-page/browse-by-page.component.ts b/src/app/browse-by/browse-by-page/browse-by-page.component.ts index ad26133485..9df02562c6 100644 --- a/src/app/browse-by/browse-by-page/browse-by-page.component.ts +++ b/src/app/browse-by/browse-by-page/browse-by-page.component.ts @@ -3,7 +3,7 @@ import { map } from 'rxjs/operators'; import { BrowseDefinition } from '../../core/shared/browse-definition.model'; import { ActivatedRoute } from '@angular/router'; import { Observable } from 'rxjs'; -import { BrowseByDataType } from '../browse-by-switcher/browse-by-decorator'; +import { BrowseByDataType } from '../browse-by-switcher/browse-by-data-type'; @Component({ selector: 'ds-browse-by-page', diff --git a/src/app/browse-by/browse-by-switcher/browse-by-data-type.ts b/src/app/browse-by/browse-by-switcher/browse-by-data-type.ts new file mode 100644 index 0000000000..5324018b34 --- /dev/null +++ b/src/app/browse-by/browse-by-switcher/browse-by-data-type.ts @@ -0,0 +1,6 @@ +export enum BrowseByDataType { + Title = 'title', + Metadata = 'text', + Date = 'date', + Hierarchy = 'hierarchy', +} diff --git a/src/app/browse-by/browse-by-switcher/browse-by-decorator.spec.ts b/src/app/browse-by/browse-by-switcher/browse-by-decorator.spec.ts index 19a6277151..64604cdc04 100644 --- a/src/app/browse-by/browse-by-switcher/browse-by-decorator.spec.ts +++ b/src/app/browse-by/browse-by-switcher/browse-by-decorator.spec.ts @@ -1,4 +1,5 @@ -import { BrowseByDataType, rendersBrowseBy } from './browse-by-decorator'; +import { BrowseByDataType } from './browse-by-data-type'; +import { rendersBrowseBy } from './browse-by-decorator'; describe('BrowseByDecorator', () => { const titleDecorator = rendersBrowseBy(BrowseByDataType.Title); diff --git a/src/app/browse-by/browse-by-switcher/browse-by-decorator.ts b/src/app/browse-by/browse-by-switcher/browse-by-decorator.ts index 6891fba003..835c34ebcb 100644 --- a/src/app/browse-by/browse-by-switcher/browse-by-decorator.ts +++ b/src/app/browse-by/browse-by-switcher/browse-by-decorator.ts @@ -1,25 +1,13 @@ import { hasNoValue } from '../../shared/empty.util'; -import { InjectionToken } from '@angular/core'; import { DEFAULT_THEME, resolveTheme } from '../../shared/object-collection/shared/listable-object/listable-object.decorator'; import { AbstractBrowseByTypeComponent } from '../abstract-browse-by-type.component'; import { Context } from '../../core/shared/context.model'; import { GenericConstructor } from '../../core/shared/generic-constructor'; - -export enum BrowseByDataType { - Title = 'title', - Metadata = 'text', - Date = 'date', - Hierarchy = 'hierarchy', -} +import { BrowseByDataType } from './browse-by-data-type'; export const DEFAULT_BROWSE_BY_TYPE = BrowseByDataType.Metadata; export const DEFAULT_BROWSE_BY_CONTEXT = Context.Any; -export const BROWSE_BY_COMPONENT_FACTORY = new InjectionToken<(browseByType: BrowseByDataType, context: Context, theme: string) => GenericConstructor>('getComponentByBrowseByType', { - providedIn: 'root', - factory: () => getComponentByBrowseByType -}); - const map: Map>>> = new Map(); /** diff --git a/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.spec.ts b/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.spec.ts index 5812a54269..418dfd45e1 100644 --- a/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.spec.ts +++ b/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.spec.ts @@ -1,7 +1,7 @@ import { BrowseBySwitcherComponent } from './browse-by-switcher.component'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { SimpleChange, Component } from '@angular/core'; -import { BrowseByDataType, rendersBrowseBy } from './browse-by-decorator'; +import { rendersBrowseBy } from './browse-by-decorator'; import { ThemeService } from '../../shared/theme-support/theme.service'; import { FlatBrowseDefinition } from '../../core/shared/flat-browse-definition.model'; import { ValueListBrowseDefinition } from '../../core/shared/value-list-browse-definition.model'; @@ -9,6 +9,7 @@ import { NonHierarchicalBrowseDefinition } from '../../core/shared/non-hierarchi import { getMockThemeService } from '../../shared/mocks/theme-service.mock'; import { DynamicComponentLoaderDirective } from '../../shared/abstract-component-loader/dynamic-component-loader.directive'; import { AbstractBrowseByTypeComponent } from '../abstract-browse-by-type.component'; +import { BrowseByDataType } from './browse-by-data-type'; @rendersBrowseBy('BrowseBySwitcherComponent' as BrowseByDataType) @Component({ diff --git a/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts b/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts index 881df71311..2ebea9a262 100644 --- a/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts +++ b/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts @@ -1,8 +1,9 @@ import { Component, Input } from '@angular/core'; -import { getComponentByBrowseByType, BrowseByDataType } from './browse-by-decorator'; +import { getComponentByBrowseByType } from './browse-by-decorator'; import { GenericConstructor } from '../../core/shared/generic-constructor'; import { AbstractComponentLoaderComponent } from '../../shared/abstract-component-loader/abstract-component-loader.component'; import { AbstractBrowseByTypeComponent } from '../abstract-browse-by-type.component'; +import { BrowseByDataType } from './browse-by-data-type'; @Component({ selector: 'ds-browse-by-switcher', diff --git a/src/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts b/src/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts index f3c2b8b6b0..f005c66c9e 100644 --- a/src/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts +++ b/src/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts @@ -4,10 +4,11 @@ import { VocabularyEntryDetail } from '../../core/submission/vocabularies/models import { ActivatedRoute } from '@angular/router'; import { Observable } from 'rxjs'; import { BrowseDefinition } from '../../core/shared/browse-definition.model'; -import { rendersBrowseBy, BrowseByDataType } from '../browse-by-switcher/browse-by-decorator'; +import { rendersBrowseBy } from '../browse-by-switcher/browse-by-decorator'; import { map } from 'rxjs/operators'; import { HierarchicalBrowseDefinition } from '../../core/shared/hierarchical-browse-definition.model'; import { AbstractBrowseByTypeComponent } from '../abstract-browse-by-type.component'; +import { BrowseByDataType } from '../browse-by-switcher/browse-by-data-type'; @Component({ selector: 'ds-browse-by-taxonomy-page', diff --git a/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts b/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts index cec30712da..1e18429fa0 100644 --- a/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts +++ b/src/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts @@ -13,7 +13,8 @@ import { map } from 'rxjs/operators'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { AppConfig, APP_CONFIG } from '../../../config/app-config.interface'; import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; -import { rendersBrowseBy, BrowseByDataType } from '../browse-by-switcher/browse-by-decorator'; +import { rendersBrowseBy } from '../browse-by-switcher/browse-by-decorator'; +import { BrowseByDataType } from '../browse-by-switcher/browse-by-data-type'; @Component({ selector: 'ds-browse-by-title-page', diff --git a/src/app/core/shared/browse-definition.model.ts b/src/app/core/shared/browse-definition.model.ts index 8a1ce71b55..72239b26f7 100644 --- a/src/app/core/shared/browse-definition.model.ts +++ b/src/app/core/shared/browse-definition.model.ts @@ -1,6 +1,6 @@ import { autoserialize } from 'cerialize'; import { CacheableObject } from '../cache/cacheable-object.model'; -import { BrowseByDataType } from '../../browse-by/browse-by-switcher/browse-by-decorator'; +import { BrowseByDataType } from '../../browse-by/browse-by-switcher/browse-by-data-type'; /** * Base class for BrowseDefinition models diff --git a/src/app/core/shared/flat-browse-definition.model.ts b/src/app/core/shared/flat-browse-definition.model.ts index 49e58e1f20..9f37f1c422 100644 --- a/src/app/core/shared/flat-browse-definition.model.ts +++ b/src/app/core/shared/flat-browse-definition.model.ts @@ -5,7 +5,7 @@ import { FLAT_BROWSE_DEFINITION } from './flat-browse-definition.resource-type'; import { ResourceType } from './resource-type'; import { NonHierarchicalBrowseDefinition } from './non-hierarchical-browse-definition'; import { HALLink } from './hal-link.model'; -import { BrowseByDataType } from '../../browse-by/browse-by-switcher/browse-by-decorator'; +import { BrowseByDataType } from '../../browse-by/browse-by-switcher/browse-by-data-type'; /** * BrowseDefinition model for browses of type 'flatBrowse' diff --git a/src/app/core/shared/hierarchical-browse-definition.model.ts b/src/app/core/shared/hierarchical-browse-definition.model.ts index cf8d7a9ed9..2410bf7b7a 100644 --- a/src/app/core/shared/hierarchical-browse-definition.model.ts +++ b/src/app/core/shared/hierarchical-browse-definition.model.ts @@ -5,7 +5,7 @@ import { HIERARCHICAL_BROWSE_DEFINITION } from './hierarchical-browse-definition import { HALLink } from './hal-link.model'; import { ResourceType } from './resource-type'; import { BrowseDefinition } from './browse-definition.model'; -import { BrowseByDataType } from '../../browse-by/browse-by-switcher/browse-by-decorator'; +import { BrowseByDataType } from '../../browse-by/browse-by-switcher/browse-by-data-type'; /** * BrowseDefinition model for browses of type 'hierarchicalBrowse' diff --git a/src/app/core/shared/non-hierarchical-browse-definition.ts b/src/app/core/shared/non-hierarchical-browse-definition.ts index d5481fcc8d..e3319affdb 100644 --- a/src/app/core/shared/non-hierarchical-browse-definition.ts +++ b/src/app/core/shared/non-hierarchical-browse-definition.ts @@ -1,6 +1,6 @@ import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize'; import { SortOption } from './sort-option.model'; -import { BrowseByDataType } from '../../browse-by/browse-by-switcher/browse-by-decorator'; +import { BrowseByDataType } from '../../browse-by/browse-by-switcher/browse-by-data-type'; import { BrowseDefinition } from './browse-definition.model'; /** diff --git a/src/app/core/shared/value-list-browse-definition.model.ts b/src/app/core/shared/value-list-browse-definition.model.ts index e22742bb78..0302ec59c7 100644 --- a/src/app/core/shared/value-list-browse-definition.model.ts +++ b/src/app/core/shared/value-list-browse-definition.model.ts @@ -5,7 +5,7 @@ import { VALUE_LIST_BROWSE_DEFINITION } from './value-list-browse-definition.res import { ResourceType } from './resource-type'; import { NonHierarchicalBrowseDefinition } from './non-hierarchical-browse-definition'; import { HALLink } from './hal-link.model'; -import { BrowseByDataType } from '../../browse-by/browse-by-switcher/browse-by-decorator'; +import { BrowseByDataType } from '../../browse-by/browse-by-switcher/browse-by-data-type'; /** * BrowseDefinition model for browses of type 'valueList' diff --git a/src/app/navbar/navbar.component.spec.ts b/src/app/navbar/navbar.component.spec.ts index 983eace055..db1c448409 100644 --- a/src/app/navbar/navbar.component.spec.ts +++ b/src/app/navbar/navbar.component.spec.ts @@ -16,7 +16,7 @@ import { RouterTestingModule } from '@angular/router/testing'; import { BrowseService } from '../core/browse/browse.service'; import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils'; import { buildPaginatedList } from '../core/data/paginated-list.model'; -import { BrowseByDataType } from '../browse-by/browse-by-switcher/browse-by-decorator'; +import { BrowseByDataType } from '../browse-by/browse-by-switcher/browse-by-data-type'; import { Item } from '../core/shared/item.model'; import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service'; import { ThemeService } from '../shared/theme-support/theme.service'; diff --git a/src/themes/custom/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts b/src/themes/custom/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts index fcbe15cfae..58a38c41e5 100644 --- a/src/themes/custom/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts +++ b/src/themes/custom/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts @@ -1,6 +1,7 @@ import { Component } from '@angular/core'; import { BrowseByDatePageComponent as BaseComponent } from '../../../../../app/browse-by/browse-by-date-page/browse-by-date-page.component'; -import { rendersBrowseBy, BrowseByDataType } from '../../../../../app/browse-by/browse-by-switcher/browse-by-decorator'; +import { BrowseByDataType } from '../../../../../app/browse-by/browse-by-switcher/browse-by-data-type'; +import { rendersBrowseBy } from '../../../../../app/browse-by/browse-by-switcher/browse-by-decorator'; import { Context } from '../../../../../app/core/shared/context.model'; @Component({ diff --git a/src/themes/custom/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts b/src/themes/custom/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts index 533dca29ca..ef57478087 100644 --- a/src/themes/custom/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts +++ b/src/themes/custom/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts @@ -1,6 +1,7 @@ import { Component } from '@angular/core'; import { BrowseByMetadataPageComponent as BaseComponent } from '../../../../../app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component'; -import { rendersBrowseBy, BrowseByDataType } from '../../../../../app/browse-by/browse-by-switcher/browse-by-decorator'; +import { BrowseByDataType } from '../../../../../app/browse-by/browse-by-switcher/browse-by-data-type'; +import { rendersBrowseBy } from '../../../../../app/browse-by/browse-by-switcher/browse-by-decorator'; import { Context } from '../../../../../app/core/shared/context.model'; @Component({ diff --git a/src/themes/custom/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts b/src/themes/custom/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts index afc0292bef..b8fb968c76 100644 --- a/src/themes/custom/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts +++ b/src/themes/custom/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts @@ -1,6 +1,7 @@ import { Component } from '@angular/core'; import { BrowseByTaxonomyPageComponent as BaseComponent } from '../../../../../app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component'; -import { rendersBrowseBy, BrowseByDataType } from '../../../../../app/browse-by/browse-by-switcher/browse-by-decorator'; +import { BrowseByDataType } from '../../../../../app/browse-by/browse-by-switcher/browse-by-data-type'; +import { rendersBrowseBy } from '../../../../../app/browse-by/browse-by-switcher/browse-by-decorator'; import { Context } from '../../../../../app/core/shared/context.model'; @Component({ diff --git a/src/themes/custom/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts b/src/themes/custom/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts index 5424293529..4f6a1a5020 100644 --- a/src/themes/custom/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts +++ b/src/themes/custom/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts @@ -1,6 +1,7 @@ import { Component } from '@angular/core'; import { BrowseByTitlePageComponent as BaseComponent } from '../../../../../app/browse-by/browse-by-title-page/browse-by-title-page.component'; -import { rendersBrowseBy, BrowseByDataType } from '../../../../../app/browse-by/browse-by-switcher/browse-by-decorator'; +import { BrowseByDataType } from '../../../../../app/browse-by/browse-by-switcher/browse-by-data-type'; +import { rendersBrowseBy } from '../../../../../app/browse-by/browse-by-switcher/browse-by-decorator'; import { Context } from '../../../../../app/core/shared/context.model'; @Component({ From 7942c900f4697368e1583eeab0ba6b5f73be3b9e Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Sun, 17 Dec 2023 16:17:16 +0100 Subject: [PATCH 19/47] Component doesn't have to be initialised anymore in ngOnChanges because connectInputsAndOutputs will automatically call the child component's ngOnChanges already --- .../abstract-component-loader.component.ts | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/app/shared/abstract-component-loader/abstract-component-loader.component.ts b/src/app/shared/abstract-component-loader/abstract-component-loader.component.ts index 12f0f02821..888edffe67 100644 --- a/src/app/shared/abstract-component-loader/abstract-component-loader.component.ts +++ b/src/app/shared/abstract-component-loader/abstract-component-loader.component.ts @@ -2,7 +2,7 @@ import { Component, ComponentRef, Input, OnChanges, OnDestroy, OnInit, SimpleCha import { Context } from '../../core/shared/context.model'; import { ThemeService } from '../theme-support/theme.service'; import { GenericConstructor } from '../../core/shared/generic-constructor'; -import { hasNoValue, hasValue, isNotEmpty } from '../empty.util'; +import { hasValue, isNotEmpty } from '../empty.util'; import { Subscription } from 'rxjs'; import { DynamicComponentLoaderDirective } from './dynamic-component-loader.directive'; @@ -57,21 +57,15 @@ export abstract class AbstractComponentLoaderComponent implements OnInit, OnC * Set up the dynamic child component */ ngOnInit(): void { - if (hasNoValue(this.compRef)) { - this.instantiateComponent(); - } + this.instantiateComponent(); } /** * Whenever the inputs change, update the inputs of the dynamic component */ ngOnChanges(changes: SimpleChanges): void { - if (hasNoValue(this.compRef)) { - // sometimes the component has not been initialized yet, so it first needs to be initialized - // before being called again - this.instantiateComponent(); - } else { - if (this.inputNamesDependentForComponent.some((name: any) => hasValue(changes[name]) && changes[name].previousValue !== changes[name].currentValue)) { + if (hasValue(this.compRef)) { + if (this.inputNamesDependentForComponent.some((name: keyof this & string) => hasValue(changes[name]) && changes[name].previousValue !== changes[name].currentValue)) { // Recreate the component when the @Input()s used by getComponent() aren't up-to-date anymore this.destroyComponentInstance(); this.instantiateComponent(); @@ -123,7 +117,7 @@ export abstract class AbstractComponentLoaderComponent implements OnInit, OnC /** * Connect the inputs and outputs of this component to the dynamic component, - * to ensure they're in sync + * to ensure they're in sync, the ngOnChanges method will automatically be called by setInput */ public connectInputsAndOutputs(): void { if (isNotEmpty(this.inputNames) && hasValue(this.compRef) && hasValue(this.compRef.instance)) { From 480c7a6ce0664e3ca3d3a096dff45e59e8519e87 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Tue, 26 Dec 2023 14:31:07 +0100 Subject: [PATCH 20/47] Destroy dynamically generated components in onDestroy & replace deprecated createComponent --- ...in-search-result-grid-element.component.ts | 43 +++++++++++------- ...t-admin-workflow-grid-element.component.ts | 45 ++++++++++++------- .../loading/themed-loading.component.ts | 5 +-- ...etadata-representation-loader.component.ts | 19 +++++--- .../claimed-task-actions-loader.component.ts | 16 +++---- ...table-object-component-loader.component.ts | 12 +++-- .../shared/theme-support/themed.component.ts | 5 +-- 7 files changed, 88 insertions(+), 57 deletions(-) diff --git a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.ts b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.ts index 1ab8fee8c2..06970661d5 100644 --- a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.ts +++ b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.ts @@ -1,4 +1,4 @@ -import { Component, ComponentFactoryResolver, ElementRef, OnInit, ViewChild } from '@angular/core'; +import { Component, ElementRef, OnInit, ViewChild, ComponentRef, OnDestroy } from '@angular/core'; import { Item } from '../../../../../core/shared/item.model'; import { ViewMode } from '../../../../../core/shared/view-mode.model'; import { @@ -13,6 +13,7 @@ import { BitstreamDataService } from '../../../../../core/data/bitstream-data.se import { GenericConstructor } from '../../../../../core/shared/generic-constructor'; import { ListableObjectDirective } from '../../../../../shared/object-collection/shared/listable-object/listable-object.directive'; import { ThemeService } from '../../../../../shared/theme-support/theme.service'; +import { hasValue } from '../../../../../shared/empty.util'; @listableObjectComponent(ItemSearchResult, ViewMode.GridElement, Context.AdminSearch) @Component({ @@ -23,15 +24,16 @@ import { ThemeService } from '../../../../../shared/theme-support/theme.service' /** * The component for displaying a list element for an item search result on the admin search page */ -export class ItemAdminSearchResultGridElementComponent extends SearchResultGridElementComponent implements OnInit { +export class ItemAdminSearchResultGridElementComponent extends SearchResultGridElementComponent implements OnDestroy, OnInit { @ViewChild(ListableObjectDirective, { static: true }) listableObjectDirective: ListableObjectDirective; @ViewChild('badges', { static: true }) badges: ElementRef; @ViewChild('buttons', { static: true }) buttons: ElementRef; + protected compRef: ComponentRef; + constructor(protected truncatableService: TruncatableService, protected bitstreamDataService: BitstreamDataService, private themeService: ThemeService, - private componentFactoryResolver: ComponentFactoryResolver ) { super(truncatableService, bitstreamDataService); } @@ -41,23 +43,32 @@ export class ItemAdminSearchResultGridElementComponent extends SearchResultGridE */ ngOnInit(): void { super.ngOnInit(); - const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.getComponent()); + const component: GenericConstructor = this.getComponent(); const viewContainerRef = this.listableObjectDirective.viewContainerRef; viewContainerRef.clear(); - const componentRef = viewContainerRef.createComponent( - componentFactory, - 0, - undefined, - [ - [this.badges.nativeElement], - [this.buttons.nativeElement] - ]); - (componentRef.instance as any).object = this.object; - (componentRef.instance as any).index = this.index; - (componentRef.instance as any).linkType = this.linkType; - (componentRef.instance as any).listID = this.listID; + this.compRef = viewContainerRef.createComponent( + component, { + index: 0, + injector: undefined, + projectableNodes: [ + [this.badges.nativeElement], + [this.buttons.nativeElement], + ], + }, + ); + (this.compRef.instance as any).object = this.object; + (this.compRef.instance as any).index = this.index; + (this.compRef.instance as any).linkType = this.linkType; + (this.compRef.instance as any).listID = this.listID; + } + + ngOnDestroy(): void { + if (hasValue(this.compRef)) { + this.compRef.destroy(); + this.compRef = undefined; + } } /** diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.ts b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.ts index 68f10916d5..14d6e81aca 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.ts @@ -1,4 +1,4 @@ -import { Component, ComponentFactoryResolver, ElementRef, ViewChild } from '@angular/core'; +import { Component, ElementRef, ViewChild, ComponentRef, OnDestroy, OnInit } from '@angular/core'; import { Item } from '../../../../../core/shared/item.model'; import { ViewMode } from '../../../../../core/shared/view-mode.model'; import { @@ -23,6 +23,7 @@ import { import { take } from 'rxjs/operators'; import { WorkflowItemSearchResult } from '../../../../../shared/object-collection/shared/workflow-item-search-result.model'; import { ThemeService } from '../../../../../shared/theme-support/theme.service'; +import { hasValue } from '../../../../../shared/empty.util'; @listableObjectComponent(WorkflowItemSearchResult, ViewMode.GridElement, Context.AdminWorkflowSearch) @Component({ @@ -33,7 +34,7 @@ import { ThemeService } from '../../../../../shared/theme-support/theme.service' /** * The component for displaying a grid element for an workflow item on the admin workflow search page */ -export class WorkflowItemSearchResultAdminWorkflowGridElementComponent extends SearchResultGridElementComponent { +export class WorkflowItemSearchResultAdminWorkflowGridElementComponent extends SearchResultGridElementComponent implements OnDestroy, OnInit { /** * Directive used to render the dynamic component in */ @@ -54,8 +55,9 @@ export class WorkflowItemSearchResultAdminWorkflowGridElementComponent extends S */ public item$: Observable; + protected compRef: ComponentRef; + constructor( - private componentFactoryResolver: ComponentFactoryResolver, private linkService: LinkService, protected truncatableService: TruncatableService, private themeService: ThemeService, @@ -73,28 +75,37 @@ export class WorkflowItemSearchResultAdminWorkflowGridElementComponent extends S this.dso = this.linkService.resolveLink(this.dso, followLink('item')); this.item$ = (this.dso.item as Observable>).pipe(getAllSucceededRemoteData(), getRemoteDataPayload()); this.item$.pipe(take(1)).subscribe((item: Item) => { - const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.getComponent(item)); + const component: GenericConstructor = this.getComponent(item); const viewContainerRef = this.listableObjectDirective.viewContainerRef; viewContainerRef.clear(); - const componentRef = viewContainerRef.createComponent( - componentFactory, - 0, - undefined, - [ - [this.badges.nativeElement], - [this.buttons.nativeElement] - ]); - (componentRef.instance as any).object = item; - (componentRef.instance as any).index = this.index; - (componentRef.instance as any).linkType = this.linkType; - (componentRef.instance as any).listID = this.listID; - componentRef.changeDetectorRef.detectChanges(); + this.compRef = viewContainerRef.createComponent( + component, { + index: 0, + injector: undefined, + projectableNodes: [ + [this.badges.nativeElement], + [this.buttons.nativeElement], + ], + }, + ); + (this.compRef.instance as any).object = item; + (this.compRef.instance as any).index = this.index; + (this.compRef.instance as any).linkType = this.linkType; + (this.compRef.instance as any).listID = this.listID; + this.compRef.changeDetectorRef.detectChanges(); } ); } + ngOnDestroy(): void { + if (hasValue(this.compRef)) { + this.compRef.destroy(); + this.compRef = undefined; + } + } + /** * Fetch the component depending on the item's entity type, view mode and context * @returns {GenericConstructor} diff --git a/src/app/shared/loading/themed-loading.component.ts b/src/app/shared/loading/themed-loading.component.ts index ffdf9d3cbe..5b45cff904 100644 --- a/src/app/shared/loading/themed-loading.component.ts +++ b/src/app/shared/loading/themed-loading.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, ComponentFactoryResolver, ChangeDetectorRef } from '@angular/core'; +import { Component, Input, ChangeDetectorRef } from '@angular/core'; import { ThemedComponent } from '../theme-support/themed.component'; import { LoadingComponent } from './loading.component'; import { ThemeService } from '../theme-support/theme.service'; @@ -20,11 +20,10 @@ export class ThemedLoadingComponent extends ThemedComponent { protected inAndOutputNames: (keyof LoadingComponent & keyof this)[] = ['message', 'showMessage', 'spinner']; constructor( - protected resolver: ComponentFactoryResolver, protected cdr: ChangeDetectorRef, protected themeService: ThemeService ) { - super(resolver, cdr, themeService); + super(cdr, themeService); } protected getComponentName(): string { diff --git a/src/app/shared/metadata-representation/metadata-representation-loader.component.ts b/src/app/shared/metadata-representation/metadata-representation-loader.component.ts index 7077949809..ddb764ed15 100644 --- a/src/app/shared/metadata-representation/metadata-representation-loader.component.ts +++ b/src/app/shared/metadata-representation/metadata-representation-loader.component.ts @@ -1,4 +1,4 @@ -import { Component, ComponentFactoryResolver, Inject, Input, OnInit, ViewChild } from '@angular/core'; +import { Component, Inject, Input, OnInit, ViewChild, ComponentRef, OnDestroy } from '@angular/core'; import { MetadataRepresentation, MetadataRepresentationType @@ -19,8 +19,9 @@ import { ThemeService } from '../theme-support/theme.service'; /** * Component for determining what component to use depending on the item's entity type (dspace.entity.type), its metadata representation and, optionally, its context */ -export class MetadataRepresentationLoaderComponent implements OnInit { +export class MetadataRepresentationLoaderComponent implements OnDestroy, OnInit { private componentRefInstance: MetadataRepresentationListElementComponent; + protected compRef: ComponentRef; /** * The item or metadata to determine the component for @@ -47,7 +48,6 @@ export class MetadataRepresentationLoaderComponent implements OnInit { @ViewChild(MetadataRepresentationDirective, {static: true}) mdRepDirective: MetadataRepresentationDirective; constructor( - private componentFactoryResolver: ComponentFactoryResolver, private themeService: ThemeService, @Inject(METADATA_REPRESENTATION_COMPONENT_FACTORY) private getMetadataRepresentationComponent: (entityType: string, mdRepresentationType: MetadataRepresentationType, context: Context, theme: string) => GenericConstructor, ) { @@ -57,16 +57,23 @@ export class MetadataRepresentationLoaderComponent implements OnInit { * Set up the dynamic child component */ ngOnInit(): void { - const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.getComponent()); + const component: GenericConstructor = this.getComponent(); const viewContainerRef = this.mdRepDirective.viewContainerRef; viewContainerRef.clear(); - const componentRef = viewContainerRef.createComponent(componentFactory); - this.componentRefInstance = componentRef.instance as MetadataRepresentationListElementComponent; + this.compRef = viewContainerRef.createComponent(component); + this.componentRefInstance = this.compRef.instance as MetadataRepresentationListElementComponent; this.componentRefInstance.metadataRepresentation = this.mdRepresentation; } + ngOnDestroy(): void { + if (hasValue(this.compRef)) { + this.compRef.destroy(); + this.compRef = undefined; + } + } + /** * Fetch the component depending on the item's entity type, metadata representation type and context * @returns {string} diff --git a/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts b/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts index 6a14aeb5bc..e835c1b1da 100644 --- a/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts @@ -1,12 +1,11 @@ import { Component, - ComponentFactoryResolver, EventEmitter, Input, OnDestroy, OnInit, Output, - ViewChild + ViewChild, ComponentRef } from '@angular/core'; import { getComponentByWorkflowTaskOption } from './claimed-task-actions-decorator'; import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model'; @@ -64,8 +63,7 @@ export class ClaimedTaskActionsLoaderComponent implements OnInit, OnDestroy { */ protected subs: Subscription[] = []; - constructor(private componentFactoryResolver: ComponentFactoryResolver) { - } + protected compRef: ComponentRef; /** * Fetch, create and initialize the relevant component @@ -74,13 +72,11 @@ export class ClaimedTaskActionsLoaderComponent implements OnInit, OnDestroy { const comp = this.getComponentByWorkflowTaskOption(this.option); if (hasValue(comp)) { - const componentFactory = this.componentFactoryResolver.resolveComponentFactory(comp); - const viewContainerRef = this.claimedTaskActionsDirective.viewContainerRef; viewContainerRef.clear(); - const componentRef = viewContainerRef.createComponent(componentFactory); - const componentInstance = (componentRef.instance as ClaimedTaskActionsAbstractComponent); + this.compRef = viewContainerRef.createComponent(comp); + const componentInstance = (this.compRef.instance as ClaimedTaskActionsAbstractComponent); componentInstance.item = this.item; componentInstance.object = this.object; componentInstance.workflowitem = this.workflowitem; @@ -98,6 +94,10 @@ export class ClaimedTaskActionsLoaderComponent implements OnInit, OnDestroy { * Unsubscribe from open subscriptions */ ngOnDestroy(): void { + if (hasValue(this.compRef)) { + this.compRef.destroy(); + this.compRef = undefined; + } this.subs .filter((subscription) => hasValue(subscription)) .forEach((subscription) => subscription.unsubscribe()); diff --git a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts index 6b75c59181..747a32cfec 100644 --- a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts +++ b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts @@ -23,7 +23,7 @@ import { getListableObjectComponent } from './listable-object.decorator'; import { GenericConstructor } from '../../../../core/shared/generic-constructor'; import { ListableObjectDirective } from './listable-object.directive'; import { CollectionElementLinkType } from '../../collection-element-link.type'; -import { hasValue, isNotEmpty } from '../../../empty.util'; +import { hasValue, isNotEmpty, hasNoValue } from '../../../empty.util'; import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; import { ThemeService } from '../../../theme-support/theme.service'; @@ -141,7 +141,9 @@ export class ListableObjectComponentLoaderComponent implements OnInit, OnChanges * Setup the dynamic child component */ ngOnInit(): void { - this.instantiateComponent(this.object); + if (hasNoValue(this.compRef)) { + this.instantiateComponent(this.object); + } } /** @@ -153,7 +155,11 @@ export class ListableObjectComponentLoaderComponent implements OnInit, OnChanges } } - ngOnDestroy() { + ngOnDestroy(): void { + if (hasValue(this.compRef)) { + this.compRef.destroy(); + this.compRef = undefined; + } this.subs .filter((subscription) => hasValue(subscription)) .forEach((subscription) => subscription.unsubscribe()); diff --git a/src/app/shared/theme-support/themed.component.ts b/src/app/shared/theme-support/themed.component.ts index 87f182a5ff..2b75f5429c 100644 --- a/src/app/shared/theme-support/themed.component.ts +++ b/src/app/shared/theme-support/themed.component.ts @@ -6,7 +6,6 @@ import { SimpleChanges, OnInit, OnDestroy, - ComponentFactoryResolver, ChangeDetectorRef, OnChanges } from '@angular/core'; @@ -31,7 +30,6 @@ export abstract class ThemedComponent implements OnInit, OnDestroy, OnChanges protected inAndOutputNames: (keyof T & keyof this)[] = []; constructor( - protected resolver: ComponentFactoryResolver, protected cdr: ChangeDetectorRef, protected themeService: ThemeService ) { @@ -87,8 +85,7 @@ export abstract class ThemedComponent implements OnInit, OnDestroy, OnChanges } }), ).subscribe((constructor: GenericConstructor) => { - const factory = this.resolver.resolveComponentFactory(constructor); - this.compRef = this.vcr.createComponent(factory); + this.compRef = this.vcr.createComponent(constructor); this.connectInputsAndOutputs(); this.cdr.markForCheck(); }); From e77898eaacb3489f9bdd077a709c90ca66ca0dec Mon Sep 17 00:00:00 2001 From: Agustina Martinez Date: Tue, 23 Jan 2024 21:47:29 +0000 Subject: [PATCH 21/47] Update item-status.component.ts Fix initialOperations typo and return ops with register doi op --- .../edit-item-page/item-status/item-status.component.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/item-page/edit-item-page/item-status/item-status.component.ts b/src/app/item-page/edit-item-page/item-status/item-status.component.ts index 8e04985c18..d3a5e033d7 100644 --- a/src/app/item-page/edit-item-page/item-status/item-status.component.ts +++ b/src/app/item-page/edit-item-page/item-status/item-status.component.ts @@ -117,7 +117,7 @@ export class ItemStatusComponent implements OnInit { * The value is supposed to be a href for the button */ const currentUrl = this.getCurrentUrl(item); - const inititalOperations: ItemOperation[] = [ + const initialOperations: ItemOperation[] = [ new ItemOperation('authorizations', `${currentUrl}/authorizations`, FeatureID.CanManagePolicies, true), new ItemOperation('mappedCollections', `${currentUrl}/mapper`, FeatureID.CanManageMappings, true), item.isWithdrawn @@ -130,7 +130,7 @@ export class ItemStatusComponent implements OnInit { new ItemOperation('delete', `${currentUrl}/delete`, FeatureID.CanDelete, true) ]; - this.operations$.next(inititalOperations); + this.operations$.next(initialOperations); /** * When the identifier data stream changes, determine whether the register DOI button should be shown or not. @@ -170,12 +170,12 @@ export class ItemStatusComponent implements OnInit { }), // Switch map pushes the register DOI operation onto a copy of the base array then returns to the pipe switchMap((showDoi: boolean) => { - const ops = [...inititalOperations]; + const ops = [...initialOperations]; if (showDoi) { const op = new ItemOperation('register-doi', `${currentUrl}/register-doi`, FeatureID.CanRegisterDOI, true); ops.splice(ops.length - 1, 0, op); // Add item before last } - return inititalOperations; + return ops; }), concatMap((op: ItemOperation) => { if (hasValue(op.featureID)) { From 6497a156aa1888da2de48dae235e67ae93d49fe9 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Thu, 28 Dec 2023 20:42:21 +0100 Subject: [PATCH 22/47] Destroy dynamically generated components in onDestroy & replace deprecated createComponent --- ...in-search-result-grid-element.component.ts | 8 ++-- ...t-admin-workflow-grid-element.component.ts | 37 ++++++++-------- ...t-admin-workflow-grid-element.component.ts | 42 ++++++++++++------- src/app/shared/context-help.directive.ts | 21 +++++----- ...anced-workflow-actions-loader.component.ts | 24 ++++++++--- 5 files changed, 78 insertions(+), 54 deletions(-) diff --git a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.ts b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.ts index 481145ab8b..36707187dd 100644 --- a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.ts +++ b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.ts @@ -61,10 +61,10 @@ export class ItemAdminSearchResultGridElementComponent extends SearchResultGridE ], }, ); - (this.compRef.instance as any).object = this.object; - (this.compRef.instance as any).index = this.index; - (this.compRef.instance as any).linkType = this.linkType; - (this.compRef.instance as any).listID = this.listID; + this.compRef.setInput('object',this.object); + this.compRef.setInput('index', this.index); + this.compRef.setInput('linkType', this.linkType); + this.compRef.setInput('listID', this.listID); } ngOnDestroy(): void { diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.ts b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.ts index 0c4527070d..4c25a9f998 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.ts @@ -77,28 +77,27 @@ export class WorkflowItemSearchResultAdminWorkflowGridElementComponent extends S this.dso = this.linkService.resolveLink(this.dso, followLink('item')); this.item$ = (this.dso.item as Observable>).pipe(getAllSucceededRemoteData(), getRemoteDataPayload()); this.item$.pipe(take(1)).subscribe((item: Item) => { - const component: GenericConstructor = this.getComponent(item); + const component: GenericConstructor = this.getComponent(item); - const viewContainerRef = this.listableObjectDirective.viewContainerRef; - viewContainerRef.clear(); + const viewContainerRef = this.listableObjectDirective.viewContainerRef; + viewContainerRef.clear(); - this.compRef = viewContainerRef.createComponent( - component, { - index: 0, - injector: undefined, - projectableNodes: [ - [this.badges.nativeElement], - [this.buttons.nativeElement], - ], - }, - ); - (this.compRef.instance as any).object = item; - (this.compRef.instance as any).index = this.index; - (this.compRef.instance as any).linkType = this.linkType; - (this.compRef.instance as any).listID = this.listID; + this.compRef = viewContainerRef.createComponent( + component, { + index: 0, + injector: undefined, + projectableNodes: [ + [this.badges.nativeElement], + [this.buttons.nativeElement], + ], + }, + ); + this.compRef.setInput('object', item); + this.compRef.setInput('index', this.index); + this.compRef.setInput('linkType', this.linkType); + this.compRef.setInput('listID', this.listID); this.compRef.changeDetectorRef.detectChanges(); - } - ); + }); } ngOnDestroy(): void { diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workspace-item/workspace-item-search-result-admin-workflow-grid-element.component.ts b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workspace-item/workspace-item-search-result-admin-workflow-grid-element.component.ts index d6f39e79fe..9404b02a2a 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workspace-item/workspace-item-search-result-admin-workflow-grid-element.component.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workspace-item/workspace-item-search-result-admin-workflow-grid-element.component.ts @@ -1,4 +1,4 @@ -import { Component, ComponentFactoryResolver, ElementRef, ViewChild, OnInit } from '@angular/core'; +import { Component, ElementRef, ViewChild, OnInit, OnDestroy, ComponentRef } from '@angular/core'; import { BehaviorSubject, Observable } from 'rxjs'; import { map, mergeMap, take, tap } from 'rxjs/operators'; @@ -37,6 +37,7 @@ import { SupervisionOrder } from '../../../../../core/supervision-order/models/s import { PaginatedList } from '../../../../../core/data/paginated-list.model'; import { SupervisionOrderDataService } from '../../../../../core/supervision-order/supervision-order-data.service'; import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; +import { hasValue } from '../../../../../shared/empty.util'; @listableObjectComponent(WorkspaceItemSearchResult, ViewMode.GridElement, Context.AdminWorkflowSearch) @Component({ @@ -47,7 +48,7 @@ import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service /** * The component for displaying a grid element for an workflow item on the admin workflow search page */ -export class WorkspaceItemSearchResultAdminWorkflowGridElementComponent extends SearchResultGridElementComponent implements OnInit { +export class WorkspaceItemSearchResultAdminWorkflowGridElementComponent extends SearchResultGridElementComponent implements OnDestroy, OnInit { /** * The item linked to the workspace item @@ -79,9 +80,13 @@ export class WorkspaceItemSearchResultAdminWorkflowGridElementComponent extends */ @ViewChild('buttons', { static: true }) buttons: ElementRef; + /** + * The reference to the dynamic component + */ + protected compRef: ComponentRef; + constructor( public dsoNameService: DSONameService, - private componentFactoryResolver: ComponentFactoryResolver, private linkService: LinkService, protected truncatableService: TruncatableService, private themeService: ThemeService, @@ -100,24 +105,24 @@ export class WorkspaceItemSearchResultAdminWorkflowGridElementComponent extends this.dso = this.linkService.resolveLink(this.dso, followLink('item')); this.item$ = (this.dso.item as Observable>).pipe(getAllSucceededRemoteData(), getRemoteDataPayload()); this.item$.pipe(take(1)).subscribe((item: Item) => { - const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.getComponent(item)); + const component: GenericConstructor = this.getComponent(item); const viewContainerRef = this.listableObjectDirective.viewContainerRef; viewContainerRef.clear(); - const componentRef = viewContainerRef.createComponent( - componentFactory, - 0, - undefined, - [ + this.compRef = viewContainerRef.createComponent( + component, { + index: 0, + projectableNodes: [ [this.badges.nativeElement], [this.buttons.nativeElement] - ]); - (componentRef.instance as any).object = item; - (componentRef.instance as any).index = this.index; - (componentRef.instance as any).linkType = this.linkType; - (componentRef.instance as any).listID = this.listID; - componentRef.changeDetectorRef.detectChanges(); + ], + }); + this.compRef.setInput('object', item); + this.compRef.setInput('index', this.index); + this.compRef.setInput('linkType', this.linkType); + this.compRef.setInput('listID', this.listID); + this.compRef.changeDetectorRef.detectChanges(); } ); @@ -130,6 +135,13 @@ export class WorkspaceItemSearchResultAdminWorkflowGridElementComponent extends }); } + ngOnDestroy(): void { + if (hasValue(this.compRef)) { + this.compRef.destroy(); + this.compRef = undefined; + } + } + /** * Fetch the component depending on the item's entity type, view mode and context * @returns {GenericConstructor} diff --git a/src/app/shared/context-help.directive.ts b/src/app/shared/context-help.directive.ts index 41d6daec21..52a822de2c 100644 --- a/src/app/shared/context-help.directive.ts +++ b/src/app/shared/context-help.directive.ts @@ -1,5 +1,4 @@ import { - ComponentFactoryResolver, ComponentRef, Directive, Input, @@ -12,6 +11,7 @@ import { PlacementArray } from '@ng-bootstrap/ng-bootstrap/util/positioning'; import { ContextHelpWrapperComponent } from './context-help-wrapper/context-help-wrapper.component'; import { PlacementDir } from './context-help-wrapper/placement-dir.model'; import { ContextHelpService } from './context-help.service'; +import { hasValue } from './empty.util'; export interface ContextHelpDirectiveInput { content: string; @@ -43,7 +43,6 @@ export class ContextHelpDirective implements OnChanges, OnDestroy { constructor( private templateRef: TemplateRef, private viewContainerRef: ViewContainerRef, - private componentFactoryResolver: ComponentFactoryResolver, private contextHelpService: ContextHelpService ) {} @@ -53,19 +52,21 @@ export class ContextHelpDirective implements OnChanges, OnDestroy { this.contextHelpService.add({id: this.dsContextHelp.id, isTooltipVisible: false}); if (this.wrapper === undefined) { - const factory - = this.componentFactoryResolver.resolveComponentFactory(ContextHelpWrapperComponent); - this.wrapper = this.viewContainerRef.createComponent(factory); + this.wrapper = this.viewContainerRef.createComponent(ContextHelpWrapperComponent); } - this.wrapper.instance.templateRef = this.templateRef; - this.wrapper.instance.content = this.dsContextHelp.content; - this.wrapper.instance.id = this.dsContextHelp.id; - this.wrapper.instance.tooltipPlacement = this.dsContextHelp.tooltipPlacement; - this.wrapper.instance.iconPlacement = this.dsContextHelp.iconPlacement; + this.wrapper.setInput('templateRef', this.templateRef); + this.wrapper.setInput('content', this.dsContextHelp.content); + this.wrapper.setInput('id', this.dsContextHelp.id); + this.wrapper.setInput('tooltipPlacement', this.dsContextHelp.tooltipPlacement); + this.wrapper.setInput('iconPlacement', this.dsContextHelp.iconPlacement); } ngOnDestroy() { this.clearMostRecentId(); + if (hasValue(this.wrapper)) { + this.wrapper.destroy(); + this.wrapper = undefined; + } } private clearMostRecentId(): void { diff --git a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.ts b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.ts index 32f14c015d..db47c6638f 100644 --- a/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.ts +++ b/src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, ViewChild, ComponentFactoryResolver, OnInit } from '@angular/core'; +import { Component, Input, ViewChild, OnInit, ComponentRef, OnDestroy } from '@angular/core'; import { hasValue } from '../../../shared/empty.util'; import { getAdvancedComponentByWorkflowTaskOption @@ -15,7 +15,7 @@ import { PAGE_NOT_FOUND_PATH } from '../../../app-routing-paths'; templateUrl: './advanced-workflow-actions-loader.component.html', styleUrls: ['./advanced-workflow-actions-loader.component.scss'], }) -export class AdvancedWorkflowActionsLoaderComponent implements OnInit { +export class AdvancedWorkflowActionsLoaderComponent implements OnDestroy, OnInit { /** * The name of the type to render @@ -28,8 +28,12 @@ export class AdvancedWorkflowActionsLoaderComponent implements OnInit { */ @ViewChild(AdvancedWorkflowActionsDirective, { static: true }) claimedTaskActionsDirective: AdvancedWorkflowActionsDirective; + /** + * The reference to the dynamic component + */ + protected compRef: ComponentRef; + constructor( - private componentFactoryResolver: ComponentFactoryResolver, private router: Router, ) { } @@ -40,16 +44,24 @@ export class AdvancedWorkflowActionsLoaderComponent implements OnInit { ngOnInit(): void { const comp = this.getComponentByWorkflowTaskOption(this.type); if (hasValue(comp)) { - const componentFactory = this.componentFactoryResolver.resolveComponentFactory(comp); - const viewContainerRef = this.claimedTaskActionsDirective.viewContainerRef; viewContainerRef.clear(); - viewContainerRef.createComponent(componentFactory); + this.compRef = viewContainerRef.createComponent(comp); } else { void this.router.navigate([PAGE_NOT_FOUND_PATH]); } } + /** + * Destroy the dynamically created component + */ + ngOnDestroy(): void { + if (hasValue(this.compRef)) { + this.compRef.destroy(); + this.compRef = undefined; + } + } + getComponentByWorkflowTaskOption(type: string): any { return getAdvancedComponentByWorkflowTaskOption(type); } From 2c2f59c7ef39a89aed02711f14098a0e4bb533dd Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 2 Feb 2024 14:27:33 -0600 Subject: [PATCH 23/47] Update cli.yml to reference dspacenet in docker-compose-rest. Remove unnecessary network from cli.assetstore.yml --- docker/cli.assetstore.yml | 5 ----- docker/cli.yml | 15 ++++++++------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/docker/cli.assetstore.yml b/docker/cli.assetstore.yml index 40e4974c7c..31bc53f64d 100644 --- a/docker/cli.assetstore.yml +++ b/docker/cli.assetstore.yml @@ -14,13 +14,8 @@ # Therefore, it should be kept in sync with that file version: "3.7" -networks: - dspacenet: - services: dspace-cli: - networks: - dspacenet: {} environment: # This assetstore zip is available from https://github.com/DSpace-Labs/AIP-Files/releases/tag/demo-entities-data - LOADASSETS=https://github.com/DSpace-Labs/AIP-Files/releases/download/demo-entities-data/assetstore.tar.gz diff --git a/docker/cli.yml b/docker/cli.yml index 223ec356b9..c2664fdf3e 100644 --- a/docker/cli.yml +++ b/docker/cli.yml @@ -13,7 +13,12 @@ # # Therefore, it should be kept in sync with that file version: "3.7" - +networks: + # Default to using network named 'dspacenet' from docker-compose-rest.yml. + # Its full name will be prepended with the project name (e.g. "-p d7" means it will be named "d7_dspacenet") + default: + name: ${COMPOSE_PROJECT_NAME}_dspacenet + external: true services: dspace-cli: image: "${DOCKER_OWNER:-dspace}/dspace-cli:${DSPACE_VER:-latest}" @@ -30,16 +35,12 @@ services: # solr.server: Ensure we are using the 'dspacesolr' image for Solr solr__P__server: http://dspacesolr:8983/solr volumes: - - "assetstore:/dspace/assetstore" + # Keep DSpace assetstore directory between reboots + - assetstore:/dspace/assetstore entrypoint: /dspace/bin/dspace command: help - networks: - - dspacenet tty: true stdin_open: true volumes: assetstore: - -networks: - dspacenet: From 239f082cce32edce7719ba3eab8f71e06e3955d0 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Fri, 2 Feb 2024 09:35:27 +0100 Subject: [PATCH 24/47] Moved the context @Input() to the loaders (since they are not always required for all the loaders) & added AbstractComponentLoaderComponent documentation --- .../browse-by-switcher.component.ts | 7 +++- .../abstract-component-loader.component.ts | 40 +++++++++++-------- ...etadata-representation-loader.component.ts | 9 ++++- .../claimed-task-actions-loader.component.ts | 2 - ...table-object-component-loader.component.ts | 11 +++-- 5 files changed, 44 insertions(+), 25 deletions(-) diff --git a/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts b/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts index 2ebea9a262..99ad720dd3 100644 --- a/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts +++ b/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts @@ -4,6 +4,7 @@ import { GenericConstructor } from '../../core/shared/generic-constructor'; import { AbstractComponentLoaderComponent } from '../../shared/abstract-component-loader/abstract-component-loader.component'; import { AbstractBrowseByTypeComponent } from '../abstract-browse-by-type.component'; import { BrowseByDataType } from './browse-by-data-type'; +import { Context } from '../../core/shared/context.model'; @Component({ selector: 'ds-browse-by-switcher', @@ -11,15 +12,17 @@ import { BrowseByDataType } from './browse-by-data-type'; }) export class BrowseBySwitcherComponent extends AbstractComponentLoaderComponent { + @Input() context: Context; + @Input() browseByType: BrowseByDataType; protected inputNamesDependentForComponent: (keyof this & string)[] = [ - ...this.inputNamesDependentForComponent, + 'context', 'browseByType', ]; protected inputNames: (keyof this & string)[] = [ - ...this.inputNames, + 'context', 'browseByType', ]; diff --git a/src/app/shared/abstract-component-loader/abstract-component-loader.component.ts b/src/app/shared/abstract-component-loader/abstract-component-loader.component.ts index 888edffe67..2ea7c50d75 100644 --- a/src/app/shared/abstract-component-loader/abstract-component-loader.component.ts +++ b/src/app/shared/abstract-component-loader/abstract-component-loader.component.ts @@ -1,22 +1,27 @@ -import { Component, ComponentRef, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewChild, ViewContainerRef } from '@angular/core'; -import { Context } from '../../core/shared/context.model'; +import { Component, ComponentRef, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewChild, ViewContainerRef } from '@angular/core'; import { ThemeService } from '../theme-support/theme.service'; import { GenericConstructor } from '../../core/shared/generic-constructor'; import { hasValue, isNotEmpty } from '../empty.util'; import { Subscription } from 'rxjs'; import { DynamicComponentLoaderDirective } from './dynamic-component-loader.directive'; +/** + * To create a new loader component you will need to: + *
    + *
  • Create a new LoaderComponent component extending this component
  • + *
  • Point the templateUrl to this component's templateUrl
  • + *
  • Add all the @Input()/@Output() names that the dynamically generated components should inherit from the loader to the inputNames/outputNames lists
  • + *
  • Create a decorator file containing the new decorator function, a map containing all the collected {@link Component}s and a function to retrieve the {@link Component}
  • + *
  • Call the function to retrieve the correct {@link Component} in getComponent()
  • + *
  • Add all the @Input()s you had to used in getComponent() in the inputNamesDependentForComponent array
  • + *
+ */ @Component({ selector: 'ds-abstract-component-loader', templateUrl: './abstract-component-loader.component.html', }) export abstract class AbstractComponentLoaderComponent implements OnInit, OnChanges, OnDestroy { - /** - * The optional context - */ - @Input() context: Context; - /** * Directive to determine where the dynamic child component is located */ @@ -33,20 +38,21 @@ export abstract class AbstractComponentLoaderComponent implements OnInit, OnC protected subs: Subscription[] = []; /** - * The @{@link Input}() that are used to find the matching component using {@link getComponent}. When the value of - * one of these @{@link Input}() change this loader needs to retrieve the best matching component again using the + * The @Input() that are used to find the matching component using {@link getComponent}. When the value of + * one of these @Input() change this loader needs to retrieve the best matching component again using the * {@link getComponent} method. */ - protected inputNamesDependentForComponent: (keyof this & string)[] = [ - 'context', - ]; + protected inputNamesDependentForComponent: (keyof this & string)[] = []; - protected inputNames: (keyof this & string)[] = [ - 'context', - ]; + /** + * The list of the @Input() names that should be passed down to the dynamically created components. + */ + protected inputNames: (keyof this & string)[] = []; - protected outputNames: (keyof this & string)[] = [ - ]; + /** + * The list of the @Output() names that should be passed down to the dynamically created components. + */ + protected outputNames: (keyof this & string)[] = []; constructor( protected themeService: ThemeService, diff --git a/src/app/shared/metadata-representation/metadata-representation-loader.component.ts b/src/app/shared/metadata-representation/metadata-representation-loader.component.ts index 4b63a31a2b..d722d24ff6 100644 --- a/src/app/shared/metadata-representation/metadata-representation-loader.component.ts +++ b/src/app/shared/metadata-representation/metadata-representation-loader.component.ts @@ -19,13 +19,20 @@ import { AbstractComponentLoaderComponent } from '../abstract-component-loader/a */ export class MetadataRepresentationLoaderComponent extends AbstractComponentLoaderComponent { + @Input() context: Context; + /** * The item or metadata to determine the component for */ @Input() mdRepresentation: MetadataRepresentation; + protected inputNamesDependentForComponent: (keyof this & string)[] = [ + 'context', + 'mdRepresentation', + ]; + protected inputNames: (keyof this & string)[] = [ - ...this.inputNames, + 'context', 'mdRepresentation', ]; diff --git a/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts b/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts index fb39347d63..7b01e98249 100644 --- a/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts @@ -47,7 +47,6 @@ export class ClaimedTaskActionsLoaderComponent extends AbstractComponentLoaderCo * The list of input and output names for the dynamic component */ protected inputNames: (keyof this & string)[] = [ - ...this.inputNames, 'item', 'object', 'option', @@ -55,7 +54,6 @@ export class ClaimedTaskActionsLoaderComponent extends AbstractComponentLoaderCo ]; protected outputNames: (keyof this & string)[] = [ - ...this.outputNames, 'processCompleted', ]; diff --git a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts index a83cdfb6b2..d280768631 100644 --- a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts +++ b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts @@ -58,7 +58,7 @@ export class ListableObjectComponentLoaderComponent extends AbstractComponentLoa /** * Whether to show the thumbnail preview */ - @Input() showThumbnails; + @Input() showThumbnails: boolean; /** * The value to display for this element @@ -70,13 +70,19 @@ export class ListableObjectComponentLoaderComponent extends AbstractComponentLoa */ @Output() contentChange = new EventEmitter(); + protected inputNamesDependentForComponent: (keyof this & string)[] = [ + 'object', + 'viewMode', + 'context', + ]; + /** * The list of input and output names for the dynamic component */ protected inputNames: (keyof this & string)[] = [ - ...this.inputNames, 'object', 'index', + 'context', 'linkType', 'listID', 'showLabel', @@ -86,7 +92,6 @@ export class ListableObjectComponentLoaderComponent extends AbstractComponentLoa ]; protected outputNames: (keyof this & string)[] = [ - ...this.outputNames, 'contentChange', ]; From ac5670a51428c58cf02cf139b97c7d358d5b5f30 Mon Sep 17 00:00:00 2001 From: Sascha Szott Date: Mon, 5 Feb 2024 12:06:11 +0100 Subject: [PATCH 25/47] remove translation (use singular form instead of plural form) --- src/assets/i18n/de.json5 | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/assets/i18n/de.json5 b/src/assets/i18n/de.json5 index c185a13432..94067fccd0 100644 --- a/src/assets/i18n/de.json5 +++ b/src/assets/i18n/de.json5 @@ -731,9 +731,6 @@ // "admin.workflow.title": "Administer Workflow", "admin.workflow.title": "Workflows verwalten", - // "admin.workflow.item.workflow": "Workflow", - "admin.workflow.item.workflow": "Workflows", - // "admin.workflow.item.delete": "Delete", "admin.workflow.item.delete": "Löschen", From 9043f63cd2f3ef9d99b4b22db041eddab9f2c95b Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 2 Feb 2024 14:57:34 -0600 Subject: [PATCH 26/47] Bug fix to GitHub CI build. Set compose project name via env variable. Also add default name if unspecified --- .github/workflows/build.yml | 2 ++ docker/cli.yml | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e2680420a2..52f20470a3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,6 +33,8 @@ jobs: #CHROME_VERSION: "90.0.4430.212-1" # Bump Node heap size (OOM in CI after upgrading to Angular 15) NODE_OPTIONS: '--max-old-space-size=4096' + # Project name to use when running docker-compose prior to e2e tests + COMPOSE_PROJECT_NAME: 'ci' strategy: # Create a matrix of Node versions to test against (in parallel) matrix: diff --git a/docker/cli.yml b/docker/cli.yml index c2664fdf3e..cc266b186f 100644 --- a/docker/cli.yml +++ b/docker/cli.yml @@ -16,8 +16,9 @@ version: "3.7" networks: # Default to using network named 'dspacenet' from docker-compose-rest.yml. # Its full name will be prepended with the project name (e.g. "-p d7" means it will be named "d7_dspacenet") + # If COMPOSITE_PROJECT_NAME is missing, default value will be "docker" (name of folder this file is in) default: - name: ${COMPOSE_PROJECT_NAME}_dspacenet + name: ${COMPOSE_PROJECT_NAME:-docker}_dspacenet external: true services: dspace-cli: From be0a6c71efae4c5d2eda09940587338e23bea630 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Fri, 2 Feb 2024 16:59:52 -0600 Subject: [PATCH 27/47] Resync docker-compose-ci and docker/docker-compose-rest with DSpace/DSpace docker scripts --- docker/docker-compose-ci.yml | 36 +++++++++++++++------------------- docker/docker-compose-rest.yml | 18 +++++++---------- 2 files changed, 23 insertions(+), 31 deletions(-) diff --git a/docker/docker-compose-ci.yml b/docker/docker-compose-ci.yml index edbb5b0759..07993e20c6 100644 --- a/docker/docker-compose-ci.yml +++ b/docker/docker-compose-ci.yml @@ -33,11 +33,11 @@ services: # Tell Statistics to commit all views immediately instead of waiting on Solr's autocommit. # This allows us to generate statistics in e2e tests so that statistics pages can be tested thoroughly. solr__D__statistics__P__autoCommit: 'false' + image: "${DOCKER_OWNER:-dspace}/dspace:${DSPACE_VER:-latest-test}" depends_on: - dspacedb - image: dspace/dspace:latest-test networks: - dspacenet: + - dspacenet ports: - published: 8080 target: 8080 @@ -45,8 +45,6 @@ services: tty: true volumes: - assetstore:/dspace/assetstore - # Mount DSpace's solr configs to a volume, so that we can share to 'dspacesolr' container (see below) - - solr_configs:/dspace/solr # Ensure that the database is ready BEFORE starting tomcat # 1. While a TCP connection to dspacedb port 5432 is not available, continue to sleep # 2. Then, run database migration to init database tables (including any out-of-order ignored migrations, if any) @@ -70,21 +68,18 @@ services: PGDATA: /pgdata image: dspace/dspace-postgres-pgcrypto:loadsql networks: - dspacenet: + - dspacenet stdin_open: true tty: true volumes: + # Keep Postgres data directory between reboots - pgdata:/pgdata # DSpace Solr container dspacesolr: container_name: dspacesolr - # Uses official Solr image at https://hub.docker.com/_/solr/ - image: solr:8.11-slim - # Needs main 'dspace' container to start first to guarantee access to solr_configs - depends_on: - - dspace + image: "${DOCKER_OWNER:-dspace}/dspace-solr:${DSPACE_VER:-latest}" networks: - dspacenet: + - dspacenet ports: - published: 8983 target: 8983 @@ -92,9 +87,6 @@ services: tty: true working_dir: /var/solr/data volumes: - # Mount our "solr_configs" volume available under the Solr's configsets folder (in a 'dspace' subfolder) - # This copies the Solr configs from main 'dspace' container into 'dspacesolr' via that volume - - solr_configs:/opt/solr/server/solr/configsets/dspace # Keep Solr data directory between reboots - solr_data:/var/solr/data # Initialize all DSpace Solr cores using the mounted configsets (see above), then start Solr @@ -103,14 +95,18 @@ services: - '-c' - | init-var-solr - precreate-core authority /opt/solr/server/solr/configsets/dspace/authority - precreate-core oai /opt/solr/server/solr/configsets/dspace/oai - precreate-core search /opt/solr/server/solr/configsets/dspace/search - precreate-core statistics /opt/solr/server/solr/configsets/dspace/statistics + precreate-core authority /opt/solr/server/solr/configsets/authority + cp -r /opt/solr/server/solr/configsets/authority/* authority + precreate-core oai /opt/solr/server/solr/configsets/oai + cp -r /opt/solr/server/solr/configsets/oai/* oai + precreate-core search /opt/solr/server/solr/configsets/search + cp -r /opt/solr/server/solr/configsets/search/* search + precreate-core statistics /opt/solr/server/solr/configsets/statistics + cp -r /opt/solr/server/solr/configsets/statistics/* statistics + precreate-core qaevent /opt/solr/server/solr/configsets/qaevent + cp -r /opt/solr/server/solr/configsets/qaevent/* qaevent exec solr -f volumes: assetstore: pgdata: solr_data: - # Special volume used to share Solr configs from 'dspace' to 'dspacesolr' container (see above) - solr_configs: \ No newline at end of file diff --git a/docker/docker-compose-rest.yml b/docker/docker-compose-rest.yml index ea766600ef..e1577ec837 100644 --- a/docker/docker-compose-rest.yml +++ b/docker/docker-compose-rest.yml @@ -43,7 +43,7 @@ services: depends_on: - dspacedb networks: - dspacenet: + - dspacenet ports: - published: 8080 target: 8080 @@ -51,8 +51,6 @@ services: tty: true volumes: - assetstore:/dspace/assetstore - # Mount DSpace's solr configs to a volume, so that we can share to 'dspacesolr' container (see below) - - solr_configs:/dspace/solr # Ensure that the database is ready BEFORE starting tomcat # 1. While a TCP connection to dspacedb port 5432 is not available, continue to sleep # 2. Then, run database migration to init database tables @@ -69,25 +67,23 @@ services: container_name: dspacedb environment: PGDATA: /pgdata - image: dspace/dspace-postgres-pgcrypto + image: "${DOCKER_OWNER:-dspace}/dspace-postgres-pgcrypto:${DSPACE_VER:-latest}" networks: - dspacenet: + - dspacenet ports: - published: 5432 target: 5432 stdin_open: true tty: true volumes: + # Keep Postgres data directory between reboots - pgdata:/pgdata # DSpace Solr container dspacesolr: container_name: dspacesolr image: "${DOCKER_OWNER:-dspace}/dspace-solr:${DSPACE_VER:-latest}" - # Needs main 'dspace' container to start first to guarantee access to solr_configs - depends_on: - - dspace networks: - dspacenet: + - dspacenet ports: - published: 8983 target: 8983 @@ -115,10 +111,10 @@ services: cp -r /opt/solr/server/solr/configsets/search/* search precreate-core statistics /opt/solr/server/solr/configsets/statistics cp -r /opt/solr/server/solr/configsets/statistics/* statistics + precreate-core qaevent /opt/solr/server/solr/configsets/qaevent + cp -r /opt/solr/server/solr/configsets/qaevent/* qaevent exec solr -f volumes: assetstore: pgdata: solr_data: - # Special volume used to share Solr configs from 'dspace' to 'dspacesolr' container (see above) - solr_configs: From 2fb393db7f8d45922f3a529b3bc1c34d2625818c Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Thu, 1 Feb 2024 21:02:55 +0100 Subject: [PATCH 28/47] Removed the AbstractBrowseByTypeComponent --- .../abstract-browse-by-type.component.ts | 32 ------------------- .../browse-by-metadata-page.component.ts | 26 +++++++++++---- .../browse-by-page.component.spec.ts | 3 +- .../browse-by-switcher/browse-by-decorator.ts | 10 +++--- .../browse-by-switcher.component.spec.ts | 3 +- .../browse-by-switcher.component.ts | 5 ++- .../browse-by-taxonomy-page.component.ts | 27 +++++++++++++--- 7 files changed, 51 insertions(+), 55 deletions(-) delete mode 100644 src/app/browse-by/abstract-browse-by-type.component.ts diff --git a/src/app/browse-by/abstract-browse-by-type.component.ts b/src/app/browse-by/abstract-browse-by-type.component.ts deleted file mode 100644 index 5bd39cfc50..0000000000 --- a/src/app/browse-by/abstract-browse-by-type.component.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Component, Input, OnDestroy } from '@angular/core'; -import { BrowseByDataType } from './browse-by-switcher/browse-by-data-type'; -import { Context } from '../core/shared/context.model'; -import { Subscription } from 'rxjs'; -import { hasValue } from '../shared/empty.util'; - -@Component({ - selector: 'ds-abstract-browse-by-type', - template: '', -}) -export abstract class AbstractBrowseByTypeComponent implements OnDestroy { - - /** - * The optional context - */ - @Input() context: Context; - - /** - * The {@link BrowseByDataType} of this Component - */ - @Input() browseByType: BrowseByDataType; - - /** - * List of subscriptions - */ - subs: Subscription[] = []; - - ngOnDestroy(): void { - this.subs.filter((sub: Subscription) => hasValue(sub)).forEach((sub: Subscription) => sub.unsubscribe()); - } - -} diff --git a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts index 4c897b40f8..499ba441f6 100644 --- a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts +++ b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts @@ -1,5 +1,5 @@ -import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; -import { Component, Inject, OnInit, OnDestroy } from '@angular/core'; +import { combineLatest as observableCombineLatest, Observable, Subscription } from 'rxjs'; +import { Component, Inject, OnInit, OnDestroy, Input } from '@angular/core'; import { RemoteData } from '../../core/data/remote-data'; import { PaginatedList } from '../../core/data/paginated-list.model'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; @@ -23,8 +23,8 @@ import { Community } from '../../core/shared/community.model'; import { APP_CONFIG, AppConfig } from '../../../config/app-config.interface'; import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; import { rendersBrowseBy } from '../browse-by-switcher/browse-by-decorator'; -import { AbstractBrowseByTypeComponent } from '../abstract-browse-by-type.component'; import { BrowseByDataType } from '../browse-by-switcher/browse-by-data-type'; +import { Context } from '../../core/shared/context.model'; export const BBM_PAGINATION_ID = 'bbm'; @@ -40,7 +40,17 @@ export const BBM_PAGINATION_ID = 'bbm'; * 'dc.contributor.*' */ @rendersBrowseBy(BrowseByDataType.Metadata) -export class BrowseByMetadataPageComponent extends AbstractBrowseByTypeComponent implements OnInit, OnDestroy { +export class BrowseByMetadataPageComponent implements OnInit, OnDestroy { + + /** + * The optional context + */ + @Input() context: Context; + + /** + * The {@link BrowseByDataType} of this Component + */ + @Input() browseByType: BrowseByDataType; /** * The list of browse-entries to display @@ -77,6 +87,11 @@ export class BrowseByMetadataPageComponent extends AbstractBrowseByTypeComponent */ currentSort$: Observable; + /** + * List of subscriptions + */ + subs: Subscription[] = []; + /** * The default browse id to resort to when none is provided */ @@ -129,7 +144,6 @@ export class BrowseByMetadataPageComponent extends AbstractBrowseByTypeComponent @Inject(APP_CONFIG) public appConfig: AppConfig, public dsoNameService: DSONameService, ) { - super(); this.fetchThumbnails = this.appConfig.browseBy.showThumbnails; this.paginationConfig = Object.assign(new PaginationComponentOptions(), { id: BBM_PAGINATION_ID, @@ -273,7 +287,7 @@ export class BrowseByMetadataPageComponent extends AbstractBrowseByTypeComponent } ngOnDestroy(): void { - super.ngOnDestroy(); + this.subs.filter((sub: Subscription) => hasValue(sub)).forEach((sub: Subscription) => sub.unsubscribe()); this.paginationService.clearPagination(this.paginationConfig.id); } diff --git a/src/app/browse-by/browse-by-page/browse-by-page.component.spec.ts b/src/app/browse-by/browse-by-page/browse-by-page.component.spec.ts index 3f9271a67d..25483028eb 100644 --- a/src/app/browse-by/browse-by-page/browse-by-page.component.spec.ts +++ b/src/app/browse-by/browse-by-page/browse-by-page.component.spec.ts @@ -9,7 +9,6 @@ import { getMockThemeService } from '../../shared/mocks/theme-service.mock'; import { ThemeService } from '../../shared/theme-support/theme.service'; import { rendersBrowseBy } from '../browse-by-switcher/browse-by-decorator'; import { Component } from '@angular/core'; -import { AbstractBrowseByTypeComponent } from '../abstract-browse-by-type.component'; import { BrowseDefinition } from '../../core/shared/browse-definition.model'; import { By } from '@angular/platform-browser'; import { BrowseByDataType } from '../browse-by-switcher/browse-by-data-type'; @@ -20,7 +19,7 @@ import { BrowseByDataType } from '../browse-by-switcher/browse-by-data-type'; selector: '', template: '', }) -class BrowseByTestComponent extends AbstractBrowseByTypeComponent { +class BrowseByTestComponent { } class TestBrowseByPageBrowseDefinition extends BrowseDefinition { diff --git a/src/app/browse-by/browse-by-switcher/browse-by-decorator.ts b/src/app/browse-by/browse-by-switcher/browse-by-decorator.ts index 835c34ebcb..62e666227d 100644 --- a/src/app/browse-by/browse-by-switcher/browse-by-decorator.ts +++ b/src/app/browse-by/browse-by-switcher/browse-by-decorator.ts @@ -1,6 +1,6 @@ +import { Component } from '@angular/core'; import { hasNoValue } from '../../shared/empty.util'; import { DEFAULT_THEME, resolveTheme } from '../../shared/object-collection/shared/listable-object/listable-object.decorator'; -import { AbstractBrowseByTypeComponent } from '../abstract-browse-by-type.component'; import { Context } from '../../core/shared/context.model'; import { GenericConstructor } from '../../core/shared/generic-constructor'; import { BrowseByDataType } from './browse-by-data-type'; @@ -8,7 +8,7 @@ import { BrowseByDataType } from './browse-by-data-type'; export const DEFAULT_BROWSE_BY_TYPE = BrowseByDataType.Metadata; export const DEFAULT_BROWSE_BY_CONTEXT = Context.Any; -const map: Map>>> = new Map(); +const map: Map>>> = new Map(); /** * Decorator used for rendering Browse-By pages by type @@ -41,12 +41,12 @@ export function rendersBrowseBy(browseByType: BrowseByDataType, context = DEFAUL * @param context The context to match * @param theme the theme to match */ -export function getComponentByBrowseByType(browseByType: BrowseByDataType, context: Context, theme: string): GenericConstructor { - let contextMap: Map>> = map.get(browseByType); +export function getComponentByBrowseByType(browseByType: BrowseByDataType, context: Context, theme: string): GenericConstructor { + let contextMap: Map>> = map.get(browseByType); if (hasNoValue(contextMap)) { contextMap = map.get(DEFAULT_BROWSE_BY_TYPE); } - let themeMap: Map> = contextMap.get(context); + let themeMap: Map> = contextMap.get(context); if (hasNoValue(themeMap)) { themeMap = contextMap.get(DEFAULT_BROWSE_BY_CONTEXT); } diff --git a/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.spec.ts b/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.spec.ts index 418dfd45e1..65e8c87c61 100644 --- a/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.spec.ts +++ b/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.spec.ts @@ -8,7 +8,6 @@ import { ValueListBrowseDefinition } from '../../core/shared/value-list-browse-d import { NonHierarchicalBrowseDefinition } from '../../core/shared/non-hierarchical-browse-definition'; import { getMockThemeService } from '../../shared/mocks/theme-service.mock'; import { DynamicComponentLoaderDirective } from '../../shared/abstract-component-loader/dynamic-component-loader.directive'; -import { AbstractBrowseByTypeComponent } from '../abstract-browse-by-type.component'; import { BrowseByDataType } from './browse-by-data-type'; @rendersBrowseBy('BrowseBySwitcherComponent' as BrowseByDataType) @@ -17,7 +16,7 @@ import { BrowseByDataType } from './browse-by-data-type'; selector: '', template: '', }) -class BrowseByTestComponent extends AbstractBrowseByTypeComponent { +class BrowseByTestComponent { } describe('BrowseBySwitcherComponent', () => { diff --git a/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts b/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts index 99ad720dd3..0c200c3453 100644 --- a/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts +++ b/src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts @@ -2,7 +2,6 @@ import { Component, Input } from '@angular/core'; import { getComponentByBrowseByType } from './browse-by-decorator'; import { GenericConstructor } from '../../core/shared/generic-constructor'; import { AbstractComponentLoaderComponent } from '../../shared/abstract-component-loader/abstract-component-loader.component'; -import { AbstractBrowseByTypeComponent } from '../abstract-browse-by-type.component'; import { BrowseByDataType } from './browse-by-data-type'; import { Context } from '../../core/shared/context.model'; @@ -10,7 +9,7 @@ import { Context } from '../../core/shared/context.model'; selector: 'ds-browse-by-switcher', templateUrl: '../../shared/abstract-component-loader/abstract-component-loader.component.html' }) -export class BrowseBySwitcherComponent extends AbstractComponentLoaderComponent { +export class BrowseBySwitcherComponent extends AbstractComponentLoaderComponent { @Input() context: Context; @@ -26,7 +25,7 @@ export class BrowseBySwitcherComponent extends AbstractComponentLoaderComponent< 'browseByType', ]; - public getComponent(): GenericConstructor { + public getComponent(): GenericConstructor { return getComponentByBrowseByType(this.browseByType, this.context, this.themeService.getThemeName()); } diff --git a/src/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts b/src/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts index f005c66c9e..fb2f28c8c5 100644 --- a/src/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts +++ b/src/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts @@ -1,14 +1,14 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Component, OnInit, OnDestroy, Input } from '@angular/core'; import { VocabularyOptions } from '../../core/submission/vocabularies/models/vocabulary-options.model'; import { VocabularyEntryDetail } from '../../core/submission/vocabularies/models/vocabulary-entry-detail.model'; import { ActivatedRoute } from '@angular/router'; -import { Observable } from 'rxjs'; +import { Observable, Subscription } from 'rxjs'; import { BrowseDefinition } from '../../core/shared/browse-definition.model'; import { rendersBrowseBy } from '../browse-by-switcher/browse-by-decorator'; import { map } from 'rxjs/operators'; import { HierarchicalBrowseDefinition } from '../../core/shared/hierarchical-browse-definition.model'; -import { AbstractBrowseByTypeComponent } from '../abstract-browse-by-type.component'; import { BrowseByDataType } from '../browse-by-switcher/browse-by-data-type'; +import { Context } from '../../core/shared/context.model'; @Component({ selector: 'ds-browse-by-taxonomy-page', @@ -19,7 +19,17 @@ import { BrowseByDataType } from '../browse-by-switcher/browse-by-data-type'; * Component for browsing items by metadata in a hierarchical controlled vocabulary */ @rendersBrowseBy(BrowseByDataType.Hierarchy) -export class BrowseByTaxonomyPageComponent extends AbstractBrowseByTypeComponent implements OnInit, OnDestroy { +export class BrowseByTaxonomyPageComponent implements OnInit, OnDestroy { + + /** + * The optional context + */ + @Input() context: Context; + + /** + * The {@link BrowseByDataType} of this Component + */ + @Input() browseByType: BrowseByDataType; /** * The {@link VocabularyOptions} object @@ -56,10 +66,14 @@ export class BrowseByTaxonomyPageComponent extends AbstractBrowseByTypeComponent */ browseDefinition$: Observable; + /** + * Subscriptions to track + */ + subs: Subscription[] = []; + public constructor( protected route: ActivatedRoute, ) { - super(); } ngOnInit(): void { @@ -108,4 +122,7 @@ export class BrowseByTaxonomyPageComponent extends AbstractBrowseByTypeComponent }; } + ngOnDestroy(): void { + this.subs.forEach((sub: Subscription) => sub.unsubscribe()); + } } From 90a277fbeb33ca29060d8ffe59954600d0eb9466 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 22:29:37 +0000 Subject: [PATCH 29/47] Bump follow-redirects from 1.15.3 to 1.15.5 Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.3 to 1.15.5. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.3...v1.15.5) --- updated-dependencies: - dependency-name: follow-redirects dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 6e04ab27b2..786a832e56 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5859,9 +5859,9 @@ flatted@^3.1.0, flatted@^3.2.7: integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== follow-redirects@^1.0.0, follow-redirects@^1.14.0, follow-redirects@^1.15.0: - version "1.15.3" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" - integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== + version "1.15.5" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" + integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== for-each@^0.3.3: version "0.3.3" From 3e33785311444682eaf1f38ffc0bb8e0b3e3f353 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 7 Feb 2024 11:55:44 +1300 Subject: [PATCH 30/47] Improve cfg handling in item-status.component.ts --- .../item-status/item-status.component.ts | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/app/item-page/edit-item-page/item-status/item-status.component.ts b/src/app/item-page/edit-item-page/item-status/item-status.component.ts index d3a5e033d7..759045b3dc 100644 --- a/src/app/item-page/edit-item-page/item-status/item-status.component.ts +++ b/src/app/item-page/edit-item-page/item-status/item-status.component.ts @@ -3,7 +3,7 @@ import { fadeIn, fadeInOut } from '../../../shared/animations/fade'; import { Item } from '../../../core/shared/item.model'; import { ActivatedRoute } from '@angular/router'; import { ItemOperation } from '../item-operation/itemOperation.model'; -import { concatMap, distinctUntilChanged, first, map, mergeMap, switchMap, toArray } from 'rxjs/operators'; +import {concatMap, distinctUntilChanged, first, map, mergeMap, switchMap, tap, toArray} from 'rxjs/operators'; import { BehaviorSubject, combineLatest, Observable, of, Subscription } from 'rxjs'; import { RemoteData } from '../../../core/data/remote-data'; import { getItemEditRoute, getItemPageRoute } from '../../item-page-routing-paths'; @@ -17,6 +17,7 @@ import { ConfigurationProperty } from '../../../core/shared/configuration-proper import { ConfigurationDataService } from '../../../core/data/configuration-data.service'; import { IdentifierData } from '../../../shared/object-list/identifier-data/identifier-data.model'; import { OrcidAuthService } from '../../../core/orcid/orcid-auth.service'; +import {getDSORoute} from "../../../app-routing-paths"; @Component({ selector: 'ds-item-status', @@ -107,7 +108,19 @@ export class ItemStatusComponent implements OnInit { // Observable for configuration determining whether the Register DOI feature is enabled let registerConfigEnabled$: Observable = this.configurationService.findByPropertyName('identifiers.item-status.register-doi').pipe( getFirstCompletedRemoteData(), - map((enabledRD: RemoteData) => enabledRD.hasSucceeded && enabledRD.payload.values.length > 0) + map((response: RemoteData) => { + // Return true if a successful response with a 'true' value was retrieved, otherwise return false + if (response.hasSucceeded) { + const payload = response.payload; + if (payload.values.length > 0 && hasValue(payload.values[0])) { + return payload.values[0] === 'true'; + } else { + return false; + } + } else { + return false; + } + }) ); /** From 68475d1ed61bd231de1a1efdb2feb3bf638108c2 Mon Sep 17 00:00:00 2001 From: Kim Shepherd Date: Wed, 7 Feb 2024 12:09:16 +1300 Subject: [PATCH 31/47] Improve cfg handling in item-status.component.ts --- .../edit-item-page/item-status/item-status.component.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/item-page/edit-item-page/item-status/item-status.component.ts b/src/app/item-page/edit-item-page/item-status/item-status.component.ts index 759045b3dc..8b4dc88d9d 100644 --- a/src/app/item-page/edit-item-page/item-status/item-status.component.ts +++ b/src/app/item-page/edit-item-page/item-status/item-status.component.ts @@ -3,7 +3,7 @@ import { fadeIn, fadeInOut } from '../../../shared/animations/fade'; import { Item } from '../../../core/shared/item.model'; import { ActivatedRoute } from '@angular/router'; import { ItemOperation } from '../item-operation/itemOperation.model'; -import {concatMap, distinctUntilChanged, first, map, mergeMap, switchMap, tap, toArray} from 'rxjs/operators'; +import {concatMap, distinctUntilChanged, first, map, mergeMap, switchMap, toArray} from 'rxjs/operators'; import { BehaviorSubject, combineLatest, Observable, of, Subscription } from 'rxjs'; import { RemoteData } from '../../../core/data/remote-data'; import { getItemEditRoute, getItemPageRoute } from '../../item-page-routing-paths'; @@ -17,7 +17,6 @@ import { ConfigurationProperty } from '../../../core/shared/configuration-proper import { ConfigurationDataService } from '../../../core/data/configuration-data.service'; import { IdentifierData } from '../../../shared/object-list/identifier-data/identifier-data.model'; import { OrcidAuthService } from '../../../core/orcid/orcid-auth.service'; -import {getDSORoute} from "../../../app-routing-paths"; @Component({ selector: 'ds-item-status', From 048361281982dcbe1617275b6e987a8fc083ace1 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Sun, 19 Nov 2023 03:22:37 +0100 Subject: [PATCH 32/47] 108588: Moved CommunityPageSubCommunityListComponent & CommunityPageSubCollectionListComponent to a custom sections folder --- .../community-page/community-page.module.ts | 8 ++-- ...ty-page-sub-collection-list.component.html | 0 ...ty-page-sub-collection-list.component.scss | 0 ...page-sub-collection-list.component.spec.ts | 42 +++++++++---------- ...nity-page-sub-collection-list.component.ts | 22 +++++----- ...nity-page-sub-collection-list.component.ts | 8 ++-- ...ity-page-sub-community-list.component.html | 0 ...ity-page-sub-community-list.component.scss | 0 ...-page-sub-community-list.component.spec.ts | 42 +++++++++---------- ...unity-page-sub-community-list.component.ts | 18 ++++---- ...unity-page-sub-community-list.component.ts | 8 ++-- ...ty-page-sub-collection-list.component.html | 0 ...ty-page-sub-collection-list.component.scss | 0 ...nity-page-sub-collection-list.component.ts | 12 ++++++ ...ity-page-sub-community-list.component.html | 0 ...ity-page-sub-community-list.component.scss | 0 ...unity-page-sub-community-list.component.ts | 12 ++++++ ...nity-page-sub-collection-list.component.ts | 12 ------ ...unity-page-sub-community-list.component.ts | 12 ------ src/themes/custom/lazy-theme.module.ts | 7 +--- 20 files changed, 99 insertions(+), 104 deletions(-) rename src/app/community-page/{ => sections/sub-com-col-section}/sub-collection-list/community-page-sub-collection-list.component.html (100%) rename src/app/community-page/{ => sections/sub-com-col-section}/sub-collection-list/community-page-sub-collection-list.component.scss (100%) rename src/app/community-page/{ => sections/sub-com-col-section}/sub-collection-list/community-page-sub-collection-list.component.spec.ts (76%) rename src/app/community-page/{ => sections/sub-com-col-section}/sub-collection-list/community-page-sub-collection-list.component.ts (78%) rename src/app/community-page/{ => sections/sub-com-col-section}/sub-collection-list/themed-community-page-sub-collection-list.component.ts (68%) rename src/app/community-page/{ => sections/sub-com-col-section}/sub-community-list/community-page-sub-community-list.component.html (100%) rename src/app/community-page/{ => sections/sub-com-col-section}/sub-community-list/community-page-sub-community-list.component.scss (100%) rename src/app/community-page/{ => sections/sub-com-col-section}/sub-community-list/community-page-sub-community-list.component.spec.ts (76%) rename src/app/community-page/{ => sections/sub-com-col-section}/sub-community-list/community-page-sub-community-list.component.ts (80%) rename src/app/community-page/{ => sections/sub-com-col-section}/sub-community-list/themed-community-page-sub-community-list.component.ts (68%) rename src/themes/custom/app/community-page/{ => sections/sub-com-col-section}/sub-collection-list/community-page-sub-collection-list.component.html (100%) rename src/themes/custom/app/community-page/{ => sections/sub-com-col-section}/sub-collection-list/community-page-sub-collection-list.component.scss (100%) create mode 100644 src/themes/custom/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.ts rename src/themes/custom/app/community-page/{ => sections/sub-com-col-section}/sub-community-list/community-page-sub-community-list.component.html (100%) rename src/themes/custom/app/community-page/{ => sections/sub-com-col-section}/sub-community-list/community-page-sub-community-list.component.scss (100%) create mode 100644 src/themes/custom/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.ts delete mode 100644 src/themes/custom/app/community-page/sub-collection-list/community-page-sub-collection-list.component.ts delete mode 100644 src/themes/custom/app/community-page/sub-community-list/community-page-sub-community-list.component.ts diff --git a/src/app/community-page/community-page.module.ts b/src/app/community-page/community-page.module.ts index 45ffb2a786..499e9092e3 100644 --- a/src/app/community-page/community-page.module.ts +++ b/src/app/community-page/community-page.module.ts @@ -4,9 +4,9 @@ import { CommonModule } from '@angular/common'; import { SharedModule } from '../shared/shared.module'; import { CommunityPageComponent } from './community-page.component'; -import { CommunityPageSubCollectionListComponent } from './sub-collection-list/community-page-sub-collection-list.component'; +import { CommunityPageSubCollectionListComponent } from './sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component'; import { CommunityPageRoutingModule } from './community-page-routing.module'; -import { CommunityPageSubCommunityListComponent } from './sub-community-list/community-page-sub-community-list.component'; +import { CommunityPageSubCommunityListComponent } from './sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component'; import { CreateCommunityPageComponent } from './create-community-page/create-community-page.component'; import { DeleteCommunityPageComponent } from './delete-community-page/delete-community-page.component'; import { StatisticsModule } from '../statistics/statistics.module'; @@ -15,10 +15,10 @@ import { ThemedCommunityPageComponent } from './themed-community-page.component' import { ComcolModule } from '../shared/comcol/comcol.module'; import { ThemedCommunityPageSubCommunityListComponent -} from './sub-community-list/themed-community-page-sub-community-list.component'; +} from './sections/sub-com-col-section/sub-community-list/themed-community-page-sub-community-list.component'; import { ThemedCollectionPageSubCollectionListComponent -} from './sub-collection-list/themed-community-page-sub-collection-list.component'; +} from './sections/sub-com-col-section/sub-collection-list/themed-community-page-sub-collection-list.component'; import { DsoPageModule } from '../shared/dso-page/dso-page.module'; const DECLARATIONS = [CommunityPageComponent, diff --git a/src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.html b/src/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.html similarity index 100% rename from src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.html rename to src/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.html diff --git a/src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.scss b/src/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.scss similarity index 100% rename from src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.scss rename to src/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.scss diff --git a/src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.spec.ts b/src/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.spec.ts similarity index 76% rename from src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.spec.ts rename to src/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.spec.ts index bca3c42a95..8c4af30991 100644 --- a/src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.spec.ts +++ b/src/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.spec.ts @@ -8,27 +8,27 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { CommunityPageSubCollectionListComponent } from './community-page-sub-collection-list.component'; -import { Community } from '../../core/shared/community.model'; -import { SharedModule } from '../../shared/shared.module'; -import { CollectionDataService } from '../../core/data/collection-data.service'; -import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; -import { buildPaginatedList } from '../../core/data/paginated-list.model'; -import { PageInfo } from '../../core/shared/page-info.model'; -import { HostWindowService } from '../../shared/host-window.service'; -import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub'; -import { SelectableListService } from '../../shared/object-list/selectable-list/selectable-list.service'; -import { PaginationService } from '../../core/pagination/pagination.service'; -import { getMockThemeService } from '../../shared/mocks/theme-service.mock'; -import { ThemeService } from '../../shared/theme-support/theme.service'; -import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub'; -import { FindListOptions } from '../../core/data/find-list-options.model'; -import { GroupDataService } from '../../core/eperson/group-data.service'; -import { LinkHeadService } from '../../core/services/link-head.service'; -import { ConfigurationDataService } from '../../core/data/configuration-data.service'; -import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service'; -import { ConfigurationProperty } from '../../core/shared/configuration-property.model'; -import { createPaginatedList } from '../../shared/testing/utils.test'; -import { SearchConfigurationServiceStub } from '../../shared/testing/search-configuration-service.stub'; +import { Community } from '../../../../core/shared/community.model'; +import { SharedModule } from '../../../../shared/shared.module'; +import { CollectionDataService } from '../../../../core/data/collection-data.service'; +import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils'; +import { buildPaginatedList } from '../../../../core/data/paginated-list.model'; +import { PageInfo } from '../../../../core/shared/page-info.model'; +import { HostWindowService } from '../../../../shared/host-window.service'; +import { HostWindowServiceStub } from '../../../../shared/testing/host-window-service.stub'; +import { SelectableListService } from '../../../../shared/object-list/selectable-list/selectable-list.service'; +import { PaginationService } from '../../../../core/pagination/pagination.service'; +import { getMockThemeService } from '../../../../shared/mocks/theme-service.mock'; +import { ThemeService } from '../../../../shared/theme-support/theme.service'; +import { PaginationServiceStub } from '../../../../shared/testing/pagination-service.stub'; +import { FindListOptions } from '../../../../core/data/find-list-options.model'; +import { GroupDataService } from '../../../../core/eperson/group-data.service'; +import { LinkHeadService } from '../../../../core/services/link-head.service'; +import { ConfigurationDataService } from '../../../../core/data/configuration-data.service'; +import { SearchConfigurationService } from '../../../../core/shared/search/search-configuration.service'; +import { ConfigurationProperty } from '../../../../core/shared/configuration-property.model'; +import { createPaginatedList } from '../../../../shared/testing/utils.test'; +import { SearchConfigurationServiceStub } from '../../../../shared/testing/search-configuration-service.stub'; describe('CommunityPageSubCollectionList Component', () => { let comp: CommunityPageSubCollectionListComponent; diff --git a/src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.ts b/src/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.ts similarity index 78% rename from src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.ts rename to src/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.ts index 3a77149e5b..bd506882ee 100644 --- a/src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.ts +++ b/src/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.ts @@ -1,19 +1,17 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; - import { BehaviorSubject, combineLatest as observableCombineLatest } from 'rxjs'; - -import { RemoteData } from '../../core/data/remote-data'; -import { Collection } from '../../core/shared/collection.model'; -import { Community } from '../../core/shared/community.model'; -import { fadeIn } from '../../shared/animations/fade'; -import { PaginatedList } from '../../core/data/paginated-list.model'; -import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; -import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; -import { CollectionDataService } from '../../core/data/collection-data.service'; -import { PaginationService } from '../../core/pagination/pagination.service'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { Collection } from '../../../../core/shared/collection.model'; +import { Community } from '../../../../core/shared/community.model'; +import { fadeIn } from '../../../../shared/animations/fade'; +import { PaginatedList } from '../../../../core/data/paginated-list.model'; +import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model'; +import { SortDirection, SortOptions } from '../../../../core/cache/models/sort-options.model'; +import { CollectionDataService } from '../../../../core/data/collection-data.service'; +import { PaginationService } from '../../../../core/pagination/pagination.service'; import { switchMap } from 'rxjs/operators'; -import { hasValue } from '../../shared/empty.util'; +import { hasValue } from '../../../../shared/empty.util'; @Component({ selector: 'ds-community-page-sub-collection-list', diff --git a/src/app/community-page/sub-collection-list/themed-community-page-sub-collection-list.component.ts b/src/app/community-page/sections/sub-com-col-section/sub-collection-list/themed-community-page-sub-collection-list.component.ts similarity index 68% rename from src/app/community-page/sub-collection-list/themed-community-page-sub-collection-list.component.ts rename to src/app/community-page/sections/sub-com-col-section/sub-collection-list/themed-community-page-sub-collection-list.component.ts index f1f49f204c..ebbec33e8e 100644 --- a/src/app/community-page/sub-collection-list/themed-community-page-sub-collection-list.component.ts +++ b/src/app/community-page/sections/sub-com-col-section/sub-collection-list/themed-community-page-sub-collection-list.component.ts @@ -1,12 +1,12 @@ -import { ThemedComponent } from '../../shared/theme-support/themed.component'; +import { ThemedComponent } from '../../../../shared/theme-support/themed.component'; import { CommunityPageSubCollectionListComponent } from './community-page-sub-collection-list.component'; import { Component, Input } from '@angular/core'; -import { Community } from '../../core/shared/community.model'; +import { Community } from '../../../../core/shared/community.model'; @Component({ selector: 'ds-themed-community-page-sub-collection-list', styleUrls: [], - templateUrl: '../../shared/theme-support/themed.component.html', + templateUrl: '../../../../shared/theme-support/themed.component.html', }) export class ThemedCollectionPageSubCollectionListComponent extends ThemedComponent { @Input() community: Community; @@ -18,7 +18,7 @@ export class ThemedCollectionPageSubCollectionListComponent extends ThemedCompon } protected importThemedComponent(themeName: string): Promise { - return import(`../../../themes/${themeName}/app/community-page/sub-collection-list/community-page-sub-collection-list.component`); + return import(`../../../../../themes/${themeName}/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component`); } protected importUnthemedComponent(): Promise { diff --git a/src/app/community-page/sub-community-list/community-page-sub-community-list.component.html b/src/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.html similarity index 100% rename from src/app/community-page/sub-community-list/community-page-sub-community-list.component.html rename to src/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.html diff --git a/src/app/community-page/sub-community-list/community-page-sub-community-list.component.scss b/src/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.scss similarity index 100% rename from src/app/community-page/sub-community-list/community-page-sub-community-list.component.scss rename to src/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.scss diff --git a/src/app/community-page/sub-community-list/community-page-sub-community-list.component.spec.ts b/src/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.spec.ts similarity index 76% rename from src/app/community-page/sub-community-list/community-page-sub-community-list.component.spec.ts rename to src/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.spec.ts index 0a14fe6dd1..c5efc9c2c1 100644 --- a/src/app/community-page/sub-community-list/community-page-sub-community-list.component.spec.ts +++ b/src/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.spec.ts @@ -8,27 +8,27 @@ import { By } from '@angular/platform-browser'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { CommunityPageSubCommunityListComponent } from './community-page-sub-community-list.component'; -import { Community } from '../../core/shared/community.model'; -import { buildPaginatedList } from '../../core/data/paginated-list.model'; -import { PageInfo } from '../../core/shared/page-info.model'; -import { SharedModule } from '../../shared/shared.module'; -import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; -import { HostWindowService } from '../../shared/host-window.service'; -import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub'; -import { CommunityDataService } from '../../core/data/community-data.service'; -import { SelectableListService } from '../../shared/object-list/selectable-list/selectable-list.service'; -import { PaginationService } from '../../core/pagination/pagination.service'; -import { getMockThemeService } from '../../shared/mocks/theme-service.mock'; -import { ThemeService } from '../../shared/theme-support/theme.service'; -import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub'; -import { FindListOptions } from '../../core/data/find-list-options.model'; -import { GroupDataService } from '../../core/eperson/group-data.service'; -import { LinkHeadService } from '../../core/services/link-head.service'; -import { ConfigurationDataService } from '../../core/data/configuration-data.service'; -import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service'; -import { SearchConfigurationServiceStub } from '../../shared/testing/search-configuration-service.stub'; -import { ConfigurationProperty } from '../../core/shared/configuration-property.model'; -import { createPaginatedList } from '../../shared/testing/utils.test'; +import { Community } from '../../../../core/shared/community.model'; +import { buildPaginatedList } from '../../../../core/data/paginated-list.model'; +import { PageInfo } from '../../../../core/shared/page-info.model'; +import { SharedModule } from '../../../../shared/shared.module'; +import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils'; +import { HostWindowService } from '../../../../shared/host-window.service'; +import { HostWindowServiceStub } from '../../../../shared/testing/host-window-service.stub'; +import { CommunityDataService } from '../../../../core/data/community-data.service'; +import { SelectableListService } from '../../../../shared/object-list/selectable-list/selectable-list.service'; +import { PaginationService } from '../../../../core/pagination/pagination.service'; +import { getMockThemeService } from '../../../../shared/mocks/theme-service.mock'; +import { ThemeService } from '../../../../shared/theme-support/theme.service'; +import { PaginationServiceStub } from '../../../../shared/testing/pagination-service.stub'; +import { FindListOptions } from '../../../../core/data/find-list-options.model'; +import { GroupDataService } from '../../../../core/eperson/group-data.service'; +import { LinkHeadService } from '../../../../core/services/link-head.service'; +import { ConfigurationDataService } from '../../../../core/data/configuration-data.service'; +import { SearchConfigurationService } from '../../../../core/shared/search/search-configuration.service'; +import { SearchConfigurationServiceStub } from '../../../../shared/testing/search-configuration-service.stub'; +import { ConfigurationProperty } from '../../../../core/shared/configuration-property.model'; +import { createPaginatedList } from '../../../../shared/testing/utils.test'; describe('CommunityPageSubCommunityListComponent Component', () => { let comp: CommunityPageSubCommunityListComponent; diff --git a/src/app/community-page/sub-community-list/community-page-sub-community-list.component.ts b/src/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.ts similarity index 80% rename from src/app/community-page/sub-community-list/community-page-sub-community-list.component.ts rename to src/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.ts index 5a0409a051..61633e5128 100644 --- a/src/app/community-page/sub-community-list/community-page-sub-community-list.component.ts +++ b/src/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.ts @@ -3,16 +3,16 @@ import { ActivatedRoute } from '@angular/router'; import { BehaviorSubject, combineLatest as observableCombineLatest } from 'rxjs'; -import { RemoteData } from '../../core/data/remote-data'; -import { Community } from '../../core/shared/community.model'; -import { fadeIn } from '../../shared/animations/fade'; -import { PaginatedList } from '../../core/data/paginated-list.model'; -import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; -import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; -import { CommunityDataService } from '../../core/data/community-data.service'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { Community } from '../../../../core/shared/community.model'; +import { fadeIn } from '../../../../shared/animations/fade'; +import { PaginatedList } from '../../../../core/data/paginated-list.model'; +import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model'; +import { SortDirection, SortOptions } from '../../../../core/cache/models/sort-options.model'; +import { CommunityDataService } from '../../../../core/data/community-data.service'; import { switchMap } from 'rxjs/operators'; -import { PaginationService } from '../../core/pagination/pagination.service'; -import { hasValue } from '../../shared/empty.util'; +import { PaginationService } from '../../../../core/pagination/pagination.service'; +import { hasValue } from '../../../../shared/empty.util'; @Component({ selector: 'ds-community-page-sub-community-list', diff --git a/src/app/community-page/sub-community-list/themed-community-page-sub-community-list.component.ts b/src/app/community-page/sections/sub-com-col-section/sub-community-list/themed-community-page-sub-community-list.component.ts similarity index 68% rename from src/app/community-page/sub-community-list/themed-community-page-sub-community-list.component.ts rename to src/app/community-page/sections/sub-com-col-section/sub-community-list/themed-community-page-sub-community-list.component.ts index 852c53186e..9c500cac10 100644 --- a/src/app/community-page/sub-community-list/themed-community-page-sub-community-list.component.ts +++ b/src/app/community-page/sections/sub-com-col-section/sub-community-list/themed-community-page-sub-community-list.component.ts @@ -1,12 +1,12 @@ -import { ThemedComponent } from '../../shared/theme-support/themed.component'; +import { ThemedComponent } from '../../../../shared/theme-support/themed.component'; import { CommunityPageSubCommunityListComponent } from './community-page-sub-community-list.component'; import { Component, Input } from '@angular/core'; -import { Community } from '../../core/shared/community.model'; +import { Community } from '../../../../core/shared/community.model'; @Component({ selector: 'ds-themed-community-page-sub-community-list', styleUrls: [], - templateUrl: '../../shared/theme-support/themed.component.html', + templateUrl: '../../../../shared/theme-support/themed.component.html', }) export class ThemedCommunityPageSubCommunityListComponent extends ThemedComponent { @@ -19,7 +19,7 @@ export class ThemedCommunityPageSubCommunityListComponent extends ThemedComponen } protected importThemedComponent(themeName: string): Promise { - return import(`../../../themes/${themeName}/app/community-page/sub-community-list/community-page-sub-community-list.component`); + return import(`../../../../../themes/${themeName}/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component`); } protected importUnthemedComponent(): Promise { diff --git a/src/themes/custom/app/community-page/sub-collection-list/community-page-sub-collection-list.component.html b/src/themes/custom/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.html similarity index 100% rename from src/themes/custom/app/community-page/sub-collection-list/community-page-sub-collection-list.component.html rename to src/themes/custom/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.html diff --git a/src/themes/custom/app/community-page/sub-collection-list/community-page-sub-collection-list.component.scss b/src/themes/custom/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.scss similarity index 100% rename from src/themes/custom/app/community-page/sub-collection-list/community-page-sub-collection-list.component.scss rename to src/themes/custom/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.scss diff --git a/src/themes/custom/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.ts b/src/themes/custom/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.ts new file mode 100644 index 0000000000..a3f51bf916 --- /dev/null +++ b/src/themes/custom/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; +import { CommunityPageSubCollectionListComponent as BaseComponent } from '../../../../../../../app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component'; + +@Component({ + selector: 'ds-community-page-sub-collection-list', + // styleUrls: ['./community-page-sub-collection-list.component.scss'], + styleUrls: ['../../../../../app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.scss'], + // templateUrl: './community-page-sub-collection-list.component.html', + templateUrl: '../../../../../app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.html', +}) +export class CommunityPageSubCollectionListComponent extends BaseComponent { +} diff --git a/src/themes/custom/app/community-page/sub-community-list/community-page-sub-community-list.component.html b/src/themes/custom/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.html similarity index 100% rename from src/themes/custom/app/community-page/sub-community-list/community-page-sub-community-list.component.html rename to src/themes/custom/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.html diff --git a/src/themes/custom/app/community-page/sub-community-list/community-page-sub-community-list.component.scss b/src/themes/custom/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.scss similarity index 100% rename from src/themes/custom/app/community-page/sub-community-list/community-page-sub-community-list.component.scss rename to src/themes/custom/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.scss diff --git a/src/themes/custom/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.ts b/src/themes/custom/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.ts new file mode 100644 index 0000000000..68af7dddd0 --- /dev/null +++ b/src/themes/custom/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; +import { CommunityPageSubCommunityListComponent as BaseComponent } from '../../../../../../../app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component'; + +@Component({ + selector: 'ds-community-page-sub-community-list', + // styleUrls: ['./community-page-sub-community-list.component.scss'], + styleUrls: ['../../../../../../../app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.scss'], + // templateUrl: './community-page-sub-community-list.component.html', + templateUrl: '../../../../../../../app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.html', +}) +export class CommunityPageSubCommunityListComponent extends BaseComponent { +} diff --git a/src/themes/custom/app/community-page/sub-collection-list/community-page-sub-collection-list.component.ts b/src/themes/custom/app/community-page/sub-collection-list/community-page-sub-collection-list.component.ts deleted file mode 100644 index a228fe1071..0000000000 --- a/src/themes/custom/app/community-page/sub-collection-list/community-page-sub-collection-list.component.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Component } from '@angular/core'; -import { CommunityPageSubCollectionListComponent as BaseComponent } - from '../../../../../app/community-page/sub-collection-list/community-page-sub-collection-list.component'; - -@Component({ - selector: 'ds-community-page-sub-collection-list', - // styleUrls: ['./community-page-sub-collection-list.component.scss'], - styleUrls: ['../../../../../app/community-page/sub-collection-list/community-page-sub-collection-list.component.scss'], - // templateUrl: './community-page-sub-collection-list.component.html', - templateUrl: '../../../../../app/community-page/sub-collection-list/community-page-sub-collection-list.component.html' -}) -export class CommunityPageSubCollectionListComponent extends BaseComponent {} diff --git a/src/themes/custom/app/community-page/sub-community-list/community-page-sub-community-list.component.ts b/src/themes/custom/app/community-page/sub-community-list/community-page-sub-community-list.component.ts deleted file mode 100644 index 1db265a844..0000000000 --- a/src/themes/custom/app/community-page/sub-community-list/community-page-sub-community-list.component.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Component } from '@angular/core'; -import { CommunityPageSubCommunityListComponent as BaseComponent } - from '../../../../../app/community-page/sub-community-list/community-page-sub-community-list.component'; - -@Component({ - selector: 'ds-community-page-sub-community-list', - // styleUrls: ['./community-page-sub-community-list.component.scss'], - styleUrls: ['../../../../../app/community-page/sub-community-list/community-page-sub-community-list.component.scss'], - // templateUrl: './community-page-sub-community-list.component.html', - templateUrl: '../../../../../app/community-page/sub-community-list/community-page-sub-community-list.component.html' -}) -export class CommunityPageSubCommunityListComponent extends BaseComponent {} diff --git a/src/themes/custom/lazy-theme.module.ts b/src/themes/custom/lazy-theme.module.ts index e102089361..3bef57cb18 100644 --- a/src/themes/custom/lazy-theme.module.ts +++ b/src/themes/custom/lazy-theme.module.ts @@ -93,8 +93,8 @@ import { SearchResultsComponent } from './app/shared/search/search-results/searc import { AdminSidebarComponent } from './app/admin/admin-sidebar/admin-sidebar.component'; import { ComcolPageBrowseByComponent } from './app/shared/comcol-page-browse-by/comcol-page-browse-by.component'; import { SearchSettingsComponent } from './app/shared/search/search-settings/search-settings.component'; -import { CommunityPageSubCommunityListComponent } from './app/community-page/sub-community-list/community-page-sub-community-list.component'; -import { CommunityPageSubCollectionListComponent } from './app/community-page/sub-collection-list/community-page-sub-collection-list.component'; +import { CommunityPageSubCommunityListComponent } from './app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component'; +import { CommunityPageSubCollectionListComponent } from './app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component'; import { ObjectListComponent } from './app/shared/object-list/object-list.component'; import { BrowseByMetadataPageComponent } from './app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component'; @@ -299,9 +299,6 @@ const DECLARATIONS = [ RequestCopyModule, ], declarations: DECLARATIONS, - exports: [ - CommunityPageSubCollectionListComponent - ] }) /** From bb0b66f927a368f8bb35329eab242ad57f6f33b7 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Sun, 19 Nov 2023 03:39:25 +0100 Subject: [PATCH 33/47] 108588: Moved sub communities & collections from community page to new component --- .../community-page-routing.module.ts | 9 +++++- .../community-page/community-page.module.ts | 5 ++- .../sub-com-col-section.component.html | 8 +++++ .../sub-com-col-section.component.scss | 0 .../sub-com-col-section.component.spec.ts | 32 +++++++++++++++++++ .../sub-com-col-section.component.ts | 28 ++++++++++++++++ 6 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 src/app/community-page/sections/sub-com-col-section/sub-com-col-section.component.html create mode 100644 src/app/community-page/sections/sub-com-col-section/sub-com-col-section.component.scss create mode 100644 src/app/community-page/sections/sub-com-col-section/sub-com-col-section.component.spec.ts create mode 100644 src/app/community-page/sections/sub-com-col-section/sub-com-col-section.component.ts diff --git a/src/app/community-page/community-page-routing.module.ts b/src/app/community-page/community-page-routing.module.ts index c37f8832f8..a2d727218e 100644 --- a/src/app/community-page/community-page-routing.module.ts +++ b/src/app/community-page/community-page-routing.module.ts @@ -15,6 +15,7 @@ import { LinkMenuItemModel } from '../shared/menu/menu-item/models/link.model'; import { ThemedCommunityPageComponent } from './themed-community-page.component'; import { MenuItemType } from '../shared/menu/menu-item-type.model'; import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; +import { SubComColSectionComponent } from './sections/sub-com-col-section/sub-com-col-section.component'; @NgModule({ imports: [ @@ -46,9 +47,15 @@ import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; canActivate: [AuthenticatedGuard], }, { - path: '', + path: '**', component: ThemedCommunityPageComponent, pathMatch: 'full', + children: [ + { + path: '', + component: SubComColSectionComponent, + }, + ], } ], data: { diff --git a/src/app/community-page/community-page.module.ts b/src/app/community-page/community-page.module.ts index 499e9092e3..61c17de8fa 100644 --- a/src/app/community-page/community-page.module.ts +++ b/src/app/community-page/community-page.module.ts @@ -20,6 +20,7 @@ import { ThemedCollectionPageSubCollectionListComponent } from './sections/sub-com-col-section/sub-collection-list/themed-community-page-sub-collection-list.component'; import { DsoPageModule } from '../shared/dso-page/dso-page.module'; +import { SubComColSectionComponent } from './sections/sub-com-col-section/sub-com-col-section.component'; const DECLARATIONS = [CommunityPageComponent, ThemedCommunityPageComponent, @@ -28,7 +29,9 @@ const DECLARATIONS = [CommunityPageComponent, ThemedCollectionPageSubCollectionListComponent, CommunityPageSubCommunityListComponent, CreateCommunityPageComponent, - DeleteCommunityPageComponent]; + DeleteCommunityPageComponent, + SubComColSectionComponent, +]; @NgModule({ imports: [ diff --git a/src/app/community-page/sections/sub-com-col-section/sub-com-col-section.component.html b/src/app/community-page/sections/sub-com-col-section/sub-com-col-section.component.html new file mode 100644 index 0000000000..515e08ffdf --- /dev/null +++ b/src/app/community-page/sections/sub-com-col-section/sub-com-col-section.component.html @@ -0,0 +1,8 @@ + + + + + + diff --git a/src/app/community-page/sections/sub-com-col-section/sub-com-col-section.component.scss b/src/app/community-page/sections/sub-com-col-section/sub-com-col-section.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/community-page/sections/sub-com-col-section/sub-com-col-section.component.spec.ts b/src/app/community-page/sections/sub-com-col-section/sub-com-col-section.component.spec.ts new file mode 100644 index 0000000000..804299d3d9 --- /dev/null +++ b/src/app/community-page/sections/sub-com-col-section/sub-com-col-section.component.spec.ts @@ -0,0 +1,32 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { SubComColSectionComponent } from './sub-com-col-section.component'; +import { ActivatedRoute } from '@angular/router'; +import { ActivatedRouteStub } from '../../../shared/testing/active-router.stub'; + +describe('SubComColSectionComponent', () => { + let component: SubComColSectionComponent; + let fixture: ComponentFixture; + + let activatedRoute: ActivatedRouteStub; + + beforeEach(async () => { + activatedRoute = new ActivatedRouteStub(); + + await TestBed.configureTestingModule({ + declarations: [ + SubComColSectionComponent, + ], + providers: [ + { provide: ActivatedRoute, useValue: activatedRoute }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(SubComColSectionComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/community-page/sections/sub-com-col-section/sub-com-col-section.component.ts b/src/app/community-page/sections/sub-com-col-section/sub-com-col-section.component.ts new file mode 100644 index 0000000000..ff30e51607 --- /dev/null +++ b/src/app/community-page/sections/sub-com-col-section/sub-com-col-section.component.ts @@ -0,0 +1,28 @@ +import { Component, OnInit } from '@angular/core'; +import { Observable } from 'rxjs'; +import { RemoteData } from '../../../core/data/remote-data'; +import { Community } from '../../../core/shared/community.model'; +import { ActivatedRoute, Data } from '@angular/router'; +import { map } from 'rxjs/operators'; + +@Component({ + selector: 'ds-sub-com-col-section', + templateUrl: './sub-com-col-section.component.html', + styleUrls: ['./sub-com-col-section.component.scss'], +}) +export class SubComColSectionComponent implements OnInit { + + community$: Observable; + + constructor( + private route: ActivatedRoute, + ) { + } + + ngOnInit(): void { + this.community$ = this.route.data.pipe( + map((data: Data) => (data.dso as RemoteData).payload), + ); + } + +} From 2c543ad570e7e20d287060e41d9f1e8ac721fea7 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Sun, 19 Nov 2023 18:29:27 +0100 Subject: [PATCH 34/47] 108588: Updated CommunityPageRoutingModule to use custom sections --- .../browse-by-metadata-page.component.html | 83 ++++------------ .../community-page-routing.module.ts | 17 +++- .../community-page.component.html | 7 +- .../community-page.component.ts | 8 -- .../community-page/community-page.module.ts | 5 +- .../community-browse-section.component.html | 0 .../comcol-page-browse-by.component.html | 14 ++- .../comcol-page-browse-by.component.ts | 99 ++++++++++++------- 8 files changed, 113 insertions(+), 120 deletions(-) create mode 100644 src/app/community-page/sections/community-browse-section/community-browse-section.component.html diff --git a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html index 8d062d739f..bac74736cf 100644 --- a/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html +++ b/src/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.html @@ -1,67 +1,24 @@
- - -
- -
- - - - - - - - - - - - - - - -
- -
- - -
- -
+
-
- - -
-
- - - - + + +
-
-
-
+
diff --git a/src/app/community-page/community-page-routing.module.ts b/src/app/community-page/community-page-routing.module.ts index a2d727218e..f84fa1c9f7 100644 --- a/src/app/community-page/community-page-routing.module.ts +++ b/src/app/community-page/community-page-routing.module.ts @@ -16,6 +16,9 @@ import { ThemedCommunityPageComponent } from './themed-community-page.component' import { MenuItemType } from '../shared/menu/menu-item-type.model'; import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; import { SubComColSectionComponent } from './sections/sub-com-col-section/sub-com-col-section.component'; +import { BrowseByI18nBreadcrumbResolver } from '../browse-by/browse-by-i18n-breadcrumb.resolver'; +import { BrowseByGuard } from '../browse-by/browse-by-guard'; +import { BrowseBySwitcherComponent } from '../browse-by/browse-by-switcher/browse-by-switcher.component'; @NgModule({ imports: [ @@ -47,14 +50,24 @@ import { SubComColSectionComponent } from './sections/sub-com-col-section/sub-co canActivate: [AuthenticatedGuard], }, { - path: '**', + path: '', component: ThemedCommunityPageComponent, - pathMatch: 'full', children: [ { path: '', + pathMatch: 'full', component: SubComColSectionComponent, }, + { + path: 'browse/:id', + pathMatch: 'full', + component: BrowseBySwitcherComponent, + canActivate: [BrowseByGuard], + resolve: { + breadcrumb: BrowseByI18nBreadcrumbResolver, + }, + data: { breadcrumbKey: 'browse.metadata' }, + }, ], } ], diff --git a/src/app/community-page/community-page.component.html b/src/app/community-page/community-page.component.html index 6d5262d933..fdefccdd51 100644 --- a/src/app/community-page/community-page.component.html +++ b/src/app/community-page/community-page.component.html @@ -17,7 +17,7 @@ + [title]="'community.page.news'"> @@ -31,10 +31,9 @@ - - + -
+
diff --git a/src/app/community-page/community-page.component.ts b/src/app/community-page/community-page.component.ts index a5bbff3cee..206aa54cb0 100644 --- a/src/app/community-page/community-page.component.ts +++ b/src/app/community-page/community-page.component.ts @@ -1,16 +1,10 @@ import { mergeMap, filter, map } from 'rxjs/operators'; import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; - import { Observable } from 'rxjs'; -import { CommunityDataService } from '../core/data/community-data.service'; import { RemoteData } from '../core/data/remote-data'; import { Bitstream } from '../core/shared/bitstream.model'; - import { Community } from '../core/shared/community.model'; - -import { MetadataService } from '../core/metadata/metadata.service'; - import { fadeInOut } from '../shared/animations/fade'; import { hasValue } from '../shared/empty.util'; import { getAllSucceededRemoteDataPayload} from '../core/shared/operators'; @@ -53,8 +47,6 @@ export class CommunityPageComponent implements OnInit { communityPageRoute$: Observable; constructor( - private communityDataService: CommunityDataService, - private metadata: MetadataService, private route: ActivatedRoute, private router: Router, private authService: AuthService, diff --git a/src/app/community-page/community-page.module.ts b/src/app/community-page/community-page.module.ts index 61c17de8fa..5ebd7c3057 100644 --- a/src/app/community-page/community-page.module.ts +++ b/src/app/community-page/community-page.module.ts @@ -21,8 +21,10 @@ import { } from './sections/sub-com-col-section/sub-collection-list/themed-community-page-sub-collection-list.component'; import { DsoPageModule } from '../shared/dso-page/dso-page.module'; import { SubComColSectionComponent } from './sections/sub-com-col-section/sub-com-col-section.component'; +import { BrowseByPageModule } from '../browse-by/browse-by-page.module'; -const DECLARATIONS = [CommunityPageComponent, +const DECLARATIONS = [ + CommunityPageComponent, ThemedCommunityPageComponent, ThemedCommunityPageSubCommunityListComponent, CommunityPageSubCollectionListComponent, @@ -42,6 +44,7 @@ const DECLARATIONS = [CommunityPageComponent, CommunityFormModule, ComcolModule, DsoPageModule, + BrowseByPageModule, ], declarations: [ ...DECLARATIONS diff --git a/src/app/community-page/sections/community-browse-section/community-browse-section.component.html b/src/app/community-page/sections/community-browse-section/community-browse-section.component.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.html b/src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.html index 504d9f4bcd..dd7d8d12f7 100644 --- a/src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.html +++ b/src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.html @@ -1,5 +1,5 @@

{{'browse.comcol.head' | translate}}

-
diff --git a/src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.ts b/src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.ts index 0527d283f0..2a3ec220d6 100644 --- a/src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.ts +++ b/src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.ts @@ -1,7 +1,7 @@ -import { Component, Input, OnInit } from '@angular/core'; -import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; -import { ActivatedRoute, Params, Router } from '@angular/router'; +import { Component, Input, OnInit, OnDestroy } from '@angular/core'; +import { BehaviorSubject, combineLatest, Observable, Subscription } from 'rxjs'; +import { map, take } from 'rxjs/operators'; +import { Router, EventType, Scroll } from '@angular/router'; import { getCommunityPageRoute } from '../../../community-page/community-page-routing-paths'; import { getCollectionPageRoute } from '../../../collection-page/collection-page-routing-paths'; import { getFirstCompletedRemoteData } from '../../../core/shared/operators'; @@ -26,62 +26,87 @@ export interface ComColPageNavOption { styleUrls: ['./comcol-page-browse-by.component.scss'], templateUrl: './comcol-page-browse-by.component.html' }) -export class ComcolPageBrowseByComponent implements OnInit { +export class ComcolPageBrowseByComponent implements OnDestroy, OnInit { /** * The ID of the Community or Collection */ @Input() id: string; @Input() contentType: string; - allOptions: ComColPageNavOption[]; + allOptions$: Observable; - currentOptionId$: Observable; + currentOption$: BehaviorSubject = new BehaviorSubject(undefined); + + subs: Subscription[] = []; constructor( - private route: ActivatedRoute, - private router: Router, + public router: Router, private browseService: BrowseService ) { } ngOnInit(): void { - this.browseService.getBrowseDefinitions() - .pipe(getFirstCompletedRemoteData>()) - .subscribe((browseDefListRD: RemoteData>) => { + this.allOptions$ = this.browseService.getBrowseDefinitions().pipe( + getFirstCompletedRemoteData(), + map((browseDefListRD: RemoteData>) => { + const allOptions: ComColPageNavOption[] = []; if (browseDefListRD.hasSucceeded) { - this.allOptions = browseDefListRD.payload.page - .map((config: BrowseDefinition) => ({ - id: config.id, - label: `browse.comcol.by.${config.id}`, - routerLink: `/browse/${config.id}`, - params: { scope: this.id } - })); - + let comColRoute: string; if (this.contentType === 'collection') { - this.allOptions = [{ - id: this.id, + comColRoute = getCollectionPageRoute(this.id); + allOptions.push({ + id: 'recent_submissions', label: 'collection.page.browse.recent.head', - routerLink: getCollectionPageRoute(this.id) - }, ...this.allOptions]; + routerLink: comColRoute, + }); } else if (this.contentType === 'community') { - this.allOptions = [{ - id: this.id, + comColRoute = getCommunityPageRoute(this.id); + allOptions.push({ + id: 'comcols', label: 'community.all-lists.head', - routerLink: getCommunityPageRoute(this.id) - }, ...this.allOptions]; + routerLink: comColRoute, + }); + } + + allOptions.push(...browseDefListRD.payload.page.map((config: BrowseDefinition) => ({ + id: `browse_${config.id}`, + label: `browse.comcol.by.${config.id}`, + routerLink: `${comColRoute}/browse/${config.id}`, + }))); + } + return allOptions; + }), + ); + + this.subs.push(combineLatest([ + this.allOptions$, + this.router.events, + ]).subscribe(([navOptions, scrollEvent]: [ComColPageNavOption[], Scroll]) => { + if (scrollEvent.type === EventType.Scroll) { + for (let option of navOptions) { + if (option.routerLink === scrollEvent.routerEvent.urlAfterRedirects.split('?')[0]) { + this.currentOption$.next(option); } } - }); - - this.currentOptionId$ = this.route.params.pipe( - map((params: Params) => params.id) - ); + } + })); } - onSelectChange(newId: string) { - const selectedOption = this.allOptions - .find((option: ComColPageNavOption) => option.id === newId); + ngOnDestroy(): void { + this.subs.forEach((sub: Subscription) => sub.unsubscribe()); + } - this.router.navigate([selectedOption.routerLink], { queryParams: selectedOption.params }); + onSelectChange(event: any): void { + this.allOptions$.pipe( + take(1), + ).subscribe((allOptions: ComColPageNavOption[]) => { + for (let option of allOptions) { + if (option.id === event.target.value) { + this.currentOption$.next(option[0]); + void this.router.navigate([option.routerLink], { queryParams: option.params }); + break; + } + } + }); } } From e66a14cffd6b754b347346f21ed3f034b4733f26 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Tue, 5 Dec 2023 00:45:19 +0100 Subject: [PATCH 35/47] 108588: Fixed accessibility issues on ds-comcol-page-browse-by --- .../comcol-page-browse-by.component.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.html b/src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.html index dd7d8d12f7..d0fd33a834 100644 --- a/src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.html +++ b/src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.html @@ -2,9 +2,11 @@