From 5c1ee5dc1af8aeb83a15085fe198e6144ee4b98d Mon Sep 17 00:00:00 2001 From: WofWca Date: Thu, 12 Mar 2026 20:34:20 +0400 Subject: [PATCH] feat!: fire `MsgRead` on subsequent MDNs BREAKING CHANGE: previously this event only fired if message state really did just transition from `DC_STATE_OUT_DELIVERED` to `DC_STATE_OUT_MDN_RCVD`. Now this is only the case for `MsgRead` events that have the newly added `first_time == true`. Closes https://github.com/deltachat/deltachat-desktop/issues/5220. This is also useful for channels as it facilitates updating the post (message) read count live. Despite the fact that it's a breaking change, this should not be problematic in most cases because clients mostly use this event as an "it's time to reload" indicator. There is a case in Delta Chat Desktop where and adjustment will be needed: https://github.com/deltachat/deltachat-desktop/blob/d1fbb309793934b3ca09ed5a0266b6c84e333dd0/packages/frontend/src/stores/messagelist.ts#L119-L123 It seems that the message state could later transition to `DC_STATE_OUT_FAILED`, so we should not uncoditionally set it to `DC_STATE_OUT_MDN_RCVD`. Note that this does not expose `first_time` in CFFI. --- deltachat-jsonrpc/src/api/types/events.rs | 16 +++++++++++++--- src/events/payload.rs | 9 +++++++-- src/mimeparser.rs | 6 +++++- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/deltachat-jsonrpc/src/api/types/events.rs b/deltachat-jsonrpc/src/api/types/events.rs index 73862631b..fb54688fb 100644 --- a/deltachat-jsonrpc/src/api/types/events.rs +++ b/deltachat-jsonrpc/src/api/types/events.rs @@ -192,8 +192,7 @@ pub enum EventType { msg_id: u32, }, - /// A single message is read by the receiver. State changed from DC_STATE_OUT_DELIVERED to - /// DC_STATE_OUT_MDN_RCVD, see `Message.state`. + /// A single message is read by a receiver. #[serde(rename_all = "camelCase")] MsgRead { /// ID of the chat which the message belongs to. @@ -201,6 +200,12 @@ pub enum EventType { /// ID of the message that was read. msg_id: u32, + + /// Read for the first time (e.g. by just one group member + /// / channel subscriber). + /// State changed from DC_STATE_OUT_DELIVERED to + /// DC_STATE_OUT_MDN_RCVD, see dc_msg_get_state(). + first_time: bool, }, /// A single message was deleted. @@ -540,9 +545,14 @@ impl From for EventType { chat_id: chat_id.to_u32(), msg_id: msg_id.to_u32(), }, - CoreEventType::MsgRead { chat_id, msg_id } => MsgRead { + CoreEventType::MsgRead { + chat_id, + msg_id, + first_time, + } => MsgRead { chat_id: chat_id.to_u32(), msg_id: msg_id.to_u32(), + first_time, }, CoreEventType::MsgDeleted { chat_id, msg_id } => MsgDeleted { chat_id: chat_id.to_u32(), diff --git a/src/events/payload.rs b/src/events/payload.rs index 82bf70517..223ba45d0 100644 --- a/src/events/payload.rs +++ b/src/events/payload.rs @@ -171,14 +171,19 @@ pub enum EventType { msg_id: MsgId, }, - /// A single message is read by the receiver. State changed from DC_STATE_OUT_DELIVERED to - /// DC_STATE_OUT_MDN_RCVD, see dc_msg_get_state(). + /// A single message is read by a receiver. MsgRead { /// ID of the chat which the message belongs to. chat_id: ChatId, /// ID of the message that was read. msg_id: MsgId, + + /// Read for the first time (e.g. by just one group member + /// / channel subscriber). + /// State changed from DC_STATE_OUT_DELIVERED to + /// DC_STATE_OUT_MDN_RCVD, see dc_msg_get_state(). + first_time: bool, }, /// A single message was deleted. diff --git a/src/mimeparser.rs b/src/mimeparser.rs index a2d07fa4d..bc2365004 100644 --- a/src/mimeparser.rs +++ b/src/mimeparser.rs @@ -2596,8 +2596,12 @@ async fn handle_mdn( (msg_id, from_id, timestamp_sent), ) .await?; + context.emit_event(EventType::MsgRead { + chat_id, + msg_id, + first_time: !has_mdns, + }); if !has_mdns { - context.emit_event(EventType::MsgRead { chat_id, msg_id }); // note(treefit): only matters if it is the last message in chat (but probably too expensive to check, debounce also solves it) chatlist_events::emit_chatlist_item_changed(context, chat_id); }