Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
"maplibre-gl": "^5.0.0",
"matrix-encrypt-attachment": "^1.0.3",
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
"matrix-widget-api": "^1.16.1",
"matrix-widget-api": "^1.18.0",
"memoize-one": "^6.0.0",
"mime": "^4.0.4",
"opus-recorder": "^8.0.3",
Expand Down
45 changes: 18 additions & 27 deletions apps/web/src/stores/widgets/ElementWidgetDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
type IWidgetApiErrorResponseDataDetails,
type ISearchUserDirectoryResult,
type IGetMediaConfigResult,
type IRtcTransportsResult,
} from "matrix-widget-api";
import {
ClientEvent,
Expand All @@ -39,7 +40,6 @@ import {
type StateEvents,
type TimelineEvents,
type Room,
type SendDelayedEventRequestOpts,
type MatrixClient,
} from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger";
Expand Down Expand Up @@ -123,6 +123,7 @@ export class ElementWidgetDriver extends WidgetDriver {
// This is a trusted Element Call widget that we control
this.allowedCapabilities.add(MatrixCapabilities.AlwaysOnScreen);
this.allowedCapabilities.add(MatrixCapabilities.MSC3846TurnServers);
this.allowedCapabilities.add(MatrixCapabilities.MSC4515RtcTransports);
this.allowedCapabilities.add(`org.matrix.msc2762.timeline:${inRoomId}`);
this.allowedCapabilities.add(MatrixCapabilities.MSC4157SendDelayedEvent);
this.allowedCapabilities.add(MatrixCapabilities.MSC4157UpdateDelayedEvent);
Expand Down Expand Up @@ -405,27 +406,12 @@ export class ElementWidgetDriver extends WidgetDriver {
return stickyEvents;
}

private getSendDelayedEventOpts(delay: number | null, parentDelayId: string | null): SendDelayedEventRequestOpts {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the delayed event changes because of the matrix-widget-sdk update? They do seem misplaced in this PR?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the delayed event changes because of the matrix-widget-sdk update? They do seem misplaced in this PR?

Yes they are due to the widget-api-changes (remove parent delayed id), but the js-sdk hasn't yet been updated to the MSC changes, so the parentId related types are still around.

There is a js-sdk draft PR matrix-org/matrix-js-sdk#5199
It would be nicer if this landed first :/, but I think they are a bit entangled

@AndrewFerr WDYT?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's doubtful that I'll be able to finish that js-sdk PR this week, so as long as it's not a strict requirement of this PR, maybe just charge ahead with this one for now.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Half-Shot is it fine to go ahead for you? there will be a follow up PR to finish the clean up of deprecated parent_id thing for delayed event?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is fairly high-priority and it feels acceptable to me to adapt to the parent ID changes in the same breath as upgrading matrix-widget-api, so I will proceed with getting this merged.

if (delay !== null) {
return {
delay,
...(parentDelayId !== null && { parent_delay_id: parentDelayId }),
};
} else if (parentDelayId !== null) {
return {
parent_delay_id: parentDelayId,
};
}
throw new Error("Must provide at least one of delay or parentDelayId");
}

/**
* @experimental Part of MSC4140 & MSC4157
* @see {@link WidgetDriver#sendDelayedEvent}
*/
public async sendDelayedEvent<K extends keyof StateEvents>(
delay: number | null,
parentDelayId: string | null,
delay: number,
eventType: K,
content: StateEvents[K],
stateKey: string | null,
Expand All @@ -435,23 +421,21 @@ export class ElementWidgetDriver extends WidgetDriver {
* @experimental Part of MSC4140 & MSC4157
*/
public async sendDelayedEvent<K extends keyof TimelineEvents>(
delay: number | null,
parentDelayId: string | null,
delay: number,
eventType: K,
content: TimelineEvents[K],
stateKey: null,
targetRoomId: string | null,
): Promise<ISendDelayedEventDetails>;
public async sendDelayedEvent(
delay: number | null,
parentDelayId: string | null,
delay: number,
eventType: string,
content: IContent,
stateKey: string | null = null,
targetRoomId: string | null = null,
): Promise<ISendDelayedEventDetails> {
const { client, roomId } = this.getSendEventTarget(targetRoomId);
const delayOpts = this.getSendDelayedEventOpts(delay, parentDelayId);
const delayOpts = { delay };

let r: SendDelayedEventResponse | null;
if (stateKey !== null) {
Expand Down Expand Up @@ -485,20 +469,17 @@ export class ElementWidgetDriver extends WidgetDriver {
* @see {@link WidgetDriver#sendStickyEvent}
*/
public async sendDelayedStickyEvent(
delay: number | null,
parentDelayId: string | null,
delay: number,
stickyDurationMs: number,
eventType: string,
content: unknown,
targetRoomId?: string | null,
): Promise<ISendDelayedEventDetails> {
const { client, roomId } = this.getSendEventTarget(targetRoomId);
const delayOpts = this.getSendDelayedEventOpts(delay, parentDelayId);

const r = await client._unstable_sendStickyDelayedEvent(
roomId,
stickyDurationMs,
delayOpts,
{ delay },
null,
eventType as keyof TimelineEvents,
content as TimelineEvents[keyof TimelineEvents] & { msc4354_sticky_key: string },
Expand Down Expand Up @@ -765,6 +746,16 @@ export class ElementWidgetDriver extends WidgetDriver {
}
}

public async getRtcTransports(): Promise<IRtcTransportsResult> {
const client = MatrixClientPeg.safeGet();
// Delegate to the authenticated CS endpoint (MSC4143). Any error (e.g. the
// homeserver not supporting it) propagates and is turned into a widget error
// response by ClientWidgetApi. The js-sdk Transport and widget-api IRtcTransport
// types are structurally identical.
const transports = await client._unstable_getRTCTransports();
return { rtc_transports: transports };
}

public async readEventRelations(
eventId: string,
roomId?: string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
type ITurnServer as IClientTurnServer,
Direction,
EventType,
MatrixError,
MatrixEvent,
MsgType,
RelationType,
Expand Down Expand Up @@ -89,6 +90,7 @@ describe("ElementWidgetDriver", () => {
const requestedCapabilities = new Set([
"m.always_on_screen",
"town.robin.msc3846.turn_servers",
"org.matrix.msc4515.rtc_transports",
"org.matrix.msc2762.timeline:!1:example.org",
"org.matrix.msc2762.send.event:org.matrix.msc4075.call.notify",
"org.matrix.msc2762.send.event:org.matrix.msc4075.rtc.notification",
Expand Down Expand Up @@ -420,6 +422,31 @@ describe("ElementWidgetDriver", () => {
});
});

describe("getRtcTransports", () => {
let driver: WidgetDriver;

beforeEach(() => {
driver = mkDefaultDriver();
});

it("gets the RTC transports from the homeserver", async () => {
const transports = [{ type: "livekit", livekit_service_url: "https://livekit-jwt.example.com" }];
client._unstable_getRTCTransports.mockResolvedValue(transports);

await expect(driver.getRtcTransports()).resolves.toEqual({ rtc_transports: transports });

expect(client._unstable_getRTCTransports).toHaveBeenCalledWith();
});

it("propagates errors from the homeserver", async () => {
const error = new MatrixError({ errcode: "M_NOT_FOUND", error: "Not found" }, 404);

client._unstable_getRTCTransports.mockRejectedValue(error);

await expect(driver.getRtcTransports()).rejects.toBe(error);
});
});

describe("readEventRelations", () => {
let driver: WidgetDriver;

Expand Down Expand Up @@ -540,18 +567,12 @@ describe("ElementWidgetDriver", () => {
driver = mkDefaultDriver();
});

it("cannot send delayed events with missing arguments", async () => {
await expect(driver.sendDelayedEvent(null, null, EventType.RoomMessage, {})).rejects.toThrow(
"Must provide at least one of",
);
});

it("sends delayed message events", async () => {
client._unstable_sendDelayedEvent.mockResolvedValue({
delay_id: "id",
});

await expect(driver.sendDelayedEvent(2000, null, EventType.RoomMessage, {})).resolves.toEqual({
await expect(driver.sendDelayedEvent(2000, EventType.RoomMessage, {})).resolves.toEqual({
roomId,
delayId: "id",
});
Expand All @@ -565,31 +586,12 @@ describe("ElementWidgetDriver", () => {
);
});

it("sends child action delayed message events", async () => {
client._unstable_sendDelayedEvent.mockResolvedValue({
delay_id: "id-child",
});

await expect(driver.sendDelayedEvent(null, "id-parent", EventType.RoomMessage, {})).resolves.toEqual({
roomId,
delayId: "id-child",
});

expect(client._unstable_sendDelayedEvent).toHaveBeenCalledWith(
roomId,
{ parent_delay_id: "id-parent" },
null,
EventType.RoomMessage,
{},
);
});

it("sends delayed state events", async () => {
client._unstable_sendDelayedStateEvent.mockResolvedValue({
delay_id: "id",
});

await expect(driver.sendDelayedEvent(2000, null, EventType.RoomTopic, {}, "")).resolves.toEqual({
await expect(driver.sendDelayedEvent(2000, EventType.RoomTopic, {}, "")).resolves.toEqual({
roomId,
delayId: "id",
});
Expand All @@ -602,25 +604,6 @@ describe("ElementWidgetDriver", () => {
"",
);
});

it("sends child action delayed state events", async () => {
client._unstable_sendDelayedStateEvent.mockResolvedValue({
delay_id: "id-child",
});

await expect(driver.sendDelayedEvent(null, "id-parent", EventType.RoomTopic, {}, "")).resolves.toEqual({
roomId,
delayId: "id-child",
});

expect(client._unstable_sendDelayedStateEvent).toHaveBeenCalledWith(
roomId,
{ parent_delay_id: "id-parent" },
EventType.RoomTopic,
{},
"",
);
});
});

describe("updateDelayedEvent", () => {
Expand Down Expand Up @@ -743,7 +726,7 @@ describe("ElementWidgetDriver", () => {
delay_id: "id",
});

await expect(driver.sendDelayedStickyEvent(1000, null, 2000, EventType.RoomMessage, {})).resolves.toEqual({
await expect(driver.sendDelayedStickyEvent(1000, 2000, EventType.RoomMessage, {})).resolves.toEqual({
roomId,
delayId: "id",
});
Expand All @@ -757,27 +740,6 @@ describe("ElementWidgetDriver", () => {
{},
);
});
it("sends child action delayed sticky message events", async () => {
client._unstable_sendStickyDelayedEvent.mockResolvedValue({
delay_id: "id-child",
});

await expect(
driver.sendDelayedStickyEvent(null, "id-parent", 2000, EventType.RoomMessage, {}),
).resolves.toEqual({
roomId,
delayId: "id-child",
});

expect(client._unstable_sendStickyDelayedEvent).toHaveBeenCalledWith(
roomId,
2000,
{ parent_delay_id: "id-parent" },
null,
EventType.RoomMessage,
{},
);
});
});

describe("If the feature_dynamic_room_predecessors feature is not enabled", () => {
Expand Down
26 changes: 13 additions & 13 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ overrides:
"@types/react": "catalog:"
"@types/react-dom": "catalog:"
caniuse-lite: 1.0.30001806
matrix-widget-api: "^1.17.0"
matrix-widget-api: "^1.18.0"
"@types/node": 25.9.3
config-file-ts: 0.2.8-rc1
node-abi: 4.33.0
Expand Down
Loading