Fast listing of messages in a folder

This adds a new flag when listing messages, FAST.  This indicates that the caller wants
messages that are immediately available to the Folder, avoiding a round-trip to the server
(or even disk) if possible.  Not all folders will support FAST, but it can be used (as it is
now in the client) to quickly populate the message list and then initiate a connection in
the background to get the straight dope.
This commit is contained in:
Jim Nelson 2011-10-04 18:44:18 -07:00
parent aa3044f57d
commit 428c0825a9
10 changed files with 187 additions and 80 deletions

View file

@ -32,6 +32,7 @@ public class MainWindow : Gtk.Window {
private Gtk.UIManager ui = new Gtk.UIManager();
private Geary.EngineAccount? account = null;
private Geary.Folder? current_folder = null;
private bool second_list_pass_required = false;
private int window_width;
private int window_height;
private bool window_maximized;
@ -283,19 +284,28 @@ public class MainWindow : Gtk.Window {
yield current_folder.open_async(true);
current_folder.lazy_list_email(-1, 50, MessageListStore.REQUIRED_FIELDS, on_list_email_ready);
// Do a quick-list of the messages (which should return what's in the local store) if
// supported by the Folder, followed by a complete list if needed
second_list_pass_required =
current_folder.get_supported_list_flags().is_all_set(Geary.Folder.ListFlags.FAST);
current_folder.lazy_list_email(-1, 50, MessageListStore.REQUIRED_FIELDS,
current_folder.get_supported_list_flags() & Geary.Folder.ListFlags.FAST,
on_list_email_ready);
}
private void on_list_email_ready(Gee.List<Geary.Email>? email, Error? err) {
if (email != null && email.size > 0) {
foreach (Geary.Email envelope in email)
message_list_store.append_envelope(envelope);
debug("Listing %d emails", email.size);
foreach (Geary.Email envelope in email) {
if (!message_list_store.has_envelope(envelope))
message_list_store.append_envelope(envelope);
}
}
if (err != null)
debug("Error while listing email: %s", err.message);
// end of list
// end of list, go get the previews for them
if (email == null)
do_fetch_previews.begin();
}
@ -303,13 +313,21 @@ public class MainWindow : Gtk.Window {
private async void do_fetch_previews(Cancellable? cancellable = null) throws Error {
int count = message_list_store.get_count();
for (int ctr = 0; ctr < count; ctr++) {
Geary.Email? email = message_list_store.get_message_at_pos(ctr);
Geary.Email? email = message_list_store.get_message_at_index(ctr);
Geary.Email? body = yield current_folder.fetch_email_async(email.id,
Geary.Email.Field.HEADER | Geary.Email.Field.BODY | Geary.Email.Field.ENVELOPE |
Geary.Email.Field.PROPERTIES, cancellable);
message_list_store.set_preview_at_pos(ctr, body);
message_list_store.set_preview_at_index(ctr, body);
}
}
// with all the previews fetched, now go back and do a full list (if required)
if (second_list_pass_required) {
second_list_pass_required = false;
debug("Doing second list pass now");
current_folder.lazy_list_email(-1, 50, MessageListStore.REQUIRED_FIELDS,
Geary.Folder.ListFlags.NONE, on_list_email_ready);
}
}
private void on_select_folder_completed(Object? source, AsyncResult result) {
try {
@ -384,7 +402,7 @@ public class MainWindow : Gtk.Window {
// Want to get the one *after* the highest position in the message list
current_folder.lazy_list_email(high + 1, -1, MessageListStore.REQUIRED_FIELDS,
on_list_email_ready);
Geary.Folder.ListFlags.NONE, on_list_email_ready);
}
private async void search_folders_for_children(Gee.Collection<Geary.Folder> folders) {

View file

@ -61,14 +61,31 @@ public class MessageListStore : Gtk.TreeStore {
envelope.location.position_deleted.connect(on_email_position_deleted);
}
public Geary.Email? get_message_at_pos(int pos) {
return get_message_at(new Gtk.TreePath.from_indices(pos, -1));
// The Email should've been fetched with REQUIRED_FIELDS.
public bool has_envelope(Geary.Email envelope) {
assert(envelope.fields.fulfills(REQUIRED_FIELDS));
int count = get_count();
for (int ctr = 0; ctr < count; ctr++) {
Geary.Email? email = get_message_at_index(ctr);
if (email == null)
break;
if (email.location.position == envelope.location.position)
return true;
}
return false;
}
public void set_preview_at_pos(int pos, Geary.Email email) {
public Geary.Email? get_message_at_index(int index) {
return get_message_at(new Gtk.TreePath.from_indices(index, -1));
}
public void set_preview_at_index(int index, Geary.Email email) {
Gtk.TreeIter iter;
if (!get_iter(out iter, new Gtk.TreePath.from_indices(pos, -1))) {
warning("Unable to get tree path from position: %d".printf(pos));
if (!get_iter(out iter, new Gtk.TreePath.from_indices(index, -1))) {
warning("Unable to get tree path from position: %d".printf(index));
return;
}

View file

@ -25,6 +25,8 @@ public abstract class Geary.AbstractFolder : Object, Geary.Folder {
public abstract Geary.FolderProperties? get_properties();
public abstract Geary.Folder.ListFlags get_supported_list_flags();
public abstract async void open_async(bool readonly, Cancellable? cancellable = null) throws Error;
public abstract async void close_async(Cancellable? cancellable = null) throws Error;
@ -35,17 +37,18 @@ public abstract class Geary.AbstractFolder : Object, Geary.Folder {
throws Error;
public abstract async Gee.List<Geary.Email>? list_email_async(int low, int count,
Geary.Email.Field required_fields, Cancellable? cancellable = null) throws Error;
Geary.Email.Field required_fields, Folder.ListFlags flags, Cancellable? cancellable = null)
throws Error;
public virtual void lazy_list_email(int low, int count, Geary.Email.Field required_fields,
EmailCallback cb, Cancellable? cancellable = null) {
do_lazy_list_email_async.begin(low, count, required_fields, cb, cancellable);
Folder.ListFlags flags, EmailCallback cb, Cancellable? cancellable = null) {
do_lazy_list_email_async.begin(low, count, required_fields, flags, cb, cancellable);
}
private async void do_lazy_list_email_async(int low, int count, Geary.Email.Field required_fields,
EmailCallback cb, Cancellable? cancellable = null) {
Folder.ListFlags flags, EmailCallback cb, Cancellable? cancellable = null) {
try {
Gee.List<Geary.Email>? list = yield list_email_async(low, count, required_fields,
Gee.List<Geary.Email>? list = yield list_email_async(low, count, required_fields, flags,
cancellable);
if (list != null && list.size > 0)
@ -58,18 +61,21 @@ public abstract class Geary.AbstractFolder : Object, Geary.Folder {
}
public abstract async Gee.List<Geary.Email>? list_email_sparse_async(int[] by_position,
Geary.Email.Field required_fields, Cancellable? cancellable = null) throws Error;
Geary.Email.Field required_fields, Folder.ListFlags flags, Cancellable? cancellable = null)
throws Error;
public virtual void lazy_list_email_sparse(int[] by_position,
Geary.Email.Field required_fields, EmailCallback cb, Cancellable? cancellable = null) {
do_lazy_list_email_sparse_async.begin(by_position, required_fields, cb, cancellable);
Geary.Email.Field required_fields, Folder.ListFlags flags, EmailCallback cb,
Cancellable? cancellable = null) {
do_lazy_list_email_sparse_async.begin(by_position, required_fields, flags, cb, cancellable);
}
private async void do_lazy_list_email_sparse_async(int[] by_position,
Geary.Email.Field required_fields, EmailCallback cb, Cancellable? cancellable = null) {
Geary.Email.Field required_fields, Folder.ListFlags flags, EmailCallback cb,
Cancellable? cancellable = null) {
try {
Gee.List<Geary.Email>? list = yield list_email_sparse_async(by_position,
required_fields, cancellable);
required_fields, flags, cancellable);
if (list != null && list.size > 0)
cb(list, null);

View file

@ -69,6 +69,10 @@ private class Geary.EngineFolder : Geary.AbstractFolder {
return null;
}
public override Geary.Folder.ListFlags get_supported_list_flags() {
return Geary.Folder.ListFlags.FAST;
}
public override async void create_email_async(Geary.Email email, Cancellable? cancellable) throws Error {
throw new EngineError.READONLY("Engine currently read-only");
}
@ -223,7 +227,7 @@ private class Geary.EngineFolder : Geary.AbstractFolder {
// normalize starting at the message *after* the highest position of the local store,
// which has now changed
Gee.List<Geary.Email>? list = yield remote_folder.list_email_async(remote_count + 1, -1,
Geary.Email.Field.PROPERTIES, null);
Geary.Email.Field.PROPERTIES, Geary.Folder.ListFlags.NONE, null);
assert(list != null && list.size > 0);
foreach (Geary.Email email in list)
@ -287,27 +291,30 @@ private class Geary.EngineFolder : Geary.AbstractFolder {
}
public override async Gee.List<Geary.Email>? list_email_async(int low, int count,
Geary.Email.Field required_fields, Cancellable? cancellable = null) throws Error {
Geary.Email.Field required_fields, Folder.ListFlags flags, Cancellable? cancellable = null)
throws Error {
if (count == 0)
return null;
// block on do_list_email_async(), using an accumulator to gather the emails and return
// them all at once to the caller
Gee.List<Geary.Email> accumulator = new Gee.ArrayList<Geary.Email>();
yield do_list_email_async(low, count, required_fields, accumulator, null, cancellable);
yield do_list_email_async(low, count, required_fields, accumulator, null, cancellable,
flags.is_any_set(Folder.ListFlags.FAST));
return accumulator;
}
public override void lazy_list_email(int low, int count, Geary.Email.Field required_fields,
EmailCallback cb, Cancellable? cancellable = null) {
Geary.Folder.ListFlags flags, EmailCallback cb, Cancellable? cancellable = null) {
// schedule do_list_email_async(), using the callback to drive availability of email
do_list_email_async.begin(low, count, required_fields, null, cb, cancellable);
do_list_email_async.begin(low, count, required_fields, null, cb, cancellable,
flags.is_any_set(Folder.ListFlags.FAST));
}
private async void do_list_email_async(int low, int count, Geary.Email.Field required_fields,
Gee.List<Geary.Email>? accumulator, EmailCallback? cb, Cancellable? cancellable = null)
throws Error {
Gee.List<Geary.Email>? accumulator, EmailCallback? cb, Cancellable? cancellable,
bool local_only) throws Error {
check_span_specifiers(low, count);
if (!opened)
@ -321,19 +328,26 @@ private class Geary.EngineFolder : Geary.AbstractFolder {
return;
}
// normalize the position (ordering) of what's available locally with the situation on
// the server
int local_count, remote_count;
yield normalize_email_positions_async(low, count, out local_count, out remote_count,
cancellable);
// normalize the arguments to match what's on the remote server
normalize_span_specifiers(ref low, ref count, remote_count);
// 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
int local_low = (low - (remote_count - local_count)).clamp(1, local_count);
int local_count, remote_count, local_low;
if (!local_only) {
// normalize the position (ordering) of what's available locally with the situation on
// the server
yield normalize_email_positions_async(low, count, out local_count, out remote_count,
cancellable);
// normalize the arguments to match what's on the remote server
normalize_span_specifiers(ref low, ref count, remote_count);
// 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);
} else {
// local_only means just that
local_count = yield local_folder.get_email_count_async(cancellable);
local_low = 1;
remote_count = -1;
}
debug("do_list_email_async: low=%d count=%d local_count=%d remote_count=%d local_low=%d",
low, count, local_count, remote_count, local_low);
@ -341,7 +355,7 @@ private class Geary.EngineFolder : Geary.AbstractFolder {
Gee.List<Geary.Email>? local_list = null;
try {
local_list = yield local_folder.list_email_async(local_low, count, required_fields,
cancellable);
Geary.Folder.ListFlags.NONE, cancellable);
} catch (Error local_err) {
if (cb != null)
cb (null, local_err);
@ -361,7 +375,7 @@ private class Geary.EngineFolder : Geary.AbstractFolder {
cb(local_list, null);
}
if (local_list_size == count) {
if (local_list_size == count || local_only) {
// signal finished
if (cb != null)
cb(null, null);
@ -430,25 +444,27 @@ private class Geary.EngineFolder : Geary.AbstractFolder {
}
public override async Gee.List<Geary.Email>? list_email_sparse_async(int[] by_position,
Geary.Email.Field required_fields, Cancellable? cancellable = null) throws Error {
Geary.Email.Field required_fields, Folder.ListFlags flags, Cancellable? cancellable = null)
throws Error {
if (by_position.length == 0)
return null;
Gee.List<Geary.Email> accumulator = new Gee.ArrayList<Geary.Email>();
yield do_list_email_sparse_async(by_position, required_fields, accumulator, null,
cancellable);
cancellable, flags.is_any_set(Folder.ListFlags.FAST));
return accumulator;
}
public override void lazy_list_email_sparse(int[] by_position, Geary.Email.Field required_fields,
EmailCallback cb, Cancellable? cancellable = null) {
Folder.ListFlags flags, EmailCallback cb, Cancellable? cancellable = null) {
// schedule listing in the background, using the callback to drive availability of email
do_list_email_sparse_async.begin(by_position, required_fields, null, cb, cancellable);
do_list_email_sparse_async.begin(by_position, required_fields, null, cb, cancellable,
flags.is_any_set(Folder.ListFlags.FAST));
}
private async void do_list_email_sparse_async(int[] by_position, Geary.Email.Field required_fields,
Gee.List<Geary.Email>? accumulator, EmailCallback? cb, Cancellable? cancellable = null)
Gee.List<Geary.Email>? accumulator, EmailCallback? cb, Cancellable? cancellable, bool local_only)
throws Error {
if (!opened)
throw new EngineError.OPEN_REQUIRED("%s is not open", to_string());
@ -461,26 +477,36 @@ private class Geary.EngineFolder : Geary.AbstractFolder {
return;
}
// normalize the position (ordering) of what's available locally with the situation on
// the server
int low, high;
Arrays.int_find_high_low(by_position, out low, out high);
int local_count, remote_count;
yield normalize_email_positions_async(low, high - low + 1, out local_count, out remote_count,
cancellable);
int local_offset = (remote_count > local_count) ? (remote_count - local_count - 1) : 0;
int local_count, remote_count, local_offset;
if (!local_only) {
// normalize the position (ordering) of what's available locally with the situation on
// the server
yield normalize_email_positions_async(low, high - low + 1, out local_count, out remote_count,
cancellable);
local_offset = (remote_count > local_count) ? (remote_count - local_count - 1) : 0;
} else {
local_count = yield local_folder.get_email_count_async(cancellable);
remote_count = -1;
local_offset = 0;
}
// Fixup all the positions to match the local store's notions
int[] local_by_position = new int[by_position.length];
for (int ctr = 0; ctr < by_position.length; ctr++)
local_by_position[ctr] = by_position[ctr] - local_offset;
if (local_offset > 0) {
int[] local_by_position = new int[by_position.length];
for (int ctr = 0; ctr < by_position.length; ctr++)
local_by_position[ctr] = by_position[ctr] - local_offset;
by_position = local_by_position;
}
Gee.List<Geary.Email>? local_list = null;
try {
local_list = yield local_folder.list_email_sparse_async(local_by_position, required_fields,
cancellable);
local_list = yield local_folder.list_email_sparse_async(by_position, required_fields,
Folder.ListFlags.NONE, cancellable);
} catch (Error local_err) {
if (cb != null)
cb(null, local_err);
@ -491,7 +517,7 @@ private class Geary.EngineFolder : Geary.AbstractFolder {
int local_list_size = (local_list != null) ? local_list.size : 0;
// reverse the process, fixing up all the returned messages to match the server's notions
if (local_list_size > 0) {
if (local_list_size > 0 && local_offset > 0) {
foreach (Geary.Email email in local_list) {
int new_position = email.location.position + local_offset;
email.update_location(new Geary.EmailLocation(this, new_position,
@ -499,7 +525,7 @@ private class Geary.EngineFolder : Geary.AbstractFolder {
}
}
if (local_list_size == by_position.length) {
if (local_list_size == by_position.length || local_only) {
if (accumulator != null)
accumulator.add_all(local_list);
@ -595,7 +621,8 @@ private class Geary.EngineFolder : Geary.AbstractFolder {
// Always get the flags, and the generic end-user won't know to ask for them until they
// need them
Gee.List<Geary.Email>? remote_list = yield remote_folder.list_email_sparse_async(
list, required_fields | Geary.Email.Field.PROPERTIES, cancellable);
list, required_fields | Geary.Email.Field.PROPERTIES, Geary.Folder.ListFlags.NONE,
cancellable);
if (remote_list == null || remote_list.size == 0)
break;
@ -696,8 +723,7 @@ private class Geary.EngineFolder : Geary.AbstractFolder {
// means there can be no gaps between the last in the database and the last on the server.
// This method takes care of that.
private async void normalize_email_positions_async(int low, int count, out int local_count,
out int remote_count, Cancellable? cancellable)
throws Error {
out int remote_count, Cancellable? cancellable) throws Error {
if (!yield wait_for_remote_to_open())
throw new EngineError.SERVER_UNAVAILABLE("No connection to %s", remote.to_string());
@ -727,7 +753,7 @@ private class Geary.EngineFolder : Geary.AbstractFolder {
// TODO: Consider only fetching their UID; would need Geary.Email.Field.LOCATION (or
// perhaps NONE is considered a call for just the UID).
Gee.List<Geary.Email>? list = yield remote_folder.list_email_async(high, prefetch_count,
Geary.Email.Field.PROPERTIES, cancellable);
Geary.Email.Field.PROPERTIES, Geary.Folder.ListFlags.NONE, cancellable);
if (list == null || list.size != prefetch_count) {
throw new EngineError.BAD_PARAMETERS("Unable to prefetch %d email starting at %d in %s",
count, low, to_string());

View file

@ -24,6 +24,20 @@ public interface Geary.Folder : Object {
AFTER
}
[Flags]
public enum ListFlags {
NONE = 0,
FAST;
public bool is_any_set(ListFlags flags) {
return (this & flags) != 0;
}
public bool is_all_set(ListFlags flags) {
return (this & flags) == flags;
}
}
/**
* This is fired when the Folder is successfully opened by a caller. It will only fire once
* until the Folder is closed, with the OpenState indicating what has been opened.
@ -86,6 +100,8 @@ public interface Geary.Folder : Object {
public abstract Geary.FolderProperties? get_properties();
public abstract ListFlags get_supported_list_flags();
/**
* The Folder must be opened before most operations may be performed on it. Depending on the
* implementation this might entail opening a network connection or setting the connection to
@ -165,12 +181,23 @@ public interface Geary.Folder : Object {
* and fetch from the network only what it needs, so that the caller gets a full list.
* Note that this means the call may require a round-trip to the server.
*
* If the caller would prefer the Folder return emails it has immediately available rather than
* make an expensive I/O call to "properly" fetch the emails, it should pass ListFlags.FAST.
* However, this also means avoiding a full synchronization, so it's possible the fetched
* emails do not correspond to what's actually available on the server.
* The best use of this method is to quickly retrieve a block of email for display or processing
* purposes, immediately followed by a non-fast list operation and then merging the two results.
*
* Note that implementing ListFlags.FAST is advisory, not required. The implementation may
* ignore it completely. See get_supported_list_flags() for more information.
*
* The Folder must be opened prior to attempting this operation.
*
* low is one-based, unless -1 is specified, as explained above.
*/
public abstract async Gee.List<Geary.Email>? list_email_async(int low, int count,
Geary.Email.Field required_fields, Cancellable? cancellable = null) throws Error;
Geary.Email.Field required_fields, ListFlags flags, Cancellable? cancellable = null)
throws Error;
/**
* Similar in contract to list_email_async(), however instead of the emails being returned all
@ -183,7 +210,7 @@ public interface Geary.Folder : Object {
* The Folder must be opened prior to attempting this operation.
*/
public abstract void lazy_list_email(int low, int count, Geary.Email.Field required_fields,
EmailCallback cb, Cancellable? cancellable = null);
ListFlags flags, EmailCallback cb, Cancellable? cancellable = null);
/**
* Like list_email_async(), but the caller passes a sparse list of email by it's ordered
@ -198,7 +225,8 @@ public interface Geary.Folder : Object {
* All positions are one-based.
*/
public abstract async Gee.List<Geary.Email>? list_email_sparse_async(int[] by_position,
Geary.Email.Field required_fields, Cancellable? cancellable = null) throws Error;
Geary.Email.Field required_fields, ListFlags flags, Cancellable? cancellable = null)
throws Error;
/**
* Similar in contract to list_email_sparse_async(), but like lazy_list_email(), the
@ -210,7 +238,8 @@ public interface Geary.Folder : Object {
* The Folder must be opened prior to attempting this operation.
*/
public abstract void lazy_list_email_sparse(int[] by_position,
Geary.Email.Field required_fields, EmailCallback cb, Cancellable? cancellable = null);
Geary.Email.Field required_fields, ListFlags flags, EmailCallback cb,
Cancellable? cancellable = null);
/**
* Returns a single email that fulfills the required_fields flag at the ordered position in

View file

@ -178,7 +178,7 @@ public class Geary.Imap.Account : Geary.AbstractAccount, Geary.RemoteAccount {
} finally {
// always logout
try {
yield smtp.quit_async(cancellable);
yield smtp.logout_async(cancellable);
} catch (Error err) {
message("Unable to disconnect from SMTP server %s: %s", smtp.to_string(), err.message);
}

View file

@ -39,6 +39,10 @@ public class Geary.Imap.Folder : Geary.AbstractFolder, Geary.RemoteFolder, Geary
return properties;
}
public override Geary.Folder.ListFlags get_supported_list_flags() {
return Geary.Folder.ListFlags.NONE;
}
public override async void open_async(bool readonly, Cancellable? cancellable = null) throws Error {
if (mailbox != null)
throw new EngineError.ALREADY_OPEN("%s already open", to_string());
@ -110,7 +114,7 @@ public class Geary.Imap.Folder : Geary.AbstractFolder, Geary.RemoteFolder, Geary
}
public override async Gee.List<Geary.Email>? list_email_async(int low, int count, Geary.Email.Field fields,
Cancellable? cancellable = null) throws Error {
Geary.Folder.ListFlags flags, Cancellable? cancellable = null) throws Error {
if (mailbox == null)
throw new EngineError.OPEN_REQUIRED("%s not opened", to_string());
@ -120,7 +124,8 @@ public class Geary.Imap.Folder : Geary.AbstractFolder, Geary.RemoteFolder, Geary
}
public override async Gee.List<Geary.Email>? list_email_sparse_async(int[] by_position,
Geary.Email.Field fields, Cancellable? cancellable = null) throws Error {
Geary.Email.Field fields, Geary.Folder.ListFlags flags, Cancellable? cancellable = null)
throws Error {
if (mailbox == null)
throw new EngineError.OPEN_REQUIRED("%s not opened", to_string());

View file

@ -75,7 +75,7 @@ public class Geary.Smtp.ClientSession {
return greeting;
}
public async Response? quit_async(Cancellable? cancellable = null) throws Error {
public async Response? logout_async(Cancellable? cancellable = null) throws Error {
Response? response = null;
try {
response = yield cx.transaction_async(new Request(Command.QUIT), cancellable);

View file

@ -46,6 +46,10 @@ public class Geary.Sqlite.Folder : Geary.AbstractFolder, Geary.LocalFolder, Gear
return properties;
}
public override Geary.Folder.ListFlags get_supported_list_flags() {
return Geary.Folder.ListFlags.NONE;
}
internal void update_properties(Geary.Imap.FolderProperties? properties) {
this.properties = properties;
}
@ -114,7 +118,8 @@ public class Geary.Sqlite.Folder : Geary.AbstractFolder, Geary.LocalFolder, Gear
}
public override async Gee.List<Geary.Email>? list_email_async(int low, int count,
Geary.Email.Field required_fields, Cancellable? cancellable) throws Error {
Geary.Email.Field required_fields, Geary.Folder.ListFlags flags, Cancellable? cancellable)
throws Error {
check_open();
normalize_span_specifiers(ref low, ref count, yield get_email_count_async(cancellable));
@ -129,7 +134,8 @@ public class Geary.Sqlite.Folder : Geary.AbstractFolder, Geary.LocalFolder, Gear
}
public override async Gee.List<Geary.Email>? list_email_sparse_async(int[] by_position,
Geary.Email.Field required_fields, Cancellable? cancellable = null) throws Error {
Geary.Email.Field required_fields, Geary.Folder.ListFlags flags, Cancellable? cancellable = null)
throws Error {
check_open();
Gee.List<MessageLocationRow>? list = yield location_table.list_sparse_async(folder_row.id,

View file

@ -21,8 +21,8 @@ async void main_async() throws Error {
yield session.send_email_async(msg.message);
stdout.printf("Sent\n");
Geary.Smtp.Response? quit = yield session.quit_async();
stdout.printf("%s\n", quit.to_string());
Geary.Smtp.Response? logout = yield session.logout_async();
stdout.printf("%s\n", logout.to_string());
}
void on_main_completed(Object? object, AsyncResult result) {