Closes #7154 DB upgrade dialog

This commit is contained in:
Eric Gregory 2013-07-08 14:22:23 -07:00
parent a2f6ad8c38
commit ffa642f458
11 changed files with 267 additions and 17 deletions

View file

@ -299,6 +299,7 @@ client/dialogs/alert-dialog.vala
client/dialogs/attachment-dialog.vala
client/dialogs/password-dialog.vala
client/dialogs/preferences-dialog.vala
client/dialogs/upgrade-dialog.vala
client/folder-list/folder-list-abstract-folder-entry.vala
client/folder-list/folder-list-account-branch.vala

View file

@ -0,0 +1,67 @@
/* Copyright 2013 Yorba Foundation
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
*/
public class UpgradeDialog : Object {
public const string PROP_VISIBLE_NAME = "visible";
// Progress monitor associated with the upgrade.
public Geary.AggregateProgressMonitor monitor { public get; private set;
default = new Geary.AggregateProgressMonitor(); }
// Whether or not this dialog is visible.
public bool visible { get; set; }
private Gtk.Dialog dialog;
private Gee.HashSet<Cancellable> cancellables = new Gee.HashSet<Cancellable>();
/**
* Creates and loads the upgrade progress dialog.
*/
public UpgradeDialog() {
// Load UI.
Gtk.Builder builder = GearyApplication.instance.create_builder("upgrade_dialog.glade");
dialog = (Gtk.Dialog) builder.get_object("dialog");
// Hook up signals.
monitor.start.connect(on_start);
monitor.finish.connect(on_close);
dialog.delete_event.connect(on_delete_event);
// Bind visibility flag.
dialog.bind_property(PROP_VISIBLE_NAME, this, PROP_VISIBLE_NAME, BindingFlags.BIDIRECTIONAL |
BindingFlags.SYNC_CREATE);
}
private void on_start() {
dialog.show();
}
private bool on_delete_event() {
// Don't allow window to close until we're finished.
return !monitor.is_in_progress;
}
private void on_close() {
// If the user quit the dialog before the upgrade completed, cancel everything.
if (monitor.is_in_progress) {
foreach(Cancellable c in cancellables)
c.cancel();
}
if (dialog.visible)
dialog.destroy();
}
/**
* Add accounts before opening them.
*/
public void add_account(Geary.Account account, Cancellable? cancellable = null) {
monitor.add(account.db_upgrade_monitor);
if (cancellable != null)
cancellables.add(cancellable);
}
}

View file

@ -58,6 +58,8 @@ public class GearyController {
private const int SELECT_FOLDER_TIMEOUT_MSEC = 100;
private const int SEARCH_TIMEOUT_MSEC = 100;
private const string PROP_ATTEMPT_OPEN_ACCOUNT = "attempt-open-account";
public MainWindow main_window { get; private set; }
private Geary.Account? current_account = null;
@ -68,6 +70,7 @@ public class GearyController {
private Cancellable cancellable_folder = new Cancellable();
private Cancellable cancellable_message = new Cancellable();
private Cancellable cancellable_search = new Cancellable();
private Cancellable cancellable_open_account = new Cancellable();
private Gee.HashMap<Geary.Account, Cancellable> inbox_cancellables
= new Gee.HashMap<Geary.Account, Cancellable>();
private int busy_count = 0;
@ -86,6 +89,7 @@ public class GearyController {
private Geary.Folder? previous_non_search_folder = null;
private uint search_timeout_id = 0;
private LoginDialog? login_dialog = null;
private UpgradeDialog upgrade_dialog;
/**
* Fired when the currently selected account has changed.
@ -137,6 +141,10 @@ public class GearyController {
// Listen for attempts to close the application.
GearyApplication.instance.exiting.connect(on_application_exiting);
// Create DB upgrade dialog.
upgrade_dialog = new UpgradeDialog();
upgrade_dialog.notify[UpgradeDialog.PROP_VISIBLE_NAME].connect(display_main_window_if_ready);
// Create the main window (must be done after creating actions.)
main_window = new MainWindow();
main_window.notify["has-toplevel-focus"].connect(on_has_toplevel_focus);
@ -193,8 +201,6 @@ public class GearyController {
GearyApplication.instance.get_resource_directory(), new SecretMediator());
if (Geary.Engine.instance.get_accounts().size == 0) {
create_account();
} else {
main_window.show_all();
}
} catch (Error e) {
error("Error opening Geary.Engine instance: %s", e.message);
@ -367,7 +373,7 @@ public class GearyController {
private void open_account(Geary.Account account) {
account.report_problem.connect(on_report_problem);
connect_account_async.begin(account);
connect_account_async.begin(account, cancellable_open_account);
}
private void close_account(Geary.Account account) {
@ -384,7 +390,10 @@ public class GearyController {
}
private void on_account_available(Geary.AccountInformation account_information) {
open_account(get_account_instance(account_information));
Geary.Account account = get_account_instance(account_information);
upgrade_dialog.add_account(account, cancellable_open_account);
open_account(account);
}
private void on_account_unavailable(Geary.AccountInformation account_information) {
@ -404,9 +413,6 @@ public class GearyController {
result = yield validate_or_retry_async(result, cancellable);
} while (result != null);
if (main_window != null) {
main_window.show_all();
}
if (login_dialog != null)
login_dialog.hide();
}
@ -571,6 +577,7 @@ public class GearyController {
account.folders_available_unavailable.connect(on_folders_available_unavailable);
try {
account.set_data(PROP_ATTEMPT_OPEN_ACCOUNT, true);
yield account.open_async(cancellable);
} catch (Error open_err) {
// TODO: Better error reporting to user
@ -584,6 +591,7 @@ public class GearyController {
account.email_sent.connect(on_sent);
main_window.folder_list.set_user_folders_root_name(account, _("Labels"));
display_main_window_if_ready();
}
public async void disconnect_account_async(Geary.Account account, Cancellable? cancellable = null) {
@ -630,6 +638,32 @@ public class GearyController {
}
}
/**
* Returns true if we've attempted to open all accounts at this point.
*/
private bool did_attempt_open_all_accounts() {
try {
foreach (Geary.AccountInformation info in Geary.Engine.instance.get_accounts().values) {
Geary.Account a = Geary.Engine.instance.get_account_instance(info);
if (a.get_data<bool?>(PROP_ATTEMPT_OPEN_ACCOUNT) == null)
return false;
}
} catch(Error e) {
error("Could not open accounts: %s", e.message);
}
return true;
}
/**
* Displays the main window if we're ready. Otherwise does nothing.
*/
private void display_main_window_if_ready() {
if (did_attempt_open_all_accounts() && !upgrade_dialog.visible &&
!cancellable_open_account.is_cancelled())
main_window.show_all();
}
/**
* Returns the number of accounts that exist in Geary. Note that not all accounts may be
* open. Zero is returned on an error.

View file

@ -7,6 +7,7 @@
public abstract class Geary.AbstractAccount : BaseObject, Geary.Account {
public Geary.AccountInformation information { get; protected set; }
public Geary.ProgressMonitor search_upgrade_monitor { get; protected set; }
public Geary.ProgressMonitor db_upgrade_monitor { get; protected set; }
private string name;

View file

@ -16,6 +16,7 @@ public interface Geary.Account : BaseObject {
public abstract Geary.AccountInformation information { get; protected set; }
public abstract Geary.ProgressMonitor search_upgrade_monitor { get; protected set; }
public abstract Geary.ProgressMonitor db_upgrade_monitor { get; protected set; }
public signal void opened();

View file

@ -136,7 +136,9 @@ public class Geary.Engine : BaseObject {
FileEnumerator enumerator
= yield user_data_dir.enumerate_children_async("standard::*",
FileQueryInfoFlags.NONE, Priority.DEFAULT, cancellable);
Gee.List<AccountInformation> account_list = new Gee.ArrayList<AccountInformation>();
for (;;) {
List<FileInfo> info_list;
try {
@ -152,9 +154,12 @@ public class Geary.Engine : BaseObject {
FileInfo info = info_list.nth_data(0);
if (info.get_file_type() == FileType.DIRECTORY) {
// TODO: check for geary.ini
add_account(new AccountInformation.from_file(user_data_dir.get_child(info.get_name())));
account_list.add(new AccountInformation.from_file(user_data_dir.get_child(info.get_name())));
}
}
foreach(AccountInformation info in account_list)
add_account(info);
}
/**

View file

@ -6,11 +6,13 @@
public class Geary.Db.VersionedDatabase : Geary.Db.Database {
public File schema_dir { get; private set; }
public ProgressMonitor upgrade_monitor { get; private set; }
public VersionedDatabase(File db_file, File schema_dir) {
public VersionedDatabase(File db_file, File schema_dir, ProgressMonitor upgrade_monitor) {
base (db_file);
this.schema_dir = schema_dir;
this.upgrade_monitor = upgrade_monitor;
}
protected virtual void pre_upgrade(int version) {
@ -40,6 +42,11 @@ public class Geary.Db.VersionedDatabase : Geary.Db.Database {
if (!upgrade_script.query_exists(cancellable))
break;
if (!upgrade_monitor.is_in_progress)
upgrade_monitor.notify_start();
pump_event_loop();
pre_upgrade(db_version);
check_cancelled("VersionedDatabase.open", cancellable);
@ -58,8 +65,18 @@ public class Geary.Db.VersionedDatabase : Geary.Db.Database {
throw err;
}
pump_event_loop();
post_upgrade(db_version);
}
if (upgrade_monitor.is_in_progress)
upgrade_monitor.notify_finish();
}
protected void pump_event_loop() {
while (Gtk.events_pending())
Gtk.main_iteration();
}
}

View file

@ -30,6 +30,11 @@ private class Geary.ImapDB.Account : BaseObject {
// Only available when the Account is opened
public SmtpOutboxFolder? outbox { get; private set; default = null; }
public SearchFolder? search_folder { get; private set; default = null; }
public ImapEngine.ContactStore contact_store { get; private set; }
public IntervalProgressMonitor search_index_monitor { get; private set;
default = new IntervalProgressMonitor(ProgressType.SEARCH_INDEX, 0, 0); }
public SimpleProgressMonitor upgrade_monitor { get; private set; default = new SimpleProgressMonitor(
ProgressType.DB_UPGRADE); }
private string name;
private AccountInformation account_information;
@ -37,9 +42,6 @@ private class Geary.ImapDB.Account : BaseObject {
private Gee.HashMap<Geary.FolderPath, FolderReference> folder_refs =
new Gee.HashMap<Geary.FolderPath, FolderReference>();
private Cancellable? background_cancellable = null;
public ImapEngine.ContactStore contact_store { get; private set; }
public IntervalProgressMonitor search_index_monitor { get; private set;
default = new IntervalProgressMonitor(ProgressType.SEARCH_INDEX, 0, 0); }
public Account(Geary.AccountInformation account_information) {
this.account_information = account_information;
@ -58,7 +60,7 @@ private class Geary.ImapDB.Account : BaseObject {
if (db != null)
throw new EngineError.ALREADY_OPEN("IMAP database already open");
db = new ImapDB.Database(user_data_dir, schema_dir, account_information.email);
db = new ImapDB.Database(user_data_dir, schema_dir, upgrade_monitor, account_information.email);
try {
db.open(Db.DatabaseFlags.CREATE_DIRECTORY | Db.DatabaseFlags.CREATE_FILE, null,

View file

@ -10,8 +10,9 @@ private class Geary.ImapDB.Database : Geary.Db.VersionedDatabase {
private const string DB_FILENAME = "geary.db";
private string account_owner_email;
public Database(File db_dir, File schema_dir, string account_owner_email) {
base (db_dir.get_child(DB_FILENAME), schema_dir);
public Database(File db_dir, File schema_dir, ProgressMonitor upgrade_monitor,
string account_owner_email) {
base (db_dir.get_child(DB_FILENAME), schema_dir, upgrade_monitor);
this.account_owner_email = account_owner_email;
}
@ -52,8 +53,11 @@ private class Geary.ImapDB.Database : Geary.Db.VersionedDatabase {
while (!result.finished) {
MessageAddresses message_addresses =
new MessageAddresses.from_result(account_owner_email, result);
foreach (Contact contact in message_addresses.contacts)
foreach (Contact contact in message_addresses.contacts) {
do_update_contact(get_master_connection(), contact, null);
pump_event_loop();
}
result.next();
}
} catch (Error err) {
@ -81,6 +85,8 @@ private class Geary.ImapDB.Database : Geary.Db.VersionedDatabase {
}
select.next();
pump_event_loop();
}
} catch (Error e) {
debug("Error decoding folder names during upgrade to database schema 6: %s", e.message);
@ -173,6 +179,8 @@ private class Geary.ImapDB.Database : Geary.Db.VersionedDatabase {
}
select.next();
pump_event_loop();
}
return Db.TransactionOutcome.COMMIT;

View file

@ -32,6 +32,7 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.AbstractAccount {
this.remote.email_sent.connect(on_email_sent);
search_upgrade_monitor = local.search_index_monitor;
db_upgrade_monitor = local.upgrade_monitor;
if (outbox_path == null) {
outbox_path = new SmtpOutboxFolderRoot();

113
ui/upgrade_dialog.glade Normal file
View file

@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<!-- interface-requires gtk+ 3.6 -->
<object class="GtkDialog" id="dialog">
<property name="can_focus">False</property>
<property name="border_width">5</property>
<property name="resizable">False</property>
<property name="window_position">center</property>
<property name="type_hint">dialog</property>
<property name="deletable">False</property>
<child internal-child="vbox">
<object class="GtkBox" id="dialog-vbox1">
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">2</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="dialog-action_area1">
<property name="can_focus">False</property>
<property name="layout_style">end</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="grid1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">8</property>
<property name="margin_right">8</property>
<property name="margin_top">8</property>
<property name="margin_bottom">8</property>
<property name="row_spacing">8</property>
<property name="column_spacing">8</property>
<child>
<object class="GtkSpinner" id="spinner1">
<property name="width_request">45</property>
<property name="height_request">45</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="active">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Geary upgrade in progress.</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">2</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</interface>