Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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 package.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@
"matrix-encrypt-attachment": "^1.0.3",
"matrix-events-sdk": "0.0.1",
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
"matrix-widget-api": "^1.14.0",
"matrix-widget-api": "^1.15.0",
"memoize-one": "^6.0.0",
"mime": "^4.0.4",
"oidc-client-ts": "^3.0.1",
Expand Down
6 changes: 2 additions & 4 deletions src/stores/widgets/StopGapWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -522,10 +522,8 @@ export class StopGapWidget extends EventEmitter {
});
};

private onToDeviceMessage = async (payload: ReceivedToDeviceMessage): Promise<void> => {
const { message, encryptionInfo } = payload;
// TODO: Update the widget API to use a proper IToDeviceMessage instead of a IRoomEvent
await this.messaging?.feedToDevice(message as IRoomEvent, encryptionInfo != null);
private onToDeviceMessage = async ({ message, encryptionInfo }: ReceivedToDeviceMessage): Promise<void> => {
await this.messaging?.feedToDevice(message, encryptionInfo != null);
Copy link
Member Author

Choose a reason for hiding this comment

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

Drive-by update since we fixed the types upstream in the latest release.

};

/**
Expand Down
102 changes: 77 additions & 25 deletions src/stores/widgets/StopGapWidgetDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
type SendDelayedEventResponse,
type StateEvents,
type TimelineEvents,
SendDelayedEventRequestOpts,
} from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger";
import {
Expand Down Expand Up @@ -121,6 +122,7 @@ export class StopGapWidgetDriver extends WidgetDriver {
this.allowedCapabilities.add(`org.matrix.msc2762.timeline:${inRoomId}`);
this.allowedCapabilities.add(MatrixCapabilities.MSC4157SendDelayedEvent);
this.allowedCapabilities.add(MatrixCapabilities.MSC4157UpdateDelayedEvent);
this.allowedCapabilities.add(MatrixCapabilities.MSC4354SendStickyEvent);

this.allowedCapabilities.add(
WidgetEventCapability.forStateEvent(EventDirection.Receive, EventType.RoomName).raw,
Expand Down Expand Up @@ -284,6 +286,13 @@ export class StopGapWidgetDriver extends WidgetDriver {
return allAllowed;
}

private getSendEventTarget(roomId: string | null = null) {
const client = MatrixClientPeg.safeGet();
roomId = roomId || SdkContextClass.instance.roomViewStore.getRoomId() || null;
if (!roomId) throw new Error("No room specified and no room in RoomViewStore focus.");
return { client, roomId };
}

public async sendEvent<K extends keyof StateEvents>(
eventType: K,
content: StateEvents[K],
Expand All @@ -302,10 +311,7 @@ export class StopGapWidgetDriver extends WidgetDriver {
stateKey: string | null = null,
targetRoomId: string | null = null,
): Promise<ISendEventDetails> {
const client = MatrixClientPeg.get();
const roomId = targetRoomId || SdkContextClass.instance.roomViewStore.getRoomId();

if (!client || !roomId) throw new Error("Not in a room or not attached to a client");
const { client, roomId } = this.getSendEventTarget(targetRoomId);

let r: { event_id: string } | null;
if (stateKey !== null) {
Expand Down Expand Up @@ -344,6 +350,42 @@ export class StopGapWidgetDriver extends WidgetDriver {
return { roomId, eventId: r.event_id };
}

/**
* @experimental Part of MSC4354
* @see {@link WidgetDriver#sendStickyEvent}
*/
public async sendStickyEvent(
stickyDurationMs: number,
eventType: string,
content: unknown,
targetRoomId?: string | null,
): Promise<ISendEventDetails> {
const { client, roomId } = this.getSendEventTarget(targetRoomId);

const r = await client._unstable_sendStickyEvent(
roomId,
stickyDurationMs,
null,
eventType as keyof TimelineEvents,
content as TimelineEvents[keyof TimelineEvents] & { msc4354_sticky_key: string },
);
return { roomId, eventId: r.event_id };
}

private getSendDelayedEventOpts(delay: number | null, parentDelayId: string | null): SendDelayedEventRequestOpts {
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}
Expand Down Expand Up @@ -375,24 +417,8 @@ export class StopGapWidgetDriver extends WidgetDriver {
stateKey: string | null = null,
targetRoomId: string | null = null,
): Promise<ISendDelayedEventDetails> {
const client = MatrixClientPeg.get();
const roomId = targetRoomId || SdkContextClass.instance.roomViewStore.getRoomId();

if (!client || !roomId) throw new Error("Not in a room or not attached to a client");

let delayOpts;
if (delay !== null) {
delayOpts = {
delay,
...(parentDelayId !== null && { parent_delay_id: parentDelayId }),
};
} else if (parentDelayId !== null) {
delayOpts = {
parent_delay_id: parentDelayId,
};
} else {
throw new Error("Must provide at least one of delay or parentDelayId");
}
const { client, roomId } = this.getSendEventTarget(targetRoomId);
let delayOpts = this.getSendDelayedEventOpts(delay, parentDelayId);

let r: SendDelayedEventResponse | null;
if (stateKey !== null) {
Expand Down Expand Up @@ -421,13 +447,39 @@ export class StopGapWidgetDriver extends WidgetDriver {
};
}

/**
* @experimental Part of MSC4354
* @see {@link WidgetDriver#sendStickyEvent}
*/
public async sendDelayedStickyEvent(
Copy link
Member

Choose a reason for hiding this comment

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

we need more parameters :p

Copy link
Member Author

Choose a reason for hiding this comment

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

it's awful yes :D.

delay: number | null,
parentDelayId: string | null,
stickyDurationMs: number,
eventType: string,
content: unknown,
targetRoomId?: string | null,
): Promise<ISendDelayedEventDetails> {
const { client, roomId } = this.getSendEventTarget(targetRoomId);
let delayOpts = this.getSendDelayedEventOpts(delay, parentDelayId);

const r = await client._unstable_sendStickyDelayedEvent(
roomId,
stickyDurationMs,
delayOpts,
null,
eventType as keyof TimelineEvents,
content as TimelineEvents[keyof TimelineEvents] & { msc4354_sticky_key: string },
);
return { roomId, delayId: r.delay_id };
}

/**
* @experimental Part of MSC4140 & MSC4157
*/
public async cancelScheduledDelayedEvent(delayId: string): Promise<void> {
const client = MatrixClientPeg.get();

if (!client) throw new Error("Not in a room or not attached to a client");
if (!client) throw new Error("Not attached to a client");

await client._unstable_cancelScheduledDelayedEvent(delayId);
}
Expand All @@ -438,7 +490,7 @@ export class StopGapWidgetDriver extends WidgetDriver {
public async restartScheduledDelayedEvent(delayId: string): Promise<void> {
const client = MatrixClientPeg.get();

if (!client) throw new Error("Not in a room or not attached to a client");
if (!client) throw new Error("Not attached to a client");

await client._unstable_restartScheduledDelayedEvent(delayId);
}
Expand All @@ -449,7 +501,7 @@ export class StopGapWidgetDriver extends WidgetDriver {
public async sendScheduledDelayedEvent(delayId: string): Promise<void> {
const client = MatrixClientPeg.get();

if (!client) throw new Error("Not in a room or not attached to a client");
if (!client) throw new Error("Not attached to a client");

await client._unstable_sendScheduledDelayedEvent(delayId);
}
Expand Down
2 changes: 2 additions & 0 deletions test/test-utils/test-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,8 @@ export function createTestClient(): MatrixClient {
_unstable_cancelScheduledDelayedEvent: jest.fn(),
_unstable_restartScheduledDelayedEvent: jest.fn(),
_unstable_sendScheduledDelayedEvent: jest.fn(),
_unstable_sendStickyEvent: jest.fn(),
_unstable_sendStickyDelayedEvent: jest.fn(),

searchUserDirectory: jest.fn().mockResolvedValue({ limited: false, results: [] }),
setDeviceVerified: jest.fn(),
Expand Down
79 changes: 79 additions & 0 deletions test/unit-tests/stores/widgets/StopGapWidgetDriver-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ describe("StopGapWidgetDriver", () => {
"org.matrix.msc3819.receive.to_device:m.call.replaces",
"org.matrix.msc4157.send.delayed_event",
"org.matrix.msc4157.update_delayed_event",
"org.matrix.msc4354.send_sticky_event",
// RTC decline events
"org.matrix.msc2762.send.event:org.matrix.msc4310.rtc.decline",
"org.matrix.msc2762.receive.event:org.matrix.msc4310.rtc.decline",
Expand Down Expand Up @@ -590,6 +591,84 @@ describe("StopGapWidgetDriver", () => {
});
});

describe("sendStickyEvent", () => {
let driver: WidgetDriver;
const roomId = "!this-room-id";

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

it("sends sticky message events", async () => {
client._unstable_sendStickyEvent.mockResolvedValue({
event_id: "id",
});

await expect(driver.sendStickyEvent(2000, EventType.RoomMessage, {})).resolves.toEqual({
roomId,
eventId: "id",
});

expect(client._unstable_sendStickyEvent).toHaveBeenCalledWith(
roomId,
2000,
null,
EventType.RoomMessage,
{},
);
});
});

describe("sendDelayedStickyEvent", () => {
let driver: WidgetDriver;
const roomId = "!this-room-id";

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

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

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

expect(client._unstable_sendStickyDelayedEvent).toHaveBeenCalledWith(
roomId,
2000,
{ delay: 1000 },
null,
EventType.RoomMessage,
{},
);
});
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", () => {
beforeEach(() => {
jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
Expand Down
13 changes: 11 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4220,13 +4220,14 @@

"@vector-im/matrix-wysiwyg-wasm@link:../../Library/Caches/Yarn/v6/npm-@vector-im-matrix-wysiwyg-2.40.0-53c9ca5ea907d91e4515da64f20a82e5586b882c-integrity/node_modules/bindings/wysiwyg-wasm":
version "0.0.0"
uid ""

"@vector-im/[email protected]":
version "2.40.0"
resolved "https://registry.yarnpkg.com/@vector-im/matrix-wysiwyg/-/matrix-wysiwyg-2.40.0.tgz#53c9ca5ea907d91e4515da64f20a82e5586b882c"
integrity sha512-8LRFLs5PEKYs4lOL7aJ4lL/hGCrvEvOYkCR3JggXYXDVMtX4LmfdlKYucSAe98pCmqAAbLRvlRcR1bTOYvM8ug==
dependencies:
"@vector-im/matrix-wysiwyg-wasm" "link:../../Library/Caches/Yarn/v6/npm-@vector-im-matrix-wysiwyg-2.40.0-53c9ca5ea907d91e4515da64f20a82e5586b882c-integrity/node_modules/bindings/wysiwyg-wasm"
"@vector-im/matrix-wysiwyg-wasm" "link:../../../.cache/yarn/v6/npm-@vector-im-matrix-wysiwyg-2.40.0-53c9ca5ea907d91e4515da64f20a82e5586b882c-integrity/node_modules/bindings/wysiwyg-wasm"

"@vitest/[email protected]":
version "3.2.4"
Expand Down Expand Up @@ -9669,7 +9670,7 @@ [email protected]:
jwt-decode "^4.0.0"
loglevel "^1.9.2"
matrix-events-sdk "0.0.1"
matrix-widget-api "^1.14.0"
matrix-widget-api "^1.10.0"
oidc-client-ts "^3.0.1"
p-retry "7"
sdp-transform "^3.0.0"
Expand All @@ -9695,6 +9696,14 @@ matrix-widget-api@^1.14.0:
"@types/events" "^3.0.0"
events "^3.2.0"

matrix-widget-api@^1.15.0:
version "1.15.0"
resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-1.15.0.tgz#a508f72a5993a95382bdf890bd9e54525295b321"
integrity sha512-Yu9rX9wyF3A1sqviKgiYHz8aGgL3HhJe9OXKi/lccr1eZnNb6y+ELdbshTjs+VLKM4rkTWt6CE3THsw3f/CZhg==
dependencies:
"@types/events" "^3.0.0"
events "^3.2.0"

[email protected]:
version "2.0.28"
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.28.tgz#5ec48e7bef120654539069e1ae4ddc81ca490eba"
Expand Down
Loading