diff --git a/spec/unit/embedded.spec.ts b/spec/unit/embedded.spec.ts index d3b3e12359d..dc9952465a6 100644 --- a/spec/unit/embedded.spec.ts +++ b/spec/unit/embedded.spec.ts @@ -35,7 +35,7 @@ import { import { createRoomWidgetClient, MsgType, UpdateDelayedEventAction } from "../../src/matrix"; import { MatrixClient, ClientEvent, ITurnServer as IClientTurnServer } from "../../src/client"; import { SyncState } from "../../src/sync"; -import { ICapabilities } from "../../src/embedded"; +import { ICapabilities, RoomWidgetClient } from "../../src/embedded"; import { MatrixEvent } from "../../src/models/event"; import { ToDeviceBatch } from "../../src/models/ToDeviceMessage"; import { DeviceInfo } from "../../src/crypto/deviceinfo"; @@ -493,6 +493,23 @@ describe("RoomWidgetClient", () => { ["@bob:example.org"]: { ["bobDesktop"]: { hello: "bob!" } }, }; + const encryptedContentMap = new Map>([ + ["@alice:example.org", new Map([["aliceMobile", { hello: "alice!" }]])], + ["@bob:example.org", new Map([["bobDesktop", { hello: "bob!" }]])], + ]); + + it("sends unencrypted (sendToDeviceViaWidgetApi)", async () => { + await makeClient({ sendToDevice: ["org.example.foo"] }); + expect(widgetApi.requestCapabilityToSendToDevice).toHaveBeenCalledWith("org.example.foo"); + + await (client as RoomWidgetClient).sendToDeviceViaWidgetApi( + "org.example.foo", + false, + unencryptedContentMap, + ); + expect(widgetApi.sendToDevice).toHaveBeenCalledWith("org.example.foo", false, expectedRequestData); + }); + it("sends unencrypted (sendToDevice)", async () => { await makeClient({ sendToDevice: ["org.example.foo"] }); expect(widgetApi.requestCapabilityToSendToDevice).toHaveBeenCalledWith("org.example.foo"); @@ -534,6 +551,17 @@ describe("RoomWidgetClient", () => { }); }); + it("sends encrypted (sendToDeviceViaWidgetApi)", async () => { + await makeClient({ sendToDevice: ["org.example.foo"] }); + expect(widgetApi.requestCapabilityToSendToDevice).toHaveBeenCalledWith("org.example.foo"); + + await (client as RoomWidgetClient).sendToDeviceViaWidgetApi("org.example.foo", true, encryptedContentMap); + expect(widgetApi.sendToDevice).toHaveBeenCalledWith("org.example.foo", true, { + "@alice:example.org": { aliceMobile: { hello: "alice!" } }, + "@bob:example.org": { bobDesktop: { hello: "bob!" } }, + }); + }); + it.each([ { encrypted: false, title: "unencrypted" }, { encrypted: true, title: "encrypted" }, diff --git a/src/embedded.ts b/src/embedded.ts index 6e5efc67b28..a2be6209cdd 100644 --- a/src/embedded.ts +++ b/src/embedded.ts @@ -420,6 +420,27 @@ export class RoomWidgetClient extends MatrixClient { await this.widgetApi.sendToDevice((payload as { type: string }).type, true, recursiveMapToObject(contentMap)); } + /** + * Send an event to a specific list of devices via the widget API. Optionally encrypts the event. + * + * If you are using a full MatrixClient you would be calling {@link MatrixClient.getCrypto().encryptToDeviceMessages()} followed + * by {@link MatrixClient.queueToDevice}. + * + * However, this is combined into a single step when running as an embedded widget client. So, we expose this method for those + * that need it. + * + * @param eventType - Type of the event to send. + * @param encrypted - Whether the event should be encrypted. + * @param contentMap - The content to send. Map from user_id to device_id to content object. + */ + public async sendToDeviceViaWidgetApi( + eventType: string, + encrypted: boolean, + contentMap: SendToDeviceContentMap, + ): Promise { + await this.widgetApi.sendToDevice(eventType, encrypted, recursiveMapToObject(contentMap)); + } + // Overridden since we get TURN servers automatically over the widget API, // and this method would otherwise complain about missing an access token public async checkTurnServers(): Promise {