diff --git a/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.ts b/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.ts index d276e452c5b..3e022e8c377 100644 --- a/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.ts +++ b/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.ts @@ -183,7 +183,7 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy { */ findAllOptions = { elementsPerPage: 20, - currentPage: 1 + currentPage: 1, }; /** @@ -485,9 +485,9 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy { const bitstreamFormats$ = this.bitstreamFormatsRD$.pipe( reduce((acc: BitstreamFormat[], response: RemoteData>) => { - return acc.concat(response.payload.page); - }, []) - ) + return acc.concat(response.payload.page); + }, []), + ); const bitstream$ = this.bitstreamRD$.pipe( getFirstSucceededRemoteData(), diff --git a/src/app/core/data/bitstream-data.service.ts b/src/app/core/data/bitstream-data.service.ts index b8776d25309..9455a456fa7 100644 --- a/src/app/core/data/bitstream-data.service.ts +++ b/src/app/core/data/bitstream-data.service.ts @@ -241,11 +241,12 @@ export class BitstreamDataService extends IdentifiableDataService imp * no valid cached version. Defaults to true * @param reRequestOnStale Whether or not the request should automatically be re- * requested after the response becomes stale + * @param options the {@link FindListOptions} for the request * @return {Observable} * Return an observable that contains primary bitstream information or null */ - public findPrimaryBitstreamByItemAndName(item: Item, bundleName: string, useCachedVersionIfAvailable = true, reRequestOnStale = true): Observable { - return this.bundleService.findByItemAndName(item, bundleName, useCachedVersionIfAvailable, reRequestOnStale, followLink('primaryBitstream')).pipe( + public findPrimaryBitstreamByItemAndName(item: Item, bundleName: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, options?: FindListOptions): Observable { + return this.bundleService.findByItemAndName(item, bundleName, useCachedVersionIfAvailable, reRequestOnStale, options, followLink('primaryBitstream')).pipe( getFirstCompletedRemoteData(), switchMap((rd: RemoteData) => { if (!rd.hasSucceeded) { diff --git a/src/app/core/data/bundle-data.service.ts b/src/app/core/data/bundle-data.service.ts index 5d552c9bf0f..027ec4e68a7 100644 --- a/src/app/core/data/bundle-data.service.ts +++ b/src/app/core/data/bundle-data.service.ts @@ -78,10 +78,11 @@ export class BundleDataService extends IdentifiableDataService implement * requested after the response becomes stale * @param linksToFollow List of {@link FollowLinkConfig} that indicate which * {@link HALLink}s should be automatically resolved + * @param options the {@link FindListOptions} for the request */ // TODO should be implemented rest side - findByItemAndName(item: Item, bundleName: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig[]): Observable> { - return this.findAllByItem(item, { elementsPerPage: 9999 }, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow).pipe( + findByItemAndName(item: Item, bundleName: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, options?: FindListOptions, ...linksToFollow: FollowLinkConfig[]): Observable> { + return this.findAllByItem(item, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow).pipe( map((rd: RemoteData>) => { if (hasValue(rd.payload) && hasValue(rd.payload.page)) { const matchingBundle = rd.payload.page.find((bundle: Bundle) => @@ -114,6 +115,47 @@ export class BundleDataService extends IdentifiableDataService implement ); } + // findByItemAndName(item: Item, bundleName: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig[]): Observable> { + // return this.findAllByItem(item, { elementsPerPage: 1, currentPage: 1 }, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow).pipe( + // expand((rd: RemoteData>) => { + // if (rd.hasSucceeded && hasValue(rd.payload) && hasValue(rd.payload.page) && rd.payload.currentPage < rd.payload.totalPages) { + // const nextPageOptions = { elementsPerPage: 1, currentPage: rd.payload.currentPage + 1 }; + // return this.findAllByItem(item, nextPageOptions, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); + // } else { + // return EMPTY; + // } + // }), + // map((rd: RemoteData>) => { + // if (hasValue(rd.payload) && hasValue(rd.payload.page)) { + // const matchingBundle = rd.payload.page.find((bundle: Bundle) => bundle.name === bundleName); + // if (hasValue(matchingBundle)) { + // return new RemoteData( + // rd.timeCompleted, + // rd.msToLive, + // rd.lastUpdated, + // RequestEntryState.Success, + // null, + // matchingBundle, + // 200 + // ); + // } else { + // return new RemoteData( + // rd.timeCompleted, + // rd.msToLive, + // rd.lastUpdated, + // RequestEntryState.Error, + // `The bundle with name ${bundleName} was not found.`, + // null, + // 404 + // ); + // } + // } else { + // return rd as any; + // } + // }) + // ); + // } + /** * Get the bitstreams endpoint for a bundle * @param bundleId diff --git a/src/app/core/data/relationship-type-data.service.ts b/src/app/core/data/relationship-type-data.service.ts index 43c018bbce2..dc553973ee5 100644 --- a/src/app/core/data/relationship-type-data.service.ts +++ b/src/app/core/data/relationship-type-data.service.ts @@ -1,12 +1,15 @@ import { Injectable } from '@angular/core'; import { combineLatest as observableCombineLatest, + EMPTY, + from, Observable, } from 'rxjs'; import { + expand, map, mergeMap, - switchMap, + reduce, toArray, } from 'rxjs/operators'; @@ -75,11 +78,26 @@ export class RelationshipTypeDataService extends BaseDataService { // Retrieve all relationship types from the server in a single page - return this.findAllData.findAll({ currentPage: 1, elementsPerPage: 9999 }, true, true, followLink('leftType'), followLink('rightType')) + const initialPageInfo = { currentPage: 1, elementsPerPage: 20 }; + return this.findAllData.findAll(initialPageInfo, true, true, followLink('leftType'), followLink('rightType')) .pipe( getFirstSucceededRemoteData(), // Emit each type in the page array separately - switchMap((typeListRD: RemoteData>) => typeListRD.payload.page), + expand((typeListRD: RemoteData>) => { + const currentPage = typeListRD.payload.pageInfo.currentPage; + const totalPages = typeListRD.payload.pageInfo.totalPages; + if (currentPage < totalPages) { + const nextPageInfo = { currentPage: currentPage + 1, elementsPerPage: 20 }; + return this.findAllData.findAll(nextPageInfo, true, true, followLink('leftType'), followLink('rightType')).pipe( + getFirstSucceededRemoteData(), + ); + } else { + return EMPTY; + } + }), + // Collect all pages into a single array + reduce((acc: RelationshipType[], typeListRD: RemoteData>) => acc.concat(typeListRD.payload.page), []), + mergeMap((relationshipTypes: RelationshipType[]) => from(relationshipTypes)), // Check each type individually, to see if it matches the provided types mergeMap((relationshipType: RelationshipType) => { if (relationshipType.leftwardType === relationshipTypeLabel) { diff --git a/src/app/core/metadata/head-tag.service.ts b/src/app/core/metadata/head-tag.service.ts index 270e5fde723..8041bb3a4ac 100644 --- a/src/app/core/metadata/head-tag.service.ts +++ b/src/app/core/metadata/head-tag.service.ts @@ -50,6 +50,7 @@ import { coreSelector } from '../core.selectors'; import { CoreState } from '../core-state.model'; import { BundleDataService } from '../data/bundle-data.service'; import { AuthorizationDataService } from '../data/feature-authorization/authorization-data.service'; +import { FindListOptions } from '../data/find-list-options.model'; import { PaginatedList } from '../data/paginated-list.model'; import { RemoteData } from '../data/remote-data'; import { RootDataService } from '../data/root-data.service'; @@ -331,6 +332,7 @@ export class HeadTagService { 'ORIGINAL', true, true, + new FindListOptions(), followLink('primaryBitstream'), followLink('bitstreams', { findListOptions: { diff --git a/src/app/process-page/form/scripts-select/scripts-select.component.html b/src/app/process-page/form/scripts-select/scripts-select.component.html index 9cd86d1a6d0..5c161f8d8d5 100644 --- a/src/app/process-page/form/scripts-select/scripts-select.component.html +++ b/src/app/process-page/form/scripts-select/scripts-select.component.html @@ -1,20 +1,47 @@ -
- - - +
+
+
+ + +
+
+
-
- {{'process.new.select-script.required' | translate}} -
+
+ {{ 'process.new.select-script.required' | translate }} +
+
diff --git a/src/app/process-page/form/scripts-select/scripts-select.component.scss b/src/app/process-page/form/scripts-select/scripts-select.component.scss index e69de29bb2d..4f9ca623107 100644 --- a/src/app/process-page/form/scripts-select/scripts-select.component.scss +++ b/src/app/process-page/form/scripts-select/scripts-select.component.scss @@ -0,0 +1,23 @@ +.dropdown-item { + padding: 0.35rem 1rem; + + &:active { + color: white !important; + } +} + +.scrollable-menu { + height: auto; + max-height: var(--ds-dropdown-menu-max-height); + overflow-x: hidden; +} + +li:not(:last-of-type) .dropdown-item { + border-bottom: var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color); +} + +#entityControlsDropdownMenu { + outline: 0; + left: 0 !important; + box-shadow: var(--bs-btn-focus-box-shadow); +} diff --git a/src/app/process-page/form/scripts-select/scripts-select.component.spec.ts b/src/app/process-page/form/scripts-select/scripts-select.component.spec.ts index 6b8c39eb8f3..7b095926fbb 100644 --- a/src/app/process-page/form/scripts-select/scripts-select.component.spec.ts +++ b/src/app/process-page/form/scripts-select/scripts-select.component.spec.ts @@ -87,7 +87,7 @@ describe('ScriptsSelectComponent', () => { fixture.detectChanges(); tick(); - const select = fixture.debugElement.query(By.css('select')); + const select = fixture.debugElement.query(By.css('#process-script')); select.triggerEventHandler('blur', null); fixture.detectChanges(); @@ -101,7 +101,7 @@ describe('ScriptsSelectComponent', () => { fixture.detectChanges(); tick(); - const select = fixture.debugElement.query(By.css('select')); + const select = fixture.debugElement.query(By.css('#process-script')); select.triggerEventHandler('blur', null); fixture.detectChanges(); diff --git a/src/app/process-page/form/scripts-select/scripts-select.component.ts b/src/app/process-page/form/scripts-select/scripts-select.component.ts index 63c11bd91a7..9eccb7ceffc 100644 --- a/src/app/process-page/form/scripts-select/scripts-select.component.ts +++ b/src/app/process-page/form/scripts-select/scripts-select.component.ts @@ -19,32 +19,29 @@ import { } from '@angular/forms'; import { ActivatedRoute, - Params, Router, } from '@angular/router'; +import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; import { TranslateModule } from '@ngx-translate/core'; +import { InfiniteScrollModule } from 'ngx-infinite-scroll'; import { - Observable, + BehaviorSubject, Subscription, } from 'rxjs'; import { - distinctUntilChanged, - filter, map, - switchMap, - take, + tap, } from 'rxjs/operators'; +import { FindListOptions } from '../../../core/data/find-list-options.model'; import { PaginatedList } from '../../../core/data/paginated-list.model'; import { ScriptDataService } from '../../../core/data/processes/script-data.service'; import { - getFirstSucceededRemoteData, + getFirstCompletedRemoteData, getRemoteDataPayload, } from '../../../core/shared/operators'; -import { - hasNoValue, - hasValue, -} from '../../../shared/empty.util'; +import { hasValue } from '../../../shared/empty.util'; +import { ThemedLoadingComponent } from '../../../shared/loading/themed-loading.component'; import { Script } from '../../scripts/script.model'; import { controlContainerFactory } from '../process-form-factory'; @@ -61,7 +58,7 @@ const SCRIPT_QUERY_PARAMETER = 'script'; useFactory: controlContainerFactory, deps: [[new Optional(), NgForm]] }], standalone: true, - imports: [NgIf, FormsModule, NgFor, AsyncPipe, TranslateModule], + imports: [NgIf, FormsModule, NgFor, AsyncPipe, TranslateModule, InfiniteScrollModule, ThemedLoadingComponent, NgbDropdownModule], }) export class ScriptsSelectComponent implements OnInit, OnDestroy { /** @@ -71,9 +68,19 @@ export class ScriptsSelectComponent implements OnInit, OnDestroy { /** * All available scripts */ - scripts$: Observable; + scripts: Script[] = []; + private _selectedScript: Script; - private routeSub: Subscription; + private subscription: Subscription; + + private _isLastPage = false; + + scriptOptions: FindListOptions = { + elementsPerPage: 20, + currentPage: 1, + }; + + isLoading$: BehaviorSubject = new BehaviorSubject(false); constructor( private scriptService: ScriptDataService, @@ -87,31 +94,46 @@ export class ScriptsSelectComponent implements OnInit, OnDestroy { * Checks if the route contains a script ID and auto selects this scripts */ ngOnInit() { - this.scripts$ = this.scriptService.findAll({ elementsPerPage: 9999 }) - .pipe( - getFirstSucceededRemoteData(), - getRemoteDataPayload(), - map((paginatedList: PaginatedList