import Component from '@ember/component'; import { get, setProperties, computed, getProperties } from '@ember/object'; import { task, TaskInstance } from 'ember-concurrency'; import { IBrowserRouteParams } from 'wherehows-web/routes/browse/entity'; import { IDatasetsGetResponse, IDatasetView } from 'wherehows-web/typings/api/datasets/dataset'; import { readDatasets } from 'wherehows-web/utils/api/datasets/dataset'; import { action } from 'ember-decorators/object'; import ComputedProperty from '@ember/object/computed'; // Describes the index signature for strategy pattern in the getEntityDataTask type IGetEntityTaskStrategy = { [K in IBrowserRouteParams['entity']]: TaskInstance> }; // Describes the operations that can be performed on an entity list type listOp = 'push' | 'set'; export default class BrowserViewport extends Component { /** * Passed in parameters containing route or query parameters values to be used in request * @type {IBrowserRouteParams} * @memberof BrowserViewport */ params: IBrowserRouteParams; /** * Initial value for the entity being viewed * @memberof BrowserViewport */ currentEntity: IBrowserRouteParams['entity'] = 'datasets'; /** * Ember route for the entities being rendered * @type {string} * @memberof BrowserViewport */ entityRoute = ''; /** * List of entities to be rendered in view * @type {Array} * @memberof BrowserViewport */ entities: Array = []; /** * The total number of entities in the segment * @type {number} */ total: number = 0; /** * The number of entities to request at a time * @type {number} */ count: number = 0; /** * The position index in the list to begin requesting data from * @type {number} */ start: number = 0; didUpdateAttrs() { this._super(...arguments); get(this, 'getEntityDataTask').perform(); } didInsertElement() { this._super(...arguments); get(this, 'getEntityDataTask').perform(); } /** * Async request a list of datasets with a variable start point * @type {TaskProperty> & {perform: (a1: "push" | "set" | undefined, a2: number) => TaskInstance>}} * @memberof BrowserViewport */ getDatasetsTask = task(function*( this: BrowserViewport, op: listOp = 'set', start: number ): IterableIterator> { const { prefix, platform, entity } = get(this, 'params'); const response: IDatasetsGetResponse = yield readDatasets({ platform, prefix, start }); const { total, count, elements } = response; const entities = get(this, 'entities'); // If new segment / data-source, replace all items rather than append const listOp = op === 'set' ? [].setObjects : [].pushObjects; listOp.call(entities, elements); setProperties(this, { total, count, currentEntity: entity, entityRoute: `${entity}.${entity.slice(0, -1)}`, start: entities.length }); return response; }).drop(); /** * Async requests for the list of entities and sets the value on class * @type {TaskProperty> & {perform: (a?: number) => TaskInstance>}} * @memberof BrowserViewport */ getEntityDataTask = task(function*( this: BrowserViewport, offset: number = 0 ): IterableIterator>> { const { entity } = get(this, 'params'); yield ({ datasets: get(this, 'getDatasetsTask').perform('set', offset) })[entity]; }); /** * The max possible entities that can currently be requested up to this.count * @type {ComputedProperty} * @memberof BrowserViewport */ nextCountSize: ComputedProperty = computed('entities.length', function(this: BrowserViewport): number { const { entities: { length }, total, count } = getProperties(this, ['entities', 'total', 'count']); return Math.min(count, total - length); }); /** * The total number of items left that can be fetched * @type {ComputedProperty} * @memberof BrowserViewport */ remainingEntityCount: ComputedProperty = computed('entities.length', function(this: BrowserViewport): number { const { entities: { length }, total } = getProperties(this, ['entities', 'total']); return total >= length ? total - length : 0; }); /** * Requests the next number of entities for the segment or data source */ @action getNextEntities(this: BrowserViewport) { const { remainingEntityCount, start } = getProperties(this, ['remainingEntityCount', 'start']); // Invoke task only if there are more entities that may be available if (remainingEntityCount) { get(this, 'getDatasetsTask').perform('push', start); } } }