fix(schedule): load scheduled messages from the server; offer to cancel them at logout (#322)
CI / Build & Quality Checks (pull_request) Successful in 2m56s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 7s
CI / Playwright smoke (e2e) (pull_request) Successful in 15m43s
CI / Build & Quality Checks (pull_request) Successful in 2m56s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 7s
CI / Playwright smoke (e2e) (pull_request) Successful in 15m43s
The tray only knew what this browser had scheduled (localStorage), but Synapse lists every pending one via MSC4140 GET /delayed_events. Load it when the tray mounts and on window focus (localStorage stays as a first- paint cache), keeping only non-state m.room.message events. Logout now says "N scheduled messages still send after you log out" and offers to cancel them. Fixes #322 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
3878a85339
commit
8bbb436ca7
@@ -1,4 +1,5 @@
|
||||
import { IContent, MatrixClient, Method } from 'matrix-js-sdk';
|
||||
import type { ScheduledMessage } from '../state/scheduledMessages';
|
||||
|
||||
/**
|
||||
* Schedule a message via MSC4140 (delayed messages).
|
||||
@@ -76,3 +77,61 @@ export async function sendScheduledMessageNow(mx: MatrixClient, delayId: string)
|
||||
{ prefix: '/_matrix/client/unstable/org.matrix.msc4140' },
|
||||
);
|
||||
}
|
||||
|
||||
type DelayedEventItem = {
|
||||
delay_id?: unknown;
|
||||
room_id?: unknown;
|
||||
type?: unknown;
|
||||
state_key?: unknown;
|
||||
content?: unknown;
|
||||
delay?: unknown;
|
||||
delay_ms?: unknown;
|
||||
running_since?: unknown;
|
||||
delayed_since_ts?: unknown;
|
||||
};
|
||||
|
||||
/**
|
||||
* [Gitea #322] Pure: the user's pending scheduled messages from an MSC4140
|
||||
* `GET /delayed_events` response. Synapse answers `{ delayed_events: [...] }`;
|
||||
* the current MSC shape is `{ scheduled: [...] }`. Call-membership delayed
|
||||
* events share the endpoint, so only non-state `m.room.message`s are kept.
|
||||
*/
|
||||
export function parseDelayedEvents(res: unknown): ScheduledMessage[] {
|
||||
if (!res || typeof res !== 'object') return [];
|
||||
const r = res as { delayed_events?: unknown; scheduled?: unknown };
|
||||
const list = Array.isArray(r.delayed_events) ? r.delayed_events : r.scheduled;
|
||||
if (!Array.isArray(list)) return [];
|
||||
const out: ScheduledMessage[] = [];
|
||||
list.forEach((raw: DelayedEventItem) => {
|
||||
if (!raw || typeof raw !== 'object') return;
|
||||
if (raw.type !== 'm.room.message' || raw.state_key !== undefined) return;
|
||||
if (typeof raw.delay_id !== 'string' || typeof raw.room_id !== 'string') return;
|
||||
if (!raw.content || typeof raw.content !== 'object') return;
|
||||
const delay = Number(raw.delay ?? raw.delay_ms);
|
||||
const since = Number(raw.running_since ?? raw.delayed_since_ts);
|
||||
if (!Number.isFinite(delay) || !Number.isFinite(since)) return;
|
||||
out.push({
|
||||
delayId: raw.delay_id,
|
||||
roomId: raw.room_id,
|
||||
content: raw.content as IContent,
|
||||
sendAt: since + delay,
|
||||
});
|
||||
});
|
||||
return out;
|
||||
}
|
||||
|
||||
/** The user's pending scheduled messages, as the server has them. */
|
||||
export async function fetchScheduledMessages(mx: MatrixClient): Promise<ScheduledMessage[]> {
|
||||
const res = await mx.http.authedRequest(Method.Get, '/delayed_events', undefined, undefined, {
|
||||
prefix: '/_matrix/client/unstable/org.matrix.msc4140',
|
||||
});
|
||||
return parseDelayedEvents(res);
|
||||
}
|
||||
|
||||
/** Group by room, the shape of `scheduledMessagesAtom`. */
|
||||
export function groupScheduledByRoom(msgs: ScheduledMessage[]): Map<string, ScheduledMessage[]> {
|
||||
const map = new Map<string, ScheduledMessage[]>();
|
||||
msgs.forEach((m) => map.set(m.roomId, [...(map.get(m.roomId) ?? []), m]));
|
||||
map.forEach((list) => list.sort((a, b) => a.sendAt - b.sendAt));
|
||||
return map;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user