diff options
Diffstat (limited to 'includes/external/matrix/node_modules/matrix-widget-api/src/WidgetApi.ts')
-rw-r--r-- | includes/external/matrix/node_modules/matrix-widget-api/src/WidgetApi.ts | 717 |
1 files changed, 0 insertions, 717 deletions
diff --git a/includes/external/matrix/node_modules/matrix-widget-api/src/WidgetApi.ts b/includes/external/matrix/node_modules/matrix-widget-api/src/WidgetApi.ts deleted file mode 100644 index a74187a..0000000 --- a/includes/external/matrix/node_modules/matrix-widget-api/src/WidgetApi.ts +++ /dev/null @@ -1,717 +0,0 @@ -/* - * Copyright 2020 - 2021 The Matrix.org Foundation C.I.C. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { EventEmitter } from "events"; -import { Capability } from "./interfaces/Capabilities"; -import { IWidgetApiRequest, IWidgetApiRequestEmptyData } from "./interfaces/IWidgetApiRequest"; -import { IWidgetApiAcknowledgeResponseData } from "./interfaces/IWidgetApiResponse"; -import { WidgetApiDirection } from "./interfaces/WidgetApiDirection"; -import { - ISupportedVersionsActionRequest, - ISupportedVersionsActionResponseData, -} from "./interfaces/SupportedVersionsAction"; -import { ApiVersion, CurrentApiVersions, UnstableApiVersion } from "./interfaces/ApiVersion"; -import { - ICapabilitiesActionRequest, - ICapabilitiesActionResponseData, - INotifyCapabilitiesActionRequest, - IRenegotiateCapabilitiesRequestData, -} from "./interfaces/CapabilitiesAction"; -import { ITransport } from "./transport/ITransport"; -import { PostmessageTransport } from "./transport/PostmessageTransport"; -import { WidgetApiFromWidgetAction, WidgetApiToWidgetAction } from "./interfaces/WidgetApiAction"; -import { IWidgetApiErrorResponseData } from "./interfaces/IWidgetApiErrorResponse"; -import { IStickerActionRequestData } from "./interfaces/StickerAction"; -import { IStickyActionRequestData, IStickyActionResponseData } from "./interfaces/StickyAction"; -import { - IGetOpenIDActionRequestData, - IGetOpenIDActionResponse, - IOpenIDCredentials, - OpenIDRequestState, -} from "./interfaces/GetOpenIDAction"; -import { IOpenIDCredentialsActionRequest } from "./interfaces/OpenIDCredentialsAction"; -import { MatrixWidgetType, WidgetType } from "./interfaces/WidgetType"; -import { - BuiltInModalButtonID, - IModalWidgetCreateData, - IModalWidgetOpenRequestData, - IModalWidgetOpenRequestDataButton, - IModalWidgetReturnData, - ModalButtonID, -} from "./interfaces/ModalWidgetActions"; -import { ISetModalButtonEnabledActionRequestData } from "./interfaces/SetModalButtonEnabledAction"; -import { ISendEventFromWidgetRequestData, ISendEventFromWidgetResponseData } from "./interfaces/SendEventAction"; -import { - ISendToDeviceFromWidgetRequestData, - ISendToDeviceFromWidgetResponseData, -} from "./interfaces/SendToDeviceAction"; -import { EventDirection, WidgetEventCapability } from "./models/WidgetEventCapability"; -import { INavigateActionRequestData } from "./interfaces/NavigateAction"; -import { IReadEventFromWidgetRequestData, IReadEventFromWidgetResponseData } from "./interfaces/ReadEventAction"; -import { IRoomEvent } from "./interfaces/IRoomEvent"; -import { ITurnServer, IUpdateTurnServersRequest } from "./interfaces/TurnServerActions"; -import { Symbols } from "./Symbols"; -import { - IReadRelationsFromWidgetRequestData, - IReadRelationsFromWidgetResponseData, -} from "./interfaces/ReadRelationsAction"; -import { - IUserDirectorySearchFromWidgetRequestData, - IUserDirectorySearchFromWidgetResponseData, -} from "./interfaces/UserDirectorySearchAction"; - -/** - * API handler for widgets. This raises events for each action - * received as `action:${action}` (eg: "action:screenshot"). - * Default handling can be prevented by using preventDefault() - * on the raised event. The default handling varies for each - * action: ones which the SDK can handle safely are acknowledged - * appropriately and ones which are unhandled (custom or require - * the widget to do something) are rejected with an error. - * - * Events which are preventDefault()ed must reply using the - * transport. The events raised will have a detail of an - * IWidgetApiRequest interface. - * - * When the WidgetApi is ready to start sending requests, it will - * raise a "ready" CustomEvent. After the ready event fires, actions - * can be sent and the transport will be ready. - */ -export class WidgetApi extends EventEmitter { - public readonly transport: ITransport; - - private capabilitiesFinished = false; - private supportsMSC2974Renegotiate = false; - private requestedCapabilities: Capability[] = []; - private approvedCapabilities?: Capability[]; - private cachedClientVersions?: ApiVersion[]; - private turnServerWatchers = 0; - - /** - * Creates a new API handler for the given widget. - * @param {string} widgetId The widget ID to listen for. If not supplied then - * the API will use the widget ID from the first valid request it receives. - * @param {string} clientOrigin The origin of the client, or null if not known. - */ - public constructor(widgetId: string | null = null, private clientOrigin: string | null = null) { - super(); - if (!window.parent) { - throw new Error("No parent window. This widget doesn't appear to be embedded properly."); - } - this.transport = new PostmessageTransport( - WidgetApiDirection.FromWidget, - widgetId, - window.parent, - window, - ); - this.transport.targetOrigin = clientOrigin; - this.transport.on("message", this.handleMessage.bind(this)); - } - - /** - * Determines if the widget was granted a particular capability. Note that on - * clients where the capabilities are not fed back to the widget this function - * will rely on requested capabilities instead. - * @param {Capability} capability The capability to check for approval of. - * @returns {boolean} True if the widget has approval for the given capability. - */ - public hasCapability(capability: Capability): boolean { - if (Array.isArray(this.approvedCapabilities)) { - return this.approvedCapabilities.includes(capability); - } - return this.requestedCapabilities.includes(capability); - } - - /** - * Request a capability from the client. It is not guaranteed to be allowed, - * but will be asked for. - * @param {Capability} capability The capability to request. - * @throws Throws if the capabilities negotiation has already started and the - * widget is unable to request additional capabilities. - */ - public requestCapability(capability: Capability) { - if (this.capabilitiesFinished && !this.supportsMSC2974Renegotiate) { - throw new Error("Capabilities have already been negotiated"); - } - - this.requestedCapabilities.push(capability); - } - - /** - * Request capabilities from the client. They are not guaranteed to be allowed, - * but will be asked for if the negotiation has not already happened. - * @param {Capability[]} capabilities The capabilities to request. - * @throws Throws if the capabilities negotiation has already started. - */ - public requestCapabilities(capabilities: Capability[]) { - capabilities.forEach(cap => this.requestCapability(cap)); - } - - /** - * Requests the capability to interact with rooms other than the user's currently - * viewed room. Applies to event receiving and sending. - * @param {string | Symbols.AnyRoom} roomId The room ID, or `Symbols.AnyRoom` to - * denote all known rooms. - */ - public requestCapabilityForRoomTimeline(roomId: string | Symbols.AnyRoom) { - this.requestCapability(`org.matrix.msc2762.timeline:${roomId}`); - } - - /** - * Requests the capability to send a given state event with optional explicit - * state key. It is not guaranteed to be allowed, but will be asked for if the - * negotiation has not already happened. - * @param {string} eventType The state event type to ask for. - * @param {string} stateKey If specified, the specific state key to request. - * Otherwise all state keys will be requested. - */ - public requestCapabilityToSendState(eventType: string, stateKey?: string) { - this.requestCapability(WidgetEventCapability.forStateEvent(EventDirection.Send, eventType, stateKey).raw); - } - - /** - * Requests the capability to receive a given state event with optional explicit - * state key. It is not guaranteed to be allowed, but will be asked for if the - * negotiation has not already happened. - * @param {string} eventType The state event type to ask for. - * @param {string} stateKey If specified, the specific state key to request. - * Otherwise all state keys will be requested. - */ - public requestCapabilityToReceiveState(eventType: string, stateKey?: string) { - this.requestCapability(WidgetEventCapability.forStateEvent(EventDirection.Receive, eventType, stateKey).raw); - } - - /** - * Requests the capability to send a given to-device event. It is not - * guaranteed to be allowed, but will be asked for if the negotiation has - * not already happened. - * @param {string} eventType The room event type to ask for. - */ - public requestCapabilityToSendToDevice(eventType: string) { - this.requestCapability(WidgetEventCapability.forToDeviceEvent(EventDirection.Send, eventType).raw); - } - - /** - * Requests the capability to receive a given to-device event. It is not - * guaranteed to be allowed, but will be asked for if the negotiation has - * not already happened. - * @param {string} eventType The room event type to ask for. - */ - public requestCapabilityToReceiveToDevice(eventType: string) { - this.requestCapability(WidgetEventCapability.forToDeviceEvent(EventDirection.Receive, eventType).raw); - } - - /** - * Requests the capability to send a given room event. It is not guaranteed to be - * allowed, but will be asked for if the negotiation has not already happened. - * @param {string} eventType The room event type to ask for. - */ - public requestCapabilityToSendEvent(eventType: string) { - this.requestCapability(WidgetEventCapability.forRoomEvent(EventDirection.Send, eventType).raw); - } - - /** - * Requests the capability to receive a given room event. It is not guaranteed to be - * allowed, but will be asked for if the negotiation has not already happened. - * @param {string} eventType The room event type to ask for. - */ - public requestCapabilityToReceiveEvent(eventType: string) { - this.requestCapability(WidgetEventCapability.forRoomEvent(EventDirection.Receive, eventType).raw); - } - - /** - * Requests the capability to send a given message event with optional explicit - * `msgtype`. It is not guaranteed to be allowed, but will be asked for if the - * negotiation has not already happened. - * @param {string} msgtype If specified, the specific msgtype to request. - * Otherwise all message types will be requested. - */ - public requestCapabilityToSendMessage(msgtype?: string) { - this.requestCapability(WidgetEventCapability.forRoomMessageEvent(EventDirection.Send, msgtype).raw); - } - - /** - * Requests the capability to receive a given message event with optional explicit - * `msgtype`. It is not guaranteed to be allowed, but will be asked for if the - * negotiation has not already happened. - * @param {string} msgtype If specified, the specific msgtype to request. - * Otherwise all message types will be requested. - */ - public requestCapabilityToReceiveMessage(msgtype?: string) { - this.requestCapability(WidgetEventCapability.forRoomMessageEvent(EventDirection.Receive, msgtype).raw); - } - - /** - * Requests an OpenID Connect token from the client for the currently logged in - * user. This token can be validated server-side with the federation API. Note - * that the widget is responsible for validating the token and caching any results - * it needs. - * @returns {Promise<IOpenIDCredentials>} Resolves to a token for verification. - * @throws Throws if the user rejected the request or the request failed. - */ - public requestOpenIDConnectToken(): Promise<IOpenIDCredentials> { - return new Promise<IOpenIDCredentials>((resolve, reject) => { - this.transport.sendComplete<IGetOpenIDActionRequestData, IGetOpenIDActionResponse>( - WidgetApiFromWidgetAction.GetOpenIDCredentials, {}, - ).then(response => { - const rdata = response.response; - if (rdata.state === OpenIDRequestState.Allowed) { - resolve(rdata); - } else if (rdata.state === OpenIDRequestState.Blocked) { - reject(new Error("User declined to verify their identity")); - } else if (rdata.state === OpenIDRequestState.PendingUserConfirmation) { - const handlerFn = (ev: CustomEvent<IOpenIDCredentialsActionRequest>) => { - ev.preventDefault(); - const request = ev.detail; - if (request.data.original_request_id !== response.requestId) return; - if (request.data.state === OpenIDRequestState.Allowed) { - resolve(request.data); - this.transport.reply(request, <IWidgetApiRequestEmptyData>{}); // ack - } else if (request.data.state === OpenIDRequestState.Blocked) { - reject(new Error("User declined to verify their identity")); - this.transport.reply(request, <IWidgetApiRequestEmptyData>{}); // ack - } else { - reject(new Error("Invalid state on reply: " + rdata.state)); - this.transport.reply(request, <IWidgetApiErrorResponseData>{ - error: { - message: "Invalid state", - }, - }); - } - this.off(`action:${WidgetApiToWidgetAction.OpenIDCredentials}`, handlerFn); - }; - this.on(`action:${WidgetApiToWidgetAction.OpenIDCredentials}`, handlerFn); - } else { - reject(new Error("Invalid state: " + rdata.state)); - } - }).catch(reject); - }); - } - - /** - * Asks the client for additional capabilities. Capabilities can be queued for this - * request with the requestCapability() functions. - * @returns {Promise<void>} Resolves when complete. Note that the promise resolves when - * the capabilities request has gone through, not when the capabilities are approved/denied. - * Use the WidgetApiToWidgetAction.NotifyCapabilities action to detect changes. - */ - public updateRequestedCapabilities(): Promise<void> { - return this.transport.send(WidgetApiFromWidgetAction.MSC2974RenegotiateCapabilities, - <IRenegotiateCapabilitiesRequestData>{ - capabilities: this.requestedCapabilities, - }).then(); - } - - /** - * Tell the client that the content has been loaded. - * @returns {Promise} Resolves when the client acknowledges the request. - */ - public sendContentLoaded(): Promise<void> { - return this.transport.send(WidgetApiFromWidgetAction.ContentLoaded, <IWidgetApiRequestEmptyData>{}).then(); - } - - /** - * Sends a sticker to the client. - * @param {IStickerActionRequestData} sticker The sticker to send. - * @returns {Promise} Resolves when the client acknowledges the request. - */ - public sendSticker(sticker: IStickerActionRequestData): Promise<void> { - return this.transport.send(WidgetApiFromWidgetAction.SendSticker, sticker).then(); - } - - /** - * Asks the client to set the always-on-screen status for this widget. - * @param {boolean} value The new state to request. - * @returns {Promise<boolean>} Resolve with true if the client was able to fulfill - * the request, resolves to false otherwise. Rejects if an error occurred. - */ - public setAlwaysOnScreen(value: boolean): Promise<boolean> { - return this.transport.send<IStickyActionRequestData, IStickyActionResponseData>( - WidgetApiFromWidgetAction.UpdateAlwaysOnScreen, {value}, - ).then(res => res.success); - } - - /** - * Opens a modal widget. - * @param {string} url The URL to the modal widget. - * @param {string} name The name of the widget. - * @param {IModalWidgetOpenRequestDataButton[]} buttons The buttons to have on the widget. - * @param {IModalWidgetCreateData} data Data to supply to the modal widget. - * @param {WidgetType} type The type of modal widget. - * @returns {Promise<void>} Resolves when the modal widget has been opened. - */ - public openModalWidget( - url: string, - name: string, - buttons: IModalWidgetOpenRequestDataButton[] = [], - data: IModalWidgetCreateData = {}, - type: WidgetType = MatrixWidgetType.Custom, - ): Promise<void> { - return this.transport.send<IModalWidgetOpenRequestData>( - WidgetApiFromWidgetAction.OpenModalWidget, { type, url, name, buttons, data }, - ).then(); - } - - /** - * Closes the modal widget. The widget's session will be terminated shortly after. - * @param {IModalWidgetReturnData} data Optional data to close the modal widget with. - * @returns {Promise<void>} Resolves when complete. - */ - public closeModalWidget(data: IModalWidgetReturnData = {}): Promise<void> { - return this.transport.send<IModalWidgetReturnData>(WidgetApiFromWidgetAction.CloseModalWidget, data).then(); - } - - public sendRoomEvent( - eventType: string, - content: unknown, - roomId?: string, - ): Promise<ISendEventFromWidgetResponseData> { - return this.transport.send<ISendEventFromWidgetRequestData, ISendEventFromWidgetResponseData>( - WidgetApiFromWidgetAction.SendEvent, - {type: eventType, content, room_id: roomId}, - ); - } - - public sendStateEvent( - eventType: string, - stateKey: string, - content: unknown, - roomId?: string, - ): Promise<ISendEventFromWidgetResponseData> { - return this.transport.send<ISendEventFromWidgetRequestData, ISendEventFromWidgetResponseData>( - WidgetApiFromWidgetAction.SendEvent, - {type: eventType, content, state_key: stateKey, room_id: roomId}, - ); - } - - /** - * Sends a to-device event. - * @param {string} eventType The type of events being sent. - * @param {boolean} encrypted Whether to encrypt the message contents. - * @param {Object} contentMap A map from user IDs to device IDs to message contents. - * @returns {Promise<ISendToDeviceFromWidgetResponseData>} Resolves when complete. - */ - public sendToDevice( - eventType: string, - encrypted: boolean, - contentMap: { [userId: string]: { [deviceId: string]: object } }, - ): Promise<ISendToDeviceFromWidgetResponseData> { - return this.transport.send<ISendToDeviceFromWidgetRequestData, ISendToDeviceFromWidgetResponseData>( - WidgetApiFromWidgetAction.SendToDevice, - {type: eventType, encrypted, messages: contentMap}, - ); - } - - public readRoomEvents( - eventType: string, - limit?: number, - msgtype?: string, - roomIds?: (string | Symbols.AnyRoom)[], - ): Promise<IRoomEvent[]> { - const data: IReadEventFromWidgetRequestData = {type: eventType, msgtype: msgtype}; - if (limit !== undefined) { - data.limit = limit; - } - if (roomIds) { - if (roomIds.includes(Symbols.AnyRoom)) { - data.room_ids = Symbols.AnyRoom; - } else { - data.room_ids = roomIds; - } - } - return this.transport.send<IReadEventFromWidgetRequestData, IReadEventFromWidgetResponseData>( - WidgetApiFromWidgetAction.MSC2876ReadEvents, - data, - ).then(r => r.events); - } - - /** - * Reads all related events given a known eventId. - * @param eventId The id of the parent event to be read. - * @param roomId The room to look within. When undefined, the user's currently - * viewed room. - * @param relationType The relationship type of child events to search for. - * When undefined, all relations are returned. - * @param eventType The event type of child events to search for. When undefined, - * all related events are returned. - * @param limit The maximum number of events to retrieve per room. If not - * supplied, the server will apply a default limit. - * @param from The pagination token to start returning results from, as - * received from a previous call. If not supplied, results start at the most - * recent topological event known to the server. - * @param to The pagination token to stop returning results at. If not - * supplied, results continue up to limit or until there are no more events. - * @param direction The direction to search for according to MSC3715. - * @returns Resolves to the room relations. - */ - public async readEventRelations( - eventId: string, - roomId?: string, - relationType?: string, - eventType?: string, - limit?: number, - from?: string, - to?: string, - direction?: 'f' | 'b', - ): Promise<IReadRelationsFromWidgetResponseData> { - const versions = await this.getClientVersions(); - if (!versions.includes(UnstableApiVersion.MSC3869)) { - throw new Error("The read_relations action is not supported by the client.") - } - - const data: IReadRelationsFromWidgetRequestData = { - event_id: eventId, - rel_type: relationType, - event_type: eventType, - room_id: roomId, - to, - from, - limit, - direction, - }; - - return this.transport.send<IReadRelationsFromWidgetRequestData, IReadRelationsFromWidgetResponseData>( - WidgetApiFromWidgetAction.MSC3869ReadRelations, - data, - ) - } - - public readStateEvents( - eventType: string, - limit?: number, - stateKey?: string, - roomIds?: (string | Symbols.AnyRoom)[], - ): Promise<IRoomEvent[]> { - const data: IReadEventFromWidgetRequestData = { - type: eventType, - state_key: stateKey === undefined ? true : stateKey, - }; - if (limit !== undefined) { - data.limit = limit; - } - if (roomIds) { - if (roomIds.includes(Symbols.AnyRoom)) { - data.room_ids = Symbols.AnyRoom; - } else { - data.room_ids = roomIds; - } - } - return this.transport.send<IReadEventFromWidgetRequestData, IReadEventFromWidgetResponseData>( - WidgetApiFromWidgetAction.MSC2876ReadEvents, - data, - ).then(r => r.events); - } - - /** - * Sets a button as disabled or enabled on the modal widget. Buttons are enabled by default. - * @param {ModalButtonID} buttonId The button ID to enable/disable. - * @param {boolean} isEnabled Whether or not the button is enabled. - * @returns {Promise<void>} Resolves when complete. - * @throws Throws if the button cannot be disabled, or the client refuses to disable the button. - */ - public setModalButtonEnabled(buttonId: ModalButtonID, isEnabled: boolean): Promise<void> { - if (buttonId === BuiltInModalButtonID.Close) { - throw new Error("The close button cannot be disabled"); - } - return this.transport.send<ISetModalButtonEnabledActionRequestData>( - WidgetApiFromWidgetAction.SetModalButtonEnabled, {button: buttonId, enabled: isEnabled}, - ).then(); - } - - /** - * Attempts to navigate the client to the given URI. This can only be called with Matrix URIs - * (currently only matrix.to, but in future a Matrix URI scheme will be defined). - * @param {string} uri The URI to navigate to. - * @returns {Promise<void>} Resolves when complete. - * @throws Throws if the URI is invalid or cannot be processed. - * @deprecated This currently relies on an unstable MSC (MSC2931). - */ - public navigateTo(uri: string): Promise<void> { - if (!uri || !uri.startsWith("https://matrix.to/#")) { - throw new Error("Invalid matrix.to URI"); - } - - return this.transport.send<INavigateActionRequestData>( - WidgetApiFromWidgetAction.MSC2931Navigate, {uri}, - ).then(); - } - - /** - * Starts watching for TURN servers, yielding an initial set of credentials as soon as possible, - * and thereafter yielding new credentials whenever the previous ones expire. - * @yields {ITurnServer} The TURN server URIs and credentials currently available to the widget. - */ - public async* getTurnServers(): AsyncGenerator<ITurnServer> { - let setTurnServer: (server: ITurnServer) => void; - - const onUpdateTurnServers = async (ev: CustomEvent<IUpdateTurnServersRequest>) => { - ev.preventDefault(); - setTurnServer(ev.detail.data); - await this.transport.reply<IWidgetApiAcknowledgeResponseData>(ev.detail, {}); - }; - - // Start listening for updates before we even start watching, to catch - // TURN data that is sent immediately - this.on(`action:${WidgetApiToWidgetAction.UpdateTurnServers}`, onUpdateTurnServers); - - // Only send the 'watch' action if we aren't already watching - if (this.turnServerWatchers === 0) { - try { - await this.transport.send<IWidgetApiRequestEmptyData>(WidgetApiFromWidgetAction.WatchTurnServers, {}); - } catch (e) { - this.off(`action:${WidgetApiToWidgetAction.UpdateTurnServers}`, onUpdateTurnServers); - throw e; - } - } - this.turnServerWatchers++; - - try { - // Watch for new data indefinitely (until this generator's return method is called) - while (true) { - yield await new Promise<ITurnServer>(resolve => setTurnServer = resolve); - } - } finally { - // The loop was broken by the caller - clean up - this.off(`action:${WidgetApiToWidgetAction.UpdateTurnServers}`, onUpdateTurnServers); - - // Since sending the 'unwatch' action will end updates for all other - // consumers, only send it if we're the only consumer remaining - this.turnServerWatchers--; - if (this.turnServerWatchers === 0) { - await this.transport.send<IWidgetApiRequestEmptyData>(WidgetApiFromWidgetAction.UnwatchTurnServers, {}); - } - } - } - - /** - * Search for users in the user directory. - * @param searchTerm The term to search for. - * @param limit The maximum number of results to return. If not supplied, the - * @returns Resolves to the search results. - */ - public async searchUserDirectory( - searchTerm: string, - limit?: number, - ): Promise<IUserDirectorySearchFromWidgetResponseData> { - const versions = await this.getClientVersions(); - if (!versions.includes(UnstableApiVersion.MSC3973)) { - throw new Error("The user_directory_search action is not supported by the client.") - } - - const data: IUserDirectorySearchFromWidgetRequestData = { - search_term: searchTerm, - limit, - }; - - return this.transport.send< - IUserDirectorySearchFromWidgetRequestData, - IUserDirectorySearchFromWidgetResponseData - >(WidgetApiFromWidgetAction.MSC3973UserDirectorySearch, data); - } - - /** - * Starts the communication channel. This should be done early to ensure - * that messages are not missed. Communication can only be stopped by the client. - */ - public start() { - this.transport.start(); - this.getClientVersions().then(v => { - if (v.includes(UnstableApiVersion.MSC2974)) { - this.supportsMSC2974Renegotiate = true; - } - }); - } - - private handleMessage(ev: CustomEvent<IWidgetApiRequest>) { - const actionEv = new CustomEvent(`action:${ev.detail.action}`, { - detail: ev.detail, - cancelable: true, - }); - this.emit(`action:${ev.detail.action}`, actionEv); - if (!actionEv.defaultPrevented) { - switch (ev.detail.action) { - case WidgetApiToWidgetAction.SupportedApiVersions: - return this.replyVersions(<ISupportedVersionsActionRequest>ev.detail); - case WidgetApiToWidgetAction.Capabilities: - return this.handleCapabilities(<ICapabilitiesActionRequest>ev.detail); - case WidgetApiToWidgetAction.UpdateVisibility: - return this.transport.reply(ev.detail, <IWidgetApiRequestEmptyData>{}); // ack to avoid error spam - case WidgetApiToWidgetAction.NotifyCapabilities: - return this.transport.reply(ev.detail, <IWidgetApiRequestEmptyData>{}); // ack to avoid error spam - default: - return this.transport.reply(ev.detail, <IWidgetApiErrorResponseData>{ - error: { - message: "Unknown or unsupported action: " + ev.detail.action, - }, - }); - } - } - } - - private replyVersions(request: ISupportedVersionsActionRequest) { - this.transport.reply<ISupportedVersionsActionResponseData>(request, { - supported_versions: CurrentApiVersions, - }); - } - - public getClientVersions(): Promise<ApiVersion[]> { - if (Array.isArray(this.cachedClientVersions)) { - return Promise.resolve(this.cachedClientVersions); - } - - return this.transport.send<IWidgetApiRequestEmptyData, ISupportedVersionsActionResponseData>( - WidgetApiFromWidgetAction.SupportedApiVersions, {}, - ).then(r => { - this.cachedClientVersions = r.supported_versions; - return r.supported_versions; - }).catch(e => { - console.warn("non-fatal error getting supported client versions: ", e); - return []; - }); - } - - private handleCapabilities(request: ICapabilitiesActionRequest) { - if (this.capabilitiesFinished) { - return this.transport.reply<IWidgetApiErrorResponseData>(request, { - error: { - message: "Capability negotiation already completed", - }, - }); - } - - // See if we can expect a capabilities notification or not - return this.getClientVersions().then(v => { - if (v.includes(UnstableApiVersion.MSC2871)) { - this.once( - `action:${WidgetApiToWidgetAction.NotifyCapabilities}`, - (ev: CustomEvent<INotifyCapabilitiesActionRequest>) => { - this.approvedCapabilities = ev.detail.data.approved; - this.emit("ready"); - }, - ); - } else { - // if we can't expect notification, we're as done as we can be - this.emit("ready"); - } - - // in either case, reply to that capabilities request - this.capabilitiesFinished = true; - return this.transport.reply<ICapabilitiesActionResponseData>(request, { - capabilities: this.requestedCapabilities, - }); - }); - } -} |