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.
62 lines
1.9 KiB
TypeScript
62 lines
1.9 KiB
TypeScript
import type * as tg from "generic-type-guard";
|
|
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>> {
|
|
|
|
typeChecker: Guard,
|
|
callback: (payloadData: T) => void
|
|
}
|
|
|
|
export interface IframeCallbackContribution<Key extends keyof IframeResponseEventMap> extends IframeCallback<Key> {
|
|
|
|
type: Key
|
|
}
|
|
|
|
/**
|
|
* !! be aware that the implemented attributes (addMethodsAtRoot and subObjectIdentifier) must be readonly
|
|
*
|
|
*
|
|
*/
|
|
|
|
export abstract class IframeApiContribution<T extends {
|
|
callbacks: Array<IframeCallbackContribution<keyof IframeResponseEventMap>>,
|
|
}> {
|
|
|
|
abstract callbacks: T["callbacks"]
|
|
}
|