From bf971eb6aa999813038e9fee76016d31ea7c4ee3 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Thu, 19 Nov 2020 17:57:01 -0500 Subject: [PATCH] LP1904788 Browse paging show next/prev WIP Signed-off-by: Bill Erickson --- .../eg2/src/app/staff/catalog/catalog.service.ts | 2 +- .../catalog/result/browse-pager.component.html | 8 +- .../staff/catalog/result/browse-pager.component.ts | 201 ++++++++++++--------- .../app/staff/catalog/result/results.component.ts | 2 +- 4 files changed, 123 insertions(+), 90 deletions(-) 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 52e1173f2e..c828c169c1 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 @@ -46,7 +46,7 @@ export class StaffCatalogService { // Cache of browse results so the browse pager is not forced to // re-run the browse search on each navigation. - browsePagerResults: any[]; + browsePagerData: any[]; constructor( private router: Router, 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 index 6b46379ee2..8d592da36a 100644 --- 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 @@ -21,7 +21,13 @@
- Heading: + + Title: + Author: + Subject: + Series: + + {{searchContext.termSearch.browseEntry.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 index 83054b6096..92fb4074ac 100644 --- 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 @@ -7,6 +7,12 @@ import {StaffCatalogService} from '../catalog.service'; import {IdlObject} from '@eg/core/idl.service'; import {BasketService} from '@eg/share/catalog/basket.service'; +interface BrowsePage { + leftPivot: number; + rightPivot: number; + entries: any[]; +} + @Component({ selector: 'eg-catalog-browse-pager', templateUrl: 'browse-pager.component.html' @@ -28,31 +34,53 @@ export class BrowsePagerComponent implements OnInit { this.fetchPageData().then(_ => this.setPrevNext()); } - fetchPageData(): Promise { + pageEntryId(): number { + return Number( + this.searchContext.termSearch.hasBrowseEntry.split(',')[0] + ); + } - let found = false; - const mbeId = this.pageEntryId(); - this.staffCat.browsePagerResults.forEach(result => { - if (result.browse_entry === mbeId) { - found = true; - } + getEntryPageIndex(mbeId: number): number { + let idx = null; + this.staffCat.browsePagerData.forEach((page, index) => { + page.entries.forEach(entry => { + if (entry.browse_entry === mbeId) { + idx = index; + } + }); }); + return idx; + } + - if (found) { + getEntryPage(mbeId: number): BrowsePage { + return this.staffCat.browsePagerData[this.getEntryPageIndex(mbeId)]; + } + + fetchPageData(): Promise { + + if (this.getEntryPage(this.pageEntryId())) { + // We have this page's data already return Promise.resolve(); - } else { - return this.fetchBrowseData(null); } + + return this.fetchBrowsePage(null); } // Grab a page of browse results - fetchBrowseData(prev: boolean): Promise { + fetchBrowsePage(prev: boolean): Promise { const ctx = this.searchContext.clone(); ctx.pager.limit = this.searchContext.pager.limit; ctx.termSearch.hasBrowseEntry = null; // avoid term search if (prev !== null) { - ctx.browseSearch.pivot = this.getBoundaryPivot(prev); + const page = this.getEntryPage(this.pageEntryId()); + const pivot = prev ? page.leftPivot : page.rightPivot; + if (pivot === null) { + console.debug('Browse has reached the end of the rainbow'); + return; + } + ctx.browseSearch.pivot = pivot; } const results = []; @@ -61,120 +89,119 @@ export class BrowsePagerComponent implements OnInit { return this.cat.browse(ctx) .pipe(tap(result => results.push(result))) .toPromise().then(_ => { + if (results.length === 0) { return; } + + // At the end of the data set, final pivots are not present + let leftPivot = null; + let rightPivot = null; + if (results[0].pivot_point) { + leftPivot = results.shift().pivot_point; + } + if (results[results.length - 1].pivot_point) { + rightPivot = results.pop().pivot_point; + } + + // We only care about entries with bib record sources + let keepEntries = results.filter(e => Boolean(e.sources)) + + if (leftPivot === null || rightPivot === null) { + // When you reach the edge of the data set, you can get + // the same browse entries from different API calls. + // From what I can tell, the last page will always have + // 5 entries, even if you've already seen some of those + // entries. Trim the dupes since they affect the logic. + const keep = []; + keepEntries.forEach(e => { + if (!this.getEntryPage(e.browse_entry)) { + keep.push(e); + } + }); + keepEntries = keep; + } + + const page: BrowsePage = { + leftPivot: leftPivot, + rightPivot: rightPivot, + entries: keepEntries + }; + if (prev) { - this.staffCat.browsePagerResults = - results.concat(this.staffCat.browsePagerResults); + this.staffCat.browsePagerData.unshift(page); } else { - this.staffCat.browsePagerResults = - this.staffCat.browsePagerResults.concat(results); + this.staffCat.browsePagerData.push(page); } 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(take: number = 1): Promise { + setPrevNext(take2: boolean = false): Promise { - // Should never have to call this more than 3 times, with the - // first 2 collecting data and the final assing prev/next. - if (take > 3) { return Promise.resolve(); } - - let pageEntry, previous: any; + let previous: any; const mbeId = this.pageEntryId(); - this.staffCat.browsePagerResults.forEach(result => { - // ignore pivot and authority-only entries - if (!result.sources) { return; } - - if (result.browse_entry === mbeId) { - pageEntry = result; - } + this.staffCat.browsePagerData.forEach(page => { + page.entries.forEach(entry => { - if (previous) { - if (result.browse_entry === mbeId) { - this.prevEntry = previous; + if (previous) { + if (entry.browse_entry === mbeId) { + this.prevEntry = previous; + } + if (previous.browse_entry === mbeId) { + this.nextEntry = entry; + } } - if (previous.browse_entry === mbeId) { - this.nextEntry = result; - } - } - previous = result; + previous = entry; + }); }); + if (take2) { + // If we have to call this more than twice it means we've + // reached the boundary of the full data set and there's + // no more data to fetch. + return Promise.resolve(); + } + let promise; - if (!pageEntry) { - promise = this.fetchBrowseData(null) - } else if (!this.prevEntry) { - promise = this.fetchBrowseData(true); + if (!this.prevEntry) { + promise = this.fetchBrowsePage(true); } else if (!this.nextEntry) { - promise = this.fetchBrowseData(false); + promise = this.fetchBrowsePage(false); } if (promise) { - return promise.then(_ => this.setPrevNext(take + 1)); + return promise.then(_ => this.setPrevNext(true)); } 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 = Number( - prev ? this.prevEntry.browse_entry : this.nextEntry.browse_entry + prev ? this.prevEntry.browse_entry : this.nextEntry.browse_entry ); - let results = [].concat(this.staffCat.browsePagerResults); - if (prev) { results = results.reverse(); } - - let current; - let lastPivot = null; - let lastWasPivot = false; - let done = false; - results.forEach(result => { - if (done) { return; } - const entryId = Number(result.browse_entry || -1); - - // Pivots will be back-to-back in the full results set - // We only care about the first pivot encountered in a - // 2-pivot cluster. - if (result.pivot_point && !lastWasPivot) { - lastPivot = result.pivot_point; - lastWasPivot = true; - return; - } + const curPageIdx = this.getEntryPageIndex(this.pageEntryId()); + const targetPageIdx = this.getEntryPageIndex(targetMbe); - lastWasPivot = false; + if (targetPageIdx !== curPageIdx) { + // We are crossing a page boundary - if (current) { + const curPage = this.getEntryPage(this.pageEntryId()); - if (entryId === targetMbe && lastPivot) { - this.searchContext.browseSearch.pivot = lastPivot; - done = true; - } - } else if (entryId === mbeId) { - current = result; + if (prev) { + this.searchContext.browseSearch.pivot = curPage.leftPivot; + + } else { + this.searchContext.browseSearch.pivot = curPage.rightPivot; } - }); + } } // Find the browse entry for the next/prev page and navigate there 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 b4eb0ad78b..34be7d5128 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 @@ -41,7 +41,7 @@ export class ResultsComponent implements OnInit, OnDestroy { ngOnInit() { this.searchContext = this.staffCat.searchContext; - this.staffCat.browsePagerResults = []; + this.staffCat.browsePagerData = []; // Our search context is initialized on page load. Once // ResultsComponent is active, it will not be reinitialized, -- 2.11.0