You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-12-14 19:22:15 +03:00
Delayed event management: split endpoints, no auth (#5066)
* Delayed event management: split endpoints, no auth Add dedicated endpoints for each of the cancel/restart/send actions for updating a delayed event, and make them unauthenticated. Also keep support for the original endpoint where the update action is in the request body, and make the split-endpoint versions fall back to it if they are unsupported by the homeserver. * Don't @link parameters in method docstrings as TypeDoc doesn't support that * Reduce code duplication * Reduce code duplication again * Add a little more test coverage * Use split delayed event management for widgets * Specify which eslint rule to ignore Co-authored-by: Will Hunt <2072976+Half-Shot@users.noreply.github.com> * Restore embedded non-split delay evt update method Keep supporting it to not break widgets that currently use it. Also add back the test for it. * Deprecate the non-split delay evt update methods * Comment to explain fallback to non-split endpoint * Add backwards compatibility with authed endpoints * Comment backwards compatibility helper method * Await returned promises because `return await promise` is at least as fast as `return promise` --------- Co-authored-by: Will Hunt <2072976+Half-Shot@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
1dee1ba581
commit
df88edfda0
@@ -56,7 +56,7 @@
|
|||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
"loglevel": "^1.9.2",
|
"loglevel": "^1.9.2",
|
||||||
"matrix-events-sdk": "0.0.1",
|
"matrix-events-sdk": "0.0.1",
|
||||||
"matrix-widget-api": "^1.10.0",
|
"matrix-widget-api": "^1.14.0",
|
||||||
"oidc-client-ts": "^3.0.1",
|
"oidc-client-ts": "^3.0.1",
|
||||||
"p-retry": "7",
|
"p-retry": "7",
|
||||||
"sdp-transform": "^3.0.0",
|
"sdp-transform": "^3.0.0",
|
||||||
|
|||||||
@@ -86,7 +86,9 @@ class MockWidgetApi extends EventEmitter {
|
|||||||
? { event_id: `$${Math.random()}` }
|
? { event_id: `$${Math.random()}` }
|
||||||
: { delay_id: `id-${Math.random()}` },
|
: { delay_id: `id-${Math.random()}` },
|
||||||
);
|
);
|
||||||
public updateDelayedEvent = jest.fn().mockResolvedValue(undefined);
|
public cancelScheduledDelayedEvent = jest.fn().mockResolvedValue(undefined);
|
||||||
|
public restartScheduledDelayedEvent = jest.fn().mockResolvedValue(undefined);
|
||||||
|
public sendScheduledDelayedEvent = jest.fn().mockResolvedValue(undefined);
|
||||||
public sendToDevice = jest.fn().mockResolvedValue(undefined);
|
public sendToDevice = jest.fn().mockResolvedValue(undefined);
|
||||||
public requestOpenIDConnectToken = jest.fn(async () => {
|
public requestOpenIDConnectToken = jest.fn(async () => {
|
||||||
return testOIDCToken;
|
return testOIDCToken;
|
||||||
@@ -531,17 +533,49 @@ describe("RoomWidgetClient", () => {
|
|||||||
).rejects.toThrow();
|
).rejects.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("updates delayed events", async () => {
|
it.each([UpdateDelayedEventAction.Cancel, UpdateDelayedEventAction.Restart, UpdateDelayedEventAction.Send])(
|
||||||
|
"can %s scheduled delayed events (action in parameter)",
|
||||||
|
async (action: UpdateDelayedEventAction) => {
|
||||||
|
await makeClient({ updateDelayedEvents: true, sendEvent: ["org.matrix.rageshake_request"] });
|
||||||
|
expect(widgetApi.requestCapability).toHaveBeenCalledWith(
|
||||||
|
MatrixCapabilities.MSC4157UpdateDelayedEvent,
|
||||||
|
);
|
||||||
|
await client._unstable_updateDelayedEvent("id", action);
|
||||||
|
let updateDelayedEvent: (delayId: string) => Promise<unknown>;
|
||||||
|
switch (action) {
|
||||||
|
case UpdateDelayedEventAction.Cancel:
|
||||||
|
updateDelayedEvent = widgetApi.cancelScheduledDelayedEvent;
|
||||||
|
break;
|
||||||
|
case UpdateDelayedEventAction.Restart:
|
||||||
|
updateDelayedEvent = widgetApi.cancelScheduledDelayedEvent;
|
||||||
|
break;
|
||||||
|
case UpdateDelayedEventAction.Send:
|
||||||
|
updateDelayedEvent = widgetApi.sendScheduledDelayedEvent;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
expect(updateDelayedEvent).toHaveBeenCalledWith("id");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
it("can cancel scheduled delayed events (action in method)", async () => {
|
||||||
await makeClient({ updateDelayedEvents: true, sendEvent: ["org.matrix.rageshake_request"] });
|
await makeClient({ updateDelayedEvents: true, sendEvent: ["org.matrix.rageshake_request"] });
|
||||||
expect(widgetApi.requestCapability).toHaveBeenCalledWith(MatrixCapabilities.MSC4157UpdateDelayedEvent);
|
expect(widgetApi.requestCapability).toHaveBeenCalledWith(MatrixCapabilities.MSC4157UpdateDelayedEvent);
|
||||||
for (const action of [
|
await client._unstable_cancelScheduledDelayedEvent("id");
|
||||||
UpdateDelayedEventAction.Cancel,
|
expect(widgetApi.cancelScheduledDelayedEvent).toHaveBeenCalledWith("id");
|
||||||
UpdateDelayedEventAction.Restart,
|
});
|
||||||
UpdateDelayedEventAction.Send,
|
|
||||||
]) {
|
it("can restart scheduled delayed events (action in method)", async () => {
|
||||||
await client._unstable_updateDelayedEvent("id", action);
|
await makeClient({ updateDelayedEvents: true, sendEvent: ["org.matrix.rageshake_request"] });
|
||||||
expect(widgetApi.updateDelayedEvent).toHaveBeenCalledWith("id", action);
|
expect(widgetApi.requestCapability).toHaveBeenCalledWith(MatrixCapabilities.MSC4157UpdateDelayedEvent);
|
||||||
}
|
await client._unstable_restartScheduledDelayedEvent("id");
|
||||||
|
expect(widgetApi.restartScheduledDelayedEvent).toHaveBeenCalledWith("id");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can send scheduled delayed events (action in method)", async () => {
|
||||||
|
await makeClient({ updateDelayedEvents: true, sendEvent: ["org.matrix.rageshake_request"] });
|
||||||
|
expect(widgetApi.requestCapability).toHaveBeenCalledWith(MatrixCapabilities.MSC4157UpdateDelayedEvent);
|
||||||
|
await client._unstable_sendScheduledDelayedEvent("id");
|
||||||
|
expect(widgetApi.sendScheduledDelayedEvent).toHaveBeenCalledWith("id");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -583,6 +617,13 @@ describe("RoomWidgetClient", () => {
|
|||||||
"Server does not support",
|
"Server does not support",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
for (const updateDelayedEvent of [
|
||||||
|
client._unstable_cancelScheduledDelayedEvent,
|
||||||
|
client._unstable_restartScheduledDelayedEvent,
|
||||||
|
client._unstable_sendScheduledDelayedEvent,
|
||||||
|
]) {
|
||||||
|
await expect(updateDelayedEvent.call(client, "id")).rejects.toThrow("Server does not support");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -801,6 +801,10 @@ describe("MatrixClient", function () {
|
|||||||
await expect(
|
await expect(
|
||||||
client._unstable_updateDelayedEvent("anyDelayId", UpdateDelayedEventAction.Send),
|
client._unstable_updateDelayedEvent("anyDelayId", UpdateDelayedEventAction.Send),
|
||||||
).rejects.toThrow(errorMessage);
|
).rejects.toThrow(errorMessage);
|
||||||
|
|
||||||
|
await expect(client._unstable_cancelScheduledDelayedEvent("anyDelayId")).rejects.toThrow(errorMessage);
|
||||||
|
await expect(client._unstable_restartScheduledDelayedEvent("anyDelayId")).rejects.toThrow(errorMessage);
|
||||||
|
await expect(client._unstable_sendScheduledDelayedEvent("anyDelayId")).rejects.toThrow(errorMessage);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("works with null threadId", async () => {
|
it("works with null threadId", async () => {
|
||||||
@@ -1077,21 +1081,169 @@ describe("MatrixClient", function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("can update delayed events", async () => {
|
it.each([UpdateDelayedEventAction.Cancel, UpdateDelayedEventAction.Restart, UpdateDelayedEventAction.Send])(
|
||||||
|
"can %s scheduled delayed events (action in request body)",
|
||||||
|
async (action: UpdateDelayedEventAction) => {
|
||||||
|
const delayId = "id";
|
||||||
|
httpLookups = [
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
prefix: unstableMSC4140Prefix,
|
||||||
|
path: `/delayed_events/${encodeURIComponent(delayId)}`,
|
||||||
|
data: {
|
||||||
|
action,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
await client._unstable_updateDelayedEvent(delayId, action);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each([UpdateDelayedEventAction.Cancel, UpdateDelayedEventAction.Restart, UpdateDelayedEventAction.Send])(
|
||||||
|
"can %s scheduled delayed events (action in request body fallback when auth required)",
|
||||||
|
async (action: UpdateDelayedEventAction) => {
|
||||||
|
const delayId = "id";
|
||||||
|
const baseLookup = {
|
||||||
|
method: "POST",
|
||||||
|
prefix: unstableMSC4140Prefix,
|
||||||
|
path: `/delayed_events/${encodeURIComponent(delayId)}`,
|
||||||
|
};
|
||||||
|
httpLookups = [
|
||||||
|
{
|
||||||
|
...baseLookup,
|
||||||
|
error: {
|
||||||
|
httpStatus: 401,
|
||||||
|
errcode: "M_MISSING_TOKEN",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...baseLookup,
|
||||||
|
data: {
|
||||||
|
action,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
await client._unstable_updateDelayedEvent(delayId, action);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
it("can cancel scheduled delayed events (action in request path)", async () => {
|
||||||
const delayId = "id";
|
const delayId = "id";
|
||||||
const action = UpdateDelayedEventAction.Restart;
|
|
||||||
httpLookups = [
|
httpLookups = [
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
prefix: unstableMSC4140Prefix,
|
||||||
|
path: `/delayed_events/${encodeURIComponent(delayId)}/cancel`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
await client._unstable_cancelScheduledDelayedEvent(delayId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can restart scheduled delayed events (action in request path)", async () => {
|
||||||
|
const delayId = "id";
|
||||||
|
httpLookups = [
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
prefix: unstableMSC4140Prefix,
|
||||||
|
path: `/delayed_events/${encodeURIComponent(delayId)}/restart`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
await client._unstable_restartScheduledDelayedEvent(delayId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can send scheduled delayed events (action in request path)", async () => {
|
||||||
|
const delayId = "id";
|
||||||
|
httpLookups = [
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
prefix: unstableMSC4140Prefix,
|
||||||
|
path: `/delayed_events/${encodeURIComponent(delayId)}/send`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
await client._unstable_sendScheduledDelayedEvent(delayId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can cancel scheduled delayed events (action in request path fallback when unsupported)", async () => {
|
||||||
|
const delayId = "id";
|
||||||
|
httpLookups = [
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
prefix: unstableMSC4140Prefix,
|
||||||
|
path: `/delayed_events/${encodeURIComponent(delayId)}/cancel`,
|
||||||
|
error: {
|
||||||
|
httpStatus: 400,
|
||||||
|
errcode: "M_UNRECOGNIZED",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
prefix: unstableMSC4140Prefix,
|
prefix: unstableMSC4140Prefix,
|
||||||
path: `/delayed_events/${encodeURIComponent(delayId)}`,
|
path: `/delayed_events/${encodeURIComponent(delayId)}`,
|
||||||
data: {
|
data: {
|
||||||
action,
|
action: UpdateDelayedEventAction.Cancel,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
await client._unstable_updateDelayedEvent(delayId, action);
|
await client._unstable_cancelScheduledDelayedEvent(delayId);
|
||||||
|
expect(httpLookups).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can restart scheduled delayed events (action in request path fallback when unsupported)", async () => {
|
||||||
|
const delayId = "id";
|
||||||
|
httpLookups = [
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
prefix: unstableMSC4140Prefix,
|
||||||
|
path: `/delayed_events/${encodeURIComponent(delayId)}/restart`,
|
||||||
|
error: {
|
||||||
|
httpStatus: 400,
|
||||||
|
errcode: "M_UNRECOGNIZED",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
prefix: unstableMSC4140Prefix,
|
||||||
|
path: `/delayed_events/${encodeURIComponent(delayId)}`,
|
||||||
|
data: {
|
||||||
|
action: UpdateDelayedEventAction.Restart,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
await client._unstable_restartScheduledDelayedEvent(delayId);
|
||||||
|
expect(httpLookups).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can send scheduled delayed events (action in request path fallback when unsupported)", async () => {
|
||||||
|
const delayId = "id";
|
||||||
|
httpLookups = [
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
prefix: unstableMSC4140Prefix,
|
||||||
|
path: `/delayed_events/${encodeURIComponent(delayId)}/send`,
|
||||||
|
error: {
|
||||||
|
httpStatus: 400,
|
||||||
|
errcode: "M_UNRECOGNIZED",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
prefix: unstableMSC4140Prefix,
|
||||||
|
path: `/delayed_events/${encodeURIComponent(delayId)}`,
|
||||||
|
data: {
|
||||||
|
action: UpdateDelayedEventAction.Send,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
await client._unstable_sendScheduledDelayedEvent(delayId);
|
||||||
|
expect(httpLookups).toHaveLength(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -523,6 +523,9 @@ describe("MatrixRTCSession", () => {
|
|||||||
client.sendEvent = sendEventMock;
|
client.sendEvent = sendEventMock;
|
||||||
|
|
||||||
client._unstable_updateDelayedEvent = jest.fn();
|
client._unstable_updateDelayedEvent = jest.fn();
|
||||||
|
client._unstable_cancelScheduledDelayedEvent = jest.fn();
|
||||||
|
client._unstable_restartScheduledDelayedEvent = jest.fn();
|
||||||
|
client._unstable_sendScheduledDelayedEvent = jest.fn();
|
||||||
|
|
||||||
mockRoom = makeMockRoom([]);
|
mockRoom = makeMockRoom([]);
|
||||||
sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession);
|
sess = MatrixRTCSession.sessionForRoom(client, mockRoom, callSession);
|
||||||
|
|||||||
@@ -94,6 +94,9 @@ describe("MembershipManager", () => {
|
|||||||
// Provide a default mock that is like the default "non error" server behaviour.
|
// Provide a default mock that is like the default "non error" server behaviour.
|
||||||
(client._unstable_sendDelayedStateEvent as Mock<any>).mockResolvedValue({ delay_id: "id" });
|
(client._unstable_sendDelayedStateEvent as Mock<any>).mockResolvedValue({ delay_id: "id" });
|
||||||
(client._unstable_updateDelayedEvent as Mock<any>).mockResolvedValue(undefined);
|
(client._unstable_updateDelayedEvent as Mock<any>).mockResolvedValue(undefined);
|
||||||
|
(client._unstable_cancelScheduledDelayedEvent as Mock<any>).mockResolvedValue(undefined);
|
||||||
|
(client._unstable_restartScheduledDelayedEvent as Mock<any>).mockResolvedValue(undefined);
|
||||||
|
(client._unstable_sendScheduledDelayedEvent as Mock<any>).mockResolvedValue(undefined);
|
||||||
(client._unstable_sendStickyEvent as Mock<any>).mockResolvedValue({ event_id: "id" });
|
(client._unstable_sendStickyEvent as Mock<any>).mockResolvedValue({ event_id: "id" });
|
||||||
(client._unstable_sendStickyDelayedEvent as Mock<any>).mockResolvedValue({ delay_id: "id" });
|
(client._unstable_sendStickyDelayedEvent as Mock<any>).mockResolvedValue({ delay_id: "id" });
|
||||||
(client.sendStateEvent as Mock<any>).mockResolvedValue({ event_id: "id" });
|
(client.sendStateEvent as Mock<any>).mockResolvedValue({ event_id: "id" });
|
||||||
@@ -122,7 +125,9 @@ describe("MembershipManager", () => {
|
|||||||
it("sends a membership event and schedules delayed leave when joining a call", async () => {
|
it("sends a membership event and schedules delayed leave when joining a call", async () => {
|
||||||
// Spys/Mocks
|
// Spys/Mocks
|
||||||
|
|
||||||
const updateDelayedEventHandle = createAsyncHandle<void>(client._unstable_updateDelayedEvent as Mock);
|
const restartScheduledDelayedEventHandle = createAsyncHandle<void>(
|
||||||
|
client._unstable_restartScheduledDelayedEvent as Mock,
|
||||||
|
);
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
const memberManager = new MembershipManager(undefined, room, client, callSession);
|
const memberManager = new MembershipManager(undefined, room, client, callSession);
|
||||||
@@ -143,7 +148,7 @@ describe("MembershipManager", () => {
|
|||||||
},
|
},
|
||||||
"_@alice:example.org_AAAAAAA_m.call",
|
"_@alice:example.org_AAAAAAA_m.call",
|
||||||
);
|
);
|
||||||
updateDelayedEventHandle.resolve?.();
|
restartScheduledDelayedEventHandle.resolve?.();
|
||||||
expect(client._unstable_sendDelayedStateEvent).toHaveBeenCalledWith(
|
expect(client._unstable_sendDelayedStateEvent).toHaveBeenCalledWith(
|
||||||
room.roomId,
|
room.roomId,
|
||||||
{ delay: 8000 },
|
{ delay: 8000 },
|
||||||
@@ -157,13 +162,13 @@ describe("MembershipManager", () => {
|
|||||||
it("reschedules delayed leave event if sending state cancels it", async () => {
|
it("reschedules delayed leave event if sending state cancels it", async () => {
|
||||||
const memberManager = new MembershipManager(undefined, room, client, callSession);
|
const memberManager = new MembershipManager(undefined, room, client, callSession);
|
||||||
const waitForSendState = waitForMockCall(client.sendStateEvent);
|
const waitForSendState = waitForMockCall(client.sendStateEvent);
|
||||||
const waitForUpdateDelaye = waitForMockCallOnce(
|
const waitForRestartScheduledDelayedEvent = waitForMockCallOnce(
|
||||||
client._unstable_updateDelayedEvent,
|
client._unstable_restartScheduledDelayedEvent,
|
||||||
Promise.reject(new MatrixError({ errcode: "M_NOT_FOUND" })),
|
Promise.reject(new MatrixError({ errcode: "M_NOT_FOUND" })),
|
||||||
);
|
);
|
||||||
memberManager.join([focus], focusActive);
|
memberManager.join([focus], focusActive);
|
||||||
await waitForSendState;
|
await waitForSendState;
|
||||||
await waitForUpdateDelaye;
|
await waitForRestartScheduledDelayedEvent;
|
||||||
await jest.advanceTimersByTimeAsync(1);
|
await jest.advanceTimersByTimeAsync(1);
|
||||||
// Once for the initial event and once because of the errcode: "M_NOT_FOUND"
|
// Once for the initial event and once because of the errcode: "M_NOT_FOUND"
|
||||||
// Different to "sends a membership event and schedules delayed leave when joining a call" where its only called once (1)
|
// Different to "sends a membership event and schedules delayed leave when joining a call" where its only called once (1)
|
||||||
@@ -179,7 +184,7 @@ describe("MembershipManager", () => {
|
|||||||
if (useOwnedStateEvents) {
|
if (useOwnedStateEvents) {
|
||||||
room.getVersion = jest.fn().mockReturnValue("org.matrix.msc3757.default");
|
room.getVersion = jest.fn().mockReturnValue("org.matrix.msc3757.default");
|
||||||
}
|
}
|
||||||
const updatedDelayedEvent = waitForMockCall(client._unstable_updateDelayedEvent);
|
const restartScheduledDelayedEvent = waitForMockCall(client._unstable_restartScheduledDelayedEvent);
|
||||||
const sentDelayedState = waitForMockCall(
|
const sentDelayedState = waitForMockCall(
|
||||||
client._unstable_sendDelayedStateEvent,
|
client._unstable_sendDelayedStateEvent,
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
@@ -265,13 +270,13 @@ describe("MembershipManager", () => {
|
|||||||
await sentDelayedState;
|
await sentDelayedState;
|
||||||
|
|
||||||
// should have prepared the heartbeat to keep delaying the leave event while still connected
|
// should have prepared the heartbeat to keep delaying the leave event while still connected
|
||||||
await updatedDelayedEvent;
|
await restartScheduledDelayedEvent;
|
||||||
expect(client._unstable_updateDelayedEvent).toHaveBeenCalledTimes(1);
|
expect(client._unstable_restartScheduledDelayedEvent).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
// ensures that we reach the code that schedules the timeout for the next delay update before we advance the timers.
|
// ensures that we reach the code that schedules the timeout for the next delay update before we advance the timers.
|
||||||
await jest.advanceTimersByTimeAsync(5000);
|
await jest.advanceTimersByTimeAsync(5000);
|
||||||
// should update delayed disconnect
|
// should update delayed disconnect
|
||||||
expect(client._unstable_updateDelayedEvent).toHaveBeenCalledTimes(2);
|
expect(client._unstable_restartScheduledDelayedEvent).toHaveBeenCalledTimes(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
it("sends a membership event after rate limits during delayed event setup when joining a call", async () => {
|
it("sends a membership event after rate limits during delayed event setup when joining a call", async () => {
|
||||||
@@ -343,7 +348,7 @@ describe("MembershipManager", () => {
|
|||||||
// (onRTCSessionMemberUpdate)
|
// (onRTCSessionMemberUpdate)
|
||||||
// - Only then do we resolve the sending of the delayed event.
|
// - Only then do we resolve the sending of the delayed event.
|
||||||
// - We test that the manager acknowledges the leave and sends a new membership state event.
|
// - We test that the manager acknowledges the leave and sends a new membership state event.
|
||||||
(client._unstable_updateDelayedEvent as Mock<any>).mockRejectedValueOnce(
|
(client._unstable_restartScheduledDelayedEvent as Mock<any>).mockRejectedValueOnce(
|
||||||
new MatrixError({ errcode: "M_NOT_FOUND" }),
|
new MatrixError({ errcode: "M_NOT_FOUND" }),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -404,17 +409,17 @@ describe("MembershipManager", () => {
|
|||||||
manager.join([focus]);
|
manager.join([focus]);
|
||||||
await jest.advanceTimersByTimeAsync(1);
|
await jest.advanceTimersByTimeAsync(1);
|
||||||
await manager.leave();
|
await manager.leave();
|
||||||
expect(client._unstable_updateDelayedEvent).toHaveBeenLastCalledWith("id", "send");
|
expect(client._unstable_sendScheduledDelayedEvent).toHaveBeenLastCalledWith("id");
|
||||||
expect(client.sendStateEvent).toHaveBeenCalled();
|
expect(client.sendStateEvent).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
it("send leave event when leave is called and resolving delayed leave fails", async () => {
|
it("send leave event when leave is called and resolving delayed leave fails", async () => {
|
||||||
const manager = new MembershipManager({}, room, client, callSession);
|
const manager = new MembershipManager({}, room, client, callSession);
|
||||||
manager.join([focus]);
|
manager.join([focus]);
|
||||||
await jest.advanceTimersByTimeAsync(1);
|
await jest.advanceTimersByTimeAsync(1);
|
||||||
(client._unstable_updateDelayedEvent as Mock<any>).mockRejectedValue("unknown");
|
(client._unstable_sendScheduledDelayedEvent as Mock<any>).mockRejectedValue("unknown");
|
||||||
await manager.leave();
|
await manager.leave();
|
||||||
|
|
||||||
// We send a normal leave event since we failed using updateDelayedEvent with the "send" action.
|
// We send a normal leave event since we failed using sendScheduledDelayedEvent.
|
||||||
expect(client.sendStateEvent).toHaveBeenLastCalledWith(
|
expect(client.sendStateEvent).toHaveBeenLastCalledWith(
|
||||||
room.roomId,
|
room.roomId,
|
||||||
"org.matrix.msc3401.call.member",
|
"org.matrix.msc3401.call.member",
|
||||||
@@ -438,6 +443,9 @@ describe("MembershipManager", () => {
|
|||||||
expect(client.sendStateEvent).not.toHaveBeenCalled();
|
expect(client.sendStateEvent).not.toHaveBeenCalled();
|
||||||
expect(client._unstable_sendDelayedStateEvent).not.toHaveBeenCalled();
|
expect(client._unstable_sendDelayedStateEvent).not.toHaveBeenCalled();
|
||||||
expect(client._unstable_updateDelayedEvent).not.toHaveBeenCalled();
|
expect(client._unstable_updateDelayedEvent).not.toHaveBeenCalled();
|
||||||
|
expect(client._unstable_cancelScheduledDelayedEvent).not.toHaveBeenCalled();
|
||||||
|
expect(client._unstable_restartScheduledDelayedEvent).not.toHaveBeenCalled();
|
||||||
|
expect(client._unstable_sendScheduledDelayedEvent).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
it("does nothing if own membership still present", async () => {
|
it("does nothing if own membership still present", async () => {
|
||||||
const manager = new MembershipManager({}, room, client, callSession);
|
const manager = new MembershipManager({}, room, client, callSession);
|
||||||
@@ -447,6 +455,9 @@ describe("MembershipManager", () => {
|
|||||||
// reset all mocks before checking what happens when calling: `onRTCSessionMemberUpdate`
|
// reset all mocks before checking what happens when calling: `onRTCSessionMemberUpdate`
|
||||||
(client.sendStateEvent as Mock).mockClear();
|
(client.sendStateEvent as Mock).mockClear();
|
||||||
(client._unstable_updateDelayedEvent as Mock).mockClear();
|
(client._unstable_updateDelayedEvent as Mock).mockClear();
|
||||||
|
(client._unstable_cancelScheduledDelayedEvent as Mock).mockClear();
|
||||||
|
(client._unstable_restartScheduledDelayedEvent as Mock).mockClear();
|
||||||
|
(client._unstable_sendScheduledDelayedEvent as Mock).mockClear();
|
||||||
(client._unstable_sendDelayedStateEvent as Mock).mockClear();
|
(client._unstable_sendDelayedStateEvent as Mock).mockClear();
|
||||||
|
|
||||||
await manager.onRTCSessionMemberUpdate([
|
await manager.onRTCSessionMemberUpdate([
|
||||||
@@ -462,6 +473,9 @@ describe("MembershipManager", () => {
|
|||||||
expect(client.sendStateEvent).not.toHaveBeenCalled();
|
expect(client.sendStateEvent).not.toHaveBeenCalled();
|
||||||
expect(client._unstable_sendDelayedStateEvent).not.toHaveBeenCalled();
|
expect(client._unstable_sendDelayedStateEvent).not.toHaveBeenCalled();
|
||||||
expect(client._unstable_updateDelayedEvent).not.toHaveBeenCalled();
|
expect(client._unstable_updateDelayedEvent).not.toHaveBeenCalled();
|
||||||
|
expect(client._unstable_cancelScheduledDelayedEvent).not.toHaveBeenCalled();
|
||||||
|
expect(client._unstable_restartScheduledDelayedEvent).not.toHaveBeenCalled();
|
||||||
|
expect(client._unstable_sendScheduledDelayedEvent).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
it("recreates membership if it is missing", async () => {
|
it("recreates membership if it is missing", async () => {
|
||||||
const manager = new MembershipManager({}, room, client, callSession);
|
const manager = new MembershipManager({}, room, client, callSession);
|
||||||
@@ -469,7 +483,7 @@ describe("MembershipManager", () => {
|
|||||||
await jest.advanceTimersByTimeAsync(1);
|
await jest.advanceTimersByTimeAsync(1);
|
||||||
// clearing all mocks before checking what happens when calling: `onRTCSessionMemberUpdate`
|
// clearing all mocks before checking what happens when calling: `onRTCSessionMemberUpdate`
|
||||||
(client.sendStateEvent as Mock).mockClear();
|
(client.sendStateEvent as Mock).mockClear();
|
||||||
(client._unstable_updateDelayedEvent as Mock).mockClear();
|
(client._unstable_restartScheduledDelayedEvent as Mock).mockClear();
|
||||||
(client._unstable_sendDelayedStateEvent as Mock).mockClear();
|
(client._unstable_sendDelayedStateEvent as Mock).mockClear();
|
||||||
|
|
||||||
// Our own membership is removed:
|
// Our own membership is removed:
|
||||||
@@ -478,7 +492,7 @@ describe("MembershipManager", () => {
|
|||||||
expect(client.sendStateEvent).toHaveBeenCalled();
|
expect(client.sendStateEvent).toHaveBeenCalled();
|
||||||
expect(client._unstable_sendDelayedStateEvent).toHaveBeenCalled();
|
expect(client._unstable_sendDelayedStateEvent).toHaveBeenCalled();
|
||||||
|
|
||||||
expect(client._unstable_updateDelayedEvent).toHaveBeenCalled();
|
expect(client._unstable_restartScheduledDelayedEvent).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("updates the UpdateExpiry entry in the action scheduler", async () => {
|
it("updates the UpdateExpiry entry in the action scheduler", async () => {
|
||||||
@@ -487,10 +501,10 @@ describe("MembershipManager", () => {
|
|||||||
await jest.advanceTimersByTimeAsync(1);
|
await jest.advanceTimersByTimeAsync(1);
|
||||||
// clearing all mocks before checking what happens when calling: `onRTCSessionMemberUpdate`
|
// clearing all mocks before checking what happens when calling: `onRTCSessionMemberUpdate`
|
||||||
(client.sendStateEvent as Mock).mockClear();
|
(client.sendStateEvent as Mock).mockClear();
|
||||||
(client._unstable_updateDelayedEvent as Mock).mockClear();
|
(client._unstable_restartScheduledDelayedEvent as Mock).mockClear();
|
||||||
(client._unstable_sendDelayedStateEvent as Mock).mockClear();
|
(client._unstable_sendDelayedStateEvent as Mock).mockClear();
|
||||||
|
|
||||||
(client._unstable_updateDelayedEvent as Mock<any>).mockRejectedValueOnce(
|
(client._unstable_restartScheduledDelayedEvent as Mock<any>).mockRejectedValueOnce(
|
||||||
new MatrixError({ errcode: "M_NOT_FOUND" }),
|
new MatrixError({ errcode: "M_NOT_FOUND" }),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -503,7 +517,7 @@ describe("MembershipManager", () => {
|
|||||||
expect(client.sendStateEvent).toHaveBeenCalled();
|
expect(client.sendStateEvent).toHaveBeenCalled();
|
||||||
expect(client._unstable_sendDelayedStateEvent).toHaveBeenCalled();
|
expect(client._unstable_sendDelayedStateEvent).toHaveBeenCalled();
|
||||||
|
|
||||||
expect(client._unstable_updateDelayedEvent).toHaveBeenCalled();
|
expect(client._unstable_restartScheduledDelayedEvent).toHaveBeenCalled();
|
||||||
expect(manager.status).toBe(Status.Connected);
|
expect(manager.status).toBe(Status.Connected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -523,17 +537,17 @@ describe("MembershipManager", () => {
|
|||||||
|
|
||||||
// The first call is from checking id the server deleted the delayed event
|
// The first call is from checking id the server deleted the delayed event
|
||||||
// so it does not need a `advanceTimersByTime`
|
// so it does not need a `advanceTimersByTime`
|
||||||
expect(client._unstable_updateDelayedEvent).toHaveBeenCalledTimes(1);
|
expect(client._unstable_restartScheduledDelayedEvent).toHaveBeenCalledTimes(1);
|
||||||
// TODO: Check that update delayed event is called with the correct HTTP request timeout
|
// TODO: Check that update delayed event is called with the correct HTTP request timeout
|
||||||
// expect(client._unstable_updateDelayedEvent).toHaveBeenLastCalledWith("id", 10_000, { localTimeoutMs: 20_000 });
|
// expect(client._unstable_restartScheduledDelayedEvent).toHaveBeenLastCalledWith("id", 10_000, { localTimeoutMs: 20_000 });
|
||||||
|
|
||||||
for (let i = 2; i <= 12; i++) {
|
for (let i = 2; i <= 12; i++) {
|
||||||
// flush promises before advancing the timers to make sure schedulers are setup
|
// flush promises before advancing the timers to make sure schedulers are setup
|
||||||
await jest.advanceTimersByTimeAsync(10_000);
|
await jest.advanceTimersByTimeAsync(10_000);
|
||||||
|
|
||||||
expect(client._unstable_updateDelayedEvent).toHaveBeenCalledTimes(i);
|
expect(client._unstable_restartScheduledDelayedEvent).toHaveBeenCalledTimes(i);
|
||||||
// TODO: Check that update delayed event is called with the correct HTTP request timeout
|
// TODO: Check that update delayed event is called with the correct HTTP request timeout
|
||||||
// expect(client._unstable_updateDelayedEvent).toHaveBeenLastCalledWith("id", 10_000, { localTimeoutMs: 20_000 });
|
// expect(client._unstable_restartScheduledDelayedEvent).toHaveBeenLastCalledWith("id", 10_000, { localTimeoutMs: 20_000 });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -681,7 +695,7 @@ describe("MembershipManager", () => {
|
|||||||
});
|
});
|
||||||
describe("retries sending update delayed leave event restart", () => {
|
describe("retries sending update delayed leave event restart", () => {
|
||||||
it("resends the initial check delayed update event", async () => {
|
it("resends the initial check delayed update event", async () => {
|
||||||
(client._unstable_updateDelayedEvent as Mock<any>).mockRejectedValue(
|
(client._unstable_restartScheduledDelayedEvent as Mock<any>).mockRejectedValue(
|
||||||
new MatrixError(
|
new MatrixError(
|
||||||
{ errcode: "M_LIMIT_EXCEEDED" },
|
{ errcode: "M_LIMIT_EXCEEDED" },
|
||||||
429,
|
429,
|
||||||
@@ -695,17 +709,17 @@ describe("MembershipManager", () => {
|
|||||||
|
|
||||||
// Hit rate limit
|
// Hit rate limit
|
||||||
await jest.advanceTimersByTimeAsync(1);
|
await jest.advanceTimersByTimeAsync(1);
|
||||||
expect(client._unstable_updateDelayedEvent).toHaveBeenCalledTimes(1);
|
expect(client._unstable_restartScheduledDelayedEvent).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
// Hit second rate limit.
|
// Hit second rate limit.
|
||||||
await jest.advanceTimersByTimeAsync(1000);
|
await jest.advanceTimersByTimeAsync(1000);
|
||||||
expect(client._unstable_updateDelayedEvent).toHaveBeenCalledTimes(2);
|
expect(client._unstable_restartScheduledDelayedEvent).toHaveBeenCalledTimes(2);
|
||||||
|
|
||||||
// Setup resolve
|
// Setup resolve
|
||||||
(client._unstable_updateDelayedEvent as Mock<any>).mockResolvedValue(undefined);
|
(client._unstable_restartScheduledDelayedEvent as Mock<any>).mockResolvedValue(undefined);
|
||||||
await jest.advanceTimersByTimeAsync(1000);
|
await jest.advanceTimersByTimeAsync(1000);
|
||||||
|
|
||||||
expect(client._unstable_updateDelayedEvent).toHaveBeenCalledTimes(3);
|
expect(client._unstable_restartScheduledDelayedEvent).toHaveBeenCalledTimes(3);
|
||||||
expect(client.sendStateEvent).toHaveBeenCalledTimes(1);
|
expect(client.sendStateEvent).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -734,7 +748,7 @@ describe("MembershipManager", () => {
|
|||||||
// because legacy does not have a retry limit and no mechanism to communicate unrecoverable errors.
|
// because legacy does not have a retry limit and no mechanism to communicate unrecoverable errors.
|
||||||
it("throws, when reaching maximum number of retries", async () => {
|
it("throws, when reaching maximum number of retries", async () => {
|
||||||
const delayEventRestartError = jest.fn();
|
const delayEventRestartError = jest.fn();
|
||||||
(client._unstable_updateDelayedEvent as Mock<any>).mockRejectedValue(
|
(client._unstable_restartScheduledDelayedEvent as Mock<any>).mockRejectedValue(
|
||||||
new MatrixError(
|
new MatrixError(
|
||||||
{ errcode: "M_LIMIT_EXCEEDED" },
|
{ errcode: "M_LIMIT_EXCEEDED" },
|
||||||
429,
|
429,
|
||||||
@@ -808,11 +822,11 @@ describe("MembershipManager", () => {
|
|||||||
manager.join([focus], focusActive);
|
manager.join([focus], focusActive);
|
||||||
try {
|
try {
|
||||||
// Let the scheduler run one iteration so that we can send the join state event
|
// Let the scheduler run one iteration so that we can send the join state event
|
||||||
await waitForMockCall(client._unstable_updateDelayedEvent);
|
await waitForMockCall(client._unstable_restartScheduledDelayedEvent);
|
||||||
|
|
||||||
// We never resolve the delayed event so that we can test the probablyLeft event.
|
// We never resolve the delayed event so that we can test the probablyLeft event.
|
||||||
// This simulates the case where the server does not respond to the delayed event.
|
// This simulates the case where the server does not respond to the delayed event.
|
||||||
client._unstable_updateDelayedEvent = jest.fn(() => stuckPromise);
|
client._unstable_restartScheduledDelayedEvent = jest.fn(() => stuckPromise);
|
||||||
expect(client.sendStateEvent).toHaveBeenCalledTimes(1);
|
expect(client.sendStateEvent).toHaveBeenCalledTimes(1);
|
||||||
expect(manager.status).toBe(Status.Connected);
|
expect(manager.status).toBe(Status.Connected);
|
||||||
expect(probablyLeftEmit).not.toHaveBeenCalledWith(true);
|
expect(probablyLeftEmit).not.toHaveBeenCalledWith(true);
|
||||||
@@ -822,18 +836,18 @@ describe("MembershipManager", () => {
|
|||||||
await jest.advanceTimersByTimeAsync(5000);
|
await jest.advanceTimersByTimeAsync(5000);
|
||||||
// No emission after 5s
|
// No emission after 5s
|
||||||
expect(probablyLeftEmit).not.toHaveBeenCalledWith(true);
|
expect(probablyLeftEmit).not.toHaveBeenCalledWith(true);
|
||||||
expect(client._unstable_updateDelayedEvent).toHaveBeenCalledTimes(1);
|
expect(client._unstable_restartScheduledDelayedEvent).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
await jest.advanceTimersByTimeAsync(4999);
|
await jest.advanceTimersByTimeAsync(4999);
|
||||||
expect(client._unstable_updateDelayedEvent).toHaveBeenCalledTimes(3);
|
expect(client._unstable_restartScheduledDelayedEvent).toHaveBeenCalledTimes(3);
|
||||||
expect(probablyLeftEmit).not.toHaveBeenCalledWith(true);
|
expect(probablyLeftEmit).not.toHaveBeenCalledWith(true);
|
||||||
|
|
||||||
// Reset mocks before we setup the next delayed event restart by advancing the timers 1 more ms.
|
// Reset mocks before we setup the next delayed event restart by advancing the timers 1 more ms.
|
||||||
(client._unstable_updateDelayedEvent as Mock<any>).mockResolvedValue({});
|
(client._unstable_restartScheduledDelayedEvent as Mock<any>).mockResolvedValue({});
|
||||||
|
|
||||||
// Emit after 10s
|
// Emit after 10s
|
||||||
await jest.advanceTimersByTimeAsync(1);
|
await jest.advanceTimersByTimeAsync(1);
|
||||||
expect(client._unstable_updateDelayedEvent).toHaveBeenCalledTimes(4);
|
expect(client._unstable_restartScheduledDelayedEvent).toHaveBeenCalledTimes(4);
|
||||||
expect(probablyLeftEmit).toHaveBeenCalledWith(true);
|
expect(probablyLeftEmit).toHaveBeenCalledWith(true);
|
||||||
|
|
||||||
// Mock a sync which does not include our own membership
|
// Mock a sync which does not include our own membership
|
||||||
@@ -898,8 +912,8 @@ describe("MembershipManager", () => {
|
|||||||
describe("join()", () => {
|
describe("join()", () => {
|
||||||
describe("sends an rtc membership event", () => {
|
describe("sends an rtc membership event", () => {
|
||||||
it("sends a membership event and schedules delayed leave when joining a call", async () => {
|
it("sends a membership event and schedules delayed leave when joining a call", async () => {
|
||||||
const updateDelayedEventHandle = createAsyncHandle<void>(
|
const restartScheduledDelayedEventHandle = createAsyncHandle<void>(
|
||||||
client._unstable_updateDelayedEvent as Mock,
|
client._unstable_restartScheduledDelayedEvent as Mock,
|
||||||
);
|
);
|
||||||
const memberManager = new StickyEventMembershipManager(undefined, room, client, callSession);
|
const memberManager = new StickyEventMembershipManager(undefined, room, client, callSession);
|
||||||
|
|
||||||
@@ -925,7 +939,7 @@ describe("MembershipManager", () => {
|
|||||||
msc4354_sticky_key: "_@alice:example.org_AAAAAAA_m.call",
|
msc4354_sticky_key: "_@alice:example.org_AAAAAAA_m.call",
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
updateDelayedEventHandle.resolve?.();
|
restartScheduledDelayedEventHandle.resolve?.();
|
||||||
|
|
||||||
// Ensure we have sent the delayed disconnect event.
|
// Ensure we have sent the delayed disconnect event.
|
||||||
expect(client._unstable_sendStickyDelayedEvent).toHaveBeenCalledWith(
|
expect(client._unstable_sendStickyDelayedEvent).toHaveBeenCalledWith(
|
||||||
|
|||||||
@@ -52,6 +52,9 @@ export type MockClient = Pick<
|
|||||||
| "sendStateEvent"
|
| "sendStateEvent"
|
||||||
| "_unstable_sendDelayedStateEvent"
|
| "_unstable_sendDelayedStateEvent"
|
||||||
| "_unstable_updateDelayedEvent"
|
| "_unstable_updateDelayedEvent"
|
||||||
|
| "_unstable_cancelScheduledDelayedEvent"
|
||||||
|
| "_unstable_restartScheduledDelayedEvent"
|
||||||
|
| "_unstable_sendScheduledDelayedEvent"
|
||||||
| "_unstable_sendStickyEvent"
|
| "_unstable_sendStickyEvent"
|
||||||
| "_unstable_sendStickyDelayedEvent"
|
| "_unstable_sendStickyDelayedEvent"
|
||||||
| "cancelPendingEvent"
|
| "cancelPendingEvent"
|
||||||
@@ -67,6 +70,9 @@ export function makeMockClient(userId: string, deviceId: string): MockClient {
|
|||||||
sendStateEvent: jest.fn(),
|
sendStateEvent: jest.fn(),
|
||||||
cancelPendingEvent: jest.fn(),
|
cancelPendingEvent: jest.fn(),
|
||||||
_unstable_updateDelayedEvent: jest.fn(),
|
_unstable_updateDelayedEvent: jest.fn(),
|
||||||
|
_unstable_cancelScheduledDelayedEvent: jest.fn(),
|
||||||
|
_unstable_restartScheduledDelayedEvent: jest.fn(),
|
||||||
|
_unstable_sendScheduledDelayedEvent: jest.fn(),
|
||||||
_unstable_sendDelayedStateEvent: jest.fn(),
|
_unstable_sendDelayedStateEvent: jest.fn(),
|
||||||
_unstable_sendStickyEvent: jest.fn(),
|
_unstable_sendStickyEvent: jest.fn(),
|
||||||
_unstable_sendStickyDelayedEvent: jest.fn(),
|
_unstable_sendStickyDelayedEvent: jest.fn(),
|
||||||
|
|||||||
123
src/client.ts
123
src/client.ts
@@ -106,6 +106,7 @@ import { RoomMemberEvent, type RoomMemberEventHandlerMap } from "./models/room-m
|
|||||||
import { type IPowerLevelsContent, type RoomStateEvent, type RoomStateEventHandlerMap } from "./models/room-state.ts";
|
import { type IPowerLevelsContent, type RoomStateEvent, type RoomStateEventHandlerMap } from "./models/room-state.ts";
|
||||||
import {
|
import {
|
||||||
isSendDelayedEventRequestOpts,
|
isSendDelayedEventRequestOpts,
|
||||||
|
UpdateDelayedEventAction,
|
||||||
type DelayedEventInfo,
|
type DelayedEventInfo,
|
||||||
type IAddThreePidOnlyBody,
|
type IAddThreePidOnlyBody,
|
||||||
type IBindThreePidBody,
|
type IBindThreePidBody,
|
||||||
@@ -130,7 +131,6 @@ import {
|
|||||||
type KnockRoomOpts,
|
type KnockRoomOpts,
|
||||||
type SendDelayedEventRequestOpts,
|
type SendDelayedEventRequestOpts,
|
||||||
type SendDelayedEventResponse,
|
type SendDelayedEventResponse,
|
||||||
type UpdateDelayedEventAction,
|
|
||||||
} from "./@types/requests.ts";
|
} from "./@types/requests.ts";
|
||||||
import {
|
import {
|
||||||
type AccountDataEvents,
|
type AccountDataEvents,
|
||||||
@@ -3570,8 +3570,13 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
|||||||
*
|
*
|
||||||
* Note: This endpoint is unstable, and can throw an `Error`.
|
* Note: This endpoint is unstable, and can throw an `Error`.
|
||||||
* Check progress on [MSC4140](https://github.com/matrix-org/matrix-spec-proposals/pull/4140) for more details.
|
* Check progress on [MSC4140](https://github.com/matrix-org/matrix-spec-proposals/pull/4140) for more details.
|
||||||
|
*
|
||||||
|
* @deprecated Instead use one of:
|
||||||
|
* - {@link _unstable_cancelScheduledDelayedEvent}
|
||||||
|
* - {@link _unstable_restartScheduledDelayedEvent}
|
||||||
|
* - {@link _unstable_sendScheduledDelayedEvent}
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
public async _unstable_updateDelayedEvent(
|
public async _unstable_updateDelayedEvent(
|
||||||
delayId: string,
|
delayId: string,
|
||||||
action: UpdateDelayedEventAction,
|
action: UpdateDelayedEventAction,
|
||||||
@@ -3583,17 +3588,123 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
|||||||
"updateDelayedEvent",
|
"updateDelayedEvent",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
return await this.updateScheduledDelayedEventWithActionInBody(delayId, action, requestOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel the scheduled delivery of the delayed event matching the provided delayId.
|
||||||
|
*
|
||||||
|
* Note: This endpoint is unstable, and can throw an `Error`.
|
||||||
|
* Check progress on [MSC4140](https://github.com/matrix-org/matrix-spec-proposals/pull/4140) for more details.
|
||||||
|
*
|
||||||
|
* @throws A M_NOT_FOUND error if no matching delayed event could be found.
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
public async _unstable_cancelScheduledDelayedEvent(
|
||||||
|
delayId: string,
|
||||||
|
requestOptions: IRequestOpts = {},
|
||||||
|
): Promise<EmptyObject> {
|
||||||
|
return await this.updateScheduledDelayedEvent(delayId, UpdateDelayedEventAction.Cancel, requestOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restart the scheduled delivery of the delayed event matching the given delayId.
|
||||||
|
*
|
||||||
|
* Note: This endpoint is unstable, and can throw an `Error`.
|
||||||
|
* Check progress on [MSC4140](https://github.com/matrix-org/matrix-spec-proposals/pull/4140) for more details.
|
||||||
|
*
|
||||||
|
* @throws A M_NOT_FOUND error if no matching delayed event could be found.
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
public async _unstable_restartScheduledDelayedEvent(
|
||||||
|
delayId: string,
|
||||||
|
requestOptions: IRequestOpts = {},
|
||||||
|
): Promise<EmptyObject> {
|
||||||
|
return await this.updateScheduledDelayedEvent(delayId, UpdateDelayedEventAction.Restart, requestOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Immediately send the delayed event matching the given delayId,
|
||||||
|
* instead of waiting for its scheduled delivery.
|
||||||
|
*
|
||||||
|
* Note: This endpoint is unstable, and can throw an `Error`.
|
||||||
|
* Check progress on [MSC4140](https://github.com/matrix-org/matrix-spec-proposals/pull/4140) for more details.
|
||||||
|
*
|
||||||
|
* @throws A M_NOT_FOUND error if no matching delayed event could be found.
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
public async _unstable_sendScheduledDelayedEvent(
|
||||||
|
delayId: string,
|
||||||
|
requestOptions: IRequestOpts = {},
|
||||||
|
): Promise<EmptyObject> {
|
||||||
|
return await this.updateScheduledDelayedEvent(delayId, UpdateDelayedEventAction.Send, requestOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async updateScheduledDelayedEvent(
|
||||||
|
delayId: string,
|
||||||
|
action: UpdateDelayedEventAction,
|
||||||
|
requestOptions: IRequestOpts = {},
|
||||||
|
): Promise<EmptyObject> {
|
||||||
|
if (!(await this.doesServerSupportUnstableFeature(UNSTABLE_MSC4140_DELAYED_EVENTS))) {
|
||||||
|
throw new UnsupportedDelayedEventsEndpointError(
|
||||||
|
"Server does not support the delayed events API",
|
||||||
|
`${action}ScheduledDelayedEvent`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const path = utils.encodeUri("/delayed_events/$delayId/$action", {
|
||||||
|
$delayId: delayId,
|
||||||
|
$action: action,
|
||||||
|
});
|
||||||
|
return await this.http.request(Method.Post, path, undefined, undefined, {
|
||||||
|
...requestOptions,
|
||||||
|
prefix: `${ClientPrefix.Unstable}/${UNSTABLE_MSC4140_DELAYED_EVENTS}`,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof MatrixError && e.errcode === "M_UNRECOGNIZED") {
|
||||||
|
// For backwards compatibility with an older version of this endpoint
|
||||||
|
// which put the update action in the request body instead of the path
|
||||||
|
return await this.updateScheduledDelayedEventWithActionInBody(delayId, action, requestOptions);
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Present for backwards compatibility with an older version of MSC4140
|
||||||
|
* which had a single, authenticated endpoint for updating a delayed event, instead
|
||||||
|
* of one unauthenticated endpoint per update action.
|
||||||
|
*/
|
||||||
|
private async updateScheduledDelayedEventWithActionInBody(
|
||||||
|
delayId: string,
|
||||||
|
action: UpdateDelayedEventAction,
|
||||||
|
requestOptions: IRequestOpts = {},
|
||||||
|
): Promise<EmptyObject> {
|
||||||
const path = utils.encodeUri("/delayed_events/$delayId", {
|
const path = utils.encodeUri("/delayed_events/$delayId", {
|
||||||
$delayId: delayId,
|
$delayId: delayId,
|
||||||
});
|
});
|
||||||
const data = {
|
const data = {
|
||||||
action,
|
action,
|
||||||
};
|
};
|
||||||
return await this.http.authedRequest(Method.Post, path, undefined, data, {
|
try {
|
||||||
...requestOptions,
|
return await this.http.request(Method.Post, path, undefined, data, {
|
||||||
prefix: `${ClientPrefix.Unstable}/${UNSTABLE_MSC4140_DELAYED_EVENTS}`,
|
...requestOptions,
|
||||||
});
|
prefix: `${ClientPrefix.Unstable}/${UNSTABLE_MSC4140_DELAYED_EVENTS}`,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof MatrixError && e.errcode === "M_MISSING_TOKEN") {
|
||||||
|
// For backwards compatibility with an older version of this endpoint
|
||||||
|
// which required authentication
|
||||||
|
return await this.http.authedRequest(Method.Post, path, undefined, data, {
|
||||||
|
...requestOptions,
|
||||||
|
prefix: `${ClientPrefix.Unstable}/${UNSTABLE_MSC4140_DELAYED_EVENTS}`,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ import {
|
|||||||
type ISendEventResponse,
|
type ISendEventResponse,
|
||||||
type SendDelayedEventRequestOpts,
|
type SendDelayedEventRequestOpts,
|
||||||
type SendDelayedEventResponse,
|
type SendDelayedEventResponse,
|
||||||
type UpdateDelayedEventAction,
|
UpdateDelayedEventAction,
|
||||||
} from "./@types/requests.ts";
|
} from "./@types/requests.ts";
|
||||||
import { EventType, type StateEvents } from "./@types/event.ts";
|
import { EventType, type StateEvents } from "./@types/event.ts";
|
||||||
import { logger } from "./logger.ts";
|
import { logger } from "./logger.ts";
|
||||||
@@ -459,8 +459,12 @@ export class RoomWidgetClient extends MatrixClient {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @experimental This currently relies on an unstable MSC (MSC4140).
|
* @experimental This currently relies on an unstable MSC (MSC4140).
|
||||||
|
* @deprecated Instead use one of:
|
||||||
|
* - {@link _unstable_cancelScheduledDelayedEvent}
|
||||||
|
* - {@link _unstable_restartScheduledDelayedEvent}
|
||||||
|
* - {@link _unstable_sendScheduledDelayedEvent}
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
public async _unstable_updateDelayedEvent(delayId: string, action: UpdateDelayedEventAction): Promise<EmptyObject> {
|
public async _unstable_updateDelayedEvent(delayId: string, action: UpdateDelayedEventAction): Promise<EmptyObject> {
|
||||||
if (!(await this.doesServerSupportUnstableFeature(UNSTABLE_MSC4140_DELAYED_EVENTS))) {
|
if (!(await this.doesServerSupportUnstableFeature(UNSTABLE_MSC4140_DELAYED_EVENTS))) {
|
||||||
throw new UnsupportedDelayedEventsEndpointError(
|
throw new UnsupportedDelayedEventsEndpointError(
|
||||||
@@ -469,7 +473,67 @@ export class RoomWidgetClient extends MatrixClient {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.widgetApi.updateDelayedEvent(delayId, action).catch(timeoutToConnectionError);
|
let updateDelayedEvent: (delayId: string) => Promise<unknown>;
|
||||||
|
switch (action) {
|
||||||
|
case UpdateDelayedEventAction.Cancel:
|
||||||
|
updateDelayedEvent = this.widgetApi.cancelScheduledDelayedEvent;
|
||||||
|
break;
|
||||||
|
case UpdateDelayedEventAction.Restart:
|
||||||
|
updateDelayedEvent = this.widgetApi.cancelScheduledDelayedEvent;
|
||||||
|
break;
|
||||||
|
case UpdateDelayedEventAction.Send:
|
||||||
|
updateDelayedEvent = this.widgetApi.sendScheduledDelayedEvent;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
await updateDelayedEvent.call(this.widgetApi, delayId).catch(timeoutToConnectionError);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @experimental This currently relies on an unstable MSC (MSC4140).
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
public async _unstable_cancelScheduledDelayedEvent(delayId: string): Promise<EmptyObject> {
|
||||||
|
if (!(await this.doesServerSupportUnstableFeature(UNSTABLE_MSC4140_DELAYED_EVENTS))) {
|
||||||
|
throw new UnsupportedDelayedEventsEndpointError(
|
||||||
|
"Server does not support the delayed events API",
|
||||||
|
"cancelScheduledDelayedEvent",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.widgetApi.cancelScheduledDelayedEvent(delayId).catch(timeoutToConnectionError);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @experimental This currently relies on an unstable MSC (MSC4140).
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
public async _unstable_restartScheduledDelayedEvent(delayId: string): Promise<EmptyObject> {
|
||||||
|
if (!(await this.doesServerSupportUnstableFeature(UNSTABLE_MSC4140_DELAYED_EVENTS))) {
|
||||||
|
throw new UnsupportedDelayedEventsEndpointError(
|
||||||
|
"Server does not support the delayed events API",
|
||||||
|
"restartScheduledDelayedEvent",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.widgetApi.restartScheduledDelayedEvent(delayId).catch(timeoutToConnectionError);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @experimental This currently relies on an unstable MSC (MSC4140).
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
public async _unstable_sendScheduledDelayedEvent(delayId: string): Promise<EmptyObject> {
|
||||||
|
if (!(await this.doesServerSupportUnstableFeature(UNSTABLE_MSC4140_DELAYED_EVENTS))) {
|
||||||
|
throw new UnsupportedDelayedEventsEndpointError(
|
||||||
|
"Server does not support the delayed events API",
|
||||||
|
"sendScheduledDelayedEvent",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.widgetApi.sendScheduledDelayedEvent(delayId).catch(timeoutToConnectionError);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,14 @@ export class ClientStoppedError extends Error {
|
|||||||
export class UnsupportedDelayedEventsEndpointError extends Error {
|
export class UnsupportedDelayedEventsEndpointError extends Error {
|
||||||
public constructor(
|
public constructor(
|
||||||
message: string,
|
message: string,
|
||||||
public clientEndpoint: "sendDelayedEvent" | "updateDelayedEvent" | "sendDelayedStateEvent" | "getDelayedEvents",
|
public clientEndpoint:
|
||||||
|
| "sendDelayedEvent"
|
||||||
|
| "updateDelayedEvent"
|
||||||
|
| "cancelScheduledDelayedEvent"
|
||||||
|
| "restartScheduledDelayedEvent"
|
||||||
|
| "sendScheduledDelayedEvent"
|
||||||
|
| "sendDelayedStateEvent"
|
||||||
|
| "getDelayedEvents",
|
||||||
) {
|
) {
|
||||||
super(message);
|
super(message);
|
||||||
this.name = "UnsupportedDelayedEventsEndpointError";
|
this.name = "UnsupportedDelayedEventsEndpointError";
|
||||||
|
|||||||
@@ -178,7 +178,8 @@ export interface MembershipConfig {
|
|||||||
* In the presence of network packet loss (hurting TCP connections), the custom delayedEventRestartLocalTimeoutMs
|
* In the presence of network packet loss (hurting TCP connections), the custom delayedEventRestartLocalTimeoutMs
|
||||||
* helps by keeping more delayed event reset candidates in flight,
|
* helps by keeping more delayed event reset candidates in flight,
|
||||||
* improving the chances of a successful reset. (its is equivalent to the js-sdk `localTimeout` configuration,
|
* improving the chances of a successful reset. (its is equivalent to the js-sdk `localTimeout` configuration,
|
||||||
* but only applies to calls to the `_unstable_updateDelayedEvent` endpoint with a body of `{action:"restart"}`.)
|
* but only applies to calls to the `_unstable_restartScheduledDelayedEvent` endpoint
|
||||||
|
* or the `_unstable_updateDelayedEvent` endpoint with a body of `{action:"restart"}`.)
|
||||||
*/
|
*/
|
||||||
delayedLeaveEventRestartLocalTimeoutMs?: number;
|
delayedLeaveEventRestartLocalTimeoutMs?: number;
|
||||||
|
|
||||||
@@ -514,6 +515,9 @@ export class MatrixRTCSession extends TypedEventEmitter<
|
|||||||
| "sendStateEvent"
|
| "sendStateEvent"
|
||||||
| "_unstable_sendDelayedStateEvent"
|
| "_unstable_sendDelayedStateEvent"
|
||||||
| "_unstable_updateDelayedEvent"
|
| "_unstable_updateDelayedEvent"
|
||||||
|
| "_unstable_cancelScheduledDelayedEvent"
|
||||||
|
| "_unstable_restartScheduledDelayedEvent"
|
||||||
|
| "_unstable_sendScheduledDelayedEvent"
|
||||||
| "_unstable_sendStickyEvent"
|
| "_unstable_sendStickyEvent"
|
||||||
| "_unstable_sendStickyDelayedEvent"
|
| "_unstable_sendStickyDelayedEvent"
|
||||||
| "cancelPendingEvent"
|
| "cancelPendingEvent"
|
||||||
|
|||||||
@@ -16,11 +16,7 @@ limitations under the License.
|
|||||||
import { AbortError } from "p-retry";
|
import { AbortError } from "p-retry";
|
||||||
|
|
||||||
import { EventType, RelationType } from "../@types/event.ts";
|
import { EventType, RelationType } from "../@types/event.ts";
|
||||||
import {
|
import { type ISendEventResponse, type SendDelayedEventResponse } from "../@types/requests.ts";
|
||||||
type ISendEventResponse,
|
|
||||||
type SendDelayedEventResponse,
|
|
||||||
UpdateDelayedEventAction,
|
|
||||||
} from "../@types/requests.ts";
|
|
||||||
import { type EmptyObject } from "../@types/common.ts";
|
import { type EmptyObject } from "../@types/common.ts";
|
||||||
import type { MatrixClient } from "../client.ts";
|
import type { MatrixClient } from "../client.ts";
|
||||||
import { ConnectionError, HTTPError, MatrixError } from "../http-api/errors.ts";
|
import { ConnectionError, HTTPError, MatrixError } from "../http-api/errors.ts";
|
||||||
@@ -169,7 +165,14 @@ function createReplaceActionUpdate(type: MembershipActionType, offset?: number):
|
|||||||
|
|
||||||
type MembershipManagerClient = Pick<
|
type MembershipManagerClient = Pick<
|
||||||
MatrixClient,
|
MatrixClient,
|
||||||
"getUserId" | "getDeviceId" | "sendStateEvent" | "_unstable_sendDelayedStateEvent" | "_unstable_updateDelayedEvent"
|
| "getUserId"
|
||||||
|
| "getDeviceId"
|
||||||
|
| "sendStateEvent"
|
||||||
|
| "_unstable_sendDelayedStateEvent"
|
||||||
|
| "_unstable_updateDelayedEvent"
|
||||||
|
| "_unstable_cancelScheduledDelayedEvent"
|
||||||
|
| "_unstable_restartScheduledDelayedEvent"
|
||||||
|
| "_unstable_sendScheduledDelayedEvent"
|
||||||
>;
|
>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -544,7 +547,7 @@ export class MembershipManager
|
|||||||
private async cancelKnownDelayIdBeforeSendDelayedEvent(delayId: string): Promise<ActionUpdate> {
|
private async cancelKnownDelayIdBeforeSendDelayedEvent(delayId: string): Promise<ActionUpdate> {
|
||||||
// Remove all running updates and restarts
|
// Remove all running updates and restarts
|
||||||
return await this.client
|
return await this.client
|
||||||
._unstable_updateDelayedEvent(delayId, UpdateDelayedEventAction.Cancel)
|
._unstable_cancelScheduledDelayedEvent(delayId)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.state.delayId = undefined;
|
this.state.delayId = undefined;
|
||||||
this.resetRateLimitCounter(MembershipActionType.SendDelayedEvent);
|
this.resetRateLimitCounter(MembershipActionType.SendDelayedEvent);
|
||||||
@@ -552,7 +555,7 @@ export class MembershipManager
|
|||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
const repeatActionType = MembershipActionType.SendDelayedEvent;
|
const repeatActionType = MembershipActionType.SendDelayedEvent;
|
||||||
const update = this.actionUpdateFromErrors(e, repeatActionType, "updateDelayedEvent");
|
const update = this.actionUpdateFromErrors(e, repeatActionType, "cancelScheduledDelayedEvent");
|
||||||
if (update) return update;
|
if (update) return update;
|
||||||
|
|
||||||
if (this.isNotFoundError(e)) {
|
if (this.isNotFoundError(e)) {
|
||||||
@@ -606,10 +609,7 @@ export class MembershipManager
|
|||||||
|
|
||||||
// The obvious choice here would be to use the `IRequestOpts` to set the timeout. Since this call might be forwarded
|
// The obvious choice here would be to use the `IRequestOpts` to set the timeout. Since this call might be forwarded
|
||||||
// to the widget driver this information would get lost. That is why we mimic the AbortError using the race.
|
// to the widget driver this information would get lost. That is why we mimic the AbortError using the race.
|
||||||
return await Promise.race([
|
return await Promise.race([this.client._unstable_restartScheduledDelayedEvent(delayId), abortPromise])
|
||||||
this.client._unstable_updateDelayedEvent(delayId, UpdateDelayedEventAction.Restart),
|
|
||||||
abortPromise,
|
|
||||||
])
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// Whenever we successfully restart the delayed event we update the `state.expectedServerDelayLeaveTs`
|
// Whenever we successfully restart the delayed event we update the `state.expectedServerDelayLeaveTs`
|
||||||
// which stores the predicted timestamp at which the server will send the delayed leave event if there wont be any further
|
// which stores the predicted timestamp at which the server will send the delayed leave event if there wont be any further
|
||||||
@@ -637,7 +637,7 @@ export class MembershipManager
|
|||||||
if (this.isUnsupportedDelayedEndpoint(e)) return {};
|
if (this.isUnsupportedDelayedEndpoint(e)) return {};
|
||||||
|
|
||||||
// TODO this also needs a test: get rate limit while checking id delayed event is scheduled
|
// TODO this also needs a test: get rate limit while checking id delayed event is scheduled
|
||||||
const update = this.actionUpdateFromErrors(e, repeatActionType, "updateDelayedEvent");
|
const update = this.actionUpdateFromErrors(e, repeatActionType, "restartScheduledDelayedEvent");
|
||||||
if (update) return update;
|
if (update) return update;
|
||||||
|
|
||||||
// In other error cases we have no idea what is happening
|
// In other error cases we have no idea what is happening
|
||||||
@@ -647,7 +647,7 @@ export class MembershipManager
|
|||||||
|
|
||||||
private async sendScheduledDelayedLeaveEventOrFallbackToSendLeaveEvent(delayId: string): Promise<ActionUpdate> {
|
private async sendScheduledDelayedLeaveEventOrFallbackToSendLeaveEvent(delayId: string): Promise<ActionUpdate> {
|
||||||
return await this.client
|
return await this.client
|
||||||
._unstable_updateDelayedEvent(delayId, UpdateDelayedEventAction.Send)
|
._unstable_sendScheduledDelayedEvent(delayId)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.state.hasMemberStateEvent = false;
|
this.state.hasMemberStateEvent = false;
|
||||||
this.resetRateLimitCounter(MembershipActionType.SendScheduledDelayedLeaveEvent);
|
this.resetRateLimitCounter(MembershipActionType.SendScheduledDelayedLeaveEvent);
|
||||||
@@ -661,7 +661,7 @@ export class MembershipManager
|
|||||||
this.state.delayId = undefined;
|
this.state.delayId = undefined;
|
||||||
return createInsertActionUpdate(repeatActionType);
|
return createInsertActionUpdate(repeatActionType);
|
||||||
}
|
}
|
||||||
const update = this.actionUpdateFromErrors(e, repeatActionType, "updateDelayedEvent");
|
const update = this.actionUpdateFromErrors(e, repeatActionType, "sendScheduledDelayedEvent");
|
||||||
if (update) return update;
|
if (update) return update;
|
||||||
|
|
||||||
// On any other error we fall back to SendLeaveEvent (this includes hard errors from rate limiting)
|
// On any other error we fall back to SendLeaveEvent (this includes hard errors from rate limiting)
|
||||||
|
|||||||
@@ -5513,10 +5513,10 @@ matrix-mock-request@^2.5.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
expect "^28.1.0"
|
expect "^28.1.0"
|
||||||
|
|
||||||
matrix-widget-api@^1.10.0:
|
matrix-widget-api@^1.14.0:
|
||||||
version "1.13.1"
|
version "1.14.0"
|
||||||
resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-1.13.1.tgz#5b1caeed2fc58148bcd2984e0546d2d06a1713ad"
|
resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-1.14.0.tgz#aa90c40ace27d3165299f7dbc760a53001ce1446"
|
||||||
integrity sha512-mkOHUVzaN018TCbObfGOSaMW2GoUxOfcxNNlTVx5/HeMk3OSQPQM0C9oEME5Liiv/dBUoSrEB64V8wF7e/gb1w==
|
integrity sha512-DDvZGOQhI/rilPWg5VlLN7pHIsPt0Jt14lsuHDP+KU+fmpAQNITJ6aIld1ZoXWsrVGv2PS3x6K/MHtfruIOQJQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/events" "^3.0.0"
|
"@types/events" "^3.0.0"
|
||||||
events "^3.2.0"
|
events "^3.2.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user