[geary/wip/765516-gtk-widget-conversation-viewer: 62/103] Ensure message are marked ready when their message bodies are visible.



commit 4640c8d64c2789302c3b2376b2d4a98d4032982e
Author: Michael James Gratton <mike vee net>
Date:   Sat Apr 16 18:10:49 2016 +1000

    Ensure message are marked ready when their message bodies are visible.
    
    * src/client/conversation-viewer/conversation-message.vala: Keep track of
      the web_view's allocation so we can work out if it is visible, and the
      loading state of the web_view so we know if it should be considered or
      not.
      (ConversationMessage::load_message_body): Rearranged a bit so only one
      web_view.load_status signal handler is needed and there isn't a race
      between loading completion and showing remote images. Also set the new
      is_loading_complete property.
    
    * src/client/conversation-viewer/conversation-viewer.vala
      (check_mark_read): Reimplement using widget coordinates rather than
      HTML coordinates.

 .../conversation-viewer/conversation-message.vala  |   53 +++++++++++------
 .../conversation-viewer/conversation-viewer.vala   |   62 +++++++++++++------
 2 files changed, 77 insertions(+), 38 deletions(-)
---
diff --git a/src/client/conversation-viewer/conversation-message.vala 
b/src/client/conversation-viewer/conversation-message.vala
index 3fb8c68..3875c67 100644
--- a/src/client/conversation-viewer/conversation-message.vala
+++ b/src/client/conversation-viewer/conversation-message.vala
@@ -54,6 +54,12 @@ public class ConversationMessage : Gtk.Box {
     // The HTML viewer to view the emails.
     public ConversationWebView web_view { get; private set; }
 
+    // The allocation for the web view
+    public Gdk.Rectangle web_view_allocation { get; private set; }
+
+    // Has the message body been been fully loaded?
+    public bool is_loading_complete = false;
+
     [GtkChild]
     private Gtk.Image avatar_image;
 
@@ -180,8 +186,9 @@ public class ConversationMessage : Gtk.Box {
         // web_view.context_menu.connect(() => { return true; }); // Suppress default context menu.
         // web_view.realize.connect( () => { web_view.get_vadjustment().value_changed.connect(mark_read); });
         // web_view.size_allocate.connect(mark_read);
-        web_view.realize.connect(() => { debug("web_view: realised"); });
-        web_view.size_allocate.connect(() => { debug("web_view: allocated"); });
+        web_view.size_allocate.connect((widget, allocation) => {
+                web_view_allocation = allocation;
+            });
 
         body_box.set_has_tooltip(true);
         web_view.hovering_over_link.connect(on_hovering_over_link);
@@ -372,6 +379,10 @@ public class ConversationMessage : Gtk.Box {
         return menu;
     }
 
+    public bool is_manual_read() {
+        return get_style_context().has_class("geary_manual_read");
+    }
+
     public void update_flags(Geary.Email email) {
         Geary.EmailFlags flags = email.email_flags;
         Gtk.StyleContext style = get_style_context();
@@ -404,6 +415,7 @@ public class ConversationMessage : Gtk.Box {
 
     private void load_message_body() {
         bool remote_images = false;
+        bool load_images = false;
         string body_text = "";
         try {
             if (message.has_html_body()) {
@@ -416,9 +428,24 @@ public class ConversationMessage : Gtk.Box {
         }
 
         body_text = clean_html_markup(body_text, message, out remote_images);
+        if (remote_images) {
+            Geary.Contact contact =
+                containing_folder.account.get_contact_store().get_by_rfc822(
+                    email.get_primary_originator()
+                );
+            bool always_load = contact != null && contact.always_load_remote_images();
+            if (always_load || email.load_remote_images().is_certain()) {
+                load_images = true;
+            } else {
+                remote_images_infobar.show();
+            }
+        }
 
         web_view.notify["load-status"].connect((source, param) => {
                 if (web_view.load_status == WebKit.LoadStatus.FINISHED) {
+                    if (load_images) {
+                        show_images(false);
+                    }
                     WebKit.DOM.HTMLElement html = (
                         web_view.get_dom_document().document_element as
                         WebKit.DOM.HTMLElement
@@ -437,25 +464,15 @@ public class ConversationMessage : Gtk.Box {
                                (Callback) on_show_quote_clicked, this);
                     bind_event(web_view, ".quote_container > .hider", "click",
                                (Callback) on_hide_quote_clicked, this);
+
+                    // XXX Not actually true since remote images will
+                    // still be loading.
+                    is_loading_complete = true;
                 }
             });
-        web_view.load_string(body_text, "text/html", "UTF8", "");
 
-        if (remote_images) {
-            Geary.Contact contact = containing_folder.account.get_contact_store().get_by_rfc822(
-                email.get_primary_originator());
-            bool always_load = contact != null && contact.always_load_remote_images();
-            
-            if (always_load || email.load_remote_images().is_certain()) {
-                web_view.notify["load-status"].connect((source, param) => {
-                        if (web_view.load_status == WebKit.LoadStatus.FINISHED) {
-                            show_images(false);
-                        }
-                    });
-            } else {
-                remote_images_infobar.show();
-            }
-        }
+        // Only load it after we've hooked up the load-status signal above
+        web_view.load_string(body_text, "text/html", "UTF8", "");
     }
     
     // This delegate is called from within Geary.RFC822.Message.get_body while assembling the plain
diff --git a/src/client/conversation-viewer/conversation-viewer.vala 
b/src/client/conversation-viewer/conversation-viewer.vala
index 803ffac..114054d 100644
--- a/src/client/conversation-viewer/conversation-viewer.vala
+++ b/src/client/conversation-viewer/conversation-viewer.vala
@@ -151,6 +151,11 @@ public class ConversationViewer : Gtk.Stack {
                     toggle_show_message(row);
                 }
             });
+        conversation_listbox.realize.connect(() => {
+                conversation_page.get_vadjustment()
+                    .value_changed.connect(check_mark_read);
+            });
+        conversation_listbox.size_allocate.connect(check_mark_read);
 
         // Setup state machine for search/find states.
         Geary.State.Mapping[] mappings = {
@@ -201,26 +206,43 @@ public class ConversationViewer : Gtk.Stack {
     }
     
     public void check_mark_read() {
-        Gee.ArrayList<Geary.EmailIdentifier> emails = new Gee.ArrayList<Geary.EmailIdentifier>();
-        // foreach (Geary.Email message in messages) {
-        //  try {
-        //      if (message.email_flags.is_unread()) {
-        //          ConversationMessage row = conversation_message_for_id(message.id);
-        //          if (!row.is_manual_read() &&
-        //              body.offset_top + body.offset_height > scroll_top &&
-        //              body.offset_top + 28 < scroll_top + scroll_height) {  // 28 = 15 padding + 13 first 
line of text
-        //              emails.add(message.id);
-                        
-        //              // since it can take some time for the new flags
-        //              // to round-trip back to ConversationViewer's
-        //              // signal handlers, mark as manually read here
-        //              mark_manual_read(message.id);
-        //          }
-        //      }
-        //  } catch (Error error) {
-        //      debug("Problem checking email class: %s", error.message);
-        //  }
-        // }
+        Gee.ArrayList<Geary.EmailIdentifier> emails =
+            new Gee.ArrayList<Geary.EmailIdentifier>();
+
+        Gtk.Adjustment adj = conversation_page.vadjustment;
+        int top_bound = (int) adj.value;
+        int bottom_bound = top_bound + (int) adj.page_size;
+
+        const int TEXT_PADDING = 50;
+        foreach (Geary.Email message in messages) {
+            ConversationMessage row = conversation_message_for_id(message.id);
+            // Don't bother with not-yet-loaded messages since the
+            // size of the body will be off, affecting the visibility
+            // of messages further down the conversation.
+            if (message.email_flags.is_unread() &&
+                row.is_loading_complete &&
+                !row.is_manual_read()) {
+                 int body_top = 0;
+                 int body_left = 0;
+                 row.web_view.translate_coordinates(
+                     conversation_listbox,
+                     0, 0,
+                     out body_left, out body_top
+                 );
+                 int body_bottom = body_top + row.web_view_allocation.height;
+
+                 // Only mark the message as read if it's actually visible
+                 if (body_bottom > top_bound &&
+                     body_top + TEXT_PADDING < bottom_bound) {
+                     emails.add(message.id);
+
+                     // Since it can take some time for the new flags
+                     // to round-trip back to ConversationViewer's
+                     // signal handlers, mark as manually read here
+                     row.mark_manual_read();
+                 }
+             }
+        }
 
         if (emails.size > 0) {
             Geary.EmailFlags flags = new Geary.EmailFlags();


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