borogove
created pr with
borogove/call-fixes.1
added borogove/call-fixes.2
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
lgtm
cmds
checkout latest patchset:
ssh pr.pico.sh pull borogove:call-fixes | git am -3checkout specific patchset revision:
ssh pr.pico.sh pull borogove:call-fixes [rev] | git am -3add changes to patch request:
git format-patch main --stdout | ssh pr.pico.sh borogove:call-fixesadd comment to patch request:
ssh pr.pico.sh comment borogove:call-fixes "lgtm!"
Patchset
borogove/call-fixes.1
Presetve stanza ID for call updates
Stephen Paul Weber
2026-11-03T20:39:43ZUse 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 { | |
| 243 | 243 | msg.type = MessageCall; | |
| 244 | 244 | msg.payloads.push(jmi); | |
| 245 | 245 | 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"); | |
| 248 | 246 | } | |
| 249 | 247 | ||
| 250 | 248 | if (addContext != null) msg = addContext(msg, stanza); | |
| 251 | 249 | final timeSent = msg.timeSent ?? msg.timeReceived ?? Date.format(std.Date.now()); | |
| 252 | 250 | ||
| 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 | + | ||
| 253 | 260 | final xep0070 = stanza.getChild("confirm", "http://jabber.org/protocol/http-auth"); | |
| 254 | 261 | if (xep0070 != null) { | |
| 255 | 262 | final threadId = msg.threadId; |
| ... | ... | @@ -261,10 +268,6 @@ class Message { | |
| 261 | 268 | } | |
| 262 | 269 | } | |
| 263 | 270 | ||
| 264 | - | if (jmi != null && jmi.name != "accept" && jmi.name != "propose") { | |
| 265 | - | msg.versions = [msg.build()]; | |
| 266 | - | } | |
| 267 | - | ||
| 268 | 271 | final reactionsEl = stanza.getChild("reactions", "urn:xmpp:reactions:0"); | |
| 269 | 272 | if (reactionsEl != null) { | |
| 270 | 273 | // A reaction update is never also a chat message |
+90
-0
test/persistence-tests.ts
#
| ... | ... | @@ -2373,6 +2373,96 @@ export function sharedPersistenceTests(test) { | |
| 2373 | 2373 | } | |
| 2374 | 2374 | }); | |
| 2375 | 2375 | ||
| 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 | + | }); | |
| 2376 | 2466 | } | |
| 2377 | 2467 | ||
| 2378 | 2468 | type TestKeyPair = { |