From dd72e2a5bd5cbaef796a58db1db744d5e9136b7f Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Thu, 19 Nov 2020 13:02:19 -0500 Subject: [PATCH] LP1904788 Browse paging show next/prev WIP Signed-off-by: Bill Erickson --- .../eg2/src/app/staff/catalog/catalog.module.ts | 4 +- .../eg2/src/app/staff/catalog/catalog.service.ts | 4 + .../catalog/result/browse-pager.component.html | 45 ++++++ .../staff/catalog/result/browse-pager.component.ts | 176 +++++++++++++++++++++ .../staff/catalog/result/results.component.html | 38 +---- .../app/staff/catalog/result/results.component.ts | 113 +------------ 6 files changed, 237 insertions(+), 143 deletions(-) create mode 100644 Open-ILS/src/eg2/src/app/staff/catalog/result/browse-pager.component.html create mode 100644 Open-ILS/src/eg2/src/app/staff/catalog/result/browse-pager.component.ts diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts index 9b7d57acdc..14c52e9672 100644 --- a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts +++ b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts @@ -31,6 +31,7 @@ import {CnBrowseResultsComponent} from './cnbrowse/results.component'; import {SearchTemplatesComponent} from './search-templates.component'; import {MarcEditModule} from '@eg/staff/share/marc-edit/marc-edit.module'; import {PreferencesComponent} from './prefs.component'; +import {BrowsePagerComponent} from './result/browse-pager.component'; @NgModule({ declarations: [ @@ -56,7 +57,8 @@ import {PreferencesComponent} from './prefs.component'; CnBrowseComponent, OpacViewComponent, PreferencesComponent, - CnBrowseResultsComponent + CnBrowseResultsComponent, + BrowsePagerComponent ], imports: [ StaffCommonModule, diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.service.ts b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.service.ts index a0652744f6..52e1173f2e 100644 --- a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.service.ts +++ b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.service.ts @@ -44,6 +44,10 @@ export class StaffCatalogService { // Add digital bookplate to search options. enableBookplates = false; + // Cache of browse results so the browse pager is not forced to + // re-run the browse search on each navigation. + browsePagerResults: any[]; + constructor( private router: Router, private route: ActivatedRoute, diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/result/browse-pager.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/result/browse-pager.component.html new file mode 100644 index 0000000000..6b46379ee2 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/catalog/result/browse-pager.component.html @@ -0,0 +1,45 @@ + +
+ + +
+ +
+
+ + +
+
+
+ +
+
+ {{prevEntry.value}} +
+
+
+
+
+ Heading: + {{searchContext.termSearch.browseEntry.value()}} + +
+
+
+
+
+
+ {{nextEntry.value}} +
+
+ +
+
+
+
+
+ +
+ diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/result/browse-pager.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/result/browse-pager.component.ts new file mode 100644 index 0000000000..ed34719da5 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/catalog/result/browse-pager.component.ts @@ -0,0 +1,176 @@ +import {Component, OnInit, OnDestroy, Input} from '@angular/core'; +import {Observable, Subscription} from 'rxjs'; +import {tap, map, switchMap, distinctUntilChanged} from 'rxjs/operators'; +import {CatalogService} from '@eg/share/catalog/catalog.service'; +import {CatalogSearchContext, CatalogSearchState} from '@eg/share/catalog/search-context'; +import {StaffCatalogService} from '../catalog.service'; +import {IdlObject} from '@eg/core/idl.service'; +import {BasketService} from '@eg/share/catalog/basket.service'; + +@Component({ + selector: 'eg-catalog-browse-pager', + templateUrl: 'browse-pager.component.html' +}) +export class BrowsePagerComponent implements OnInit { + + searchContext: CatalogSearchContext; + browseLoading = false; + prevEntry: any; + nextEntry: any; + + constructor( + private cat: CatalogService, + private staffCat: StaffCatalogService + ) {} + + ngOnInit() { + this.searchContext = this.staffCat.searchContext; + this.fetchPageData().then(_ => this.setPrevNext()); + } + + fetchPageData(): Promise { + + let found = false; + const mbeId = this.pageEntryId(); + this.staffCat.browsePagerResults.forEach(result => { + if (result.browse_entry === mbeId) { + found = true; + } + }); + + if (found) { + return Promise.resolve(); + } else { + return this.fetchBrowseData(null); + } + } + + // Grab a page of browse results + fetchBrowseData(prev: boolean): Promise { + const ctx = this.searchContext.clone(); + ctx.termSearch.hasBrowseEntry = null; // avoid term search + + if (prev !== null) { + ctx.browseSearch.pivot = this.getBoundaryPivot(prev); + } + + // Grab a few extras so we can last a bit longer before + // having to fetch more browse data. Could make this bigger. + ctx.pager.limit = 20; + + const results = []; + this.browseLoading = true; + + return this.cat.browse(ctx) + .pipe(tap(result => results.push(result))) + .toPromise().then(_ => { + if (prev) { + this.staffCat.browsePagerResults = + results.concat(this.staffCat.browsePagerResults); + } else { + this.staffCat.browsePagerResults = + this.staffCat.browsePagerResults.concat(results); + } + this.browseLoading = false; + }); + } + + pageEntryId(): number { + return Number( + this.searchContext.termSearch.hasBrowseEntry.split(',')[0] + ); + } + + // Collect enough browse data to display previous, current, and + // next heading. This can mean fetching an additional page of data. + setPrevNext(): Promise { + let previous: any; + const mbeId = this.pageEntryId(); + + this.staffCat.browsePagerResults.forEach(result => { + // ignore pivot and authority-only entries + if (!result.sources) { return; } + + if (previous) { + if (result.browse_entry === mbeId) { + this.prevEntry = previous; + } + if (previous.browse_entry === mbeId) { + this.nextEntry = result; + } + } + previous = result; + }); + + if (!this.prevEntry) { + return this.fetchBrowseData(true); + } else if (!this.nextEntry) { + return this.fetchBrowseData(false); + } else { + return Promise.resolve(); + } + } + + getBoundaryPivot(prev?: boolean): number { + const results = this.staffCat.browsePagerResults; + return prev ? results[0].pivot_point : + results[results.length - 1].pivot_point; + } + + setSearchPivot(prev?: boolean) { + // When traversing browse result page boundaries, modify the + // search pivot to keep up. + + const mbeId = Number( // for this page + this.searchContext.termSearch.hasBrowseEntry.split(',')[0]); + + const targetMbe = prev ? + this.prevEntry.browse_entry : this.nextEntry.browse_entry; + + let results = [].concat(this.staffCat.browsePagerResults); + if (prev) { results.reverse(); } + + let current; + let done = false; + results.forEach(result => { + if (done) { return; } + + if (current) { + + if (result.browse_entry === targetMbe) { + // The target entry is on the current page of results + // No pivot movement required. + done = true; + + } else if (result.pivot) { + // We have crossed a pivot boundary. + this.searchContext.browseSearch.pivot = result.pivot; + done = true; + } + + } else if (result.browse_entry === mbeId) { + current = result; + } + }); + } + + // Find the browse entry for the next/prev page and navigate there + // if possible. Returns false if not enough data is available. + goToBrowsePage(prev: boolean): boolean { + const ctx = this.searchContext; + const target = prev ? this.prevEntry : this.nextEntry; + + if (!target) { return false; } + + this.setSearchPivot(); + + // Jump to the selected browse entry's page. + ctx.termSearch.hasBrowseEntry = target.browse_entry + ',' + target.fields; + ctx.pager.offset = 0; // this is a new records-for-browse-entry search + this.staffCat.search(); + + return true; + } +} + + diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/result/results.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/result/results.component.html index 0c04c9efaa..a6c27cc2ac 100644 --- a/Open-ILS/src/eg2/src/app/staff/catalog/result/results.component.html +++ b/Open-ILS/src/eg2/src/app/staff/catalog/result/results.component.html @@ -31,11 +31,10 @@
-
-
-

Results for browse entry "{{searchContext.termSearch.browseEntry.value()}}"

-
-
+ + + +
@@ -54,32 +53,11 @@
- - -
- - - - - - - +
+
+
-
-
- -
-
- - -
-
- -
-
-
+
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/result/results.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/result/results.component.ts index b73e7b5459..b4eb0ad78b 100644 --- a/Open-ILS/src/eg2/src/app/staff/catalog/result/results.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/catalog/result/results.component.ts @@ -28,8 +28,6 @@ export class ResultsComponent implements OnInit, OnDestroy { searchSub: Subscription; routeSub: Subscription; basketSub: Subscription; - browseResults: any[] = []; - browseLoading = false; constructor( private route: ActivatedRoute, @@ -43,6 +41,7 @@ export class ResultsComponent implements OnInit, OnDestroy { ngOnInit() { this.searchContext = this.staffCat.searchContext; + this.staffCat.browsePagerResults = []; // Our search context is initialized on page load. Once // ResultsComponent is active, it will not be reinitialized, @@ -71,15 +70,6 @@ export class ResultsComponent implements OnInit, OnDestroy { // Watch for basket changes applied by other components. this.basketSub = this.basket.onChange.subscribe( () => this.applyRecordSelection()); - - - // Load browse data to support browse paging. - /* - if (this.searchContext.termSearch.hasBrowseEntry && - this.searchContext.browseSearch.value) { - this.fetchBrowseData().toPromise(); - } - */ } ngOnDestroy() { @@ -138,107 +128,6 @@ export class ResultsComponent implements OnInit, OnDestroy { this.basket.removeRecordIds(ids); } } - - // Grab a page of browse results - fetchBrowseData(prev?: boolean): Promise { - const ctx = this.searchContext.clone(); - ctx.termSearch.hasBrowseEntry = null; // avoid term search - - // Grab a few extras so we can last a bit longer before - // having to fetch more browse data. Could make this bigger. - ctx.pager.limit = 20; - - const results = []; - - return this.cat.browse(ctx) - .pipe(tap(result => results.push(result))) - .toPromise().then(_ => { - if (prev) { - this.browseResults = results.concat(this.browseResults); - } else { - this.browseResults = this.browseResults.concat(results); - } - }); - } - - // Find the browse entry for the next/prev page and navigate there - // if possible. Returns false if not enough data is available. - goToBrowsePage(prev?: boolean): boolean { - const ctx = this.searchContext; - - // See if we have enough info to direct to the selected page. - - const mbeId = Number( // for this page - this.searchContext.termSearch.hasBrowseEntry.split(',')[0]); - - let target, previous; - - this.browseResults.forEach(result => { - // ignore pivot and authority-only entries - if (!result.sources) { return; } - - if (previous) { - if (prev) { - if (result.browse_entry === mbeId) { - target = previous; - } - } else { - if (previous.browse_entry === mbeId) { - target = result; - } - } - } - previous = result; - }); - - if (!target) { return false; } - - // Jump to the selected browse entry's page. - ctx.termSearch.hasBrowseEntry = target.browse_entry + ',' + target.fields; - ctx.pager.offset = 0; // this is a brand new search - this.staffCat.search(); - - return true; - } - - - // Navigate to next or previous browse entry page. - // take1: navigate without fetching data if possible - // take2: fetch data and navigate if possible. - // take3: data fetched in take2 contains data for this page, but - // not the requested page. This happens when the user navigates - // cold to a browse page boundary and the current page of data - // does not yet exist. - browseNav(prev?: boolean, take?: number) { - if (!take) { take = 1; } - - this.browseLoading = true; - - if (take > 3) { - // We tried our best, but could not find the requested - // browse data Could happen if we reach the end of the data set. - this.browseLoading = false; - return; - } - - if (this.goToBrowsePage(prev)) { - // Navigation successful - this.browseLoading = false; - return; - } - - // We need another page of browse data before we can direct - // the user to the requested page. - - const results = this.browseResults; - if (results.length > 0) { - this.searchContext.browseSearch.pivot = prev ? - results[0].pivot_point : - results[results.length - 1].pivot_point; - } - - this.fetchBrowseData(prev).then(_ => this.browseNav(prev, take + 1)); - } } -- 2.11.0