@@ -39,6 +39,7 @@ use crate::mimeparser::SystemMessage;
39
39
use crate :: param:: { Param , Params } ;
40
40
use crate :: peerstate:: Peerstate ;
41
41
use crate :: receive_imf:: ReceivedMsg ;
42
+ use crate :: rusqlite:: OptionalExtension ;
42
43
use crate :: securejoin:: BobState ;
43
44
use crate :: smtp:: send_msg_to_smtp;
44
45
use crate :: sql;
@@ -3247,18 +3248,20 @@ pub(crate) async fn marknoticed_chat_if_older_than(
3247
3248
/// Marks all messages in the chat as noticed.
3248
3249
/// If the given chat-id is the archive-link, marks all messages in all archived chats as noticed.
3249
3250
pub async fn marknoticed_chat ( context : & Context , chat_id : ChatId ) -> Result < ( ) > {
3250
- // "WHERE" below uses the index `(state, hidden, chat_id)`, see get_fresh_msg_cnt() for reasoning
3251
- // the additional SELECT statement may speed up things as no write-blocking is needed.
3251
+ // `WHERE` statements below use the index `(state, hidden, chat_id)`, that's why we enumerate
3252
+ // `hidden` values, see `get_fresh_msg_cnt()` for reasoning.
3253
+ // The additional `SELECT` statement may speed up things as no write-blocking is needed.
3252
3254
if chat_id. is_archived_link ( ) {
3253
3255
let chat_ids_in_archive = context
3254
3256
. sql
3255
3257
. query_map (
3256
3258
"SELECT DISTINCT(m.chat_id) FROM msgs m
3257
3259
LEFT JOIN chats c ON m.chat_id=c.id
3258
- WHERE m.state=10 AND m.hidden=0 AND m.chat_id>9 AND c.blocked=0 AND c.archived=1" ,
3259
- ( ) ,
3260
+ WHERE m.state=10 AND m.hidden IN (0,1) AND m.chat_id>9
3261
+ AND c.blocked=0 AND c.archived=1" ,
3262
+ ( ) ,
3260
3263
|row| row. get :: < _ , ChatId > ( 0 ) ,
3261
- |ids| ids. collect :: < Result < Vec < _ > , _ > > ( ) . map_err ( Into :: into)
3264
+ |ids| ids. collect :: < Result < Vec < _ > , _ > > ( ) . map_err ( Into :: into) ,
3262
3265
)
3263
3266
. await ?;
3264
3267
if chat_ids_in_archive. is_empty ( ) {
@@ -3269,7 +3272,8 @@ pub async fn marknoticed_chat(context: &Context, chat_id: ChatId) -> Result<()>
3269
3272
. sql
3270
3273
. execute (
3271
3274
& format ! (
3272
- "UPDATE msgs SET state=13 WHERE state=10 AND hidden=0 AND chat_id IN ({});" ,
3275
+ "UPDATE msgs SET state=13
3276
+ WHERE state=10 AND hidden IN (0,1) AND chat_id IN ({})" ,
3273
3277
sql:: repeat_vars( chat_ids_in_archive. len( ) )
3274
3278
) ,
3275
3279
rusqlite:: params_from_iter ( & chat_ids_in_archive) ,
@@ -3279,20 +3283,57 @@ pub async fn marknoticed_chat(context: &Context, chat_id: ChatId) -> Result<()>
3279
3283
context. emit_event ( EventType :: MsgsNoticed ( chat_id_in_archive) ) ;
3280
3284
chatlist_events:: emit_chatlist_item_changed ( context, chat_id_in_archive) ;
3281
3285
}
3282
- } else if context
3283
- . sql
3284
- . execute (
3285
- "UPDATE msgs
3286
- SET state=?
3287
- WHERE state=?
3288
- AND hidden=0
3289
- AND chat_id=?;" ,
3290
- ( MessageState :: InNoticed , MessageState :: InFresh , chat_id) ,
3291
- )
3292
- . await ?
3293
- == 0
3294
- {
3295
- return Ok ( ( ) ) ;
3286
+ } else {
3287
+ let conn_fn = |conn : & mut rusqlite:: Connection | {
3288
+ // This is to trigger emitting `MsgsNoticed` on other devices when reactions are noticed
3289
+ // locally. We filter out `InNoticed` messages because they are normally a result of
3290
+ // `mark_old_messages_as_noticed()` which happens on all devices anyway. Also we limit
3291
+ // this to one message because the effect is the same anyway.
3292
+ //
3293
+ // Even if `message::markseen_msgs()` fails then, in the worst case other devices won't
3294
+ // emit `MsgsNoticed` and app notifications won't be removed. The bigger problem is that
3295
+ // another device may have more reactions received and not yet seen notifications are
3296
+ // removed from it, but the same problem already exists for "usual" messages, so let's
3297
+ // not solve it for now.
3298
+ let mut stmt = conn. prepare (
3299
+ "SELECT id, state FROM msgs
3300
+ WHERE (state=? OR state=? OR state=?)
3301
+ AND hidden=1
3302
+ AND chat_id=?
3303
+ ORDER BY id DESC LIMIT 1" ,
3304
+ ) ?;
3305
+ let id_to_markseen = stmt
3306
+ . query_row (
3307
+ (
3308
+ MessageState :: InFresh ,
3309
+ MessageState :: InNoticed ,
3310
+ MessageState :: InSeen ,
3311
+ chat_id,
3312
+ ) ,
3313
+ |row| {
3314
+ let id: MsgId = row. get ( 0 ) ?;
3315
+ let state: MessageState = row. get ( 1 ) ?;
3316
+ Ok ( ( id, state) )
3317
+ } ,
3318
+ )
3319
+ . optional ( ) ?
3320
+ . filter ( |& ( _, state) | state == MessageState :: InFresh )
3321
+ . map ( |( id, _) | id) ;
3322
+ let nr_msgs_noticed = conn. execute (
3323
+ "UPDATE msgs
3324
+ SET state=?
3325
+ WHERE state=? AND hidden IN (0,1) AND chat_id=?" ,
3326
+ ( MessageState :: InNoticed , MessageState :: InFresh , chat_id) ,
3327
+ ) ?;
3328
+ Ok ( ( nr_msgs_noticed, id_to_markseen) )
3329
+ } ;
3330
+ let ( nr_msgs_noticed, id_to_markseen) = context. sql . call_write ( conn_fn) . await ?;
3331
+ if nr_msgs_noticed == 0 {
3332
+ return Ok ( ( ) ) ;
3333
+ }
3334
+ if let Some ( id) = id_to_markseen {
3335
+ message:: markseen_msgs ( context, vec ! [ id] ) . await ?;
3336
+ }
3296
3337
}
3297
3338
3298
3339
context. emit_event ( EventType :: MsgsNoticed ( chat_id) ) ;
@@ -3333,11 +3374,12 @@ pub(crate) async fn mark_old_messages_as_noticed(
3333
3374
. transaction ( |transaction| {
3334
3375
let mut changed_chats = Vec :: new ( ) ;
3335
3376
for ( _, msg) in msgs_by_chat {
3377
+ // NB: Enumerate `hidden` values to employ the index `(state, hidden, chat_id)`.
3336
3378
let changed_rows = transaction. execute (
3337
3379
"UPDATE msgs
3338
3380
SET state=?
3339
3381
WHERE state=?
3340
- AND hidden=0
3382
+ AND hidden IN (0,1)
3341
3383
AND chat_id=?
3342
3384
AND timestamp<=?;" ,
3343
3385
(
0 commit comments