newdevicemodel: deprecate ringdevicemodel in favor of newdevicemodel

Move the devices tab inside the accountgeneraltab.
Add the ability to:
+ Rename the current device
+ Revoke devices

Start to improve the UI of settings.

Change-Id: I6f58f69bf583a0da22c66b6307424e17ec7b1c4c
diff --git a/src/accountgeneraltab.cpp b/src/accountgeneraltab.cpp
index ce3733e..1342ddd 100644
--- a/src/accountgeneraltab.cpp
+++ b/src/accountgeneraltab.cpp
@@ -1,7 +1,8 @@
 /*
  *  Copyright (C) 2015-2018 Savoir-faire Linux Inc.
- *  Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
+ *  Author: Alexandre Viau <alexandre.viau@savoirfairelinux.com>
  *  Author: Sebastien Blin <sebastien.blin@savoirfairelinux.com>
+ *  Author: Stepan Salenikovich <stepan.salenikovich@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
@@ -27,8 +28,11 @@
 #include "utils/models.h"
 #include "usernameregistrationbox.h"
 
+#include <iostream>
+#include <api/newdevicemodel.h>
 #include <api/newaccountmodel.h>
 
+
 struct _AccountGeneralTab
 {
     GtkBox parent;
@@ -46,9 +50,12 @@
     Account   *account;
     AccountInfoPointer const *accountInfo_;
 
-    GtkWidget *grid_account;
+    GtkWidget* stack_account_general;
+    GtkWidget* account_general;
+
+    GtkWidget* grid_account;
     GtkWidget* frame_parameters;
-    GtkWidget *grid_parameters;
+    GtkWidget* grid_parameters;
 
     GtkWidget* entry_current_password;
     GtkWidget* entry_new_password;
@@ -59,7 +66,34 @@
     GtkWidget* button_choose_file;
     GtkWidget* label_export_informations;
 
+    GtkWidget* list_devices;
+    GtkWidget* vbox_devices;
+    GtkWidget* button_add_device;
+
+    /* generated_pin view */
+    GtkWidget* generated_pin;
+    GtkWidget* label_generated_pin;
+    GtkWidget* button_generated_pin_ok;
+
+    /* add_device view */
+    GtkWidget* add_device_box;
+    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;
+
     QMetaObject::Connection account_updated;
+    QMetaObject::Connection new_device_added_connection;
+    QMetaObject::Connection device_updated_connection;
+    QMetaObject::Connection device_removed_connection;
+    QMetaObject::Connection export_on_ring_ended;
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE(AccountGeneralTab, account_general_tab, GTK_TYPE_BOX);
@@ -73,6 +107,10 @@
     AccountGeneralTabPrivate *priv = ACCOUNT_GENERAL_TAB_GET_PRIVATE(view);
 
     QObject::disconnect(priv->account_updated);
+    QObject::disconnect(priv->new_device_added_connection);
+    QObject::disconnect(priv->device_updated_connection);
+    QObject::disconnect(priv->device_removed_connection);
+    QObject::disconnect(priv->export_on_ring_ended);
 
     G_OBJECT_CLASS(account_general_tab_parent_class)->dispose(object);
 }
@@ -91,6 +129,9 @@
     gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
                                                 "/cx/ring/RingGnome/accountgeneraltab.ui");
 
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountGeneralTab, stack_account_general);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountGeneralTab, account_general);
+
     gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountGeneralTab, grid_account);
     gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountGeneralTab, frame_parameters);
     gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountGeneralTab, grid_parameters);
@@ -106,6 +147,118 @@
     gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountGeneralTab, button_choose_file);
     gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountGeneralTab, label_export_informations);
 
+    // Devices
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountGeneralTab, list_devices);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountGeneralTab, vbox_devices);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountGeneralTab, button_add_device);
+
+    // add_device view
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountGeneralTab, add_device_box);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountGeneralTab, button_export_on_the_ring);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountGeneralTab, button_add_device_cancel);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountGeneralTab, entry_password);
+
+    // generating pin spinner
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountGeneralTab, vbox_generating_pin_spinner);
+
+    // export on ring error
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountGeneralTab, export_on_ring_error);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountGeneralTab, label_export_on_ring_error);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountGeneralTab, button_export_on_ring_error_ok);
+
+    // generated_pin view
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountGeneralTab, generated_pin);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountGeneralTab, label_generated_pin);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountGeneralTab, button_generated_pin_ok);
+}
+
+static void
+show_account_general_view(AccountGeneralTab *view)
+{
+    g_return_if_fail(IS_ACCOUNT_GENERAL_TAB(view));
+    AccountGeneralTabPrivate *priv = ACCOUNT_GENERAL_TAB_GET_PRIVATE(view);
+    gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_general), priv->account_general);
+}
+
+static void
+show_add_device_view(AccountGeneralTab *view)
+{
+    g_return_if_fail(IS_ACCOUNT_GENERAL_TAB(view));
+    AccountGeneralTabPrivate *priv = ACCOUNT_GENERAL_TAB_GET_PRIVATE(view);
+    gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_general), priv->add_device_box);
+}
+
+static void
+show_generated_pin_view(AccountGeneralTab *view)
+{
+    g_return_if_fail(IS_ACCOUNT_GENERAL_TAB(view));
+    AccountGeneralTabPrivate *priv = ACCOUNT_GENERAL_TAB_GET_PRIVATE(view);
+    gtk_widget_show(priv->generated_pin);
+    gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_general), priv->generated_pin);
+}
+
+static void
+show_generating_pin_spinner(AccountGeneralTab *view)
+{
+    g_return_if_fail(IS_ACCOUNT_GENERAL_TAB(view));
+    AccountGeneralTabPrivate *priv = ACCOUNT_GENERAL_TAB_GET_PRIVATE(view);
+    gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_general), priv->vbox_generating_pin_spinner);
+}
+
+static void
+show_export_on_ring_error(AccountGeneralTab *view)
+{
+    g_return_if_fail(IS_ACCOUNT_GENERAL_TAB(view));
+    AccountGeneralTabPrivate *priv = ACCOUNT_GENERAL_TAB_GET_PRIVATE(view);
+    gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_general), priv->export_on_ring_error);
+}
+
+static void
+export_on_the_ring_clicked(G_GNUC_UNUSED GtkButton *button, AccountGeneralTab *view)
+{
+    g_return_if_fail(IS_ACCOUNT_GENERAL_TAB(view));
+    AccountGeneralTabPrivate *priv = ACCOUNT_GENERAL_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
@@ -173,7 +326,6 @@
     gtk_button_set_label(GTK_BUTTON(priv->button_validate_password), _("Change password"));
 }
 
-
 static void
 choose_export_file(AccountGeneralTab *view)
 {
@@ -214,6 +366,144 @@
     gtk_label_set_text(GTK_LABEL(priv->label_export_informations), label.c_str());
 }
 
+static GList*
+get_row_iterator(GtkWidget* row)
+{
+    auto box = gtk_bin_get_child(GTK_BIN(row));
+    return gtk_container_get_children(GTK_CONTAINER(box));
+}
+
+static GtkWidget*
+get_button_from_row(GtkWidget* row)
+{
+    auto* list_iterator = get_row_iterator(row);
+    auto current_child = g_list_last(list_iterator);  // button
+    return GTK_WIDGET(current_child->data);
+}
+
+static GtkWidget*
+get_name_from_row(GtkWidget* row)
+{
+    auto* list_iterator = get_row_iterator(row);
+    auto* current_child = g_list_first(list_iterator);  // box infos
+    auto box_devices_info = gtk_container_get_children(GTK_CONTAINER(current_child->data));
+    current_child = g_list_first(box_devices_info);  // device.name
+    return GTK_WIDGET(current_child->data);
+}
+
+static void
+replace_name_from_row(GtkWidget* row, const std::string& name, const std::string& id)
+{
+    // Remove previous informations
+    auto* list_iterator = get_row_iterator(row);
+    auto* box_info = g_list_first(list_iterator);  // box infos
+    auto* action_button = g_list_last(list_iterator);
+    auto box_devices_info = gtk_container_get_children(GTK_CONTAINER(box_info->data));
+    auto* device_name = g_list_first(box_devices_info);  // device.name
+    auto isEntryName = G_TYPE_CHECK_INSTANCE_TYPE(device_name->data, gtk_entry_get_type());
+    auto* label_id = g_list_next(box_devices_info);  // device.name
+    gtk_container_remove(GTK_CONTAINER(box_info->data), GTK_WIDGET(device_name->data));
+    gtk_container_remove(GTK_CONTAINER(box_info->data), GTK_WIDGET(label_id->data));
+
+    // Rebuild item
+    if (isEntryName) {
+        auto* new_label_name = gtk_label_new(name.c_str());
+        gtk_container_add(GTK_CONTAINER(box_info->data), new_label_name);
+        gtk_widget_set_halign(GTK_WIDGET(new_label_name), GTK_ALIGN_START);
+        auto image = gtk_image_new_from_resource("/cx/ring/RingGnome/edit");
+        gtk_button_set_image(GTK_BUTTON(action_button->data), image);
+    } else {
+        auto* entry_name = gtk_entry_new();
+        gtk_entry_set_text(GTK_ENTRY(entry_name), name.c_str());
+        gtk_container_add(GTK_CONTAINER(box_info->data), entry_name);
+        auto image = gtk_image_new_from_resource("/cx/ring/RingGnome/save");
+        gtk_button_set_image(GTK_BUTTON(action_button->data), image);
+    }
+    auto* new_label_id = gtk_label_new("");
+    std::string markup = "<span font_desc=\"7.0\">" + id + "</span>";
+    gtk_label_set_markup(GTK_LABEL(new_label_id), markup.c_str());
+    gtk_container_add(GTK_CONTAINER(box_info->data), new_label_id);
+    gtk_widget_show_all(GTK_WIDGET(box_info->data));
+}
+
+static std::string
+get_id_from_row(GtkWidget* row)
+{
+    auto* list_iterator = get_row_iterator(row);
+    auto* current_child = g_list_first(list_iterator);  // box infos
+    auto box_devices_info = gtk_container_get_children(GTK_CONTAINER(current_child->data));
+    current_child = g_list_first(box_devices_info);  // device.name
+    current_child = g_list_next(box_devices_info);  // device.id
+    return gtk_label_get_text(GTK_LABEL(current_child->data));
+}
+
+static void
+save_name(GtkButton* button, AccountGeneralTab *view)
+{
+    g_return_if_fail(IS_ACCOUNT_GENERAL_TAB(view));
+    AccountGeneralTabPrivate *priv = ACCOUNT_GENERAL_TAB_GET_PRIVATE(view);
+
+    auto row = 0;
+    while (GtkWidget* children = GTK_WIDGET(gtk_list_box_get_row_at_index(GTK_LIST_BOX(priv->list_devices), row))) {
+        if (GTK_BUTTON(get_button_from_row(children)) == button) {
+            GtkWidget* nameWidget = get_name_from_row(children);
+            auto deviceId = get_id_from_row(children);
+            if (G_TYPE_CHECK_INSTANCE_TYPE(nameWidget, gtk_entry_get_type())) {
+                std::string newName = gtk_entry_get_text(GTK_ENTRY(nameWidget));
+                replace_name_from_row(children, newName, deviceId);
+                (*priv->accountInfo_)->deviceModel->setCurrentDeviceName(newName);
+            } else {
+                std::string newName = gtk_label_get_text(GTK_LABEL(nameWidget));
+                replace_name_from_row(children, newName, deviceId);
+            }
+        }
+        ++row;
+    }
+}
+
+static void
+revoke_device(GtkButton* button, AccountGeneralTab *view)
+{
+    g_return_if_fail(IS_ACCOUNT_GENERAL_TAB(view));
+    AccountGeneralTabPrivate *priv = ACCOUNT_GENERAL_TAB_GET_PRIVATE(view);
+
+    auto row = 0;
+    while (GtkWidget* children = GTK_WIDGET(gtk_list_box_get_row_at_index(GTK_LIST_BOX(priv->list_devices), row))) {
+        if (GTK_BUTTON(get_button_from_row(children)) == button) {
+            auto deviceId = get_id_from_row(children);
+            auto password = "";
+            auto* top_window = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(view)));
+            auto* password_dialog = gtk_message_dialog_new(top_window,
+                GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL,
+                _("Warning! This action will revoke the device!\nNote: this action cannot be undone."));
+            gtk_window_set_title(GTK_WINDOW(password_dialog), _("Enter password to revoke device"));
+            gtk_dialog_set_default_response(GTK_DIALOG(password_dialog), GTK_RESPONSE_OK);
+
+            auto* message_area = gtk_message_dialog_get_message_area(GTK_MESSAGE_DIALOG(password_dialog));
+            if (priv->account->archiveHasPassword()) {
+                auto* password_entry = gtk_entry_new();
+                gtk_entry_set_visibility(GTK_ENTRY(password_entry), false);
+                gtk_entry_set_invisible_char(GTK_ENTRY(password_entry), '*');
+                gtk_box_pack_start(GTK_BOX(message_area), password_entry, true, true, 0);
+                gtk_widget_show_all(password_dialog);
+
+                auto res = gtk_dialog_run(GTK_DIALOG(password_dialog));
+                if (res == GTK_RESPONSE_OK) {
+                    password = gtk_entry_get_text(GTK_ENTRY(password_entry));
+                    (*priv->accountInfo_)->deviceModel->revokeDevice(deviceId, password);
+                }
+            } else {
+                auto res = gtk_dialog_run(GTK_DIALOG(password_dialog));
+                if (res == GTK_RESPONSE_OK)
+                    (*priv->accountInfo_)->deviceModel->revokeDevice(deviceId, "");
+            }
+
+            gtk_widget_destroy(password_dialog);
+        }
+        ++row;
+    }
+}
+
 static void
 change_password(AccountGeneralTab *view)
 {
@@ -243,21 +533,86 @@
 }
 
 static void
+add_device(AccountGeneralTab *view, const lrc::api::Device& device)
+{
+    g_return_if_fail(IS_ACCOUNT_GENERAL_TAB(view));
+    AccountGeneralTabPrivate *priv = ACCOUNT_GENERAL_TAB_GET_PRIVATE(view);
+
+    auto* device_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+
+    GtkStyleContext* context_box;
+    context_box = gtk_widget_get_style_context(GTK_WIDGET(device_box));
+    gtk_style_context_add_class(context_box, "device-box");
+    // Fill with devices informations
+    auto* device_info_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+    auto* label_name = gtk_label_new(device.name.c_str());
+    gtk_container_add(GTK_CONTAINER(device_info_box), label_name);
+    gtk_widget_set_halign(GTK_WIDGET(label_name), GTK_ALIGN_START);
+    auto* label_id = gtk_label_new("");
+    std::string markup = "<span font_desc=\"7.0\">" + device.id + "</span>";
+    gtk_label_set_markup(GTK_LABEL(label_id), markup.c_str());
+    gtk_container_add(GTK_CONTAINER(device_info_box), label_id);
+    gtk_container_add(GTK_CONTAINER(device_box), device_info_box);
+    // Add action button
+    auto image = gtk_image_new_from_resource("/cx/ring/RingGnome/edit");
+    if (!device.isCurrent)
+        image = gtk_image_new_from_resource("/cx/ring/RingGnome/revoke");
+    auto* action_device_button = gtk_button_new();
+    gtk_widget_set_tooltip_text(action_device_button, device.isCurrent ? _("Save name") : _("Revoke device"));
+    gtk_button_set_image(GTK_BUTTON(action_device_button), image);
+    GtkStyleContext* context;
+    context = gtk_widget_get_style_context(GTK_WIDGET(action_device_button));
+    gtk_style_context_add_class(context, "transparent-button");
+    std::string label_btn = "action_btn_" + device.id;
+    gtk_widget_set_name(action_device_button, label_btn.c_str());
+    g_signal_connect(action_device_button, "clicked", device.isCurrent ? G_CALLBACK(save_name) : G_CALLBACK(revoke_device), view);
+    gtk_container_add(GTK_CONTAINER(device_box), action_device_button);
+    // Insert at the end of the list
+    gtk_list_box_insert(GTK_LIST_BOX(priv->list_devices), device_box, -1);
+}
+
+static void
+show_revokation_error_dialog(AccountGeneralTab *view, const std::string& text)
+{
+    g_return_if_fail(IS_ACCOUNT_GENERAL_TAB(view));
+    auto* top_window = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(view)));
+    auto* error_dialog = gtk_message_dialog_new(top_window,
+        GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+        text.c_str());
+    gtk_window_set_title(GTK_WINDOW(error_dialog), _("Error when revoking device"));
+    gtk_dialog_set_default_response(GTK_DIALOG(error_dialog), GTK_RESPONSE_OK);
+
+    gtk_dialog_run(GTK_DIALOG(error_dialog));
+    gtk_widget_destroy(error_dialog);
+}
+
+static void
 build_tab_view(AccountGeneralTab *view)
 {
     g_return_if_fail(IS_ACCOUNT_GENERAL_TAB(view));
     AccountGeneralTabPrivate *priv = ACCOUNT_GENERAL_TAB_GET_PRIVATE(view);
 
+    // CSS styles
+    auto provider = gtk_css_provider_new();
+    gtk_css_provider_load_from_data(provider,
+        ".transparent-button { margin-left: 10px; border: 0; background-color: rgba(0,0,0,0); margin-right: 0; padding-right: 0;}\
+         .device-box { padding: 12px; }",
+        -1, nullptr
+    );
+    gtk_style_context_add_provider_for_screen(gdk_display_get_default_screen(gdk_display_get_default()),
+                                              GTK_STYLE_PROVIDER(provider),
+                                              GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+
     int grid_row = 0;
-    GtkWidget *label = NULL;
+    GtkWidget* label = NULL;
 
     /* separate pointers for each so that we reference them in the account changed callback */
-    GtkWidget *entry_alias = NULL;
-    GtkWidget *entry_username = NULL;
-    GtkWidget *entry_hostname = NULL;
-    GtkWidget *entry_password = NULL;
-    GtkWidget *entry_proxy = NULL;
-    GtkWidget *entry_voicemail = NULL;
+    GtkWidget* entry_alias = NULL;
+    GtkWidget* entry_username = NULL;
+    GtkWidget* entry_hostname = NULL;
+    GtkWidget* entry_password = NULL;
+    GtkWidget* entry_proxy = NULL;
+    GtkWidget* entry_voicemail = NULL;
 
     /* build account grid */
 
@@ -361,7 +716,7 @@
         ++grid_row;
 
         /* show password */
-        GtkWidget *checkbutton = gtk_check_button_new_with_label(_("Show password"));
+        GtkWidget* checkbutton = gtk_check_button_new_with_label(_("Show password"));
         gtk_grid_attach(GTK_GRID(priv->grid_parameters), checkbutton, 1, grid_row, 1, 1);
         g_signal_connect(checkbutton, "toggled", G_CALLBACK(show_password), entry_password);
         ++grid_row;
@@ -408,16 +763,113 @@
         }
     );
 
+    priv->new_device_added_connection = QObject::connect(
+        &*(*priv->accountInfo_)->deviceModel,
+        &lrc::api::NewDeviceModel::deviceAdded,
+        [view] (const std::string& id) {
+            AccountGeneralTabPrivate *priv = ACCOUNT_GENERAL_TAB_GET_PRIVATE(view);
+            // Retrieve added device
+            auto device = (*priv->accountInfo_)->deviceModel->getDevice(id);
+            if (device.id.empty()) {
+                g_debug("Can't add device with id: %s", device.id.c_str());
+                return;
+            }
+            // if exists, add to list
+            add_device(view, device);
+            gtk_widget_show_all(priv->list_devices);
+        }
+    );
+
+    priv->device_removed_connection = QObject::connect(
+        &*(*priv->accountInfo_)->deviceModel,
+        &lrc::api::NewDeviceModel::deviceRevoked,
+        [view] (const std::string& id, const lrc::api::NewDeviceModel::Status status) {
+            AccountGeneralTabPrivate *priv = ACCOUNT_GENERAL_TAB_GET_PRIVATE(view);
+            auto row = 0;
+            while (GtkWidget* children = GTK_WIDGET(gtk_list_box_get_row_at_index(GTK_LIST_BOX(priv->list_devices), row))) {
+                auto deviceId = get_id_from_row(children);
+                if (deviceId == id) {
+                    switch (status)
+                    {
+                    case lrc::api::NewDeviceModel::Status::SUCCESS:
+                        // remove device's line
+                        gtk_container_remove(GTK_CONTAINER(priv->list_devices), children);
+                        return;
+                    case lrc::api::NewDeviceModel::Status::WRONG_PASSWORD:
+                        show_revokation_error_dialog(view, _("Error: wrong password!"));
+                        break;
+                    case lrc::api::NewDeviceModel::Status::UNKNOWN_DEVICE:
+                        show_revokation_error_dialog(view, _("Error: unknown device!"));
+                        break;
+                    default:
+                        g_debug("unknown status for revoked device. BUG?");
+                        return;
+                    }
+                }
+                ++row;
+            }
+        }
+    );
+
+    priv->device_updated_connection = QObject::connect(
+        &*(*priv->accountInfo_)->deviceModel,
+        &lrc::api::NewDeviceModel::deviceUpdated,
+        [view] (const std::string& id) {
+            AccountGeneralTabPrivate *priv = ACCOUNT_GENERAL_TAB_GET_PRIVATE(view);
+            // Retrieve added device
+            auto device = (*priv->accountInfo_)->deviceModel->getDevice(id);
+            if (device.id.empty()) {
+                g_debug("Can't add device with id: %s", device.id.c_str());
+                return;
+            }
+            // if exists, update
+            auto row = 0;
+            while (GtkWidget* children = GTK_WIDGET(gtk_list_box_get_row_at_index(GTK_LIST_BOX(priv->list_devices), row))) {
+                auto device_name = get_name_from_row(children);
+                auto deviceId = get_id_from_row(children);
+                if (deviceId == id) {
+                    if (device.isCurrent) {
+                        if (G_TYPE_CHECK_INSTANCE_TYPE(device_name, gtk_entry_get_type())) {
+                            gtk_entry_set_text(GTK_ENTRY(device_name), device.name.c_str());
+                        } else {
+                            gtk_label_set_text(GTK_LABEL(device_name), device.name.c_str());
+                        }
+                    } else {
+                        gtk_label_set_text(GTK_LABEL(device_name), device.name.c_str());
+                    }
+                }
+                ++row;
+            }
+            g_debug("deviceUpdated signal received, but device not found");
+        }
+    );
+
+
     g_signal_connect_swapped(priv->button_validate_password, "clicked", G_CALLBACK(change_password), view);
     g_signal_connect_swapped(priv->entry_current_password, "changed", G_CALLBACK(reset_change_password), view);
     g_signal_connect_swapped(priv->entry_new_password, "changed", G_CALLBACK(reset_change_password), view);
     g_signal_connect_swapped(priv->entry_confirm_password, "changed", G_CALLBACK(reset_change_password), view);
     g_signal_connect_swapped(priv->button_choose_file, "clicked", G_CALLBACK(choose_export_file), 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_account_general_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_account_general_view), view);
+    g_signal_connect_swapped(priv->button_export_on_ring_error_ok, "clicked", G_CALLBACK(show_add_device_view), view);
     gtk_widget_set_visible(priv->entry_current_password, priv->account->archiveHasPassword());
 
+    if ((*priv->accountInfo_)->profileInfo.type == lrc::api::profile::Type::RING) {
+        auto devices = (*priv->accountInfo_)->deviceModel->getAllDevices();
+        for (const auto& device : devices)
+            add_device(view, device);
+        gtk_widget_set_halign(GTK_WIDGET(priv->list_devices), GTK_ALIGN_CENTER);
+        gtk_widget_show_all(priv->list_devices);
+    } else {
+        gtk_widget_hide(priv->vbox_devices);
+    }
+
 }
 
-GtkWidget *
+GtkWidget*
 account_general_tab_new(Account *account, AccountInfoPointer const & accountInfo)
 {
     g_return_val_if_fail(account != NULL, NULL);
@@ -430,5 +882,5 @@
 
     build_tab_view(ACCOUNT_GENERAL_TAB(view));
 
-    return (GtkWidget *)view;
+    return (GtkWidget* )view;
 }