blueocean-plugin/blueocean-core-js/src/js/services/Pager.js

161 lines
5.1 KiB
JavaScript

import { observable, action, computed } from 'mobx';
import { Fetch } from '../fetch';
/**
* Provide a pagination function for the generic
* blueocean pagination
*
* @export
* @param {string} url - Base url to paginate.
* @returns {function} - Function that provides pagincated urls.
*/
export function paginateUrl(url) {
const sep = url.indexOf('?') >= 0 ? '&' : '?';
return (start, limit) => `${url}${sep}start=${start}&limit=${limit}`;
}
/**
* The pager fetches pages of data from the BlueOcean api. It fetches pages of data, then
* inserts them into the [@link BunkerService], and stores the href from the data.
*
* MobX computes a data field from the hrefs backed by the backend cache. This allows for SSE events
* to be proporgated to the pager.
*
* @export
* @class Pager
*/
export class Pager {
/**
* List of deisplayed items hrefs.
*/
@observable hrefs = [];
/**
* pager is fetching data.
*/
@observable pending = false;
/**
* Will be set in an error occurs.
*/
@observable error = null;
/**
* The latest page the pager has fetched.
*/
@observable currentPage = 0;
/**
* More pages to fetch.
*/
@observable hasMore = true;
/**
* Mobx computed value that creates an array of objects from the list of hrefs stored. If either the
* bunker changes, or the hrefs change, this is recalculated and will trigger a react reaction.
*
* If item does not exist in bunker, then we just ignore it.
* @readonly
* @type {Array<Object>}
*/
@computed
get data() {
return this.hrefs.map(href => this.bunker.getItem(href)).filter(item => item !== undefined);
}
/**
* Creates an instance of Pager and fetches the first page.
*
* @param {string} url - Base url of collectin to fetch
* @param {number} pageSize - Page size to fetch during one load.
* @param {BunkerService} bunker - Data store
* @param {UrlProvider} [urlProvider=paginateUrl]
*/
constructor(url, pageSize, bunker, urlProvider = paginateUrl) {
this.pageSize = pageSize;
this.url = url;
this.urlProvider = urlProvider;
this.pagedUrl = this.urlProvider(url);
this.pageSize = pageSize;
this.bunker = bunker;
// Fetch the first page so that the user does not have to.
this.fetchNextPage();
}
/**
* Fetches the next page from the backend.
*
* @returns {Promise}
*/
@action
fetchNextPage() {
// Get the next page's url.'
const url = this.pagedUrl(this.currentPage * this.pageSize, this.pageSize + 1);
this.pending = true;
return Fetch.fetchJSON(url)
.then(action('Process pager data', data => {
// Store item in bunker.
const saved = this.bunker.setItems(data);
// 1 extra item is fetched because need to know if there are more packages. So
// slice off the last item, then map all items to just be hrefs.
const trimmedHrefs = saved.slice(0, this.pageSize).map(item => item._links.self.href);
// Append the new Hrefs to the existing ones.
this.hrefs = this.hrefs.concat(trimmedHrefs);
// True if we fetch more items than the page size.
this.hasMore = data.length > this.pageSize;
this.currentPage = this.currentPage + 1;
this.pending = false;
})).catch(err => {
console.error('Error fetching page', err);
action('set error', () => { this.error = err; });
});
}
/**
* Refreshes the Hrefs for the pager. It also stores the latest data in the [@link BunkerService]
*
* This might be called if something like sorting of a list changes.
*
* @returns {Promise}
*/
@action
refresh() {
const url = this.pagedUrl(0, this.currentPage * this.pageSize + 1);
this.pending = true;
return Fetch.fetchJSON(url) // Fetch data
.then(action('set data', data => {
this.bunker.setItems(data);
this.hrefs = data.slice(0, this.pageSize).map(x => x._links.self.href);
this.hasMore = data.length > this.pageSize;
this.currentPage = this.currentPage + 1;
this.pending = false;
})).catch(err => {
console.error('Error fetching page', err);
this.err = err;
});
}
/**
* Inserts an href into the list. This will cause a reaction render for the paged list of data.
*
* @param {string} href - href of item to display
* @param {number} [pos=0] - Position to insert it. Default is first item.
*/
@action
insert(href, pos = 0) {
this.hrefs.splice(pos, 0, href);
}
/**
* Href exists in pager.
*
* @param {string} href
* @returns {boolean} - True if this pager does have this href
*/
has(href) {
return this.hrefs.indexOf(href) > -1;
}
}