fix(matrix): bind approval reactions before option emoji

This commit is contained in:
Vincent Koc
2026-05-03 18:49:36 -07:00
parent 90c0edcb61
commit 1c2eda206e
3 changed files with 55 additions and 1 deletions

View File

@@ -266,6 +266,52 @@ describe("matrixApprovalNativeRuntime", () => {
);
});
it("binds Matrix approval reactions before publishing option reactions", async () => {
const sendSingleTextMessage = vi.fn().mockResolvedValue({
messageId: "$approval",
primaryMessageId: "$approval",
messageIds: ["$approval"],
roomId: "!room:example.org",
});
const reactMessage = vi.fn().mockImplementation(async () => {
expect(
resolveMatrixApprovalReactionTarget({
roomId: "!room:example.org",
eventId: "$approval",
reactionKey: "✅",
}),
).toEqual({
approvalId: "req-1",
decision: "allow-once",
});
});
const view = buildExecApprovalView();
const pendingPayload = await buildPendingPayload(view);
await matrixApprovalNativeRuntime.transport.deliverPending({
cfg: {} as never,
accountId: "default",
context: {
client: {} as never,
deps: {
sendSingleTextMessage,
reactMessage,
},
},
request: {} as never,
approvalKind: "exec",
plannedTarget: buildMatrixApprovalRoomTarget("!room:example.org"),
preparedTarget: {
to: "room:!room:example.org",
roomId: "!room:example.org",
},
view,
pendingPayload,
});
expect(reactMessage).toHaveBeenCalled();
});
it("falls back to chunked Matrix delivery when approval content exceeds one event", async () => {
const sendSingleTextMessage = vi
.fn()

View File

@@ -408,7 +408,7 @@ export const matrixApprovalNativeRuntime = createChannelApprovalNativeRuntimeAda
: null,
);
},
deliverPending: async ({ cfg, accountId, context, preparedTarget, pendingPayload }) => {
deliverPending: async ({ cfg, accountId, context, preparedTarget, pendingPayload, view }) => {
const resolved = resolveHandlerContext({ cfg, accountId, context });
if (!resolved) {
return null;
@@ -447,6 +447,13 @@ export const matrixApprovalNativeRuntime = createChannelApprovalNativeRuntimeAda
);
const reactionEventId =
result.primaryMessageId?.trim() || messageIds[0] || result.messageId.trim();
registerMatrixApprovalReactionTarget({
roomId: result.roomId,
eventId: reactionEventId,
approvalId: pendingPayload.approvalId,
allowedDecisions: pendingPayload.allowedDecisions,
ttlMs: view.expiresAtMs - Date.now(),
});
await Promise.allSettled(
listMatrixApprovalReactionBindings(pendingPayload.allowedDecisions).map(
async ({ emoji }) => {