import API from 'api' import type { Config } from 'config.types' import { initializeApp } from 'domains/app/actions' import { initializeConfig } from 'domains/config/actions' import { setConfig } from 'domains/config/slice' import { createStore } from 'domains/store' import { initializeVisibility } from 'domains/visibility/actions' import type ExternalApi from 'lib/external-api' import Events, { Events as EventsType } from 'minivents' import { render } from 'preact' import ChatApp from 'ui/components/chat-app' import SeamlyCore from 'ui/components/core/seamly-core' export default class Engine { config: Omit namespace: string parentElement: HTMLElement | Element externalApi: ExternalApi functions: object eventBus: EventsType api: API constructor(config: Required, externalApi: ExternalApi) { const { namespace = '', parentElement, ...restConfig } = config this.config = { ...restConfig, namespace, } this.namespace = namespace this.parentElement = parentElement this.externalApi = externalApi this.api = new API({ layoutMode: config.layoutMode, namespace: config.namespace, config: config.api, context: config.context, }) // Following are ignored because the types of minivents do not match the docs. // @ts-ignore this.eventBus = new Events() this.functions = {} this.registerFunctions({ on: this.eventBus.on, off: this.eventBus.off, }) // @ts-ignore this.eventBus.on('function.register', (functionName, fn) => this.registerFunction(functionName, fn), ) // @ts-ignore this.eventBus.on('function.unregister', (functionName, fn) => this.unregisterFunction(functionName, fn), ) } async render() { const { view: View, ...restComponents } = this.config.customComponents || {} const renderConfig: Config = { ...this.config, parentElement: this.parentElement, customComponents: Object.keys(restComponents).length ? restComponents : undefined, } const store = createStore({ api: this.api, eventBus: this.eventBus, config: renderConfig, initialState: undefined, }) store.dispatch(setConfig(renderConfig)) await store.dispatch(initializeConfig()) await store.dispatch(initializeApp()) store.dispatch(initializeVisibility()) if (View) { render( , this.parentElement, ) } else { render( , this.parentElement, ) } } destroy() { render(null, this.parentElement) this.eventBus.off() this.api.disconnect() this.functions = {} } registerFunctions(functionMap) { Object.entries(functionMap).forEach(([functionName, fn]) => { this.registerFunction(functionName, fn) }) } registerFunction(functionName, fn) { this.functions[functionName] = this.functions[functionName] || [] this.functions[functionName].push(fn) this.externalApi.handleActions() } unregisterFunction(functionName, fn) { const functions = this.functions[functionName] if (functions && functions.length) { this.functions[functionName] = functions.filter((fn2) => fn2 !== fn) } } execFunction(functionName, ...args) { const functions = this.functions[functionName] if (!functions || !functions.length) { return false } functions.forEach((fn) => { try { fn(...args) } catch (e) { console.error(`Action(${functionName}) failed: `, e) } }) return true } }