Implement Multi-Device
This patch implements multi-device support:
- The account creation wizzard has now two options "Existing Ring
account" and "New Ring account".
"Existing Ring account": Allows for fetching a Ring account archive
from the DHT. Requires pin and password.
"New Ring account": This is the previously existing wizard. It was
modified to ask for a password which will be used to encrypt the
account archive. This password is then required for exporting the
account on the Ring.
- Creating a new Ring account with the "+" button now triggers the
account creation wizard.
- The account menu now has a "devices" tab. This tab contains a table
with device names (currently a short hash) and device ids (a long
hash).
In the "devices" tab, there is an "add device" button which allows
for exporting the current account to the Ring, giving a pin to the
user.
- When the client encounters old-format accounts, it will trigger a
migration popup which asks the user for a password. This password will
be used to create an archive and encrypt it. One password will be
asked for each Ring account to migrate.
Change-Id: I3d52b2b7ca4f82cb477ee294c962b5d50d5c6a04
Tuleap: #896
diff --git a/src/accountview.cpp b/src/accountview.cpp
index 9ba34f1..8322465 100644
--- a/src/accountview.cpp
+++ b/src/accountview.cpp
@@ -29,15 +29,19 @@
#include "models/gtkqtreemodel.h"
#include "models/activeitemproxymodel.h"
#include "accountgeneraltab.h"
+#include "accountcreationwizard.h"
#include "accountaudiotab.h"
#include "accountvideotab.h"
#include "accountadvancedtab.h"
#include "accountsecuritytab.h"
+#include "accountdevicestab.h"
#include "dialogs.h"
#include <glib/gprintf.h>
#include "utils/models.h"
#include "accountimportexportview.h"
+static constexpr const char* ACCOUNT_CREATION_WIZARD_VIEW_NAME = "account-creation-wizard";
+
struct _AccountView
{
GtkPaned parent;
@@ -131,6 +135,16 @@
AccountViewPrivate *priv = ACCOUNT_VIEW_GET_PRIVATE(self);
auto old_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_account));
+ if(IS_ACCOUNT_CREATION_WIZARD(old_view))
+ {
+ /* When using the account creation wizard, The user might create
+ * accounts that will be deleted by the daemon. The fact that the
+ * selection has changed should be ignored. The account creation wizard
+ * should be responsible to remove itself of the stack and then call
+ * account_selection_changed.
+ */
+ return;
+ }
QModelIndex account_idx = get_index_from_selection(selection);
if (!account_idx.isValid()) {
@@ -176,6 +190,10 @@
gtk_notebook_append_page(GTK_NOTEBOOK(account_notebook),
security_tab,
gtk_label_new(C_("Account settings", "Security")));
+ auto devices_tab = create_scrolled_account_view(account_devices_tab_new(account));
+ gtk_notebook_append_page(GTK_NOTEBOOK(account_notebook),
+ devices_tab,
+ gtk_label_new(C_("Account settings", "Devices")));
gtk_widget_show_all(hbox_account);
/* set the tab displayed to the same as the prev account selected */
@@ -296,6 +314,54 @@
}
static void
+hide_account_creation_wizard(AccountView *view)
+{
+ g_return_if_fail(IS_ACCOUNT_VIEW(view));
+ AccountViewPrivate *priv = ACCOUNT_VIEW_GET_PRIVATE(view);
+
+ auto old_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_account));
+ if (IS_ACCOUNT_CREATION_WIZARD(old_view))
+ {
+ gtk_container_remove(GTK_CONTAINER(priv->stack_account), old_view);
+ }
+
+ auto selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_account_list));
+ account_selection_changed(selection, view);
+}
+
+static void
+show_account_creation_wizard(AccountView *view)
+{
+ g_return_if_fail(IS_ACCOUNT_VIEW(view));
+ AccountViewPrivate *priv = ACCOUNT_VIEW_GET_PRIVATE(view);
+
+ auto old_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_account));
+
+ auto account_creation_wizard = gtk_stack_get_child_by_name(
+ GTK_STACK(priv->stack_account),
+ ACCOUNT_CREATION_WIZARD_VIEW_NAME
+ );
+
+ if (!account_creation_wizard)
+ {
+ account_creation_wizard = account_creation_wizard_new(true);
+ gtk_stack_add_named(GTK_STACK(priv->stack_account),
+ account_creation_wizard,
+ ACCOUNT_CREATION_WIZARD_VIEW_NAME);
+ g_signal_connect_swapped(account_creation_wizard, "account-creation-completed", G_CALLBACK(hide_account_creation_wizard), view);
+ g_signal_connect_swapped(account_creation_wizard, "account-creation-canceled", G_CALLBACK(hide_account_creation_wizard), view);
+ }
+
+ gtk_widget_show(account_creation_wizard);
+
+ gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_account), ACCOUNT_CREATION_WIZARD_VIEW_NAME);
+
+ /* remove the old view */
+ if (old_view)
+ gtk_container_remove(GTK_CONTAINER(priv->stack_account), old_view);
+}
+
+static void
add_account(G_GNUC_UNUSED GtkWidget *entry, AccountView *view)
{
g_return_if_fail(IS_ACCOUNT_VIEW(view));
@@ -311,19 +377,26 @@
if (protocol_idx.isValid()) {
protocol_idx = priv->active_protocols->mapToSource(protocol_idx);
- /* show working dialog in case save operation takes time */
- GtkWidget *working = ring_dialog_working(GTK_WIDGET(view), NULL);
- gtk_window_present(GTK_WINDOW(working));
+ Account::Protocol protocol = qvariant_cast<Account::Protocol>(protocol_idx.data((int)ProtocolModel::Role::Protocol));
+ if (protocol == Account::Protocol::RING)
+ {
+ show_account_creation_wizard(view);
+ }
+ else
+ {
+ /* show working dialog in case save operation takes time */
+ GtkWidget *working = ring_dialog_working(GTK_WIDGET(view), NULL);
+ gtk_window_present(GTK_WINDOW(working));
- auto account = AccountModel::instance().add(QString(_("New Account")), protocol_idx);
- if (account->protocol() == Account::Protocol::RING)
- account->setDisplayName(_("New Account"));
+ AccountModel::instance().add(QString(_("New Account")), protocol_idx);
- /* now save after a short timeout to make sure that
- * the save doesn't happen before the "working" dialog is presented
- * the timeout function should destroy the "working" dialog when done saving
- */
- g_timeout_add_full(G_PRIORITY_DEFAULT, 300, (GSourceFunc)save_account, working, NULL);
+ /* now save after a short timeout to make sure that
+ * the save doesn't happen before the "working" dialog is presented
+ * the timeout function should destroy the "working" dialog when done saving
+ */
+ g_timeout_add_full(G_PRIORITY_DEFAULT, 300, (GSourceFunc)save_account, working, NULL);
+ }
+
}
}
}
@@ -365,6 +438,7 @@
display_state = g_strdup_printf("<span fgcolor=\"gray\">%s</span>", escaped_state);
break;
case Account::RegistrationState::TRYING:
+ case Account::RegistrationState::INITIALIZING:
display_state = g_strdup_printf("<span fgcolor=\"orange\">%s</span>", escaped_state);
break;
case Account::RegistrationState::ERROR: