import { Chain as ChainFunction } from './types'; import { ChainableQuery as IChainableQuery, Model, MultiQueryFunction, Query, QueryFunction, Resource } from './types'; interface ChainableQuery extends IChainableQuery { $Model?: Resource; } // Chain a query into a new one (when the query updates so does the new query) export const Chain: ChainFunction = chainConstructor; function chainConstructor(origQry: ChainableQuery, modelOrQryFn: (Resource | QueryFunction), qryFn?: QueryFunction): Query { let initialSync = false; let ResToQry: Resource; if (!qryFn) { ResToQry = origQry.$Model; qryFn = modelOrQryFn as QueryFunction; } else { ResToQry = modelOrQryFn as Resource; } // Make sure we have at least finished an initial load before generating the // query object const _qry = origQry.$promise.then(() => { initialSync = true; // We copy the array here so that we can do things like .map without auto-updating repercussions. return qryFn.call(newqry, [...origQry]); }); const newqry: Query = ResToQry.query(_qry); // Watch our results. If they change then reattempt the query origQry.$emitter.on('update', (newRes: any) => { // Only do this if we have initially synced if (initialSync) { // We copy the array here so that we can do things like .map without auto-updating repercussions. newqry.replace(qryFn.call(newqry, [...newRes])); } }); return newqry; } Chain.all = all; function all(origQueries: Array>, Model: Resource, qryFn: MultiQueryFunction) { let newqry: Query; const proms = []; let initialSync: any; let allqries: any[]; for (let ii = 0; ii < origQueries.length; ii++) { const origQry = origQueries[ii]; proms.push(origQry.$promise); origQry.$emitter.on('update', (newRes: any) => { if (initialSync) { allqries[ii] = [...newRes]; newqry.replace(qryFn.call(newqry, allqries)); } }); } newqry = Model.query(Promise.all(proms).then(res => { initialSync = true; allqries = []; for (const result of res) { allqries.push([...result]); } return qryFn.call(newqry, allqries); })); return newqry; }