engine: When opening a folder, sanitize remote unseen.

Messages may have been marked as read while waiting in
open_remote_session_locked().

Unsure we handle the diff when remote session opened.

Fix #1023
Fix #364
This commit is contained in:
Cédric Bellegarde 2023-10-02 13:48:24 +02:00 committed by Niels De Graef
parent 8164b5034e
commit 418de07582
5 changed files with 54 additions and 7 deletions

View file

@ -1060,7 +1060,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
return deleted_email_ids;
}
public async void mark_email_async(Gee.Collection<ImapDB.EmailIdentifier> to_mark,
public async int mark_email_async(Gee.Collection<ImapDB.EmailIdentifier> to_mark,
Geary.EmailFlags? flags_to_add, Geary.EmailFlags? flags_to_remove, Cancellable? cancellable)
throws Error {
int unread_change = 0; // Negative means messages are read, positive means unread.
@ -1120,6 +1120,8 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
// Signal changes so other folders can be updated.
if (unread_status.size > 0)
unread_updated(unread_status);
return unread_change;
}
internal async Gee.List<Imap.UID>? get_email_uids_async(

View file

@ -1373,6 +1373,15 @@ internal class Geary.ImapEngine.UpdateRemoteFolders : AccountOperation {
// always update, openable or not; have the folder update the UID info the next time
// it's opened
try {
// Some emails may have been marked as read locally while
// updating remote folders
if (minimal_folder.replay_queue != null &&
minimal_folder.replay_queue.pending_unread_change() != 0) {
remote_folder.properties.set_status_unseen(
remote_folder.properties.unseen +
minimal_folder.replay_queue.pending_unread_change()
);
}
yield minimal_folder.local_folder.update_folder_status(
remote_folder.properties, false, cancellable
);

View file

@ -1112,6 +1112,13 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
return;
}
// Some emails may have been marked as read locally while
// claiming folder session, handle the diff here.
session.folder.properties.set_status_unseen(
session.folder.properties.unseen +
this.replay_queue.pending_unread_change()
);
// Update the local folder's totals and UID values after
// normalisation, so it does not mistake the remote's current
// state with our previous state
@ -1371,6 +1378,10 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
replay_queue.schedule(mark);
yield mark.wait_for_ready_async(cancellable);
// Cancel any remote update, we just updated locally unread_count,
// incoming data will have an invalid unseen value
this.account.cancel_remote_update();
}
public virtual async void

View file

@ -397,6 +397,24 @@ private class Geary.ImapEngine.ReplayQueue : BaseObject, Logging.Source {
closed();
}
/**
* Get pending unread change.
*/
public int pending_unread_change() {
int unread_change = 0;
Gee.Collection<ReplayOperation> replay_ops = traverse(
remote_queue.get_all()
).to_array_list();
replay_ops.add(this.remote_op_active);
foreach (ReplayOperation op in replay_ops) {
if (op is ImapEngine.MarkEmail) {
ImapEngine.MarkEmail mark_email = op as ImapEngine.MarkEmail;
unread_change += mark_email.unread_change;
}
}
return unread_change;
}
/** {@inheritDoc} */
public Logging.State to_logging_state() {
return new Logging.State(
@ -447,7 +465,7 @@ private class Geary.ImapEngine.ReplayQueue : BaseObject, Logging.Source {
break;
}
local_op_active = op;
this.local_op_active = op;
// If this is a Close operation, shut down the queue after processing it
if (op is CloseReplayQueue)
@ -526,7 +544,7 @@ private class Geary.ImapEngine.ReplayQueue : BaseObject, Logging.Source {
failed(op);
}
local_op_active = null;
this.local_op_active = null;
}
debug("ReplayQueue.do_replay_local_async %s exiting", to_string());
@ -547,7 +565,7 @@ private class Geary.ImapEngine.ReplayQueue : BaseObject, Logging.Source {
break;
}
remote_op_active = op;
this.remote_op_active = op;
// ReplayClose means this queue (and the folder) are closing, so handle errors a little
// differently
@ -641,7 +659,7 @@ private class Geary.ImapEngine.ReplayQueue : BaseObject, Logging.Source {
else
failed(op);
remote_op_active = null;
this.remote_op_active = null;
}
debug("ReplayQueue.do_replay_remote_async %s exiting", to_string());

View file

@ -13,6 +13,8 @@ private class Geary.ImapEngine.MarkEmail : Geary.ImapEngine.SendReplayOperation
private Gee.Map<ImapDB.EmailIdentifier, Geary.EmailFlags>? original_flags = null;
private Cancellable? cancellable;
internal int unread_change { get; private set; default=0; }
public MarkEmail(MinimalFolder engine,
Gee.Collection<ImapDB.EmailIdentifier> to_mark,
EmailFlags? flags_to_add,
@ -46,8 +48,12 @@ private class Geary.ImapEngine.MarkEmail : Geary.ImapEngine.SendReplayOperation
if (original_flags == null || original_flags.size == 0)
return ReplayOperation.Status.COMPLETED;
yield engine.local_folder.mark_email_async(original_flags.keys, flags_to_add, flags_to_remove,
cancellable);
this.unread_change = yield engine.local_folder.mark_email_async(
original_flags.keys,
flags_to_add,
flags_to_remove,
cancellable
);
// We can't rely on email identifier for remote replay
// An email identifier id can match multiple uids
@ -71,6 +77,7 @@ private class Geary.ImapEngine.MarkEmail : Geary.ImapEngine.SendReplayOperation
msg_sets, flags_to_add, flags_to_remove, cancellable
);
}
this.unread_change = 0;
}
public override async void backout_local_async() throws Error {