Add new "query/answer" utility functions for the scripting API
So far, the scripting API was using events to communicate between WA and the iFrame. But often, the scripting API might actually want to "ask" WA a question and wait for an answer. We dealt with this by using 2 unrelated events (in a mostly painful way). This commit adds a "queryWorkadventure" utility function in the iFrame API that allows us to send a query, and to wait for an answer. The query and answer events have a unique ID to be sure the answer matches the correct query. On the WA side, a new `IframeListener.registerAnswerer` method can be used to register a possible answer.
This commit is contained in:
parent
d29c0cc99f
commit
5b4a72ea1f
7 changed files with 248 additions and 113 deletions
|
@ -1,9 +1,40 @@
|
|||
import type * as tg from "generic-type-guard";
|
||||
import type { IframeEvent, IframeEventMap, IframeResponseEventMap } from '../Events/IframeEvent';
|
||||
import type {
|
||||
IframeEvent,
|
||||
IframeEventMap, IframeQuery,
|
||||
IframeQueryMap,
|
||||
IframeResponseEventMap
|
||||
} from '../Events/IframeEvent';
|
||||
import type {IframeQueryWrapper} from "../Events/IframeEvent";
|
||||
|
||||
export function sendToWorkadventure(content: IframeEvent<keyof IframeEventMap>) {
|
||||
window.parent.postMessage(content, "*")
|
||||
}
|
||||
|
||||
let queryNumber = 0;
|
||||
|
||||
export const answerPromises = new Map<number, {
|
||||
resolve: (value: (IframeQueryMap[keyof IframeQueryMap]['answer'] | PromiseLike<IframeQueryMap[keyof IframeQueryMap]['answer']>)) => void,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
reject: (reason?: any) => void
|
||||
}>();
|
||||
|
||||
export function queryWorkadventure<T extends keyof IframeQueryMap>(content: IframeQuery<T>): Promise<IframeQueryMap[T]['answer']> {
|
||||
return new Promise<IframeQueryMap[T]['answer']>((resolve, reject) => {
|
||||
window.parent.postMessage({
|
||||
id: queryNumber,
|
||||
query: content
|
||||
} as IframeQueryWrapper<T>, "*");
|
||||
|
||||
answerPromises.set(queryNumber, {
|
||||
resolve,
|
||||
reject
|
||||
});
|
||||
|
||||
queryNumber++;
|
||||
});
|
||||
}
|
||||
|
||||
type GuardedType<Guard extends tg.TypeGuard<unknown>> = Guard extends tg.TypeGuard<infer T> ? T : never
|
||||
|
||||
export interface IframeCallback<Key extends keyof IframeResponseEventMap, T = IframeResponseEventMap[Key], Guard = tg.TypeGuard<T>> {
|
||||
|
|
|
@ -4,7 +4,7 @@ import { isDataLayerEvent } from "../Events/DataLayerEvent";
|
|||
import { EnterLeaveEvent, isEnterLeaveEvent } from "../Events/EnterLeaveEvent";
|
||||
import { isGameStateEvent } from "../Events/GameStateEvent";
|
||||
|
||||
import { IframeApiContribution, sendToWorkadventure } from "./IframeApiContribution";
|
||||
import {IframeApiContribution, queryWorkadventure, sendToWorkadventure} from "./IframeApiContribution";
|
||||
import { apiCallback } from "./registeredCallbacks";
|
||||
|
||||
import type { ITiledMap } from "../../Phaser/Map/ITiledMap";
|
||||
|
@ -16,7 +16,7 @@ const leaveStreams: Map<string, Subject<EnterLeaveEvent>> = new Map<string, Subj
|
|||
const dataLayerResolver = new Subject<DataLayerEvent>();
|
||||
const stateResolvers = new Subject<GameStateEvent>();
|
||||
|
||||
let immutableData: GameStateEvent;
|
||||
let immutableDataPromise: Promise<GameStateEvent> | undefined = undefined;
|
||||
|
||||
interface Room {
|
||||
id: string;
|
||||
|
@ -39,14 +39,10 @@ interface TileDescriptor {
|
|||
}
|
||||
|
||||
function getGameState(): Promise<GameStateEvent> {
|
||||
if (immutableData) {
|
||||
return Promise.resolve(immutableData);
|
||||
} else {
|
||||
return new Promise<GameStateEvent>((resolver, thrower) => {
|
||||
stateResolvers.subscribe(resolver);
|
||||
sendToWorkadventure({ type: "getState", data: null });
|
||||
});
|
||||
if (immutableDataPromise === undefined) {
|
||||
immutableDataPromise = queryWorkadventure({ type: "getState", data: undefined });
|
||||
}
|
||||
return immutableDataPromise;
|
||||
}
|
||||
|
||||
function getDataLayer(): Promise<DataLayerEvent> {
|
||||
|
@ -72,13 +68,6 @@ export class WorkadventureRoomCommands extends IframeApiContribution<Workadventu
|
|||
leaveStreams.get(payloadData.name)?.next();
|
||||
},
|
||||
}),
|
||||
apiCallback({
|
||||
type: "gameState",
|
||||
typeChecker: isGameStateEvent,
|
||||
callback: (payloadData) => {
|
||||
stateResolvers.next(payloadData);
|
||||
},
|
||||
}),
|
||||
apiCallback({
|
||||
type: "dataLayer",
|
||||
typeChecker: isDataLayerEvent,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue