Skip to content

Commit cdc0326

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 incoming 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 and `InFresh`, 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 717c18e commit cdc0326

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
@@ -285,6 +285,43 @@ def test_message(acfactory) -> None:
285285
assert reactions == snapshot.reactions
286286

287287

288+
def test_reaction_seen_on_another_dev(acfactory, tmp_path) -> None:
289+
alice, bob = acfactory.get_online_accounts(2)
290+
alice.export_backup(tmp_path)
291+
files = list(tmp_path.glob("*.tar"))
292+
alice2 = acfactory.get_unconfigured_account()
293+
alice2.import_backup(files[0])
294+
alice2.start_io()
295+
296+
bob_addr = bob.get_config("addr")
297+
alice_contact_bob = alice.create_contact(bob_addr, "Bob")
298+
alice_chat_bob = alice_contact_bob.create_chat()
299+
alice_chat_bob.send_text("Hello!")
300+
301+
event = bob.wait_for_incoming_msg_event()
302+
msg_id = event.msg_id
303+
304+
message = bob.get_message_by_id(msg_id)
305+
snapshot = message.get_snapshot()
306+
snapshot.chat.accept()
307+
message.send_reaction("😎")
308+
for a in [alice, alice2]:
309+
while True:
310+
event = a.wait_for_event()
311+
if event.kind == EventType.INCOMING_REACTION:
312+
break
313+
314+
alice_chat_bob.mark_noticed()
315+
while True:
316+
event = alice2.wait_for_event()
317+
if event.kind == EventType.MSGS_NOTICED:
318+
chat_id = event.chat_id
319+
break
320+
alice2_contact_bob = alice2.get_contact_by_addr(bob_addr)
321+
alice2_chat_bob = alice2_contact_bob.create_chat()
322+
assert chat_id == alice2_chat_bob.id
323+
324+
288325
def test_is_bot(acfactory) -> None:
289326
"""Test that we can recognize messages submitted by bots."""
290327
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};
@@ -663,7 +663,8 @@ Here's my footer -- bob@example.net"
663663
assert_eq!(get_chat_msgs(&bob, bob_msg.chat_id).await?.len(), 2);
664664

665665
let bob_reaction_msg = bob.pop_sent_msg().await;
666-
alice.recv_msg_trash(&bob_reaction_msg).await;
666+
let alice_reaction_msg = alice.recv_msg_hidden(&bob_reaction_msg).await;
667+
assert_eq!(alice_reaction_msg.state, MessageState::InFresh);
667668
assert_eq!(get_chat_msgs(&alice, chat_alice.id).await?.len(), 2);
668669

669670
let reactions = get_msg_reactions(&alice, alice_msg.sender_msg_id).await?;
@@ -680,6 +681,20 @@ Here's my footer -- bob@example.net"
680681
expect_incoming_reactions_event(&alice, alice_msg.sender_msg_id, *bob_id, "👍").await?;
681682
expect_no_unwanted_events(&alice).await;
682683

684+
marknoticed_chat(&alice, chat_alice.id).await?;
685+
assert_eq!(
686+
alice_reaction_msg.id.get_state(&alice).await?,
687+
MessageState::InSeen
688+
);
689+
// Reactions don't request MDNs.
690+
assert_eq!(
691+
alice
692+
.sql
693+
.count("SELECT COUNT(*) FROM smtp_mdns", ())
694+
.await?,
695+
0
696+
);
697+
683698
// Alice reacts to own message.
684699
send_reaction(&alice, alice_msg.sender_msg_id, "👍 😀")
685700
.await
@@ -719,7 +734,7 @@ Here's my footer -- bob@example.net"
719734
bob_msg1.chat_id.accept(&bob).await?;
720735
send_reaction(&bob, bob_msg1.id, "👍").await?;
721736
let bob_send_reaction = bob.pop_sent_msg().await;
722-
alice.recv_msg_trash(&bob_send_reaction).await;
737+
alice.recv_msg_hidden(&bob_send_reaction).await;
723738
expect_incoming_reactions_event(&alice, alice_msg1.sender_msg_id, alice_bob_id, "👍")
724739
.await?;
725740
expect_no_unwanted_events(&alice).await;
@@ -882,7 +897,7 @@ Here's my footer -- bob@example.net"
882897
let bob_reaction_msg = bob.pop_sent_msg().await;
883898

884899
// Alice receives a reaction.
885-
alice.recv_msg_trash(&bob_reaction_msg).await;
900+
alice.recv_msg_hidden(&bob_reaction_msg).await;
886901

887902
let reactions = get_msg_reactions(&alice, alice_msg_id).await?;
888903
assert_eq!(reactions.to_string(), "👍1");
@@ -934,7 +949,7 @@ Here's my footer -- bob@example.net"
934949
{
935950
send_reaction(&alice2, alice2_msg.id, "👍").await?;
936951
let msg = alice2.pop_sent_msg().await;
937-
alice1.recv_msg_trash(&msg).await;
952+
alice1.recv_msg_hidden(&msg).await;
938953
}
939954

940955
// Check that the status is still the same.
@@ -956,7 +971,7 @@ Here's my footer -- bob@example.net"
956971
let alice1_msg = alice1.recv_msg(&alice0.pop_sent_msg().await).await;
957972

958973
send_reaction(&alice0, alice0_msg_id, "👀").await?;
959-
alice1.recv_msg_trash(&alice0.pop_sent_msg().await).await;
974+
alice1.recv_msg_hidden(&alice0.pop_sent_msg().await).await;
960975

961976
expect_reactions_changed_event(&alice0, chat_id, alice0_msg_id, ContactId::SELF).await?;
962977
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)