You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-08-09 10:22:46 +03:00
Merge branch 'robertlong/group-call' into dbkr/gcmerge_oct22_3
This commit is contained in:
@@ -30,7 +30,7 @@ import {
|
|||||||
ITurnServer,
|
ITurnServer,
|
||||||
} from "matrix-widget-api";
|
} from "matrix-widget-api";
|
||||||
|
|
||||||
import { createRoomWidgetClient } from "../../src/matrix";
|
import { createRoomWidgetClient, MsgType } from "../../src/matrix";
|
||||||
import { MatrixClient, ClientEvent, ITurnServer as IClientTurnServer } from "../../src/client";
|
import { MatrixClient, ClientEvent, ITurnServer as IClientTurnServer } from "../../src/client";
|
||||||
import { SyncState } from "../../src/sync";
|
import { SyncState } from "../../src/sync";
|
||||||
import { ICapabilities } from "../../src/embedded";
|
import { ICapabilities } from "../../src/embedded";
|
||||||
@@ -43,10 +43,15 @@ class MockWidgetApi extends EventEmitter {
|
|||||||
public requestCapability = jest.fn();
|
public requestCapability = jest.fn();
|
||||||
public requestCapabilities = jest.fn();
|
public requestCapabilities = jest.fn();
|
||||||
public requestCapabilityForRoomTimeline = jest.fn();
|
public requestCapabilityForRoomTimeline = jest.fn();
|
||||||
|
public requestCapabilityToSendEvent = jest.fn();
|
||||||
|
public requestCapabilityToReceiveEvent = jest.fn();
|
||||||
|
public requestCapabilityToSendMessage = jest.fn();
|
||||||
|
public requestCapabilityToReceiveMessage = jest.fn();
|
||||||
public requestCapabilityToSendState = jest.fn();
|
public requestCapabilityToSendState = jest.fn();
|
||||||
public requestCapabilityToReceiveState = jest.fn();
|
public requestCapabilityToReceiveState = jest.fn();
|
||||||
public requestCapabilityToSendToDevice = jest.fn();
|
public requestCapabilityToSendToDevice = jest.fn();
|
||||||
public requestCapabilityToReceiveToDevice = jest.fn();
|
public requestCapabilityToReceiveToDevice = jest.fn();
|
||||||
|
public sendRoomEvent = jest.fn(() => ({ event_id: `$${Math.random()}` }));
|
||||||
public sendStateEvent = jest.fn();
|
public sendStateEvent = jest.fn();
|
||||||
public sendToDevice = jest.fn();
|
public sendToDevice = jest.fn();
|
||||||
public readStateEvents = jest.fn(() => []);
|
public readStateEvents = jest.fn(() => []);
|
||||||
@@ -75,6 +80,66 @@ describe("RoomWidgetClient", () => {
|
|||||||
await client.startClient();
|
await client.startClient();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
describe("events", () => {
|
||||||
|
it("sends", async () => {
|
||||||
|
await makeClient({ sendEvent: ["org.matrix.rageshake_request"] });
|
||||||
|
expect(widgetApi.requestCapabilityForRoomTimeline).toHaveBeenCalledWith("!1:example.org");
|
||||||
|
expect(widgetApi.requestCapabilityToSendEvent).toHaveBeenCalledWith("org.matrix.rageshake_request");
|
||||||
|
await client.sendEvent("!1:example.org", "org.matrix.rageshake_request", { request_id: 123 });
|
||||||
|
expect(widgetApi.sendRoomEvent).toHaveBeenCalledWith(
|
||||||
|
"org.matrix.rageshake_request", { request_id: 123 }, "!1:example.org",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("receives", async () => {
|
||||||
|
const event = new MatrixEvent({
|
||||||
|
type: "org.matrix.rageshake_request",
|
||||||
|
event_id: "$pduhfiidph",
|
||||||
|
room_id: "!1:example.org",
|
||||||
|
sender: "@alice:example.org",
|
||||||
|
content: { request_id: 123 },
|
||||||
|
}).getEffectiveEvent();
|
||||||
|
|
||||||
|
await makeClient({ receiveEvent: ["org.matrix.rageshake_request"] });
|
||||||
|
expect(widgetApi.requestCapabilityForRoomTimeline).toHaveBeenCalledWith("!1:example.org");
|
||||||
|
expect(widgetApi.requestCapabilityToReceiveEvent).toHaveBeenCalledWith("org.matrix.rageshake_request");
|
||||||
|
|
||||||
|
const emittedEvent = new Promise<MatrixEvent>(resolve => client.once(ClientEvent.Event, resolve));
|
||||||
|
const emittedSync = new Promise<SyncState>(resolve => client.once(ClientEvent.Sync, resolve));
|
||||||
|
widgetApi.emit(
|
||||||
|
`action:${WidgetApiToWidgetAction.SendEvent}`,
|
||||||
|
new CustomEvent(`action:${WidgetApiToWidgetAction.SendEvent}`, { detail: { data: event } }),
|
||||||
|
);
|
||||||
|
|
||||||
|
// The client should've emitted about the received event
|
||||||
|
expect((await emittedEvent).getEffectiveEvent()).toEqual(event);
|
||||||
|
expect(await emittedSync).toEqual(SyncState.Syncing);
|
||||||
|
// It should've also inserted the event into the room object
|
||||||
|
const room = client.getRoom("!1:example.org");
|
||||||
|
expect(room).not.toBeNull();
|
||||||
|
expect(room!.getLiveTimeline().getEvents().map(e => e.getEffectiveEvent())).toEqual([event]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("messages", () => {
|
||||||
|
it("requests permissions for specific message types", async () => {
|
||||||
|
await makeClient({ sendMessage: [MsgType.Text], receiveMessage: [MsgType.Text] });
|
||||||
|
expect(widgetApi.requestCapabilityForRoomTimeline).toHaveBeenCalledWith("!1:example.org");
|
||||||
|
expect(widgetApi.requestCapabilityToSendMessage).toHaveBeenCalledWith(MsgType.Text);
|
||||||
|
expect(widgetApi.requestCapabilityToReceiveMessage).toHaveBeenCalledWith(MsgType.Text);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("requests permissions for all message types", async () => {
|
||||||
|
await makeClient({ sendMessage: true, receiveMessage: true });
|
||||||
|
expect(widgetApi.requestCapabilityForRoomTimeline).toHaveBeenCalledWith("!1:example.org");
|
||||||
|
expect(widgetApi.requestCapabilityToSendMessage).toHaveBeenCalledWith();
|
||||||
|
expect(widgetApi.requestCapabilityToReceiveMessage).toHaveBeenCalledWith();
|
||||||
|
});
|
||||||
|
|
||||||
|
// No point in testing sending and receiving since it's done exactly the
|
||||||
|
// same way as non-message events
|
||||||
|
});
|
||||||
|
|
||||||
describe("state events", () => {
|
describe("state events", () => {
|
||||||
const event = new MatrixEvent({
|
const event = new MatrixEvent({
|
||||||
type: "org.example.foo",
|
type: "org.example.foo",
|
||||||
|
@@ -3972,9 +3972,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
|||||||
* @param room
|
* @param room
|
||||||
* @param event
|
* @param event
|
||||||
* @returns {Promise} returns a promise which resolves with the result of the send request
|
* @returns {Promise} returns a promise which resolves with the result of the send request
|
||||||
* @private
|
|
||||||
*/
|
*/
|
||||||
private encryptAndSendEvent(room: Room | null, event: MatrixEvent): Promise<ISendEventResponse> {
|
protected encryptAndSendEvent(room: Room | null, event: MatrixEvent): Promise<ISendEventResponse> {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
// Add an extra Promise.resolve() to turn synchronous exceptions into promise rejections,
|
// Add an extra Promise.resolve() to turn synchronous exceptions into promise rejections,
|
||||||
// so that we can handle synchronous and asynchronous exceptions with the
|
// so that we can handle synchronous and asynchronous exceptions with the
|
||||||
@@ -4099,7 +4098,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
|||||||
return this.isRoomEncrypted(roomId) ? EventType.RoomMessageEncrypted : eventType;
|
return this.isRoomEncrypted(roomId) ? EventType.RoomMessageEncrypted : eventType;
|
||||||
}
|
}
|
||||||
|
|
||||||
private updatePendingEventStatus(room: Room | null, event: MatrixEvent, newStatus: EventStatus) {
|
protected updatePendingEventStatus(room: Room | null, event: MatrixEvent, newStatus: EventStatus) {
|
||||||
if (room) {
|
if (room) {
|
||||||
room.updatePendingEvent(event, newStatus);
|
room.updatePendingEvent(event, newStatus);
|
||||||
} else {
|
} else {
|
||||||
|
@@ -22,9 +22,10 @@ import {
|
|||||||
IWidgetApiAcknowledgeResponseData,
|
IWidgetApiAcknowledgeResponseData,
|
||||||
ISendEventToWidgetActionRequest,
|
ISendEventToWidgetActionRequest,
|
||||||
ISendToDeviceToWidgetActionRequest,
|
ISendToDeviceToWidgetActionRequest,
|
||||||
|
ISendEventFromWidgetResponseData,
|
||||||
} from "matrix-widget-api";
|
} from "matrix-widget-api";
|
||||||
|
|
||||||
import type { IEvent, IContent } from "./models/event";
|
import { IEvent, IContent, EventStatus } from "./models/event";
|
||||||
import { ISendEventResponse } from "./@types/requests";
|
import { ISendEventResponse } from "./@types/requests";
|
||||||
import { EventType } from "./@types/event";
|
import { EventType } from "./@types/event";
|
||||||
import { logger } from "./logger";
|
import { logger } from "./logger";
|
||||||
@@ -44,17 +45,56 @@ interface IStateEventRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ICapabilities {
|
export interface ICapabilities {
|
||||||
// TODO: Add fields for messages and other non-state events
|
/**
|
||||||
|
* Event types that this client expects to send.
|
||||||
|
*/
|
||||||
|
sendEvent?: string[];
|
||||||
|
/**
|
||||||
|
* Event types that this client expects to receive.
|
||||||
|
*/
|
||||||
|
receiveEvent?: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message types that this client expects to send, or true for all message
|
||||||
|
* types.
|
||||||
|
*/
|
||||||
|
sendMessage?: string[] | true;
|
||||||
|
/**
|
||||||
|
* Message types that this client expects to receive, or true for all
|
||||||
|
* message types.
|
||||||
|
*/
|
||||||
|
receiveMessage?: string[] | true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Types of state events that this client expects to send.
|
||||||
|
*/
|
||||||
sendState?: IStateEventRequest[];
|
sendState?: IStateEventRequest[];
|
||||||
|
/**
|
||||||
|
* Types of state events that this client expects to receive.
|
||||||
|
*/
|
||||||
receiveState?: IStateEventRequest[];
|
receiveState?: IStateEventRequest[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To-device event types that this client expects to send.
|
||||||
|
*/
|
||||||
sendToDevice?: string[];
|
sendToDevice?: string[];
|
||||||
|
/**
|
||||||
|
* To-device event types that this client expects to receive.
|
||||||
|
*/
|
||||||
receiveToDevice?: string[];
|
receiveToDevice?: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this client needs access to TURN servers.
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
turnServers?: boolean;
|
turnServers?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A MatrixClient that routes its requests through the widget API instead of the
|
||||||
|
* real CS API.
|
||||||
|
* @experimental This class is considered unstable!
|
||||||
|
*/
|
||||||
export class RoomWidgetClient extends MatrixClient {
|
export class RoomWidgetClient extends MatrixClient {
|
||||||
private room: Room;
|
private room: Room;
|
||||||
private widgetApiReady = new Promise<void>(resolve => this.widgetApi.once("ready", resolve));
|
private widgetApiReady = new Promise<void>(resolve => this.widgetApi.once("ready", resolve));
|
||||||
@@ -70,9 +110,38 @@ export class RoomWidgetClient extends MatrixClient {
|
|||||||
super(opts);
|
super(opts);
|
||||||
|
|
||||||
// Request capabilities for the functionality this client needs to support
|
// Request capabilities for the functionality this client needs to support
|
||||||
if (capabilities.sendState?.length || capabilities.receiveState?.length) {
|
if (
|
||||||
|
capabilities.sendEvent?.length
|
||||||
|
|| capabilities.receiveEvent?.length
|
||||||
|
|| capabilities.sendMessage === true
|
||||||
|
|| (Array.isArray(capabilities.sendMessage) && capabilities.sendMessage.length)
|
||||||
|
|| capabilities.receiveMessage === true
|
||||||
|
|| (Array.isArray(capabilities.receiveMessage) && capabilities.receiveMessage.length)
|
||||||
|
|| capabilities.sendState?.length
|
||||||
|
|| capabilities.receiveState?.length
|
||||||
|
) {
|
||||||
widgetApi.requestCapabilityForRoomTimeline(roomId);
|
widgetApi.requestCapabilityForRoomTimeline(roomId);
|
||||||
}
|
}
|
||||||
|
capabilities.sendEvent?.forEach(eventType =>
|
||||||
|
widgetApi.requestCapabilityToSendEvent(eventType),
|
||||||
|
);
|
||||||
|
capabilities.receiveEvent?.forEach(eventType =>
|
||||||
|
widgetApi.requestCapabilityToReceiveEvent(eventType),
|
||||||
|
);
|
||||||
|
if (capabilities.sendMessage === true) {
|
||||||
|
widgetApi.requestCapabilityToSendMessage();
|
||||||
|
} else if (Array.isArray(capabilities.sendMessage)) {
|
||||||
|
capabilities.sendMessage.forEach(msgType =>
|
||||||
|
widgetApi.requestCapabilityToSendMessage(msgType),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (capabilities.receiveMessage === true) {
|
||||||
|
widgetApi.requestCapabilityToReceiveMessage();
|
||||||
|
} else if (Array.isArray(capabilities.receiveMessage)) {
|
||||||
|
capabilities.receiveMessage.forEach(msgType =>
|
||||||
|
widgetApi.requestCapabilityToReceiveMessage(msgType),
|
||||||
|
);
|
||||||
|
}
|
||||||
capabilities.sendState?.forEach(({ eventType, stateKey }) =>
|
capabilities.sendState?.forEach(({ eventType, stateKey }) =>
|
||||||
widgetApi.requestCapabilityToSendState(eventType, stateKey),
|
widgetApi.requestCapabilityToSendState(eventType, stateKey),
|
||||||
);
|
);
|
||||||
@@ -155,6 +224,19 @@ export class RoomWidgetClient extends MatrixClient {
|
|||||||
throw new Error(`Unknown room: ${roomIdOrAlias}`);
|
throw new Error(`Unknown room: ${roomIdOrAlias}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected async encryptAndSendEvent(room: Room, event: MatrixEvent): Promise<ISendEventResponse> {
|
||||||
|
let response: ISendEventFromWidgetResponseData;
|
||||||
|
try {
|
||||||
|
response = await this.widgetApi.sendRoomEvent(event.getType(), event.getContent(), room.roomId);
|
||||||
|
} catch (e) {
|
||||||
|
this.updatePendingEventStatus(room, event, EventStatus.NOT_SENT);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
room.updatePendingEvent(event, EventStatus.SENT, response.event_id);
|
||||||
|
return { event_id: response.event_id };
|
||||||
|
}
|
||||||
|
|
||||||
public async sendStateEvent(
|
public async sendStateEvent(
|
||||||
roomId: string,
|
roomId: string,
|
||||||
eventType: string,
|
eventType: string,
|
||||||
|
Reference in New Issue
Block a user