UNPKG

@multisynq/react

Version:

React bindings for Multisynq

117 lines (116 loc) 5.44 kB
var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; import { Model } from '@multisynq/client'; export class ReactModel extends Model { constructor() { super(...arguments); this.__reactEvents = []; this.__views = null; } init(options) { const { trackViews } = options, opts = __rest(options, ["trackViews"]); super.init(opts); this.__reactEvents = []; // We don't want to track the joined views in every model. // the trackViews option enables this behavior if (trackViews === true) { this.__views = new Map(); } // Subscribe directly via super since we don't need to publish react-updated super.subscribe(this.sessionId, 'view-join', this.__viewJoin); super.subscribe(this.sessionId, 'view-exit', this.__viewExit); } // The view-join event provides either a `viewId` string, or if viewData was // specified, a ViewInfo<T> object __viewJoin(viewIdOrInfo) { const viewInfo = typeof viewIdOrInfo !== 'string' ? viewIdOrInfo : { viewId: viewIdOrInfo }; const viewId = viewInfo.viewId; if (this.__views) { this.__views.set(viewId, viewInfo); this.publish(this.sessionId, 'views-updated'); } this.handleViewJoin(viewId, viewInfo); } // The view-exit event provides either a `viewId` string, or if viewData was // specified, a ViewInfo<T> object __viewExit(viewIdOrInfo) { var _a, _b; const viewId = typeof viewIdOrInfo === 'string' ? viewIdOrInfo : viewIdOrInfo.viewId; const viewInfo = (_b = (_a = this.__views) === null || _a === void 0 ? void 0 : _a.get(viewId)) !== null && _b !== void 0 ? _b : { viewId }; if (this.__views) { this.__views.delete(viewId); this.publish(this.sessionId, 'views-updated'); } this.handleViewExit(viewId, viewInfo); } // Override these methods to add custom handling handleViewJoin(viewId, viewInfo) { } // eslint-disable-line @typescript-eslint/no-unused-vars handleViewExit(viewId, viewInfo) { } // eslint-disable-line @typescript-eslint/no-unused-vars // Public function to assert that users do not subscribe directly to view-join/exit events subscribe(scope, event, handler) { if (event === 'view-join' || event === 'view-exit') { throw new Error(`In @multisynq/react you cannot directly subscribe to ${event}.\n` + `Override ${event === 'view-join' ? 'handleViewJoin' : 'handleViewExit'} instead\n`); } this.__subscribe(scope, event, handler); } // This function is the one that performs the subscription logic, without performing any checks. // It's separate from the one above since we need to directly subscribe to view-join/exit events __subscribe(scope, event, handler) { this.__reactEvents.push({ scope, event }); // normalize handler into string or QFunc if (typeof handler === 'function') { const model = this; // if the handler is a method of the model, use the method name if (model[handler.name] === handler) handler = handler.name; // otherwise, we assume that the handler is a QFunc } // we call the original handler, and then publish a react-updated event // We pass as a javascript string to survive minification const reactHandler = this.createQFunc({ handler }, `(data) => { if (typeof handler === 'function') handler(data) else this[handler](data) this.publish(this.sessionId, 'react-updated') }`); super.subscribe(scope, event, reactHandler); } // Function that helps ReactModel publish a react-updated event // every time a future message is executed __future_wrapper(methodName, ...args) { const value = this[methodName]; if (typeof value === 'function') { value.apply(this, args); this.publish(this.sessionId, 'react-updated'); } else { console.error(`${methodName} is not a function of ${this}`); } } future(tOffset, method, ...args) { // non-proxy case: we schedule the call to a __future_wrapper function // that will call method, and then publish a react-updated event if (method !== undefined) { return super.future(tOffset, '__future_wrapper', method, ...args); } // We want to allow for the `this.future(tOffset).methodName(...args) syntax // To do so, we return a Proxy that invokes this function // with all the arguments for the non-proxy case return new Proxy(this, { // eslint-disable-next-line @typescript-eslint/no-unused-vars get(target, prop, _) { return (...args) => target.future(tOffset, prop, ...args); }, }); } } ReactModel.register('ReactModel');