Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
52 changes: 40 additions & 12 deletions src/ClientWidgetApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -601,17 +601,30 @@ export class ClientWidgetApi extends EventEmitter {
const isDelayedEvent = request.data.delay !== undefined || request.data.parent_delay_id !== undefined;
if (isDelayedEvent && !this.hasCapability(MatrixCapabilities.MSC4157SendDelayedEvent)) {
return this.transport.reply<IWidgetApiErrorResponseData>(request, {
error: { message: "Missing capability" },
error: { message: `Missing capability for ${MatrixCapabilities.MSC4157SendDelayedEvent}` },
});
}

const isStickyEvent = request.data.sticky_duration_ms !== undefined;
if (isStickyEvent && !this.hasCapability(MatrixCapabilities.MSC4354SendStickyEvent)) {
return this.transport.reply<IWidgetApiErrorResponseData>(request, {
error: { message: `Missing capability for ${MatrixCapabilities.MSC4354SendStickyEvent}` },
});
}


let sendEventPromise: Promise<ISendEventDetails | ISendDelayedEventDetails>;
if (request.data.state_key !== undefined) {
if (!this.canSendStateEvent(request.data.type, request.data.state_key)) {
return this.transport.reply<IWidgetApiErrorResponseData>(request, {
error: { message: "Cannot send state events of this type" },
});
}
if (isStickyEvent) {
return this.transport.reply<IWidgetApiErrorResponseData>(request, {
error: { message: "Cannot send a state event with a sticky duration" },
});
}

if (isDelayedEvent) {
sendEventPromise = this.driver.sendDelayedEvent(
Expand Down Expand Up @@ -639,22 +652,37 @@ export class ClientWidgetApi extends EventEmitter {
});
}

if (isDelayedEvent) {
// Events can be sticky, delayed, both, or neither. The following
// section of code takes the common parameters and uses the correct
// function depending on the request type.

const params: Parameters<WidgetDriver["sendEvent"]> = [
request.data.type,
content,
null, // not sending a state event
request.data.room_id,
];

if (isDelayedEvent && request.data.sticky_duration_ms) {
sendEventPromise = this.driver.sendDelayedStickyEvent(
request.data.delay ?? null,
request.data.parent_delay_id ?? null,
request.data.sticky_duration_ms,
...params,
);
} else if (isDelayedEvent) {
sendEventPromise = this.driver.sendDelayedEvent(
request.data.delay ?? null,
request.data.parent_delay_id ?? null,
request.data.type,
content,
null, // not sending a state event
request.data.room_id,
...params,
);
} else {
sendEventPromise = this.driver.sendEvent(
request.data.type,
content,
null, // not sending a state event
request.data.room_id,
} else if (request.data.sticky_duration_ms) {
sendEventPromise = this.driver.sendStickyEvent(
request.data.sticky_duration_ms,
...params,
);
} else {
sendEventPromise = this.driver.sendEvent(...params);
}
}

Expand Down
15 changes: 15 additions & 0 deletions src/WidgetApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,7 @@ export class WidgetApi extends EventEmitter {
roomId?: string,
delay?: number,
parentDelayId?: string,
stickyDurationMs?: number,
): Promise<ISendEventFromWidgetResponseData> {
return this.transport.send<ISendEventFromWidgetRequestData, ISendEventFromWidgetResponseData>(
WidgetApiFromWidgetAction.SendEvent,
Expand All @@ -473,6 +474,7 @@ export class WidgetApi extends EventEmitter {
...(roomId !== undefined && { room_id: roomId }),
...(delay !== undefined && { delay }),
...(parentDelayId !== undefined && { parent_delay_id: parentDelayId }),
...(stickyDurationMs !== undefined && { sticky_duration_ms: stickyDurationMs })
},
);
}
Expand Down Expand Up @@ -516,6 +518,19 @@ export class WidgetApi extends EventEmitter {
);
}

/**
* @experimental This currently relies on an unstable MSC (MSC4157).
*/
public sendStickyEvent(delayId: string): Promise<IUpdateDelayedEventFromWidgetResponseData> {
return this.transport.send<IUpdateDelayedEventFromWidgetRequestData, IUpdateDelayedEventFromWidgetResponseData>(
WidgetApiFromWidgetAction.MSC4354SendStickyEvent,
{
delay_id: delayId,
action: UpdateDelayedEventAction.Send,
},
);
}

/**
* Sends a to-device event.
* @param {string} eventType The type of events being sent.
Expand Down
60 changes: 59 additions & 1 deletion src/driver/WidgetDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,37 @@ export abstract class WidgetDriver {
eventType: string,
content: unknown,
stateKey: string | null = null,
roomId: string | null = null,
roomId: string | null = null
): Promise<ISendEventDetails> {
return Promise.reject(new Error("Failed to override function"));
}

/**
* @experimental Part of MSC4354
* Sends a sticky event into a room. If `roomId` is falsy, the client should send the event
* into the room the user is currently looking at. The widget API will have already
* verified that the widget is capable of sending the event to that room.
* @param {number} stickyDurationMs The length of time a sticky event may remain sticky, in milliseconds.
* @param {string} eventType The event type to be sent.
* @param {*} content The content for the event.
* @param {string|null} stateKey The state key if this is a state event, otherwise null.
* May be an empty string.
* @param {string|null} roomId The room ID to send the event to. If falsy, the room the
* user is currently looking at.
* @returns {Promise<ISendEventDetails>} Resolves when the event has been sent with
* details of that event.
* @throws Rejected when the event could not be sent.
*/
public sendStickyEvent(
stickyDurationMs: number,
eventType: string,
content: unknown,
stateKey: string | null = null,
roomId: string | null = null
): Promise<ISendEventDetails> {
throw new Error("Method not implemented.");
}

/**
* @experimental Part of MSC4140 & MSC4157
* Sends a delayed event into a room. If `roomId` is falsy, the client should send it
Expand Down Expand Up @@ -139,6 +165,38 @@ export abstract class WidgetDriver {
return Promise.reject(new Error("Failed to override function"));
}

/**
* @experimental Part of MSC4140, MSC4157 and MSC4354
* Sends a delayed sticky event into a room. If `roomId` is falsy, the client should send the event
* into the room the user is currently looking at. The widget API will have already
* verified that the widget is capable of sending the event to that room.
* @param {number} stickyDurationMs The length of time a sticky event may remain sticky, in milliseconds.
* @param {number|null} delay How much later to send the event, or null to not send the
* event automatically. May not be null if {@link parentDelayId} is null.
* @param {string|null} parentDelayId The ID of the delayed event this one is grouped with,
* or null if it will be put in a new group. May not be null if {@link delay} is null.
* @param {string} eventType The event type to be sent.
* @param {*} content The content for the event.
* @param {string|null} stateKey The state key if this is a state event, otherwise null.
* May be an empty string.
* @param {string|null} roomId The room ID to send the event to. If falsy, the room the
* user is currently looking at.
* @returns {Promise<ISendEventDetails>} Resolves when the event has been sent with
* details of that event.
* @throws Rejected when the event could not be sent.
*/
public sendDelayedStickyEvent(
delay: number | null,
parentDelayId: string | null,
stickyDurationMs: number,
eventType: string,
content: unknown,
stateKey: string | null = null,
roomId: string | null = null
): Promise<ISendEventDetails> {
throw new Error("Method not implemented.");
}

/**
* @experimental Part of MSC4140 & MSC4157
* Cancel the scheduled delivery of the delayed event matching the provided {@link delayId}.
Expand Down
4 changes: 4 additions & 0 deletions src/interfaces/Capabilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ export enum MatrixCapabilities {
* @experimental It is not recommended to rely on this existing - it can be removed without notice.
*/
MSC4157UpdateDelayedEvent = "org.matrix.msc4157.update_delayed_event",
/**
* @experimental It is not recommended to rely on this existing - it can be removed without notice.
*/
MSC4354SendStickyEvent = "org.matrix.msc4354.send_sticky_event",
}

export type Capability = MatrixCapabilities | string;
Expand Down
4 changes: 4 additions & 0 deletions src/interfaces/IRoomEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,8 @@ export interface IRoomEvent {
origin_server_ts: number; // eslint-disable-line camelcase
content: unknown;
unsigned: unknown;
//MSC4354
sticky?: {
duration_ms: number;
};
}
3 changes: 3 additions & 0 deletions src/interfaces/SendEventAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ export interface ISendEventFromWidgetRequestData extends IWidgetApiRequestData {
// MSC4157
delay?: number; // eslint-disable-line camelcase
parent_delay_id?: string; // eslint-disable-line camelcase

// MSC4354SendStickyEvent
sticky_duration_ms?: number;
Copy link
Member

Choose a reason for hiding this comment

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

this has no dev id prefix ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I didn't want to include one for the API, seeing as we didn't for delayed events?

}

export interface ISendEventFromWidgetActionRequest extends IWidgetApiRequest {
Expand Down
5 changes: 5 additions & 0 deletions src/interfaces/WidgetApiAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ export enum WidgetApiFromWidgetAction {
* @experimental It is not recommended to rely on this existing - it can be removed without notice.
*/
MSC4157UpdateDelayedEvent = "org.matrix.msc4157.update_delayed_event",

/**
* @experimental It is not recommended to rely on this existing - it can be removed without notice.
*/
MSC4354SendStickyEvent = "org.matrix.msc4354.send_sticky_event",
}

export type WidgetApiAction = WidgetApiToWidgetAction | WidgetApiFromWidgetAction | string;
Loading