Commit 6aeb4859 authored by aviau's avatar aviau Committed by Alexandre Viau

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
parent 738e59ae
......@@ -235,10 +235,16 @@ SET( SRC_FILES
src/video/video_widget.cpp
src/accountview.h
src/accountview.cpp
src/accountcreationwizard.h
src/accountcreationwizard.cpp
src/accountmigrationview.h
src/accountmigrationview.cpp
src/accountgeneraltab.h
src/accountgeneraltab.cpp
src/accountaudiotab.h
src/accountaudiotab.cpp
src/accountdevicestab.h
src/accountdevicestab.cpp
src/accountvideotab.h
src/accountvideotab.cpp
src/accountadvancedtab.h
......
This diff is collapsed.
/*
* Copyright (C) 2016 Savoir-faire Linux Inc.
* Author: Alexandre Viau <alexandre.viau@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define ACCOUNT_CREATION_WIZARD_TYPE (account_creation_wizard_get_type ())
#define ACCOUNT_CREATION_WIZARD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ACCOUNT_CREATION_WIZARD_TYPE, AccountCreationWizard))
#define ACCOUNT_CREATION_WIZARD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), ACCOUNT_CREATION_WIZARD_TYPE, AccountCreationWizardClass))
#define IS_ACCOUNT_CREATION_WIZARD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), ACCOUNT_CREATION_WIZARD_TYPE))
#define IS_ACCOUNT_CREATION_WIZARD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), ACCOUNT_CREATION_WIZARD_TYPE))
typedef struct _AccountCreationWizard AccountCreationWizard;
typedef struct _AccountCreationWizardClass AccountCreationWizardClass;
GType account_creation_wizard_get_type (void) G_GNUC_CONST;
GtkWidget *account_creation_wizard_new (bool cancel_button);
G_END_DECLS
/*
* Copyright (C) 2016 Savoir-faire Linux Inc.
* Author: Alexandre Viau <alexandre.viau@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
// GTK+ related
#include <gtk/gtk.h>
#include <glib/gi18n.h>
// LRC
#include <account.h>
#include <codecmodel.h>
#include "ringdevicemodel.h"
#include "ringdevice.h"
// Ring Client
#include "accountdevicestab.h"
#include "models/gtkqtreemodel.h"
#include "utils/models.h"
struct _AccountDevicesTab
{
GtkBox parent;
};
struct _AccountDevicesTabClass
{
GtkBoxClass parent_class;
};
typedef struct _AccountDevicesTabPrivate AccountDevicesTabPrivate;
struct _AccountDevicesTabPrivate
{
Account *account;
GtkWidget *stack_account_devices;
QMetaObject::Connection export_on_ring_ended;
/* generated_pin view */
GtkWidget *generated_pin;
GtkWidget *label_generated_pin;
GtkWidget *button_generated_pin_ok;
/* manage_devices view */
GtkWidget *manage_devices;
GtkWidget *label_device_id;
GtkWidget *treeview_devices;
GtkWidget *button_add_device;
/* add_device view */
GtkWidget *add_device;
GtkWidget *button_export_on_the_ring;
GtkWidget *button_add_device_cancel;
GtkWidget *entry_password;
/* generating account spinner */
GtkWidget *vbox_generating_pin_spinner;
/* export on ring error */
GtkWidget *export_on_ring_error;
GtkWidget *label_export_on_ring_error;
GtkWidget* button_export_on_ring_error_ok;
};
G_DEFINE_TYPE_WITH_PRIVATE(AccountDevicesTab, account_devices_tab, GTK_TYPE_BOX);
#define ACCOUNT_DEVICES_TAB_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ACCOUNT_DEVICES_TAB_TYPE, AccountDevicesTabPrivate))
static void
account_devices_tab_dispose(GObject *object)
{
G_OBJECT_CLASS(account_devices_tab_parent_class)->dispose(object);
}
static void
account_devices_tab_init(AccountDevicesTab *view)
{
gtk_widget_init_template(GTK_WIDGET(view));
}
static void
account_devices_tab_class_init(AccountDevicesTabClass *klass)
{
G_OBJECT_CLASS(klass)->dispose = account_devices_tab_dispose;
gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
"/cx/ring/RingGnome/accountdevicestab.ui");
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, stack_account_devices);
/* generated_pin view */
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, generated_pin);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, label_generated_pin);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, button_generated_pin_ok);
/* manage_devices view */
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, manage_devices);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, label_device_id);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, treeview_devices);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, button_add_device);
/* add_device view */
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, add_device);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, button_export_on_the_ring);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, button_add_device_cancel);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, entry_password);
/* generating pin spinner */
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, vbox_generating_pin_spinner);
/* export on ring error */
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, export_on_ring_error);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, label_export_on_ring_error);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, button_export_on_ring_error_ok);
}
static void
show_manage_devices_view(AccountDevicesTab *view)
{
g_return_if_fail(IS_ACCOUNT_DEVICES_TAB(view));
AccountDevicesTabPrivate *priv = ACCOUNT_DEVICES_TAB_GET_PRIVATE(view);
gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_devices), priv->manage_devices);
}
static void
show_add_device_view(AccountDevicesTab *view)
{
g_return_if_fail(IS_ACCOUNT_DEVICES_TAB(view));
AccountDevicesTabPrivate *priv = ACCOUNT_DEVICES_TAB_GET_PRIVATE(view);
gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_devices), priv->add_device);
}
static void
show_generated_pin_view(AccountDevicesTab *view)
{
g_return_if_fail(IS_ACCOUNT_DEVICES_TAB(view));
AccountDevicesTabPrivate *priv = ACCOUNT_DEVICES_TAB_GET_PRIVATE(view);
gtk_widget_show(priv->generated_pin);
gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_devices), priv->generated_pin);
}
static void
show_generating_pin_spinner(AccountDevicesTab *view)
{
AccountDevicesTabPrivate *priv = ACCOUNT_DEVICES_TAB_GET_PRIVATE(view);
gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_devices), priv->vbox_generating_pin_spinner);
}
static void
show_export_on_ring_error(AccountDevicesTab *view)
{
AccountDevicesTabPrivate *priv = ACCOUNT_DEVICES_TAB_GET_PRIVATE(view);
gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_devices), priv->export_on_ring_error);
}
static void
export_on_the_ring_clicked(G_GNUC_UNUSED GtkButton *button, AccountDevicesTab *view)
{
g_return_if_fail(IS_ACCOUNT_DEVICES_TAB(view));
AccountDevicesTabPrivate *priv = ACCOUNT_DEVICES_TAB_GET_PRIVATE(view);
auto password = QString(gtk_entry_get_text(GTK_ENTRY(priv->entry_password)));
gtk_entry_set_text(GTK_ENTRY(priv->entry_password), "");
priv->export_on_ring_ended = QObject::connect(
priv->account,
&Account::exportOnRingEnded,
[=] (Account::ExportOnRingStatus status, QString pin) {
QObject::disconnect(priv->export_on_ring_ended);
switch (status)
{
case Account::ExportOnRingStatus::SUCCESS:
{
gtk_label_set_text(GTK_LABEL(priv->label_generated_pin), pin.toStdString().c_str());
show_generated_pin_view(view);
break;
}
case Account::ExportOnRingStatus::WRONG_PASSWORD:
{
gtk_label_set_text(GTK_LABEL(priv->label_export_on_ring_error), _("Bad password"));
show_export_on_ring_error(view);
break;
}
case Account::ExportOnRingStatus::NETWORK_ERROR:
{
gtk_label_set_text(GTK_LABEL(priv->label_export_on_ring_error), _("Network error, try again"));
show_export_on_ring_error(view);
break;
}
}
}
);
show_generating_pin_spinner(view);
if (!priv->account->exportOnRing(password))
{
QObject::disconnect(priv->export_on_ring_ended);
gtk_label_set_text(GTK_LABEL(priv->label_export_on_ring_error), _("Could not initiate export to the Ring, try again"));
g_debug("Could not initiate exportOnRing operation");
show_export_on_ring_error(view);
}
}
static void
build_tab_view(AccountDevicesTab *view)
{
g_return_if_fail(IS_ACCOUNT_DEVICES_TAB(view));
AccountDevicesTabPrivate *priv = ACCOUNT_DEVICES_TAB_GET_PRIVATE(view);
g_signal_connect_swapped(priv->button_add_device, "clicked", G_CALLBACK(show_add_device_view), view);
g_signal_connect_swapped(priv->button_add_device_cancel, "clicked", G_CALLBACK(show_manage_devices_view), view);
g_signal_connect(priv->button_export_on_the_ring, "clicked", G_CALLBACK(export_on_the_ring_clicked), view);
g_signal_connect_swapped(priv->button_generated_pin_ok, "clicked", G_CALLBACK(show_manage_devices_view), view);
g_signal_connect_swapped(priv->button_export_on_ring_error_ok, "clicked", G_CALLBACK(show_add_device_view), view);
gtk_label_set_text(GTK_LABEL(priv->label_device_id), priv->account->deviceId().toUtf8().constData());
/* treeview_devices */
auto *devices_model = gtk_q_tree_model_new(
(QAbstractItemModel*) priv->account->ringDeviceModel(),
2,
RingDevice::Column::Name, Qt::DisplayRole, G_TYPE_STRING,
RingDevice::Column::Id, Qt::DisplayRole, G_TYPE_STRING);
gtk_tree_view_set_model(GTK_TREE_VIEW(priv->treeview_devices), GTK_TREE_MODEL(devices_model));
GtkCellRenderer* renderer;
GtkTreeViewColumn* column;
renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes(C_("Device Name Column", "Name"), renderer, "text", 0, NULL);
gtk_tree_view_column_set_expand(column, TRUE);
gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_devices), column);
renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes(C_("Device ID Column", "ID"), renderer, "text", 1, NULL);
gtk_tree_view_column_set_expand(column, TRUE);
gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_devices), column);
/* Show manage-devices view */
gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_devices), priv->manage_devices);
}
GtkWidget *
account_devices_tab_new(Account *account)
{
g_return_val_if_fail(account != NULL, NULL);
gpointer view = g_object_new(ACCOUNT_DEVICES_TAB_TYPE, NULL);
AccountDevicesTabPrivate *priv = ACCOUNT_DEVICES_TAB_GET_PRIVATE(view);
priv->account = account;
build_tab_view(ACCOUNT_DEVICES_TAB(view));
return (GtkWidget *)view;
}
/*
* Copyright (C) 2016 Savoir-faire Linux Inc.
* Author: Alexandre Viau <alexandre.viau@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include <gtk/gtk.h>
class Account;
G_BEGIN_DECLS
#define ACCOUNT_DEVICES_TAB_TYPE (account_devices_tab_get_type ())
#define ACCOUNT_DEVICES_TAB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ACCOUNT_DEVICES_TAB_TYPE, AccountDevicesTab))
#define ACCOUNT_DEVICES_TAB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), ACCOUNT_DEVICES_TAB_TYPE, AccountDevicesTabClass))
#define IS_ACCOUNT_DEVICES_TAB(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), ACCOUNT_DEVICES_TAB_TYPE))
#define IS_ACCOUNT_DEVICES_TAB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), ACCOUNT_DEVICES_TAB_TYPE))
typedef struct _AccountDevicesTab AccountDevicesTab;
typedef struct _AccountDevicesTabClass AccountDevicesTabClass;
GType account_devices_tab_get_type (void) G_GNUC_CONST;
GtkWidget *account_devices_tab_new (Account *account);
G_END_DECLS
/*
* Copyright (C) 2016 Savoir-faire Linux Inc.
* Author: Alexandre Viau <alexandre.viau@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
// GTK+ related
#include <gtk/gtk.h>
#include <glib/gi18n.h>
// LRC
#include <codecmodel.h>
#include <account.h>
#include <accountmodel.h>
#include <profilemodel.h>
#include <profile.h>
#include <personmodel.h>
#include <globalinstances.h>
// Ring Client
#include "avatarmanipulation.h"
#include "accountmigrationview.h"
#include "utils/models.h"
#include "native/pixbufmanipulator.h"
#include "video/video_widget.h"
/* size of avatar */
static constexpr int AVATAR_WIDTH = 100; /* px */
static constexpr int AVATAR_HEIGHT = 100; /* px */
struct _AccountMigrationView
{
GtkBox parent;
};
struct _AccountMigrationViewClass
{
GtkBoxClass parent_class;
};
typedef struct _AccountMigrationViewPrivate AccountMigrationViewPrivate;
struct _AccountMigrationViewPrivate
{
Account *account;
GtkWidget *stack_account_migration;
QMetaObject::Connection state_changed;
guint timeout_tag;
/* generating_account_view */
GtkWidget *migrating_account_view;
/* error_view */
GtkWidget *error_view;
GtkWidget *button_error_view_ok;
/* main_view */
GtkWidget *main_view;
GtkWidget *label_account_alias;
GtkWidget *label_account_id;
GtkWidget *image_avatar;
GtkWidget *label_password_error;
GtkWidget *entry_password;
GtkWidget *entry_password_confirm;
GtkWidget *button_migrate_account;
};
G_DEFINE_TYPE_WITH_PRIVATE(AccountMigrationView, account_migration_view, GTK_TYPE_BOX);
#define ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ACCOUNT_MIGRATION_VIEW_TYPE, AccountMigrationViewPrivate))
/* signals */
enum {
ACCOUNT_MIGRATION_COMPLETED,
ACCOUNT_MIGRATION_FAILED,
LAST_SIGNAL
};
static guint account_migration_view_signals[LAST_SIGNAL] = { 0 };
static void
account_migration_view_dispose(GObject *object)
{
G_OBJECT_CLASS(account_migration_view_parent_class)->dispose(object);
}
static void
account_migration_view_init(AccountMigrationView *view)
{
gtk_widget_init_template(GTK_WIDGET(view));
}
static void
account_migration_view_class_init(AccountMigrationViewClass *klass)
{
G_OBJECT_CLASS(klass)->dispose = account_migration_view_dispose;
gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
"/cx/ring/RingGnome/accountmigrationview.ui");
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, stack_account_migration);
/* error_view */
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, error_view);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, button_error_view_ok);
/* migrating_account_view */
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, migrating_account_view);
/* main_view */
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, main_view);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, label_account_alias);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, label_account_id);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, image_avatar);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, label_password_error);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, entry_password);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, entry_password_confirm);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, button_migrate_account);
/* add signals */
account_migration_view_signals[ACCOUNT_MIGRATION_COMPLETED] = g_signal_new("account-migration-completed",
G_TYPE_FROM_CLASS(klass),
(GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
0,
nullptr,
nullptr,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
account_migration_view_signals[ACCOUNT_MIGRATION_FAILED] = g_signal_new("account-migration-failed",
G_TYPE_FROM_CLASS(klass),
(GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
0,
nullptr,
nullptr,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
static gboolean
migration_timeout(AccountMigrationView *view)
{
AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view);
QObject::disconnect(priv->state_changed);
gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_migration), priv->error_view);
return G_SOURCE_REMOVE;
}
static void
button_error_view_ok_clicked(G_GNUC_UNUSED GtkButton* button, AccountMigrationView *view)
{
AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view);
g_signal_emit(G_OBJECT(view), account_migration_view_signals[ACCOUNT_MIGRATION_FAILED], 0);
}
static void
migrate_account_clicked(G_GNUC_UNUSED GtkButton* button, AccountMigrationView *view)
{
AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view);
gtk_widget_hide(priv->label_password_error);
/* Check for correct password */
const gchar *password = gtk_entry_get_text(GTK_ENTRY(priv->entry_password));
const gchar *password_confirm = gtk_entry_get_text(GTK_ENTRY(priv->entry_password_confirm));
if (g_strcmp0(password, password_confirm) != 0)
{
gtk_widget_show(priv->label_password_error);
return;
}
else
{
gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_migration), priv->migrating_account_view);
priv->account->setArchivePassword(password);
priv->account->performAction(Account::EditAction::SAVE);
// Timeout in 30 seconds
priv->timeout_tag = g_timeout_add_full(G_PRIORITY_DEFAULT, 30000, (GSourceFunc)migration_timeout, view, NULL);
priv->state_changed = QObject::connect(
priv->account,
&Account::stateChanged,
[=] (Account::RegistrationState state) {
switch(state)
{
case Account::RegistrationState::READY:
case Account::RegistrationState::TRYING:
case Account::RegistrationState::UNREGISTERED:
{
// Make sure that the account is ready to be displayed.
priv->account << Account::EditAction::RELOAD;
AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view);
QObject::disconnect(priv->state_changed); // only want to emit once
g_source_remove(priv->timeout_tag); // We didn't timeout
g_signal_emit(G_OBJECT(view), account_migration_view_signals[ACCOUNT_MIGRATION_COMPLETED], 0);
break;
}
case Account::RegistrationState::ERROR:
case Account::RegistrationState::INITIALIZING:
case Account::RegistrationState::COUNT__:
{
// Keep waiting...
break;
}
}
}
);
}
}
static void
password_entry_changed(G_GNUC_UNUSED GtkEntry* entry, AccountMigrationView *view)
{
AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view);
const gchar *password = gtk_entry_get_text(GTK_ENTRY(priv->entry_password));
const gchar *password_confirm = gtk_entry_get_text(GTK_ENTRY(priv->entry_password_confirm));
if (strlen(password) > 0 && strlen(password_confirm) > 0)
{
gtk_widget_set_sensitive(priv->button_migrate_account, TRUE);
}
else
{
gtk_widget_set_sensitive(priv->button_migrate_account, FALSE);
}
}
static void
build_migration_view(AccountMigrationView *view)
{
g_return_if_fail(IS_ACCOUNT_MIGRATION_VIEW(view));
AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view);
g_signal_connect(priv->button_migrate_account, "clicked", G_CALLBACK(migrate_account_clicked), view);
g_signal_connect(priv->button_error_view_ok, "clicked", G_CALLBACK(button_error_view_ok_clicked), view);
g_signal_connect(priv->entry_password, "changed", G_CALLBACK(password_entry_changed), view);
g_signal_connect(priv->entry_password_confirm, "changed", G_CALLBACK(password_entry_changed), view);
gtk_label_set_text(GTK_LABEL(priv->label_account_alias), priv->account->alias().toUtf8().constData());
gtk_label_set_text(GTK_LABEL(priv->label_account_id), priv->account->id().constData());
/* set the avatar picture */
auto photo = GlobalInstances::pixmapManipulator().contactPhoto(
ProfileModel::instance().selectedProfile()->person(),
QSize(AVATAR_WIDTH, AVATAR_HEIGHT),
false);