diff --git a/src/client/ui/main-window.vala b/src/client/ui/main-window.vala index e5244d60..f5175553 100644 --- a/src/client/ui/main-window.vala +++ b/src/client/ui/main-window.vala @@ -31,6 +31,7 @@ public class MainWindow : Gtk.Window { message_list_view = new MessageListView(message_list_store); message_list_view.conversation_selected.connect(on_conversation_selected); + message_list_view.load_more.connect(on_load_more); folder_list_view = new FolderListView(folder_list_store); folder_list_view.folder_selected.connect(on_folder_selected); @@ -202,8 +203,10 @@ public class MainWindow : Gtk.Window { current_conversations.lazy_load(-1, -1, Geary.Folder.ListFlags.FAST, cancellable_folder); } - public void on_scan_started() { - debug("on scan started"); + public void on_scan_started(Geary.EmailIdentifier? id, int low, int count) { + debug("on scan started. id = %s low = %d count = %d", id != null ? id.to_string() : "(null)", + low, count); + message_list_view.enable_load_more = false; } public void on_scan_error(Error err) { @@ -214,6 +217,7 @@ public class MainWindow : Gtk.Window { debug("on scan completed"); do_fetch_previews.begin(cancellable_message); + message_list_view.enable_load_more = true; } public void on_conversations_added(Gee.Collection conversations) { @@ -242,6 +246,25 @@ public class MainWindow : Gtk.Window { message_list_store.update_conversation(conversation); } + private void on_load_more() { + debug("on_load_more"); + message_list_view.enable_load_more = false; + + Geary.EmailIdentifier? low_id = message_list_store.get_email_id_lowest(); + + current_conversations.load_by_id_async.begin(low_id, - FETCH_EMAIL_CHUNK_COUNT, + Geary.Folder.ListFlags.NONE, cancellable_folder, on_load_more_completed); + } + + private void on_load_more_completed(Object? source, AsyncResult result) { + debug("on load more completed"); + try { + current_conversations.load_by_id_async.end(result); + } catch (Error err) { + debug("Error, unable to load conversations: %s", err.message); + } + } + private async void do_fetch_previews(Cancellable? cancellable) throws Error { int count = message_list_store.get_count(); for (int ctr = 0; ctr < count; ctr++) { diff --git a/src/client/ui/message-list-store.vala b/src/client/ui/message-list-store.vala index 1dd5d442..6557e22d 100644 --- a/src/client/ui/message-list-store.vala +++ b/src/client/ui/message-list-store.vala @@ -157,6 +157,23 @@ public class MessageListStore : Gtk.TreeStore { return conversation; } + public Geary.EmailIdentifier? get_email_id_lowest() { + Geary.EmailIdentifier? low = null; + int count = get_count(); + for (int ctr = 0; ctr < count; ctr++) { + Geary.Conversation c = get_conversation_at_index(ctr); + Gee.SortedSet? mail = c.get_pool_sorted(compare_email_id_desc); + if (mail == null) + continue; + + Geary.EmailIdentifier pos = mail.first().id; + if (low == null || pos.ordering < low.ordering) + low = pos; + } + + return low; + } + private bool find_conversation(Geary.Conversation conversation, out Gtk.TreeIter iter) { iter = Gtk.TreeIter(); int count = get_count(); diff --git a/src/client/ui/message-list-view.vala b/src/client/ui/message-list-view.vala index 3037e361..4e0ed5c1 100644 --- a/src/client/ui/message-list-view.vala +++ b/src/client/ui/message-list-view.vala @@ -5,7 +5,16 @@ */ public class MessageListView : Gtk.TreeView { + const int LOAD_MORE_HEIGHT = 100; + + public bool enable_load_more { get; set; default = true; } + + // Used to avoid repeated calls to load_more(). Contains the last "upper" bound of the + // scroll adjustment seen at the call to load_more(). + double last_upper = -1.0; + public signal void conversation_selected(Geary.Conversation? conversation); + public signal void load_more(); public MessageListView(MessageListStore store) { set_model(store); @@ -18,7 +27,8 @@ public class MessageListView : Gtk.TreeView { MessageListStore.Column.MESSAGE_DATA.to_string(), 0)); get_selection().changed.connect(on_selection_changed); - this.style_set.connect(on_style_changed); + style_set.connect(on_style_changed); + show.connect(on_show); } private void on_style_changed() { @@ -26,6 +36,25 @@ public class MessageListView : Gtk.TreeView { MessageListCellRenderer.style_changed(this); } + private void on_show() { + // Wait until we're visible to set this signal up. + get_vadjustment().value_changed.connect(on_value_changed); + } + + private void on_value_changed() { + if (!enable_load_more) + return; + + // Check if we're at the very bottom of the list. If we are, it's time to + // issue a load_more signal. + if (get_vadjustment().get_value() >= get_vadjustment().get_upper() - + get_vadjustment().page_size - LOAD_MORE_HEIGHT && get_vadjustment().get_upper() + > last_upper) { + load_more(); + last_upper = get_vadjustment().get_upper(); + } + } + private static Gtk.TreeViewColumn create_column(MessageListStore.Column column, Gtk.CellRenderer renderer, string attr, int width = 0) { Gtk.TreeViewColumn view_column = new Gtk.TreeViewColumn.with_attributes(column.to_string(), diff --git a/src/client/ui/message-viewer.vala b/src/client/ui/message-viewer.vala index cf04ae61..15e078cb 100644 --- a/src/client/ui/message-viewer.vala +++ b/src/client/ui/message-viewer.vala @@ -70,6 +70,7 @@ public class MessageViewer : Gtk.Viewport { public void add_message(Geary.Email email) { messages.add(email); Gtk.Builder builder = GearyApplication.instance.create_builder("message.glade"); + debug("Message id: %s", email.id.to_string()); string username; try { diff --git a/src/client/util/util-email.vala b/src/client/util/util-email.vala index 60d7bf5f..397aa89d 100644 --- a/src/client/util/util-email.vala +++ b/src/client/util/util-email.vala @@ -10,3 +10,7 @@ public int compare_email(Geary.Email aenvelope, Geary.Email benvelope) { // stabilize sort by using the mail's ordering, which is always unique in a folder return (diff != 0) ? diff : aenvelope.id.compare(benvelope.id); } + +public int compare_email_id_desc(Geary.Email aenvelope, Geary.Email benvelope) { + return (int) (aenvelope.id.ordering - benvelope.id.ordering); +} diff --git a/src/engine/api/geary-conversations.vala b/src/engine/api/geary-conversations.vala index 5f7085c6..62b1f9ee 100644 --- a/src/engine/api/geary-conversations.vala +++ b/src/engine/api/geary-conversations.vala @@ -374,6 +374,7 @@ public class Geary.Conversations : Object { on_email_listed(null, null); } catch (Error err) { on_email_listed(null, err); + throw err; } } diff --git a/src/engine/impl/geary-engine-folder.vala b/src/engine/impl/geary-engine-folder.vala index 38f3dee3..7dc68d81 100644 --- a/src/engine/impl/geary-engine-folder.vala +++ b/src/engine/impl/geary-engine-folder.vala @@ -387,7 +387,7 @@ private class Geary.EngineFolder : Geary.AbstractFolder { // because the local store caches messages starting from the newest (at the end of the list) // to the earliest fetched by the user, need to adjust the low value to match its offset // and range - local_low = (low - (remote_count - local_count)).clamp(1, local_count); + local_low = low - (remote_count - local_count); } else { normalize_span_specifiers(ref low, ref count, local_count); local_low = low.clamp(1, local_count); @@ -397,14 +397,16 @@ private class Geary.EngineFolder : Geary.AbstractFolder { low, count, local_count, remote_count, local_low); Gee.List? local_list = null; - try { - local_list = yield local_folder.list_email_async(local_low, count, required_fields, - Geary.Folder.ListFlags.NONE, cancellable); - } catch (Error local_err) { - if (cb != null) - cb (null, local_err); - - throw local_err; + if (local_low > 0) { + try { + local_list = yield local_folder.list_email_async(local_low, count, required_fields, + Geary.Folder.ListFlags.NONE, cancellable); + } catch (Error local_err) { + if (cb != null) + cb (null, local_err); + + throw local_err; + } } int local_list_size = (local_list != null) ? local_list.size : 0; @@ -684,6 +686,10 @@ private class Geary.EngineFolder : Geary.AbstractFolder { initial_id.to_string(), to_string()); } + // normalize the initial position to the remote folder's addressing + initial_position = remote_count - (local_count - initial_position); + assert(initial_position > 0); + // since count can also indicate "to earliest" or "to latest", normalize // (count is exclusive of initial_id, hence adding/substracting one, meaning that a count // of zero or one are accepted)