1 import {Component, OnInit, OnDestroy, Input} from '@angular/core';
2 import {Observable, Subscription} from 'rxjs';
3 import {tap, map, switchMap, distinctUntilChanged} from 'rxjs/operators';
4 import {CatalogService} from '@eg/share/catalog/catalog.service';
5 import {CatalogSearchContext, CatalogSearchState} from '@eg/share/catalog/search-context';
6 import {StaffCatalogService} from '../catalog.service';
7 import {IdlObject} from '@eg/core/idl.service';
8 import {BasketService} from '@eg/share/catalog/basket.service';
10 interface BrowsePage {
17 selector: 'eg-catalog-browse-pager',
18 templateUrl: 'browse-pager.component.html'
20 export class BrowsePagerComponent implements OnInit {
22 searchContext: CatalogSearchContext;
23 browseLoading = false;
28 private cat: CatalogService,
29 private staffCat: StaffCatalogService
33 this.searchContext = this.staffCat.searchContext;
34 this.fetchPageData().then(_ => this.setPrevNext());
37 pageEntryId(): number {
39 this.searchContext.termSearch.hasBrowseEntry.split(',')[0]
43 getEntryPageIndex(mbeId: number): number {
45 this.staffCat.browsePagerData.forEach((page, index) => {
46 page.entries.forEach(entry => {
47 if (entry.browse_entry === mbeId) {
56 getEntryPage(mbeId: number): BrowsePage {
57 return this.staffCat.browsePagerData[this.getEntryPageIndex(mbeId)];
60 fetchPageData(): Promise<any> {
62 if (this.getEntryPage(this.pageEntryId())) {
63 // We have this page's data already
64 return Promise.resolve();
67 return this.fetchBrowsePage(null);
70 // Grab a page of browse results
71 fetchBrowsePage(prev: boolean): Promise<any> {
72 const ctx = this.searchContext.clone();
73 ctx.pager.limit = this.searchContext.pager.limit;
74 ctx.termSearch.hasBrowseEntry = null; // avoid term search
77 // Fetching data for a prev/next page which is not the
79 const page = this.getEntryPage(this.pageEntryId());
80 const pivot = prev ? page.leftPivot : page.rightPivot;
82 console.debug('Browse has reached the end of the rainbow');
85 ctx.browseSearch.pivot = pivot;
89 this.browseLoading = true;
91 return this.cat.browse(ctx)
92 .pipe(tap(result => results.push(result)))
93 .toPromise().then(_ => {
94 if (results.length === 0) { return; }
96 // At the end of the data set, final pivots are not present
98 let rightPivot = null;
99 if (results[0].pivot_point) {
100 leftPivot = results.shift().pivot_point;
102 if (results[results.length - 1].pivot_point) {
103 rightPivot = results.pop().pivot_point;
106 // We only care about entries with bib record sources
107 let keepEntries = results.filter(e => Boolean(e.sources));
109 if (leftPivot === null || rightPivot === null) {
110 // When you reach the edge of the data set, you can get
111 // the same browse entries from different API calls.
112 // From what I can tell, the last page will always have
113 // a half page of entries, even if you've already seen some
114 // of them in the previous page. Trim the dupes since they
117 keepEntries.forEach(e => {
118 if (!this.getEntryPage(e.browse_entry)) {
125 const page: BrowsePage = {
126 leftPivot: leftPivot,
127 rightPivot: rightPivot,
132 this.staffCat.browsePagerData.unshift(page);
134 this.staffCat.browsePagerData.push(page);
136 this.browseLoading = false;
140 // Collect enough browse data to display previous, current, and
141 // next heading. This can mean fetching an additional page of data.
142 setPrevNext(take2: boolean = false): Promise<any> {
145 const mbeId = this.pageEntryId();
147 this.staffCat.browsePagerData.forEach(page => {
148 page.entries.forEach(entry => {
151 if (entry.browse_entry === mbeId) {
152 this.prevEntry = previous;
154 if (previous.browse_entry === mbeId) {
155 this.nextEntry = entry;
163 // If we have to call this more than twice it means we've
164 // reached the boundary of the full data set and there's
165 // no more data to fetch.
166 return Promise.resolve();
171 if (!this.prevEntry) {
172 promise = this.fetchBrowsePage(true);
174 } else if (!this.nextEntry) {
175 promise = this.fetchBrowsePage(false);
179 return promise.then(_ => this.setPrevNext(true));
182 return Promise.resolve();
185 setSearchPivot(prev?: boolean) {
186 // When traversing browse result page boundaries, modify the
187 // search pivot to keep up.
189 const targetMbe = Number(
190 prev ? this.prevEntry.browse_entry : this.nextEntry.browse_entry
193 const curPageIdx = this.getEntryPageIndex(this.pageEntryId());
194 const targetPageIdx = this.getEntryPageIndex(targetMbe);
196 if (targetPageIdx !== curPageIdx) {
197 // We are crossing a page boundary
199 const curPage = this.getEntryPage(this.pageEntryId());
202 this.searchContext.browseSearch.pivot = curPage.leftPivot;
205 this.searchContext.browseSearch.pivot = curPage.rightPivot;
210 // Find the browse entry for the next/prev page and navigate there
211 // if possible. Returns false if not enough data is available.
212 goToBrowsePage(prev: boolean): boolean {
213 const ctx = this.searchContext;
214 const target = prev ? this.prevEntry : this.nextEntry;
216 if (!target) { return false; }
218 this.setSearchPivot(prev);
220 // Jump to the selected browse entry's page.
221 ctx.termSearch.hasBrowseEntry = target.browse_entry + ',' + target.fields;
222 ctx.pager.offset = 0; // this is a new records-for-browse-entry search
223 this.staffCat.search();