UNPKG

decentraland-ui

Version:

Decentraland's UI components and styles

151 lines (143 loc) 4.43 kB
/* eslint-disable @typescript-eslint/no-explicit-any */ import mitt, { Emitter } from 'mitt' import future, { IFuture } from 'fp-future' import { EmoteEvents, IPreviewController, PreviewMessagePayload, PreviewMessageType, sendMessage } from '@dcl/schemas/dist/dapps/preview' import { Metrics } from '@dcl/schemas/dist/platform/item/metrics' const promises = new Map<string, IFuture<any>>() const emoteEvents = new Map<MessageEventSource, Emitter<EmoteEvents>>() window.onmessage = function handleMessage(event: MessageEvent) { if (event.data && event.data.type) { switch (event.data.type as PreviewMessageType) { case PreviewMessageType.CONTROLLER_RESPONSE: { const payload = event.data .payload as PreviewMessagePayload<PreviewMessageType.CONTROLLER_RESPONSE> const { id } = payload const promise = promises.get(id) if (promise) { if (payload.ok) { promise.resolve(payload.result) } else if (payload.ok === false) { promise.reject(new Error(payload.error)) } } break } case PreviewMessageType.EMOTE_EVENT: { const payload = event.data .payload as PreviewMessagePayload<PreviewMessageType.EMOTE_EVENT> const { type, payload: eventPayload } = payload const events = emoteEvents.get(event.source) if (events && type) { events.emit(type, eventPayload) } break } default: // nothing to do, invalid message } } } let nonce = 0 function createSendRequest(id: string) { return function sendRequest<T>( namespace: 'scene' | 'emote', method: | 'getScreenshot' | 'getMetrics' | 'getLength' | 'changeZoom' | 'panCamera' | 'changeCameraPosition' | 'cleanup' | 'isPlaying' | 'goTo' | 'play' | 'pause' | 'stop' | 'enableSound' | 'disableSound' | 'hasSound' | 'setUsername', params: any[] ) { const iframe = document.getElementById(id) as HTMLIFrameElement const messageId = id + '-' + nonce const promise = future<T>() promises.set(messageId, promise) const type = PreviewMessageType.CONTROLLER_REQUEST const message = { id: messageId, namespace, method, params } sendMessage(iframe.contentWindow, type, message) nonce++ return promise } } export function createController(id: string): IPreviewController { const iframe = document.getElementById(id) as HTMLIFrameElement if (!iframe) { throw new Error(`Could not find an iframe with id="${id}"`) } const events = emoteEvents.get(iframe.contentWindow) ?? mitt() emoteEvents.set(iframe.contentWindow, events) const sendRequest = createSendRequest(id) return { scene: { getScreenshot(width: number, height: number) { return sendRequest<string>('scene', 'getScreenshot', [width, height]) }, getMetrics() { return sendRequest<Metrics>('scene', 'getMetrics', []) }, changeZoom: function (zoom) { return sendRequest('scene', 'changeZoom', [zoom]) }, panCamera: function (offset) { return sendRequest('scene', 'panCamera', [offset]) }, changeCameraPosition: function (position) { return sendRequest('scene', 'changeCameraPosition', [position]) }, setUsername(username: string) { return sendRequest<void>('scene', 'setUsername', [username]) }, cleanup() { return sendRequest<void>('scene', 'cleanup', []) } }, emote: { getLength() { return sendRequest<number>('emote', 'getLength', []) }, isPlaying() { return sendRequest<boolean>('emote', 'isPlaying', []) }, goTo(seconds: number) { return sendRequest<void>('emote', 'goTo', [seconds]) }, play() { return sendRequest<void>('emote', 'play', []) }, pause() { return sendRequest<void>('emote', 'pause', []) }, stop() { return sendRequest<void>('emote', 'stop', []) }, enableSound() { return sendRequest<void>('emote', 'enableSound', []) }, disableSound() { return sendRequest<void>('emote', 'disableSound', []) }, hasSound() { return sendRequest<boolean>('emote', 'hasSound', []) }, events } } }