-
-
Notifications
You must be signed in to change notification settings - Fork 127
fix: markseen_msgs(): Mark reactions to specified messages as seen too (#7884) #7904
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1800,7 +1800,9 @@ pub async fn delete_msgs_ex( | |
| Ok(()) | ||
| } | ||
|
|
||
| /// Marks requested messages as seen. | ||
| /// Marks requested messages and reactions to them as seen. | ||
|
iequidoo marked this conversation as resolved.
|
||
| /// This should be called when a message comes into view or when a reaction for a message being in | ||
| /// view arrives. | ||
| pub async fn markseen_msgs(context: &Context, msg_ids: Vec<MsgId>) -> Result<()> { | ||
| if msg_ids.is_empty() { | ||
| return Ok(()); | ||
|
|
@@ -1814,10 +1816,18 @@ pub async fn markseen_msgs(context: &Context, msg_ids: Vec<MsgId>) -> Result<()> | |
|
|
||
| let mut msgs = Vec::with_capacity(msg_ids.len()); | ||
| for &id in &msg_ids { | ||
| if let Some(msg) = context | ||
| let Some(rfc724_mid): Option<String> = context | ||
| .sql | ||
| .query_row_optional( | ||
| .query_get_value("SELECT rfc724_mid FROM msgs WHERE id=?", (id,)) | ||
| .await? | ||
| else { | ||
| continue; | ||
| }; | ||
| context | ||
| .sql | ||
| .query_map( | ||
| "SELECT | ||
| m.id AS id, | ||
| m.chat_id AS chat_id, | ||
| m.state AS state, | ||
| m.ephemeral_timer AS ephemeral_timer, | ||
|
|
@@ -1828,9 +1838,11 @@ pub async fn markseen_msgs(context: &Context, msg_ids: Vec<MsgId>) -> Result<()> | |
| c.archived AS archived, | ||
| c.blocked AS blocked | ||
| FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id | ||
| WHERE m.id=? AND m.chat_id>9", | ||
| (id,), | ||
| WHERE (m.id=? OR m.mime_in_reply_to=? AND m.hidden=1) | ||
| AND m.chat_id>9 AND ?<=m.state AND m.state<?", | ||
| (id, rfc724_mid, MessageState::InFresh, MessageState::InSeen), | ||
| |row| { | ||
| let id: MsgId = row.get("id")?; | ||
| let chat_id: ChatId = row.get("chat_id")?; | ||
| let state: MessageState = row.get("state")?; | ||
| let param: Params = row.get::<_, String>("param")?.parse().unwrap_or_default(); | ||
|
|
@@ -1855,11 +1867,14 @@ pub async fn markseen_msgs(context: &Context, msg_ids: Vec<MsgId>) -> Result<()> | |
| ephemeral_timer, | ||
| )) | ||
| }, | ||
| |rows| { | ||
| for row in rows { | ||
| msgs.push(row?); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately, we need many SQL queries here because different |
||
| } | ||
| Ok(()) | ||
| }, | ||
| ) | ||
| .await? | ||
| { | ||
| msgs.push(msg); | ||
| } | ||
| .await?; | ||
| } | ||
|
|
||
| if msgs | ||
|
|
@@ -1888,60 +1903,57 @@ pub async fn markseen_msgs(context: &Context, msg_ids: Vec<MsgId>) -> Result<()> | |
| _curr_ephemeral_timer, | ||
| ) in msgs | ||
| { | ||
| if curr_state == MessageState::InFresh || curr_state == MessageState::InNoticed { | ||
| update_msg_state(context, id, MessageState::InSeen).await?; | ||
| info!(context, "Seen message {}.", id); | ||
|
|
||
| markseen_on_imap_table(context, &curr_rfc724_mid).await?; | ||
|
|
||
| // Read receipts for system messages are never sent to contacts. | ||
| // These messages have no place to display received read receipt | ||
| // anyway. And since their text is locally generated, | ||
| // quoting them is dangerous as it may contain contact names. E.g., for original message | ||
| // "Group left by me", a read receipt will quote "Group left by <name>", and the name can | ||
| // be a display name stored in address book rather than the name sent in the From field by | ||
| // the user. | ||
| // | ||
| // We also don't send read receipts for contact requests. | ||
| // Read receipts will not be sent even after accepting the chat. | ||
| let to_id = if curr_blocked == Blocked::Not | ||
| && !curr_hidden | ||
| && curr_param.get_bool(Param::WantsMdn).unwrap_or_default() | ||
| && curr_param.get_cmd() == SystemMessage::Unknown | ||
| && context.should_send_mdns().await? | ||
| { | ||
| // Clear WantsMdn to not handle a MDN twice | ||
| // if the state later is InFresh again as markfresh_chat() was called. | ||
| // BccSelf MDN messages in the next branch may be sent twice for syncing. | ||
| context | ||
| .sql | ||
| .execute( | ||
| "UPDATE msgs SET param=? WHERE id=?", | ||
| (curr_param.clone().remove(Param::WantsMdn).to_string(), id), | ||
| ) | ||
| .await | ||
| .context("failed to clear WantsMdn")?; | ||
| Some(curr_from_id) | ||
| } else if context.get_config_bool(Config::BccSelf).await? { | ||
| Some(ContactId::SELF) | ||
| } else { | ||
| None | ||
| }; | ||
| if let Some(to_id) = to_id { | ||
| context | ||
| .sql | ||
| .execute( | ||
| "INSERT INTO smtp_mdns (msg_id, from_id, rfc724_mid) VALUES(?, ?, ?)", | ||
| (id, to_id, curr_rfc724_mid), | ||
| ) | ||
| .await | ||
| .context("failed to insert into smtp_mdns")?; | ||
| context.scheduler.interrupt_smtp().await; | ||
| } | ||
| update_msg_state(context, id, MessageState::InSeen).await?; | ||
| info!(context, "Seen message {}.", id); | ||
|
|
||
| if !curr_hidden { | ||
| updated_chat_ids.insert(curr_chat_id); | ||
| } | ||
| markseen_on_imap_table(context, &curr_rfc724_mid).await?; | ||
|
|
||
| // Read receipts for system messages are never sent to contacts. These messages have no | ||
| // place to display received read receipt anyway. And since their text is locally generated, | ||
| // quoting them is dangerous as it may contain contact names. E.g., for original message | ||
| // "Group left by me", a read receipt will quote "Group left by <name>", and the name can be | ||
| // a display name stored in address book rather than the name sent in the From field by the | ||
| // user. | ||
| // | ||
| // We also don't send read receipts for contact requests. Read receipts will not be sent | ||
| // even after accepting the chat. | ||
| let to_id = if curr_blocked == Blocked::Not | ||
| && !curr_hidden | ||
| && curr_param.get_bool(Param::WantsMdn).unwrap_or_default() | ||
| && curr_param.get_cmd() == SystemMessage::Unknown | ||
| && context.should_send_mdns().await? | ||
| { | ||
| // Clear WantsMdn to not handle a MDN twice | ||
| // if the state later is InFresh again as markfresh_chat() was called. | ||
| // BccSelf MDN messages in the next branch may be sent twice for syncing. | ||
| context | ||
| .sql | ||
| .execute( | ||
| "UPDATE msgs SET param=? WHERE id=?", | ||
| (curr_param.clone().remove(Param::WantsMdn).to_string(), id), | ||
| ) | ||
| .await | ||
| .context("failed to clear WantsMdn")?; | ||
| Some(curr_from_id) | ||
| } else if context.get_config_bool(Config::BccSelf).await? { | ||
| Some(ContactId::SELF) | ||
| } else { | ||
| None | ||
| }; | ||
| if let Some(to_id) = to_id { | ||
| context | ||
| .sql | ||
| .execute( | ||
| "INSERT INTO smtp_mdns (msg_id, from_id, rfc724_mid) VALUES(?, ?, ?)", | ||
| (id, to_id, curr_rfc724_mid), | ||
| ) | ||
| .await | ||
| .context("failed to insert into smtp_mdns")?; | ||
| context.scheduler.interrupt_smtp().await; | ||
| } | ||
|
|
||
| if !curr_hidden { | ||
| updated_chat_ids.insert(curr_chat_id); | ||
| } | ||
| archived_chats_maybe_noticed |= curr_state == MessageState::InFresh | ||
| && !curr_hidden | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,7 +35,8 @@ use crate::message::{ | |
| rfc724_mid_exists, | ||
| }; | ||
| use crate::mimeparser::{ | ||
| AvatarAction, GossipedKey, MimeMessage, PreMessageMode, SystemMessage, parse_message_ids, | ||
| AvatarAction, GossipedKey, MimeMessage, PreMessageMode, SystemMessage, parse_message_id, | ||
| parse_message_ids, | ||
| }; | ||
| use crate::param::{Param, Params}; | ||
| use crate::peer_channels::{add_gossip_peer_from_header, insert_topic_stub, iroh_topic_from_str}; | ||
|
|
@@ -1018,10 +1019,14 @@ UPDATE config SET value=? WHERE keyname='configured_addr' AND value!=?1 | |
| context | ||
| .sql | ||
| .execute( | ||
| // Employ `msgs_index7 ON msgs (state, hidden, chat_id)` by explicit | ||
| // enumeration of `hidden` values, otherwise SQLite would iterate over all | ||
| // messages having `state` specified before, i.e. it would use the index | ||
| // only partly. | ||
| " | ||
| UPDATE msgs SET state=? WHERE | ||
| state=? AND | ||
| hidden=0 AND | ||
| hidden IN (0,1) AND | ||
| chat_id=? AND | ||
| timestamp<?", | ||
| ( | ||
|
|
@@ -1033,7 +1038,18 @@ UPDATE msgs SET state=? WHERE | |
| ) | ||
| .await | ||
| .context("UPDATE msgs.state")?; | ||
| if chat_id.get_fresh_msg_cnt(context).await? == 0 { | ||
| let n_fresh_msgs = context | ||
| .sql | ||
| .count( | ||
| " | ||
| SELECT COUNT(*) FROM msgs WHERE | ||
| state=? AND | ||
| (hidden=0 OR hidden=1) AND | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand what
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a way to employ But it seems that i made a mistake here,
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. EXPLAIN QUERY PLAN UPDATE msgs SET state=? WHERE state=? AND chat_id=? AND timestamp<?;
QUERY PLAN
`--SEARCH msgs USING INDEX msgs_index7 (state=?)
EXPLAIN QUERY PLAN UPDATE msgs SET state=? WHERE state=? AND hidden IN (0,1) AND chat_id=? AND timestamp<?;
QUERY PLAN
`--SEARCH msgs USING INDEX msgs_index7 (state=? AND hidden=? AND chat_id=? AND timestamp<?)
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I ran it on my db, so it employs also
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we already add |
||
| chat_id=?", | ||
| (MessageState::InFresh, chat_id), | ||
| ) | ||
| .await?; | ||
| if n_fresh_msgs == 0 { | ||
| // Removes all notifications for the chat in UIs. | ||
| context.emit_event(EventType::MsgsNoticed(chat_id)); | ||
| } else { | ||
|
|
@@ -2011,9 +2027,13 @@ async fn add_parts( | |
| ) | ||
| .await?; | ||
|
|
||
| let mime_in_reply_to = mime_parser | ||
| .get_header(HeaderDef::InReplyTo) | ||
| .unwrap_or_default(); | ||
| let mime_in_reply_to = match mime_parser.get_header(HeaderDef::InReplyTo) { | ||
| Some(in_reply_to) => parse_message_id(in_reply_to) | ||
| .log_err(context) | ||
| .ok() | ||
| .unwrap_or_default(), | ||
| None => "".to_string(), | ||
| }; | ||
| let mime_references = mime_parser | ||
| .get_header(HeaderDef::References) | ||
| .unwrap_or_default(); | ||
|
|
@@ -2140,7 +2160,7 @@ async fn add_parts( | |
| let is_incoming_fresh = mime_parser.incoming && !seen; | ||
| set_msg_reaction( | ||
| context, | ||
| mime_in_reply_to, | ||
| &mime_in_reply_to, | ||
| chat_id, | ||
| from_id, | ||
| sort_timestamp, | ||
|
|
@@ -2282,7 +2302,7 @@ RETURNING id | |
| } else { | ||
| Vec::new() | ||
| }, | ||
| if trash { "" } else { mime_in_reply_to }, | ||
| if trash { "" } else { &mime_in_reply_to }, | ||
| if trash { "" } else { mime_references }, | ||
| !trash && save_mime_modified, | ||
| if trash { "" } else { part.error.as_deref().unwrap_or_default() }, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.