Files
redash/client/app/components/items-list/classes/ItemsSource.js
Levko Kravets cbc56264ea React migration cleanup (#4572)
* Revisit ANGULAR_REMOVE_ME things

* Remove styles related to 3rd-party Angular and jQuery libraries

* Remove some more unused styles

* Revisit error handling (app-wide)

* Remove unused file

* CR1
2020-01-22 17:15:25 +02:00

163 lines
4.8 KiB
JavaScript

import { isFunction, identity, map, extend } from "lodash";
import Paginator from "./Paginator";
import Sorter from "./Sorter";
import { PlainListFetcher, PaginatedListFetcher } from "./ItemsFetcher";
export class ItemsSource {
onBeforeUpdate = null;
onAfterUpdate = null;
onError = null;
getCallbackContext = () => null;
_beforeUpdate() {
if (isFunction(this.onBeforeUpdate)) {
return Promise.resolve(this.onBeforeUpdate(this.getState(), this.getCallbackContext()));
}
return Promise.resolve();
}
_afterUpdate() {
if (isFunction(this.onAfterUpdate)) {
return Promise.resolve(this.onAfterUpdate(this.getState(), this.getCallbackContext()));
}
return Promise.resolve();
}
// changes: object with flags or null (full refresh requested)
_changed(changes) {
const state = {
paginator: this._paginator,
sorter: this._sorter,
searchTerm: this._searchTerm,
selectedTags: this._selectedTags,
};
const customParams = {};
const context = {
...this.getCallbackContext(),
setCustomParams: params => {
extend(customParams, params);
},
};
return this._beforeUpdate().then(() =>
this._fetcher
.fetch(changes, state, context)
.then(({ results, count, allResults }) => {
this._pageItems = results;
this._allItems = allResults || null;
this._paginator.setTotalCount(count);
this._params = { ...this._params, ...customParams };
return this._afterUpdate();
})
.catch(error => this.handleError(error))
);
}
constructor({ getRequest, doRequest, processResults, isPlainList = false, ...defaultState }) {
if (!isFunction(getRequest)) {
getRequest = identity;
}
this._fetcher = isPlainList
? new PlainListFetcher({ getRequest, doRequest, processResults })
: new PaginatedListFetcher({ getRequest, doRequest, processResults });
this.setState(defaultState);
this._pageItems = [];
this._params = {};
}
getState() {
return {
page: this._paginator.page,
itemsPerPage: this._paginator.itemsPerPage,
orderByField: this._sorter.field,
orderByReverse: this._sorter.reverse,
searchTerm: this._searchTerm,
selectedTags: this._selectedTags,
totalCount: this._paginator.totalCount,
pageItems: this._pageItems,
allItems: this._allItems,
params: this._params,
};
}
setState(state) {
this._paginator = new Paginator(state);
this._sorter = new Sorter(state);
this._searchTerm = state.searchTerm || "";
this._selectedTags = state.selectedTags || [];
this._savedOrderByField = this._sorter.field;
}
updatePagination = ({ page, itemsPerPage }) => {
const { page: prevPage, itemsPerPage: prevItemsPerPage } = this._paginator;
this._paginator.setItemsPerPage(itemsPerPage);
this._paginator.setPage(page);
this._changed({
pagination: {
page: prevPage !== this._paginator.page, // page changed flag
itemsPerPage: prevItemsPerPage !== this._paginator.itemsPerPage, // items per page changed flags
},
});
};
toggleSorting = orderByField => {
this._sorter.toggleField(orderByField);
this._savedOrderByField = this._sorter.field;
this._changed({ sorting: true });
};
updateSearch = searchTerm => {
// here we update state directly, but later `fetchData` will update it properly
this._searchTerm = searchTerm;
// in search mode ignore the ordering and use the ranking order
// provided by the server-side FTS backend instead, unless it was
// requested by the user by actively ordering in search mode
if (searchTerm === "") {
this._sorter.setField(this._savedOrderByField); // restore ordering
} else {
this._sorter.setField(null);
}
this._paginator.setPage(1);
this._changed({ search: true, pagination: { page: true } });
};
updateSelectedTags = selectedTags => {
this._selectedTags = selectedTags;
this._paginator.setPage(1);
this._changed({ tags: true, pagination: { page: true } });
};
update = () => this._changed();
handleError = error => {
if (isFunction(this.onError)) {
this.onError(error);
}
};
}
export class ResourceItemsSource extends ItemsSource {
constructor({ getResource, getItemProcessor, ...rest }) {
getItemProcessor = isFunction(getItemProcessor) ? getItemProcessor : () => null;
super({
...rest,
doRequest: (request, context) => {
const resource = getResource(context)(request);
return resource;
},
processResults: (results, context) => {
let processItem = getItemProcessor(context);
processItem = isFunction(processItem) ? processItem : identity;
return map(results, item => processItem(item, context));
},
});
}
}