borogove

created pr with borogove/call-fixes.1 on 2026-11-03T21:19:14Z · by 9e834e0d
added borogove/call-fixes.2 on 2026-11-09T14:58:54Z · by 9e834e0d
1: dbd8243 ! 1: a3fc6ef Never write ring before propose
2: 5ebad9d = 2: 5888d2b Presetve stanza ID for call updates
3: 9d8f8cc = 3: fc1c6dd Get all versions of a call from sqlite
commented on 2026-11-09T15:01:23Z · by 3c31bc8c
lgtm
cmds
checkout latest patchset:
ssh pr.pico.sh pull borogove:call-fixes | git am -3
checkout specific patchset revision:
ssh pr.pico.sh pull borogove:call-fixes [rev] | git am -3
add changes to patch request:
git format-patch main --stdout | ssh pr.pico.sh borogove:call-fixes
add comment to patch request:
ssh pr.pico.sh comment borogove:call-fixes "lgtm!"

Patchset borogove/call-fixes.1 on 2026-11-03T21:19:14Z · commit 5ebad9d

Presetve stanza ID for call updates
Stephen Paul Weber 2026-11-03T20:39:43Z
Use the callSid as the "main" ID for the propose that they all join
against, but keep the individual ids for the versions.
Semantic diff summary
0 added, 1 modified, 0 signature changed, 0 removed across 1 analyzed file (1 file skipped: unsupported file type)
+9 -6 borogove/Message.hx #
......@@ -243,13 +243,20 @@ class Message {
243243 msg.type = MessageCall;
244244 msg.payloads.push(jmi);
245245 if (msg.text == null) msg.text = "call " + jmi.name;
246- // The session id is what really identifies us
247- msg.localId = jmi.attr.get("id");
248246 }
249247
250248 if (addContext != null) msg = addContext(msg, stanza);
251249 final timeSent = msg.timeSent ?? msg.timeReceived ?? Date.format(std.Date.now());
252250
251+ if (jmi != null && jmi.name != "accept") {
252+ final version = msg.build();
253+ // The session id is what really identifies us
254+ msg.localId = jmi.attr.get("id");
255+ if (jmi.name != "propose") {
256+ msg.versions = [version];
257+ }
258+ }
259+
253260 final xep0070 = stanza.getChild("confirm", "http://jabber.org/protocol/http-auth");
254261 if (xep0070 != null) {
255262 final threadId = msg.threadId;
......@@ -261,10 +268,6 @@ class Message {
261268 }
262269 }
263270
264- if (jmi != null && jmi.name != "accept" && jmi.name != "propose") {
265- msg.versions = [msg.build()];
266- }
267-
268271 final reactionsEl = stanza.getChild("reactions", "urn:xmpp:reactions:0");
269272 if (reactionsEl != null) {
270273 // A reaction update is never also a chat message
+90 -0 test/persistence-tests.ts #
......@@ -2373,6 +2373,96 @@ export function sharedPersistenceTests(test) {
23732373 }
23742374 });
23752375
2376+ test("getChatUnreadDetails keeps outgoing call direction after a later remote status", async ({
2377+ borogove,
2378+ persistence,
2379+ }) => {
2380+ const account = "ordering-alice@example.com";
2381+ const contact = "ordering-hatter@example.com";
2382+ const sid = "ordering-outgoing-call";
2383+ const proposedAt = "2024-01-01T00:00:00.000Z";
2384+ const rejectedAt = "2024-01-01T00:00:01.000Z";
2385+ const callMessage = ({
2386+ direction,
2387+ event,
2388+ localId,
2389+ sortId,
2390+ timeReceived,
2391+ serverId,
2392+ versions,
2393+ }: {
2394+ direction: number;
2395+ event: string;
2396+ localId?: string;
2397+ sortId: string;
2398+ timeReceived: string;
2399+ serverId?: string;
2400+ versions?: any[];
2401+ }) => {
2402+ const received = direction === borogove.MessageDirection.MessageReceived;
2403+ const from = received ? contact : account;
2404+ const to = received ? account : contact;
2405+ const builder = new borogove.ChatMessageBuilder({
2406+ localId: localId ?? sid,
2407+ serverId: serverId ?? null,
2408+ serverIdBy: serverId == null ? null : account,
2409+ senderId: from,
2410+ direction,
2411+ type: borogove.MessageType.MessageCall,
2412+ timeReceived,
2413+ versions: versions ?? [],
2414+ payloads: [
2415+ borogove.Stanza.parse(
2416+ `<${event} xmlns="urn:xmpp:jingle-message:0" id="${sid}"/>`,
2417+ ),
2418+ ],
2419+ text: "call " + event,
2420+ });
2421+ builder.sortId = sortId;
2422+ builder.to = borogove.JID.parse(to);
2423+ builder.from = borogove.JID.parse(from);
2424+ builder.recipients = [builder.to];
2425+ builder.replyTo = [builder.from];
2426+ return builder.build();
2427+ };
2428+
2429+ const propose = callMessage({
2430+ direction: borogove.MessageDirection.MessageSent,
2431+ event: "propose",
2432+ sortId: "order-b1",
2433+ timeReceived: proposedAt,
2434+ serverId: "ordering-propose-server-id",
2435+ });
2436+ const rejectVersion = callMessage({
2437+ direction: borogove.MessageDirection.MessageReceived,
2438+ event: "reject",
2439+ localId: "ordering-reject-stanza-id",
2440+ sortId: "order-b2",
2441+ timeReceived: rejectedAt,
2442+ serverId: "ordering-reject-server-id",
2443+ });
2444+ const reject = callMessage({
2445+ direction: borogove.MessageDirection.MessageReceived,
2446+ event: "reject",
2447+ sortId: "order-b2",
2448+ timeReceived: rejectedAt,
2449+ serverId: "ordering-reject-server-id",
2450+ versions: [rejectVersion],
2451+ });
2452+
2453+ await persistence.storeMessages(account, [propose]);
2454+ await persistence.storeMessages(account, [reject]);
2455+
2456+ const chat = Object.create(borogove.DirectChat.prototype);
2457+ chat.chatId = contact;
2458+ chat.readUpToId = "no-such-message";
2459+ chat.notificationsFiltered = () => false;
2460+
2461+ const details = await persistence.getChatUnreadDetails(account, chat);
2462+ expect(details.message.direction).toBe(
2463+ borogove.MessageDirection.MessageSent,
2464+ );
2465+ });
23762466 }
23772467
23782468 type TestKeyPair = {
Back to top