Skip to content

Commit d9482dc

Browse files
committed
feat: Mark reactions as IMAP-seen in marknoticed_chat() (#6210)
When a reaction notification is shown in the UIs, there's an option "Mark Read", but the UIs are unaware of reactions message ids, moreover there are no `msgs` rows for reactions in the core, so the UIs just call `marknoticed_chat()` in this case. We don't want to introduce reactions message ids to the UIs (at least currently), but let's make received reactions usual messages, just hidden, so that the existing `\Seen` flag synchronisation mechanism works for them, and mark all incoming hidden messages in the chat as seen in `marknoticed_chat()`. It's interesting that sent out reactions are already hidden messages, so this change mostly just unifies things.
1 parent 8a0c913 commit d9482dc

File tree

6 files changed

+118
-39
lines changed

6 files changed

+118
-39
lines changed

deltachat-rpc-client/src/deltachat_rpc_client/const.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class EventType(str, Enum):
3939
ERROR_SELF_NOT_IN_GROUP = "ErrorSelfNotInGroup"
4040
MSGS_CHANGED = "MsgsChanged"
4141
REACTIONS_CHANGED = "ReactionsChanged"
42+
INCOMING_REACTION = "IncomingReaction"
4243
INCOMING_MSG = "IncomingMsg"
4344
INCOMING_MSG_BUNCH = "IncomingMsgBunch"
4445
MSGS_NOTICED = "MsgsNoticed"

deltachat-rpc-client/tests/test_something.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,43 @@ def test_message(acfactory) -> None:
286286
assert reactions == snapshot.reactions
287287

288288

289+
def test_reaction_seen_on_another_dev(acfactory, tmp_path) -> None:
290+
alice, bob = acfactory.get_online_accounts(2)
291+
alice.export_backup(tmp_path)
292+
files = list(tmp_path.glob("*.tar"))
293+
alice2 = acfactory.get_unconfigured_account()
294+
alice2.import_backup(files[0])
295+
alice2.start_io()
296+
297+
bob_addr = bob.get_config("addr")
298+
alice_contact_bob = alice.create_contact(bob_addr, "Bob")
299+
alice_chat_bob = alice_contact_bob.create_chat()
300+
alice_chat_bob.send_text("Hello!")
301+
302+
event = bob.wait_for_incoming_msg_event()
303+
msg_id = event.msg_id
304+
305+
message = bob.get_message_by_id(msg_id)
306+
snapshot = message.get_snapshot()
307+
snapshot.chat.accept()
308+
message.send_reaction("😎")
309+
for a in [alice, alice2]:
310+
while True:
311+
event = a.wait_for_event()
312+
if event.kind == EventType.INCOMING_REACTION:
313+
break
314+
315+
alice_chat_bob.mark_noticed()
316+
while True:
317+
event = alice2.wait_for_event()
318+
if event.kind == EventType.MSGS_NOTICED:
319+
chat_id = event.chat_id
320+
break
321+
alice2_contact_bob = alice2.get_contact_by_addr(bob_addr)
322+
alice2_chat_bob = alice2_contact_bob.create_chat()
323+
assert chat_id == alice2_chat_bob.id
324+
325+
289326
def test_is_bot(acfactory) -> None:
290327
"""Test that we can recognize messages submitted by bots."""
291328
alice, bob = acfactory.get_online_accounts(2)

src/chat.rs

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3290,10 +3290,10 @@ pub async fn marknoticed_chat(context: &Context, chat_id: ChatId) -> Result<()>
32903290
.query_map(
32913291
"SELECT DISTINCT(m.chat_id) FROM msgs m
32923292
LEFT JOIN chats c ON m.chat_id=c.id
3293-
WHERE m.state=10 AND m.hidden=0 AND m.chat_id>9 AND c.blocked=0 AND c.archived=1",
3294-
(),
3293+
WHERE m.state=10 AND m.chat_id>9 AND c.blocked=0 AND c.archived=1",
3294+
(),
32953295
|row| row.get::<_, ChatId>(0),
3296-
|ids| ids.collect::<Result<Vec<_>, _>>().map_err(Into::into)
3296+
|ids| ids.collect::<Result<Vec<_>, _>>().map_err(Into::into),
32973297
)
32983298
.await?;
32993299
if chat_ids_in_archive.is_empty() {
@@ -3304,7 +3304,7 @@ pub async fn marknoticed_chat(context: &Context, chat_id: ChatId) -> Result<()>
33043304
.sql
33053305
.execute(
33063306
&format!(
3307-
"UPDATE msgs SET state=13 WHERE state=10 AND hidden=0 AND chat_id IN ({});",
3307+
"UPDATE msgs SET state=13 WHERE state=10 AND chat_id IN ({});",
33083308
sql::repeat_vars(chat_ids_in_archive.len())
33093309
),
33103310
rusqlite::params_from_iter(&chat_ids_in_archive),
@@ -3314,20 +3314,39 @@ pub async fn marknoticed_chat(context: &Context, chat_id: ChatId) -> Result<()>
33143314
context.emit_event(EventType::MsgsNoticed(chat_id_in_archive));
33153315
chatlist_events::emit_chatlist_item_changed(context, chat_id_in_archive);
33163316
}
3317-
} else if context
3318-
.sql
3319-
.execute(
3320-
"UPDATE msgs
3321-
SET state=?
3322-
WHERE state=?
3323-
AND hidden=0
3324-
AND chat_id=?;",
3325-
(MessageState::InNoticed, MessageState::InFresh, chat_id),
3326-
)
3327-
.await?
3328-
== 0
3329-
{
3330-
return Ok(());
3317+
} else {
3318+
let conn_fn = |conn: &mut rusqlite::Connection| {
3319+
let nr_msgs_noticed = conn.execute(
3320+
"UPDATE msgs
3321+
SET state=?
3322+
WHERE state=?
3323+
AND hidden=0
3324+
AND chat_id=?",
3325+
(MessageState::InNoticed, MessageState::InFresh, chat_id),
3326+
)?;
3327+
let mut stmt = conn.prepare(
3328+
"SELECT id FROM msgs
3329+
WHERE state>=? AND state<?
3330+
AND hidden=1
3331+
AND chat_id=?
3332+
ORDER BY id DESC LIMIT 100",
3333+
)?;
3334+
let hidden_msgs = stmt
3335+
.query_map(
3336+
(MessageState::InFresh, MessageState::InSeen, chat_id),
3337+
|row| {
3338+
let id: MsgId = row.get(0)?;
3339+
Ok(id)
3340+
},
3341+
)?
3342+
.collect::<std::result::Result<Vec<_>, _>>()?;
3343+
Ok((nr_msgs_noticed, hidden_msgs))
3344+
};
3345+
let (nr_msgs_noticed, hidden_msgs) = context.sql.call_write(conn_fn).await?;
3346+
if nr_msgs_noticed == 0 && hidden_msgs.is_empty() {
3347+
return Ok(());
3348+
}
3349+
message::markseen_msgs(context, hidden_msgs).await?;
33313350
}
33323351

33333352
context.emit_event(EventType::MsgsNoticed(chat_id));
@@ -3372,7 +3391,6 @@ pub(crate) async fn mark_old_messages_as_noticed(
33723391
"UPDATE msgs
33733392
SET state=?
33743393
WHERE state=?
3375-
AND hidden=0
33763394
AND chat_id=?
33773395
AND timestamp<=?;",
33783396
(

src/reaction.rs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@ mod tests {
397397
use deltachat_contact_tools::ContactAddress;
398398

399399
use super::*;
400-
use crate::chat::{forward_msgs, get_chat_msgs, send_text_msg};
400+
use crate::chat::{forward_msgs, get_chat_msgs, marknoticed_chat, send_text_msg};
401401
use crate::chatlist::Chatlist;
402402
use crate::config::Config;
403403
use crate::contact::{Contact, Origin};
@@ -641,7 +641,8 @@ Here's my footer -- [email protected]"
641641
assert_eq!(get_chat_msgs(&bob, bob_msg.chat_id).await?.len(), 2);
642642

643643
let bob_reaction_msg = bob.pop_sent_msg().await;
644-
alice.recv_msg_trash(&bob_reaction_msg).await;
644+
let alice_reaction_msg = alice.recv_msg_hidden(&bob_reaction_msg).await;
645+
assert_eq!(alice_reaction_msg.state, MessageState::InFresh);
645646
assert_eq!(get_chat_msgs(&alice, chat_alice.id).await?.len(), 2);
646647

647648
let reactions = get_msg_reactions(&alice, alice_msg.sender_msg_id).await?;
@@ -657,6 +658,20 @@ Here's my footer -- [email protected]"
657658
.await?;
658659
expect_incoming_reactions_event(&alice, alice_msg.sender_msg_id, *bob_id, "👍").await?;
659660

661+
marknoticed_chat(&alice, chat_alice.id).await?;
662+
assert_eq!(
663+
alice_reaction_msg.id.get_state(&alice).await?,
664+
MessageState::InSeen
665+
);
666+
// Reactions don't request MDNs.
667+
assert_eq!(
668+
alice
669+
.sql
670+
.count("SELECT COUNT(*) FROM smtp_mdns", ())
671+
.await?,
672+
0
673+
);
674+
660675
// Alice reacts to own message.
661676
send_reaction(&alice, alice_msg.sender_msg_id, "👍 😀")
662677
.await
@@ -695,7 +710,7 @@ Here's my footer -- [email protected]"
695710
bob_msg1.chat_id.accept(&bob).await?;
696711
send_reaction(&bob, bob_msg1.id, "👍").await?;
697712
let bob_send_reaction = bob.pop_sent_msg().await;
698-
alice.recv_msg_trash(&bob_send_reaction).await;
713+
alice.recv_msg_hidden(&bob_send_reaction).await;
699714
assert!(has_incoming_reactions_event(&alice).await);
700715

701716
let chatlist = Chatlist::try_load(&bob, 0, None, None).await?;
@@ -855,7 +870,7 @@ Here's my footer -- [email protected]"
855870
let bob_reaction_msg = bob.pop_sent_msg().await;
856871

857872
// Alice receives a reaction.
858-
alice.recv_msg_trash(&bob_reaction_msg).await;
873+
alice.recv_msg_hidden(&bob_reaction_msg).await;
859874

860875
let reactions = get_msg_reactions(&alice, alice_msg_id).await?;
861876
assert_eq!(reactions.to_string(), "👍1");
@@ -907,7 +922,7 @@ Here's my footer -- [email protected]"
907922
{
908923
send_reaction(&alice2, alice2_msg.id, "👍").await?;
909924
let msg = alice2.pop_sent_msg().await;
910-
alice1.recv_msg_trash(&msg).await;
925+
alice1.recv_msg_hidden(&msg).await;
911926
}
912927

913928
// Check that the status is still the same.
@@ -929,7 +944,7 @@ Here's my footer -- [email protected]"
929944
let alice1_msg = alice1.recv_msg(&alice0.pop_sent_msg().await).await;
930945

931946
send_reaction(&alice0, alice0_msg_id, "👀").await?;
932-
alice1.recv_msg_trash(&alice0.pop_sent_msg().await).await;
947+
alice1.recv_msg_hidden(&alice0.pop_sent_msg().await).await;
933948

934949
expect_reactions_changed_event(&alice0, chat_id, alice0_msg_id, ContactId::SELF).await?;
935950
expect_reactions_changed_event(&alice1, alice1_msg.chat_id, alice1_msg.id, ContactId::SELF)

src/receive_imf.rs

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -760,7 +760,7 @@ async fn add_parts(
760760
// (of course, the user can add other chats manually later)
761761
let to_id: ContactId;
762762
let state: MessageState;
763-
let mut hidden = false;
763+
let mut hidden = is_reaction;
764764
let mut needs_delete_job = false;
765765
let mut restore_protection = false;
766766

@@ -1018,12 +1018,7 @@ async fn add_parts(
10181018
}
10191019
}
10201020

1021-
state = if seen
1022-
|| fetching_existing_messages
1023-
|| is_mdn
1024-
|| is_reaction
1025-
|| chat_id_blocked == Blocked::Yes
1026-
{
1021+
state = if seen || fetching_existing_messages || is_mdn || chat_id_blocked == Blocked::Yes {
10271022
MessageState::InSeen
10281023
} else {
10291024
MessageState::InFresh
@@ -1232,7 +1227,7 @@ async fn add_parts(
12321227
}
12331228

12341229
let orig_chat_id = chat_id;
1235-
let mut chat_id = if is_mdn || is_reaction {
1230+
let mut chat_id = if is_mdn {
12361231
DC_CHAT_ID_TRASH
12371232
} else {
12381233
chat_id.unwrap_or_else(|| {
@@ -1407,7 +1402,7 @@ async fn add_parts(
14071402
// that the ui should show button to display the full message.
14081403

14091404
// a flag used to avoid adding "show full message" button to multiple parts of the message.
1410-
let mut save_mime_modified = mime_parser.is_mime_modified;
1405+
let mut save_mime_modified = mime_parser.is_mime_modified && !hidden;
14111406

14121407
let mime_headers = if save_mime_headers || save_mime_modified {
14131408
let headers = if !mime_parser.decoded_data.is_empty() {
@@ -1599,10 +1594,10 @@ RETURNING id
15991594
state,
16001595
is_dc_message,
16011596
if trash { "" } else { msg },
1602-
if trash { None } else { message::normalize_text(msg) },
1603-
if trash { "" } else { &subject },
1597+
if trash || hidden { None } else { message::normalize_text(msg) },
1598+
if trash || hidden { "" } else { &subject },
16041599
// txt_raw might contain invalid utf8
1605-
if trash { "" } else { &txt_raw },
1600+
if trash || hidden { "" } else { &txt_raw },
16061601
if trash {
16071602
"".to_string()
16081603
} else {
@@ -1618,7 +1613,7 @@ RETURNING id
16181613
mime_in_reply_to,
16191614
mime_references,
16201615
mime_modified,
1621-
part.error.as_deref().unwrap_or_default(),
1616+
if trash || hidden { "" } else { part.error.as_deref().unwrap_or_default() },
16221617
ephemeral_timer,
16231618
ephemeral_timestamp,
16241619
if is_partial_download.is_some() {
@@ -1628,7 +1623,7 @@ RETURNING id
16281623
} else {
16291624
DownloadState::Done
16301625
},
1631-
mime_parser.hop_info
1626+
if trash || hidden { "" } else { &mime_parser.hop_info },
16321627
],
16331628
|row| {
16341629
let msg_id: MsgId = row.get(0)?;

src/test_utils.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,19 @@ impl TestContext {
606606
msg
607607
}
608608

609+
/// Receive a message using the `receive_imf()` pipeline. Panics if it's not hidden.
610+
pub async fn recv_msg_hidden(&self, msg: &SentMessage<'_>) -> Message {
611+
let received = self
612+
.recv_msg_opt(msg)
613+
.await
614+
.expect("receive_imf() seems not to have added a new message to the db");
615+
let msg = Message::load_from_db(self, *received.msg_ids.last().unwrap())
616+
.await
617+
.unwrap();
618+
assert!(msg.hidden);
619+
msg
620+
}
621+
609622
/// Receive a message using the `receive_imf()` pipeline. This is similar
610623
/// to `recv_msg()`, but doesn't assume that the message is shown in the chat.
611624
pub async fn recv_msg_opt(

0 commit comments

Comments
 (0)