-
-
Notifications
You must be signed in to change notification settings - Fork 104
feat: Mark reactions as IMAP-seen in marknoticed_chat() (#6210) #6213
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
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 |
---|---|---|
|
@@ -39,6 +39,7 @@ use crate::mimeparser::SystemMessage; | |
use crate::param::{Param, Params}; | ||
use crate::peerstate::Peerstate; | ||
use crate::receive_imf::ReceivedMsg; | ||
use crate::rusqlite::OptionalExtension; | ||
use crate::securejoin::BobState; | ||
use crate::smtp::send_msg_to_smtp; | ||
use crate::sql; | ||
|
@@ -3243,15 +3244,16 @@ pub async fn get_chat_msgs_ex( | |
/// Marks all messages in the chat as noticed. | ||
/// If the given chat-id is the archive-link, marks all messages in all archived chats as noticed. | ||
pub async fn marknoticed_chat(context: &Context, chat_id: ChatId) -> Result<()> { | ||
// "WHERE" below uses the index `(state, hidden, chat_id)`, see get_fresh_msg_cnt() for reasoning | ||
// the additional SELECT statement may speed up things as no write-blocking is needed. | ||
// `WHERE` statements below use the index `(state, hidden, chat_id)`, that's why we enumerate | ||
// `hidden` values, see `get_fresh_msg_cnt()` for reasoning. | ||
// The additional `SELECT` statement may speed up things as no write-blocking is needed. | ||
if chat_id.is_archived_link() { | ||
let chat_ids_in_archive = context | ||
.sql | ||
.query_map( | ||
"SELECT DISTINCT(m.chat_id) FROM msgs m | ||
LEFT JOIN chats c ON m.chat_id=c.id | ||
WHERE m.state=10 AND m.hidden=0 AND m.chat_id>9 AND c.archived=1", | ||
WHERE m.state=10 AND m.hidden IN (0,1) AND m.chat_id>9 AND c.archived=1", | ||
(), | ||
|row| row.get::<_, ChatId>(0), | ||
|ids| ids.collect::<Result<Vec<_>, _>>().map_err(Into::into), | ||
Hocuri marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
@@ -3265,7 +3267,7 @@ pub async fn marknoticed_chat(context: &Context, chat_id: ChatId) -> Result<()> | |
.sql | ||
.transaction(|transaction| { | ||
let mut stmt = transaction.prepare( | ||
"UPDATE msgs SET state=13 WHERE state=10 AND hidden=0 AND chat_id = ?", | ||
"UPDATE msgs SET state=13 WHERE state=10 AND hidden IN (0,1) AND chat_id=?", | ||
)?; | ||
for chat_id_in_archive in &chat_ids_in_archive { | ||
stmt.execute((chat_id_in_archive,))?; | ||
|
@@ -3281,22 +3283,56 @@ pub async fn marknoticed_chat(context: &Context, chat_id: ChatId) -> Result<()> | |
} | ||
} else { | ||
start_chat_ephemeral_timers(context, chat_id).await?; | ||
|
||
if context | ||
.sql | ||
.execute( | ||
let conn_fn = |conn: &mut rusqlite::Connection| { | ||
// This is to trigger emitting `MsgsNoticed` on other devices when reactions are noticed | ||
// locally. We filter out `InNoticed` messages because they are normally a result of | ||
// `mark_old_messages_as_noticed()` which happens on all devices anyway. Also we limit | ||
// this to one message because the effect is the same anyway. | ||
Comment on lines
+3289
to
+3290
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 would have assumed the previous limit of 100 to be more efficient, because all reactions need to be marked as seen eventually, and it's more efficient to mark all of them at once (although I do like having a limit at all, because otherwise the UI may become unresponsive when too many messages are marked as seen at once) 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. We don't need to mark all reactions as seen on IMAP, we only want to trigger 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 true that we don't need to, but this PR here as-is will mark all reactions as seen eventually after the user opened the chat repeatedly. 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. No, i added For uniformity it would be better to mark all reactions as seen eventually, but it's an extra work on IMAP that i suggest to avoid at least for now. |
||
// | ||
// Even if `message::markseen_msgs()` fails then, in the worst case other devices won't | ||
// emit `MsgsNoticed` and app notifications won't be removed. The bigger problem is that | ||
// another device may have more reactions received and not yet seen notifications are | ||
// removed from it, but the same problem already exists for "usual" messages, so let's | ||
// not solve it for now. | ||
let mut stmt = conn.prepare( | ||
"SELECT id, state FROM msgs | ||
WHERE (state=? OR state=? OR state=?) | ||
AND hidden=1 | ||
AND chat_id=? | ||
ORDER BY id DESC LIMIT 1", | ||
)?; | ||
let id_to_markseen = stmt | ||
.query_row( | ||
( | ||
MessageState::InFresh, | ||
MessageState::InNoticed, | ||
MessageState::InSeen, | ||
chat_id, | ||
), | ||
|row| { | ||
let id: MsgId = row.get(0)?; | ||
let state: MessageState = row.get(1)?; | ||
Ok((id, state)) | ||
}, | ||
) | ||
.optional()? | ||
.filter(|&(_, state)| state == MessageState::InFresh) | ||
Hocuri marked this conversation as resolved.
Show resolved
Hide resolved
|
||
.map(|(id, _)| id); | ||
let nr_msgs_noticed = conn.execute( | ||
"UPDATE msgs | ||
SET state=? | ||
WHERE state=? | ||
AND hidden=0 | ||
AND chat_id=?;", | ||
SET state=? | ||
WHERE state=? AND hidden IN (0,1) AND chat_id=?", | ||
(MessageState::InNoticed, MessageState::InFresh, chat_id), | ||
) | ||
.await? | ||
== 0 | ||
{ | ||
)?; | ||
Ok((nr_msgs_noticed, id_to_markseen)) | ||
}; | ||
let (nr_msgs_noticed, id_to_markseen) = context.sql.call_write(conn_fn).await?; | ||
if nr_msgs_noticed == 0 { | ||
return Ok(()); | ||
} | ||
if let Some(id) = id_to_markseen { | ||
message::markseen_msgs(context, vec![id]).await?; | ||
} | ||
} | ||
|
||
context.emit_event(EventType::MsgsNoticed(chat_id)); | ||
|
@@ -3337,11 +3373,12 @@ pub(crate) async fn mark_old_messages_as_noticed( | |
.transaction(|transaction| { | ||
let mut changed_chats = Vec::new(); | ||
for (_, msg) in msgs_by_chat { | ||
// NB: Enumerate `hidden` values to employ the index `(state, hidden, chat_id)`. | ||
let changed_rows = transaction.execute( | ||
"UPDATE msgs | ||
SET state=? | ||
WHERE state=? | ||
AND hidden=0 | ||
AND hidden IN (0,1) | ||
AND chat_id=? | ||
AND timestamp<=?;", | ||
( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -54,6 +54,9 @@ pub struct ReceivedMsg { | |
/// Received message state. | ||
pub state: MessageState, | ||
|
||
/// Whether the message is hidden. | ||
pub hidden: bool, | ||
|
||
/// Message timestamp for sorting. | ||
pub sort_timestamp: i64, | ||
|
||
|
@@ -192,6 +195,7 @@ pub(crate) async fn receive_imf_inner( | |
return Ok(Some(ReceivedMsg { | ||
chat_id: DC_CHAT_ID_TRASH, | ||
state: MessageState::Undefined, | ||
hidden: false, | ||
sort_timestamp: 0, | ||
msg_ids, | ||
needs_delete_job: false, | ||
|
@@ -373,6 +377,7 @@ pub(crate) async fn receive_imf_inner( | |
received_msg = Some(ReceivedMsg { | ||
chat_id: DC_CHAT_ID_TRASH, | ||
state: MessageState::InSeen, | ||
hidden: false, | ||
sort_timestamp: mime_parser.timestamp_sent, | ||
msg_ids: vec![msg_id], | ||
needs_delete_job: res == securejoin::HandshakeMessage::Done, | ||
|
@@ -611,7 +616,11 @@ pub(crate) async fn receive_imf_inner( | |
} else if !chat_id.is_trash() { | ||
let fresh = received_msg.state == MessageState::InFresh; | ||
for msg_id in &received_msg.msg_ids { | ||
chat_id.emit_msg_event(context, *msg_id, mime_parser.incoming && fresh); | ||
chat_id.emit_msg_event( | ||
context, | ||
*msg_id, | ||
mime_parser.incoming && fresh && !received_msg.hidden, | ||
); | ||
} | ||
} | ||
context.new_msgs_notify.notify_one(); | ||
|
@@ -764,7 +773,7 @@ async fn add_parts( | |
// (of course, the user can add other chats manually later) | ||
let to_id: ContactId; | ||
let state: MessageState; | ||
let mut hidden = false; | ||
let mut hidden = is_reaction; | ||
let mut needs_delete_job = false; | ||
let mut restore_protection = false; | ||
|
||
|
@@ -1021,11 +1030,8 @@ async fn add_parts( | |
} | ||
} | ||
|
||
state = if seen | ||
|| fetching_existing_messages | ||
|| is_mdn | ||
|| is_reaction | ||
|| chat_id_blocked == Blocked::Yes | ||
state = if seen || fetching_existing_messages || is_mdn || chat_id_blocked == Blocked::Yes | ||
// No check for `hidden` because only reactions are such and they should be `InFresh`. | ||
{ | ||
MessageState::InSeen | ||
} else { | ||
|
@@ -1235,14 +1241,10 @@ async fn add_parts( | |
} | ||
|
||
let orig_chat_id = chat_id; | ||
let mut chat_id = if is_reaction { | ||
let mut chat_id = chat_id.unwrap_or_else(|| { | ||
info!(context, "No chat id for message (TRASH)."); | ||
DC_CHAT_ID_TRASH | ||
} else { | ||
chat_id.unwrap_or_else(|| { | ||
info!(context, "No chat id for message (TRASH)."); | ||
DC_CHAT_ID_TRASH | ||
}) | ||
}; | ||
}); | ||
|
||
// Extract ephemeral timer from the message or use the existing timer if the message is not fully downloaded. | ||
let mut ephemeral_timer = if is_partial_download.is_some() { | ||
|
@@ -1600,10 +1602,10 @@ RETURNING id | |
state, | ||
is_dc_message, | ||
if trash { "" } else { msg }, | ||
if trash { None } else { message::normalize_text(msg) }, | ||
if trash { "" } else { &subject }, | ||
if trash || hidden { None } else { message::normalize_text(msg) }, | ||
if trash || hidden { "" } else { &subject }, | ||
// txt_raw might contain invalid utf8 | ||
if trash { "" } else { &txt_raw }, | ||
if trash || hidden { "" } else { &txt_raw }, | ||
if trash { | ||
"".to_string() | ||
} else { | ||
|
@@ -1619,7 +1621,7 @@ RETURNING id | |
mime_in_reply_to, | ||
mime_references, | ||
save_mime_modified, | ||
part.error.as_deref().unwrap_or_default(), | ||
if trash || hidden { "" } else { part.error.as_deref().unwrap_or_default() }, | ||
ephemeral_timer, | ||
ephemeral_timestamp, | ||
if is_partial_download.is_some() { | ||
|
@@ -1629,7 +1631,7 @@ RETURNING id | |
} else { | ||
DownloadState::Done | ||
}, | ||
mime_parser.hop_info | ||
if trash || hidden { "" } else { &mime_parser.hop_info }, | ||
Comment on lines
-1632
to
+1634
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 you can pull this improvement out into a separate PR that also updates |
||
], | ||
|row| { | ||
let msg_id: MsgId = row.get(0)?; | ||
|
@@ -1731,6 +1733,7 @@ RETURNING id | |
Ok(ReceivedMsg { | ||
chat_id, | ||
state, | ||
hidden, | ||
sort_timestamp, | ||
msg_ids: created_db_entries, | ||
needs_delete_job, | ||
|
Uh oh!
There was an error while loading. Please reload this page.