UNPKG

scrivito

Version:

Scrivito is a professional, yet easy to use SaaS Enterprise Content Management Service, built for digital agencies and medium to large businesses. It is completely maintenance-free, cost-effective, and has unprecedented performance and security.

148 lines (119 loc) 4.1 kB
import { ContextContainer, ScrivitoError, docUrl } from 'scrivito_sdk/common'; import { LoadableData } from 'scrivito_sdk/loadable'; interface CaptureList { datas: LoadableData<unknown>[]; incomplete: boolean; outdated: boolean; } const captureContextContainer = new ContextContainer<CaptureList>(); const currentCaptureList = () => captureContextContainer.current(); export function capture<T>(fn: () => T): CaptureReport<T> { const captureList = { datas: [], incomplete: false, outdated: false, }; const result = captureContextContainer.runWith(captureList, fn); return new CaptureReport(captureList, result); } export function throwNoLoadingContext() { throw new ScrivitoError( 'Content not yet loaded. ' + 'Forgot to use Scrivito.load or Scrivito.connect? ' + `See ${docUrl('content-not-yet-loaded-error')}` ); } export function isCurrentlyCapturing(): boolean { return currentCaptureList() !== undefined; } type LoadingState = 'outdated' | 'available' | 'incomplete'; export function notifyDataRequired( loadingState: LoadingState, data: LoadableData<unknown> ) { const captureList = currentCaptureList(); if (captureList) { captureList.datas.push(data); if (loadingState === 'outdated') captureList.outdated = true; else if (loadingState === 'incomplete') captureList.incomplete = true; } } export class CaptureReport<T> { result: T; private captureList: CaptureList; outdated: boolean; incomplete: boolean; constructor(captureList: CaptureList, result: T) { this.captureList = captureList; this.outdated = captureList.outdated; this.incomplete = captureList.incomplete; this.result = result; } /** creates a new report, with any 'incomplete' data treated as 'outdated' instead. */ treatIncompleteAsOutdated(): CaptureReport<T> { const newCaptureList = { datas: [...this.captureList.datas], incomplete: false, outdated: this.captureList.outdated || this.captureList.incomplete, }; return new CaptureReport(newCaptureList, this.result); } forwardToCurrent() { const currentList = currentCaptureList(); if (!currentList) { if (this.incomplete) { throwNoLoadingContext(); } return; } extendList(currentList, this.captureList); } /** get the list of data that was accessed during this capture run. * * intended for debugging */ getRequiredDatas() { return this.captureList.datas; } /** returns true iff no data is missing, doesn't care about outdated */ isAllDataLoaded() { return !this.incomplete; } /** returns true iff no data is missing or outdated */ isAllDataUpToDate() { return !this.incomplete && !this.outdated; } /** subscribes to the loading of all data that was captured, using the provided subscription. * all subscribed data is loaded automatically, and reloaded when outdated. * the subscription is automatically updated to reflect the data captured in this report, * i.e. any data that is no longer present in the capture is unsubscribed. */ subscribeLoading(subscriber: LoadingSubscriber) { const unsubscribes = this.captureList.datas.map((data) => data.subscribeLoading() ); subscriber.unsubscribe(); subscriber.storeUnsubscribe(() => { unsubscribes.forEach((unsubscribe) => unsubscribe()); }); } } /** keeps track of subscriptions to LoadableData */ export class LoadingSubscriber { private unsubscribeCallback?: () => void; /** used internally, do not call from outside 'loadable' */ storeUnsubscribe(unsubscribe: () => void) { this.unsubscribeCallback = unsubscribe; } /** unsubscribe any previously subscribed data */ unsubscribe() { if (this.unsubscribeCallback) { this.unsubscribeCallback(); } } } function extendList(target: CaptureList, source: CaptureList) { source.datas.forEach((data) => target.datas.push(data)); target.incomplete = target.incomplete || source.incomplete; target.outdated = target.outdated || source.outdated; }