[geary] Use STATUS command to keep unread counts accurate



commit c4020014dacfc85405b8cd96bf08c22cff5650b9
Author: Charles Lindsay <chaz yorba org>
Date:   Mon Mar 3 15:05:41 2014 -0800

    Use STATUS command to keep unread counts accurate
    
    We know we've got some accounting problems in our unread counts.  This
    patches over the most egregious errors by fetching the real count from
    the server more frequently.  It also more frequently triggers the full
    email synchronization process.
    
    Closes: bgo #714865

 .../imap-engine/imap-engine-generic-account.vala   |   77 +++++++++++++++++++-
 .../imap-engine/imap-engine-minimal-folder.vala    |    2 +-
 src/engine/imap/api/imap-account.vala              |   28 ++++++--
 3 files changed, 99 insertions(+), 8 deletions(-)
---
diff --git a/src/engine/imap-engine/imap-engine-generic-account.vala 
b/src/engine/imap-engine/imap-engine-generic-account.vala
index cd3b24c..29f7478 100644
--- a/src/engine/imap-engine/imap-engine-generic-account.vala
+++ b/src/engine/imap-engine/imap-engine-generic-account.vala
@@ -5,7 +5,8 @@
  */
 
 private abstract class Geary.ImapEngine.GenericAccount : Geary.AbstractAccount {
-    private const int REFRESH_FOLDER_LIST_SEC = 4 * 60;
+    private const int REFRESH_FOLDER_LIST_SEC = 2 * 60;
+    private const int REFRESH_UNSEEN_SEC = 1;
     
     private static Geary.FolderPath? outbox_path = null;
     private static Geary.FolderPath? search_path = null;
@@ -16,6 +17,9 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.AbstractAccount {
     private Gee.HashMap<FolderPath, MinimalFolder> folder_map = new Gee.HashMap<
         FolderPath, MinimalFolder>();
     private Gee.HashMap<FolderPath, Folder> local_only = new Gee.HashMap<FolderPath, Folder>();
+    private Gee.HashMap<FolderPath, uint> refresh_unseen_timeout_ids
+        = new Gee.HashMap<FolderPath, uint>();
+    private Gee.HashSet<Geary.Folder> in_refresh_unseen = new Gee.HashSet<Geary.Folder>();
     private uint refresh_folder_timeout_id = 0;
     private bool in_refresh_enumerate = false;
     private Cancellable refresh_cancellable = new Cancellable();
@@ -68,6 +72,27 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.AbstractAccount {
         }
     }
     
+    protected override void notify_email_appended(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> 
ids) {
+        base.notify_email_appended(folder, ids);
+        reschedule_unseen_update(folder);
+    }
+    
+    protected override void notify_email_inserted(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> 
ids) {
+        base.notify_email_inserted(folder, ids);
+        reschedule_unseen_update(folder);
+    }
+    
+    protected override void notify_email_removed(Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> 
ids) {
+        base.notify_email_removed(folder, ids);
+        reschedule_unseen_update(folder);
+    }
+    
+    protected override void notify_email_flags_changed(Geary.Folder folder,
+        Gee.Map<Geary.EmailIdentifier, Geary.EmailFlags> flag_map) {
+        base.notify_email_flags_changed(folder, flag_map);
+        reschedule_unseen_update(folder);
+    }
+    
     private void check_open() throws EngineError {
         if (!open)
             throw new EngineError.OPEN_REQUIRED("Account %s not opened", to_string());
@@ -261,6 +286,54 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.AbstractAccount {
         return all_folders;
     }
     
+    private void reschedule_unseen_update(Geary.Folder folder) {
+        if (!folder_map.has_key(folder.path))
+            return;
+        
+        if (refresh_unseen_timeout_ids.get(folder.path) != 0)
+            Source.remove(refresh_unseen_timeout_ids.get(folder.path));
+        
+        refresh_unseen_timeout_ids.set(folder.path,
+            Timeout.add_seconds(REFRESH_UNSEEN_SEC, () => on_refresh_unseen(folder)));
+    }
+    
+    private bool on_refresh_unseen(Geary.Folder folder) {
+        // If we're in the process already, reschedule for later.
+        if (in_refresh_unseen.contains(folder))
+            return true;
+        
+        refresh_unseen_async.begin(folder, null, on_refresh_unseen_completed);
+        
+        refresh_unseen_timeout_ids.unset(folder.path);
+        return false;
+    }
+    
+    private void on_refresh_unseen_completed(Object? source, AsyncResult result) {
+        try {
+            refresh_unseen_async.end(result);
+        } catch (Error e) {
+            debug("Error refreshing unseen counts: %s", e.message);
+        }
+    }
+    
+    private async void refresh_unseen_async(Geary.Folder folder, Cancellable? cancellable) throws Error {
+        in_refresh_unseen.add(folder);
+        
+        debug("Refreshing unseen counts for %s", folder.to_string());
+        
+        bool folder_created;
+        Imap.Folder remote_folder = yield remote.fetch_folder_async(folder.path,
+            out folder_created, cancellable);
+        
+        if (!folder_created) {
+            int unseen_count = yield remote.fetch_unseen_count_async(folder.path, cancellable);
+            remote_folder.properties.set_status_unseen(unseen_count);
+            yield local.update_folder_status_async(remote_folder, false, cancellable);
+        }
+        
+        in_refresh_unseen.remove(folder);
+    }
+    
     private void reschedule_folder_refresh(bool immediate) {
         if (in_refresh_enumerate)
             return;
@@ -420,7 +493,7 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.AbstractAccount {
                 continue;
             
             Imap.Folder remote_folder = (Imap.Folder) yield remote.fetch_folder_async(folder,
-                cancellable);
+                null, cancellable);
             
             yield local.clone_folder_async(remote_folder, cancellable);
         }
diff --git a/src/engine/imap-engine/imap-engine-minimal-folder.vala 
b/src/engine/imap-engine/imap-engine-minimal-folder.vala
index e23528e..45c1aa1 100644
--- a/src/engine/imap-engine/imap-engine-minimal-folder.vala
+++ b/src/engine/imap-engine/imap-engine-minimal-folder.vala
@@ -520,7 +520,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.AbstractFolder, Geary.Folde
         try {
             debug("Fetching information for remote folder %s", to_string());
             opening_folder = yield remote.fetch_folder_async(local_folder.get_path(),
-                cancellable);
+                null, cancellable);
             
             debug("Opening remote folder %s", opening_folder.to_string());
             yield opening_folder.open_async(cancellable);
diff --git a/src/engine/imap/api/imap-account.vala b/src/engine/imap/api/imap-account.vala
index d3a9092..c04d314 100644
--- a/src/engine/imap/api/imap-account.vala
+++ b/src/engine/imap/api/imap-account.vala
@@ -164,13 +164,17 @@ private class Geary.Imap.Account : BaseObject {
         }
     }
     
-    public async Imap.Folder fetch_folder_async(FolderPath path, Cancellable? cancellable)
-        throws Error {
+    public async Imap.Folder fetch_folder_async(FolderPath path, out bool created,
+        Cancellable? cancellable) throws Error {
         check_open();
         
+        created = false;
+        
         if (folders.has_key(path))
             return folders.get(path);
         
+        created = true;
+        
         // if not in map, use list_children_async to add it (if it exists)
         if (!path_to_mailbox.has_key(path)) {
             debug("Listing children to find %s", path.to_string());
@@ -183,7 +187,7 @@ private class Geary.Imap.Account : BaseObject {
         
         Imap.Folder folder;
         if (!mailbox_info.attrs.is_no_select) {
-            StatusData status = yield fetch_status_async(path, cancellable);
+            StatusData status = yield fetch_status_async(path, StatusDataType.all(), cancellable);
             
             folder = new Imap.Folder(session_mgr, status, mailbox_info);
         } else {
@@ -195,13 +199,27 @@ private class Geary.Imap.Account : BaseObject {
         return folder;
     }
     
-    private async StatusData fetch_status_async(FolderPath path, Cancellable? cancellable)
+    public async int fetch_unseen_count_async(FolderPath path, Cancellable? cancellable)
         throws Error {
         check_open();
         
+        MailboxInformation? mailbox_info = path_to_mailbox.get(path);
+        if (mailbox_info == null)
+            throw_not_found(path);
+        if (mailbox_info.attrs.is_no_select)
+            throw new EngineError.UNSUPPORTED("Can't fetch unseen count for unselectable folder %s", path);
+        
+        StatusData data = yield fetch_status_async(path, { StatusDataType.UNSEEN }, cancellable);
+        return data.unseen;
+    }
+    
+    private async StatusData fetch_status_async(FolderPath path, StatusDataType[] status_types,
+        Cancellable? cancellable) throws Error {
+        check_open();
+        
         Gee.List<StatusData> status_results = new Gee.ArrayList<StatusData>();
         StatusResponse response = yield send_command_async(
-            new StatusCommand(new MailboxSpecifier.from_folder_path(path, null), StatusDataType.all()),
+            new StatusCommand(new MailboxSpecifier.from_folder_path(path, null), status_types),
             null, status_results, cancellable);
         
         throw_fetch_error(response, path, status_results.size);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]