accountmigrationview.cpp 11.6 KB
Newer Older
aviau's avatar
aviau committed
1
/*
2
 *  Copyright (C) 2016-2019 Savoir-faire Linux Inc.
aviau's avatar
aviau committed
3
 *  Author: Alexandre Viau <alexandre.viau@savoirfairelinux.com>
4
 *  Author: Nicolas Jäger <nicolas.jager@savoirfairelinux.com>
aviau's avatar
aviau committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 *
 *  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>

26 27 28
// Qt
#include <QSize>

29 30
// std
#include <memory> // for std::shared_ptr
31

aviau's avatar
aviau committed
32
// LRC
33
#include <api/newaccountmodel.h>
aviau's avatar
aviau committed
34

35
// Jami  Client
aviau's avatar
aviau committed
36 37 38 39
#include "accountmigrationview.h"
#include "native/pixbufmanipulator.h"

/* size of avatar */
40 41
static constexpr int AVATAR_WIDTH  = 150; /* px */
static constexpr int AVATAR_HEIGHT = 150; /* px */
aviau's avatar
aviau committed
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56

struct _AccountMigrationView
{
    GtkBox parent;
};

struct _AccountMigrationViewClass
{
    GtkBoxClass parent_class;
};

typedef struct _AccountMigrationViewPrivate AccountMigrationViewPrivate;

struct _AccountMigrationViewPrivate
{
57
    AccountInfoPointer const *accountInfo_;
aviau's avatar
aviau committed
58 59 60 61 62
    QMetaObject::Connection state_changed;

    /* main_view */
    GtkWidget *main_view;
    GtkWidget *label_account_alias;
63 64
    GtkWidget *label_account_username;
    GtkWidget *label_account_manager;
aviau's avatar
aviau committed
65
    GtkWidget *image_avatar;
66
    GtkWidget *label_migration_error;
aviau's avatar
aviau committed
67 68
    GtkWidget *entry_password;
    GtkWidget *button_migrate_account;
69 70
    GtkWidget *username_row;
    GtkWidget *manager_row;
71
    GtkWidget *button_delete_account;
72 73

    GtkWidget *hbox_migrating_account_spinner;
aviau's avatar
aviau committed
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
};

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)
{
92 93 94 95 96
    auto priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(object);

    // make sure to disconnect from all signals when disposing of view
    QObject::disconnect(priv->state_changed);

aviau's avatar
aviau committed
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
    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),
112
                                                "/net/jami/JamiGnome/accountmigrationview.ui");
aviau's avatar
aviau committed
113 114 115
    /* 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);
116 117
    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, label_account_username);
    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, label_account_manager);
aviau's avatar
aviau committed
118
    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, image_avatar);
119
    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, label_migration_error);
aviau's avatar
aviau committed
120 121
    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, button_migrate_account);
122 123
    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, username_row);
    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, manager_row);
124
    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, button_delete_account);
125
    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, hbox_migrating_account_spinner);
aviau's avatar
aviau committed
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146

    /* 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);
}

147
static void
148
migrate(AccountMigrationView *view)
aviau's avatar
aviau committed
149
{
150
    g_return_if_fail(IS_ACCOUNT_MIGRATION_VIEW(view));
aviau's avatar
aviau committed
151 152
    AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view);
    const gchar *password = gtk_entry_get_text(GTK_ENTRY(priv->entry_password));
153 154
    priv->state_changed = QObject::connect((*priv->accountInfo_)->accountModel,
                                           &lrc::api::NewAccountModel::migrationEnded,
155
        [=] (const std::string&, bool ok)
156
        {
157
            gtk_widget_hide(priv->hbox_migrating_account_spinner);
158 159
            gtk_widget_set_sensitive(GTK_WIDGET(priv->button_delete_account), true);
            gtk_widget_set_sensitive(GTK_WIDGET(priv->button_migrate_account), true);
160 161 162 163
            if (ok) {
                g_signal_emit(G_OBJECT(view), account_migration_view_signals[ACCOUNT_MIGRATION_COMPLETED], 0);
            } else {
                gtk_widget_show(priv->label_migration_error);
aviau's avatar
aviau committed
164
            }
165
        });
aviau's avatar
aviau committed
166

167 168 169
    auto currentProp = (*priv->accountInfo_)->accountModel->getAccountConfig((*priv->accountInfo_)->id);
    currentProp.archivePassword = password;
    (*priv->accountInfo_)->accountModel->setAccountConfig((*priv->accountInfo_)->id, currentProp);
170
    gtk_entry_set_text(GTK_ENTRY(priv->entry_password), "");
171 172

    gtk_widget_show_all(priv->hbox_migrating_account_spinner);
173 174
    gtk_widget_set_sensitive(GTK_WIDGET(priv->button_delete_account), false);
    gtk_widget_set_sensitive(GTK_WIDGET(priv->button_migrate_account), false);
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
}


static void
delete_account(AccountMigrationView *view)
{
    g_return_if_fail(IS_ACCOUNT_MIGRATION_VIEW(view));
    AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view);
    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 remove this account on this device!\nNote: this action cannot be undone. Also, your registered name can be lost!"));
    gtk_window_set_title(GTK_WINDOW(password_dialog), _("Delete account"));
    gtk_dialog_set_default_response(GTK_DIALOG(password_dialog), GTK_RESPONSE_OK);

    auto res = gtk_dialog_run(GTK_DIALOG(password_dialog));
    if (res == GTK_RESPONSE_OK) {
        (*priv->accountInfo_)->accountModel->removeAccount((*priv->accountInfo_)->id);
        g_signal_emit(G_OBJECT(view), account_migration_view_signals[ACCOUNT_MIGRATION_COMPLETED], 0);
    }

    gtk_widget_destroy(password_dialog);
197

aviau's avatar
aviau committed
198 199 200 201 202 203
}

static void
password_entry_changed(G_GNUC_UNUSED GtkEntry* entry, AccountMigrationView *view)
{
    AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view);
204
    gtk_widget_hide(priv->label_migration_error);
aviau's avatar
aviau committed
205 206 207 208 209 210 211 212 213
}

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->entry_password, "changed", G_CALLBACK(password_entry_changed), view);
214
    g_signal_connect_swapped(priv->button_migrate_account, "clicked", G_CALLBACK(migrate), view);
215
    g_signal_connect_swapped(priv->button_delete_account, "clicked", G_CALLBACK(delete_account), view);
216
    g_signal_connect_swapped(priv->entry_password, "activate", G_CALLBACK(migrate), view);
aviau's avatar
aviau committed
217

218
    gtk_label_set_text(GTK_LABEL(priv->label_account_alias), (*priv->accountInfo_)->profileInfo.alias.c_str());
219

220
    // display the ringID (without "ring:")
221
    g_debug("MIGRATE FOR %s", (*priv->accountInfo_)->id.c_str());
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
    std::string username = (*priv->accountInfo_)->registeredName;
    try {
        auto conf = (*priv->accountInfo_)->accountModel->getAccountConfig((*priv->accountInfo_)->id);
        if (username.empty() && !conf.managerUsername.empty()) {
            username = conf.managerUsername;
        }
        gtk_label_set_text(GTK_LABEL(priv->label_account_username), username.c_str());
        if (username.empty()) {
            gtk_widget_hide(priv->username_row);
        }
        std::string manager = conf.managerUri;
        gtk_label_set_text(GTK_LABEL(priv->label_account_manager), manager.c_str());
        if (manager.empty()) {
            gtk_widget_hide(priv->manager_row);
        }
    } catch (...) {
        gtk_widget_hide(priv->username_row);
        gtk_widget_hide(priv->manager_row);
240 241 242 243 244 245 246 247 248 249 250 251 252 253
    }

    /* get the current or default profile avatar */
    auto default_avatar = Interfaces::PixbufManipulator().generateAvatar("", "");
    auto default_scaled = Interfaces::PixbufManipulator().scaleAndFrame(default_avatar.get(), QSize(AVATAR_WIDTH, AVATAR_HEIGHT));
    auto photo = default_scaled;
    auto photostr = (*priv->accountInfo_)->profileInfo.avatar;
    if (!photostr.empty()) {
        QByteArray byteArray(photostr.c_str(), photostr.length());
        QVariant avatar = Interfaces::PixbufManipulator().personPhoto(byteArray);
        auto pixbuf_photo = Interfaces::PixbufManipulator().scaleAndFrame(avatar.value<std::shared_ptr<GdkPixbuf>>().get(), QSize(AVATAR_WIDTH, AVATAR_HEIGHT));
        if (avatar.isValid()) {
            photo = pixbuf_photo;
        }
aviau's avatar
aviau committed
254
    }
255 256 257 258 259 260 261 262 263 264 265 266 267
    gtk_image_set_from_pixbuf(GTK_IMAGE(priv->image_avatar), photo.get());

    // CSS styles
    auto provider = gtk_css_provider_new();
    std::string css = ".button_red { color: white; background: #dc3a37; border: 0; }";
    css += ".button_red:hover { background: #dc2719; }";
    gtk_css_provider_load_from_data(provider, css.c_str(), -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);
    GtkStyleContext* context;
    context = gtk_widget_get_style_context(GTK_WIDGET(priv->button_delete_account));
    gtk_style_context_add_class(context, "button_red");
268 269

    gtk_widget_hide(priv->hbox_migrating_account_spinner);
aviau's avatar
aviau committed
270 271 272
}

GtkWidget *
273
account_migration_view_new(AccountInfoPointer const & accountInfo)
aviau's avatar
aviau committed
274 275 276
{
    gpointer view = g_object_new(ACCOUNT_MIGRATION_VIEW_TYPE, NULL);
    AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view);
277
    priv->accountInfo_ = &accountInfo;
aviau's avatar
aviau committed
278 279 280
    build_migration_view(ACCOUNT_MIGRATION_VIEW(view));
    return (GtkWidget *)view;
}