chatview.cpp 23.7 KB
Newer Older
1
/*
Guillaume Roguez's avatar
Guillaume Roguez committed
2
 *  Copyright (C) 2016-2018 Savoir-faire Linux Inc.
3
 *  Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
aviau's avatar
aviau committed
4
 *  Author: Alexandre Viau <alexandre.viau@savoirfairelinux.com>
5
6
 *  Author: Nicolas Jäger <nicolas.jager@savoirfairelinux.com>
 *  Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 *
 *  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.
 */

#include "chatview.h"

25
26
// std
#include <algorithm>
27

28
29
30
// GTK
#include <glib/gi18n.h>

31
32
33
34
// LRC
#include <api/contactmodel.h>
#include <api/conversationmodel.h>
#include <api/contact.h>
35

36
37
// Client
#include "utils/files.h"
38

39
40
41
42
43
44
45
46
47
48
49
50
51
52
struct _ChatView
{
    GtkBox parent;
};

struct _ChatViewClass
{
    GtkBoxClass parent_class;
};

typedef struct _ChatViewPrivate ChatViewPrivate;

struct _ChatViewPrivate
{
aviau's avatar
aviau committed
53
54
    GtkWidget *box_webkit_chat_container;
    GtkWidget *webkit_chat_container;
Stepan Salenikovich's avatar
Stepan Salenikovich committed
55
56
    GtkWidget *hbox_chat_info;
    GtkWidget *label_peer;
57
    GtkWidget *label_cm;
58
    GtkWidget *button_close_chatview;
59
    GtkWidget *button_placecall;
60
    GtkWidget *button_add_to_conversations;
61
    GtkWidget *button_place_audio_call;
Stepan Salenikovich's avatar
Stepan Salenikovich committed
62

63
64
    GSettings *settings;

65
    lrc::api::conversation::Info* conversation_;
66
    AccountInfoPointer const * accountInfo_;
67
    bool isTemporary_;
68

69
70
71
    QMetaObject::Connection new_interaction_connection;
    QMetaObject::Connection update_interaction_connection;
    QMetaObject::Connection update_add_to_conversations;
72
73

    gulong webkit_ready;
74
    gulong webkit_send_text;
75
76
77
78
79
80
81
82
};

G_DEFINE_TYPE_WITH_PRIVATE(ChatView, chat_view, GTK_TYPE_BOX);

#define CHAT_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CHAT_VIEW_TYPE, ChatViewPrivate))

enum {
    NEW_MESSAGES_DISPLAYED,
83
    HIDE_VIEW_CLICKED,
84
85
86
87
88
89
90
91
92
93
94
95
96
97
    LAST_SIGNAL
};

static guint chat_view_signals[LAST_SIGNAL] = { 0 };

static void
chat_view_dispose(GObject *object)
{
    ChatView *view;
    ChatViewPrivate *priv;

    view = CHAT_VIEW(object);
    priv = CHAT_VIEW_GET_PRIVATE(view);

98
99
100
    QObject::disconnect(priv->new_interaction_connection);
    QObject::disconnect(priv->update_interaction_connection);
    QObject::disconnect(priv->update_add_to_conversations);
aviau's avatar
aviau committed
101
102
103
104

    /* Destroying the box will also destroy its children, and we wouldn't
     * want that. So we remove the webkit_chat_container from the box. */
    if (priv->webkit_chat_container) {
105
106
107
        /* disconnect for webkit signals */
        g_signal_handler_disconnect(priv->webkit_chat_container, priv->webkit_ready);
        priv->webkit_ready = 0;
108
109
        g_signal_handler_disconnect(priv->webkit_chat_container, priv->webkit_send_text);
        priv->webkit_send_text = 0;
110

aviau's avatar
aviau committed
111
112
113
114
115
116
        gtk_container_remove(
            GTK_CONTAINER(priv->box_webkit_chat_container),
            GTK_WIDGET(priv->webkit_chat_container)
        );
        priv->webkit_chat_container = nullptr;
    }
117
118
119
120

    G_OBJECT_CLASS(chat_view_parent_class)->dispose(object);
}

121
122
123
124
125
126
static void
hide_chat_view(G_GNUC_UNUSED GtkWidget *widget, ChatView *self)
{
    g_signal_emit(G_OBJECT(self), chat_view_signals[HIDE_VIEW_CLICKED], 0);
}

127
128
129
130
131
132
133
134
135
136
137
138
static void
display_links_toggled(ChatView *self)
{
    auto priv = CHAT_VIEW_GET_PRIVATE(self);
    if (priv->webkit_chat_container) {
        webkit_chat_container_set_display_links(
            WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
            g_settings_get_boolean(priv->settings, "enable-display-links")
        );
    }
}

139
140
141
142
static void
placecall_clicked(ChatView *self)
{
    auto priv = CHAT_VIEW_GET_PRIVATE(self);
143
    if (!priv->conversation_) return;
144
    (*priv->accountInfo_)->conversationModel->placeCall(priv->conversation_->uid);
145
146
}

147
148
149
150
151
152
153
154
static void
place_audio_call_clicked(ChatView *self)
{
    auto priv = CHAT_VIEW_GET_PRIVATE(self);

    if (!priv->conversation_)
        return;

155
    (*priv->accountInfo_)->conversationModel->placeAudioOnlyCall(priv->conversation_->uid);
156
157
}

158
static void
159
button_add_to_conversations_clicked(ChatView *self)
160
161
{
    auto priv = CHAT_VIEW_GET_PRIVATE(self);
162
    if (!priv->conversation_) return;
163
    (*priv->accountInfo_)->conversationModel->makePermanent(priv->conversation_->uid);
164
165
}

166
static gchar*
167
file_to_manipulate(GtkWindow* top_window, bool send)
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
{
    GtkWidget* dialog;
    GtkFileChooserAction action = send? GTK_FILE_CHOOSER_ACTION_OPEN : GTK_FILE_CHOOSER_ACTION_SAVE;
    gint res;
    gchar* filename = nullptr;

    dialog = gtk_file_chooser_dialog_new(send? _("Send File") : _("Save File"),
                                         top_window,
                                         action,
                                         _("_Cancel"),
                                         GTK_RESPONSE_CANCEL,
                                         send? _("_Open"): _("_Save"),
                                         GTK_RESPONSE_ACCEPT,
                                         nullptr);

    res = gtk_dialog_run (GTK_DIALOG(dialog));

    if (res == GTK_RESPONSE_ACCEPT) {
        auto chooser = GTK_FILE_CHOOSER(dialog);
        filename = gtk_file_chooser_get_filename(chooser);
    }
    gtk_widget_destroy (dialog);

    return filename;
}

194
static void
195
webkit_chat_container_script_dialog(G_GNUC_UNUSED GtkWidget* webview, gchar *interaction, ChatView* self)
196
{
197
198
    auto priv = CHAT_VIEW_GET_PRIVATE(self);
    auto order = std::string(interaction);
199
    if (!priv->conversation_) return;
200
    if (order == "ACCEPT") {
201
        (*priv->accountInfo_)->conversationModel->makePermanent(priv->conversation_->uid);
202
    } else if (order == "REFUSE") {
203
        (*priv->accountInfo_)->conversationModel->removeConversation(priv->conversation_->uid);
204
    } else if (order == "BLOCK") {
205
        (*priv->accountInfo_)->conversationModel->removeConversation(priv->conversation_->uid, true);
206
207
208
    } else if (order.find("SEND:") == 0) {
        // Get text body
        auto toSend = order.substr(std::string("SEND:").size());
209
        (*priv->accountInfo_)->conversationModel->sendMessage(priv->conversation_->uid, toSend);
210
    } else if (order.find("SEND_FILE") == 0) {
211
        if (auto model = (*priv->accountInfo_)->conversationModel.get()) {
212
213
214
215
            if (auto filename = file_to_manipulate(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(self))), true))
                model->sendFile(priv->conversation_->uid, filename, g_path_get_basename(filename));
        }
    } else if (order.find("ACCEPT_FILE:") == 0) {
216
        if (auto model = (*priv->accountInfo_)->conversationModel.get()) {
217
218
            try {
                auto interactionId = std::stoull(order.substr(std::string("ACCEPT_FILE:").size()));
219
220

                lrc::api::datatransfer::Info info = {};
221
                (*priv->accountInfo_)->conversationModel->getTransferInfo(interactionId, info);
222

223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
                // get prefered directory destination.
                auto* download_directory_variant = g_settings_get_value(priv->settings, "download-folder");
                char* download_directory_value;
                g_variant_get(download_directory_variant, "&s", &download_directory_value);
                std::string default_download_dir = g_get_user_special_dir (G_USER_DIRECTORY_DOWNLOAD);
                auto current_value = std::string(download_directory_value);
                if (current_value.empty()) {
                    g_settings_set_value(priv->settings, "download-folder", g_variant_new("s", default_download_dir.c_str()));
                }
                // get full path
                std::string filename = current_value.empty()? default_download_dir.c_str() : download_directory_value;
                if (!filename.empty() && filename.back() != '/') filename += "/";
                filename += info.displayName;

                model->acceptTransfer(priv->conversation_->uid, interactionId, filename);
238
239
240
241
242
            } catch (...) {
                // ignore
            }
        }
    } else if (order.find("REFUSE_FILE:") == 0) {
243
        if (auto model = (*priv->accountInfo_)->conversationModel.get()) {
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
            try {
                auto interactionId = std::stoull(order.substr(std::string("REFUSE_FILE:").size()));
                model->cancelTransfer(priv->conversation_->uid, interactionId);
            } catch (...) {
                // ignore
            }
        }
    } else if (order.find("OPEN_FILE:") == 0) {
        // Get text body
        auto filename {"file://" + order.substr(std::string("OPEN_FILE:").size())};
        filename.erase(std::find_if(filename.rbegin(), filename.rend(),
            std::not1(std::ptr_fun<int, int>(std::isspace))).base(), filename.end());
        GError* error = nullptr;
        if (!gtk_show_uri(nullptr, filename.c_str(), GDK_CURRENT_TIME, &error)) {
            g_debug("Could not open file: %s", error->message);
            g_error_free(error);
        }
261
262
263
    }
}

264
265
266
267
268
269
static void
chat_view_init(ChatView *view)
{
    gtk_widget_init_template(GTK_WIDGET(view));

    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(view);
270
    priv->settings = g_settings_new_full(get_ring_schema(), NULL, NULL);
271

272
    g_signal_connect(priv->button_close_chatview, "clicked", G_CALLBACK(hide_chat_view), view);
273
    g_signal_connect_swapped(priv->button_placecall, "clicked", G_CALLBACK(placecall_clicked), view);
274
    g_signal_connect_swapped(priv->button_add_to_conversations, "clicked", G_CALLBACK(button_add_to_conversations_clicked), view);
275
    g_signal_connect_swapped(priv->button_place_audio_call, "clicked", G_CALLBACK(place_audio_call_clicked), view);
276
277
278
279
280
281
282
283
284
285
}

static void
chat_view_class_init(ChatViewClass *klass)
{
    G_OBJECT_CLASS(klass)->dispose = chat_view_dispose;

    gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
                                                "/cx/ring/RingGnome/chatview.ui");

aviau's avatar
aviau committed
286
    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, box_webkit_chat_container);
Stepan Salenikovich's avatar
Stepan Salenikovich committed
287
288
    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, hbox_chat_info);
    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, label_peer);
289
    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, label_cm);
290
    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, button_close_chatview);
291
    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, button_placecall);
292
    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, button_add_to_conversations);
293
    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, button_place_audio_call);
294
295

    chat_view_signals[NEW_MESSAGES_DISPLAYED] = g_signal_new (
296
        "new-interactions-displayed",
297
298
299
300
301
302
303
        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);
304
305
306
307
308
309
310
311
312
313

    chat_view_signals[HIDE_VIEW_CLICKED] = g_signal_new (
        "hide-view-clicked",
        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);
314
315
}

aviau's avatar
aviau committed
316
static void
317
print_interaction_to_buffer(ChatView* self, uint64_t interactionId, const lrc::api::interaction::Info& interaction)
aviau's avatar
aviau committed
318
319
320
{
    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);

321
    if (!priv->conversation_) return;
322
    if (interaction.status == lrc::api::interaction::Status::UNREAD)
323
        (*priv->accountInfo_)->conversationModel->setInteractionRead(priv->conversation_->uid, interactionId);
324
325

    webkit_chat_container_print_new_interaction(
aviau's avatar
aviau committed
326
        WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
327
        *(*priv->accountInfo_)->conversationModel,
328
329
        interactionId,
        interaction
aviau's avatar
aviau committed
330
    );
331
332
}

333
static void
334
update_interaction(ChatView* self, uint64_t interactionId, const lrc::api::interaction::Info& interaction)
335
336
{
    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
337
338
    webkit_chat_container_update_interaction(
        WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
339
        *(*priv->accountInfo_)->conversationModel,
340
341
        interactionId,
        interaction
342
    );
343
}
344

345
346
347
348
349
static void
load_participants_images(ChatView *self)
{
    g_return_if_fail(IS_CHAT_VIEW(self));
    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
350

351
    // Contact
352
    if (!priv->conversation_) return;
353
    auto contactUri = priv->conversation_->participants.front();
354
    try{
355
        auto& contact = (*priv->accountInfo_)->contactModel->getContact(contactUri);
356
357
358
        if (!contact.profileInfo.avatar.empty()) {
            webkit_chat_container_set_sender_image(
                WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
359
                (*priv->accountInfo_)->contactModel->getContactProfileId(contactUri),
360
361
362
363
364
                contact.profileInfo.avatar
                );
        }
    } catch (const std::out_of_range&) {
        // ContactModel::getContact() exception
365
366
    }

367
    // For this account
368
    if (!(*priv->accountInfo_)->profileInfo.avatar.empty()) {
369
370
        webkit_chat_container_set_sender_image(
            WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
371
372
            (*priv->accountInfo_)->contactModel->getContactProfileId((*priv->accountInfo_)->profileInfo.uri),
            (*priv->accountInfo_)->profileInfo.avatar
373
        );
374
375
376
    }
}

377
static void
378
print_text_recording(ChatView *self)
379
380
381
382
{
    g_return_if_fail(IS_CHAT_VIEW(self));
    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);

383
    // Read interactions
384
    if (!priv->conversation_) return;
385
386
    for (const auto& it: priv->conversation_->interactions) {
        if (it.second.status == lrc::api::interaction::Status::UNREAD)
387
            (*priv->accountInfo_)->conversationModel->setInteractionRead(priv->conversation_->uid, it.first);
388
389
    }

390
391
    webkit_chat_container_print_history(
        WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
392
        *(*priv->accountInfo_)->conversationModel,
393
394
        priv->conversation_->interactions
    );
395

396
    QObject::disconnect(priv->new_interaction_connection);
397
398
}

399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
static void
update_if_banned(ChatView *self)
{
    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);

    if (!priv->conversation_) return;
    auto participant = priv->conversation_->participants[0];
    try {
        auto contactInfo = (*priv->accountInfo_)->contactModel->getContact(participant);
        if (contactInfo.isBanned) {
            gtk_widget_set_sensitive (priv->button_placecall, FALSE);
            gtk_widget_set_tooltip_text(priv->button_placecall, _("Can't place call with banned contact"));
            gtk_widget_set_sensitive (priv->button_add_to_conversations, FALSE);
            gtk_widget_set_tooltip_text(priv->button_add_to_conversations, _("Can't start conversation with banned contact"));
            gtk_widget_set_sensitive (priv->button_place_audio_call, FALSE);
            gtk_widget_set_tooltip_text(priv->button_place_audio_call, _("Can't place audio call with banned contact"));
            webkit_chat_hide_message_bar(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container), true);
        } else {
            gtk_widget_set_sensitive (priv->button_placecall, TRUE);
            gtk_widget_set_sensitive (priv->button_add_to_conversations, TRUE);
            gtk_widget_set_sensitive (priv->button_place_audio_call, TRUE);
            webkit_chat_hide_message_bar(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container), false);
        }
    } catch (const std::out_of_range&) {
        // ContactModel::getContact() exception
    }
}

427
static void
428
update_add_to_conversations(ChatView *self)
429
430
431
{
    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);

432
    if (!priv->conversation_) return;
433
    auto participant = priv->conversation_->participants[0];
434
    try {
435
        auto contactInfo = (*priv->accountInfo_)->contactModel->getContact(participant);
436
437
438
439
440
441
        if(contactInfo.profileInfo.type != lrc::api::profile::Type::TEMPORARY
           && contactInfo.profileInfo.type != lrc::api::profile::Type::PENDING)
            gtk_widget_hide(priv->button_add_to_conversations);
    } catch (const std::out_of_range&) {
        // ContactModel::getContact() exception
    }
442
443
}

Stepan Salenikovich's avatar
Stepan Salenikovich committed
444
445
446
447
448
static void
update_contact_methods(ChatView *self)
{
    g_return_if_fail(IS_CHAT_VIEW(self));
    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
449
    if (!priv->conversation_) return;
450
    auto contactUri = priv->conversation_->participants.front();
451
    try {
452
        auto contactInfo = (*priv->accountInfo_)->contactModel->getContact(contactUri);
453
454
455
456
457
458
459
460
461
        auto bestId = std::string(contactInfo.registeredName).empty() ? contactInfo.profileInfo.uri : contactInfo.registeredName;
        if (contactInfo.profileInfo.alias == bestId) {
            gtk_widget_hide(priv->label_cm);
        } else {
            gtk_label_set_text(GTK_LABEL(priv->label_cm), bestId.c_str());
            gtk_widget_show(priv->label_cm);
        }
    } catch (const std::out_of_range&) {
        // ContactModel::getContact() exception
462
    }
Stepan Salenikovich's avatar
Stepan Salenikovich committed
463
464
465
}

static void
466
update_name(ChatView *self)
Stepan Salenikovich's avatar
Stepan Salenikovich committed
467
{
468
    g_return_if_fail(IS_CHAT_VIEW(self));
Stepan Salenikovich's avatar
Stepan Salenikovich committed
469
    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
470
    if (!priv->conversation_) return;
471
    auto contactUri = priv->conversation_->participants.front();
472
    try {
473
        auto contactInfo = (*priv->accountInfo_)->contactModel->getContact(contactUri);
474
475
476
477
478
479
        auto alias = contactInfo.profileInfo.alias;
        alias.erase(std::remove(alias.begin(), alias.end(), '\r'), alias.end());
        gtk_label_set_text(GTK_LABEL(priv->label_peer), alias.c_str());
    } catch (const std::out_of_range&) {
        // ContactModel::getContact() exception
    }
Stepan Salenikovich's avatar
Stepan Salenikovich committed
480
481
}

aviau's avatar
aviau committed
482
483
484
485
486
487
488
489
490
491
492
493
static void
webkit_chat_container_ready(ChatView* self)
{
    /* The webkit chat container has loaded the javascript libraries, we can
     * now use it. */

    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);

    webkit_chat_container_clear(
        WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container)
    );

494
    display_links_toggled(self);
495
496
497
498
    print_text_recording(self);
    load_participants_images(self);

    priv->new_interaction_connection = QObject::connect(
499
    &*(*priv->accountInfo_)->conversationModel, &lrc::api::ConversationModel::newInteraction,
500
    [self, priv](const std::string& uid, uint64_t interactionId, lrc::api::interaction::Info interaction) {
501
        if (!priv->conversation_) return;
502
503
504
505
        if(uid == priv->conversation_->uid) {
            print_interaction_to_buffer(self, interactionId, interaction);
        }
    });
506

507
    priv->update_interaction_connection = QObject::connect(
508
    &*(*priv->accountInfo_)->conversationModel, &lrc::api::ConversationModel::interactionStatusUpdated,
509
    [self, priv](const std::string& uid, uint64_t msgId, lrc::api::interaction::Info msg) {
510
        if (!priv->conversation_) return;
511
512
513
514
515
        if(uid == priv->conversation_->uid) {
            update_interaction(self, msgId, msg);
        }
    });

516
    if (!priv->conversation_) return;
517
    auto contactUri = priv->conversation_->participants.front();
518
    try {
519
        auto contactInfo = (*priv->accountInfo_)->contactModel->getContact(contactUri);
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
        priv->isTemporary_ = contactInfo.profileInfo.type == lrc::api::profile::Type::TEMPORARY
            || contactInfo.profileInfo.type == lrc::api::profile::Type::PENDING;
        webkit_chat_container_set_temporary(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container), priv->isTemporary_);
        auto bestName = contactInfo.profileInfo.alias;
        if (bestName.empty())
            bestName = contactInfo.registeredName;
        if (bestName.empty())
            bestName = contactInfo.profileInfo.uri;
        bestName.erase(std::remove(bestName.begin(), bestName.end(), '\r'), bestName.end());
        webkit_chat_container_set_invitation(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
                                             (contactInfo.profileInfo.type == lrc::api::profile::Type::PENDING),
                                             bestName);
        webkit_chat_disable_send_interaction(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
                                             (contactInfo.profileInfo.type == lrc::api::profile::Type::SIP)
                                             && priv->conversation_->callId.empty());
    } catch (const std::out_of_range&) {
        // ContactModel::getContact() exception
    }
aviau's avatar
aviau committed
538
539
540
541
542
543
544
545
546
547
}

static void
build_chat_view(ChatView* self)
{
    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);

    gtk_container_add(GTK_CONTAINER(priv->box_webkit_chat_container), priv->webkit_chat_container);
    gtk_widget_show(priv->webkit_chat_container);

548
    update_name(self);
549
    update_add_to_conversations(self);
550
    update_contact_methods(self);
551
    update_if_banned(self);
aviau's avatar
aviau committed
552

553
    priv->webkit_ready = g_signal_connect_swapped(
aviau's avatar
aviau committed
554
555
556
557
558
559
        priv->webkit_chat_container,
        "ready",
        G_CALLBACK(webkit_chat_container_ready),
        self
    );

560
    priv->webkit_send_text = g_signal_connect(priv->webkit_chat_container,
561
562
        "script-dialog",
        G_CALLBACK(webkit_chat_container_script_dialog),
563
564
        self);

aviau's avatar
aviau committed
565
566
567
    if (webkit_chat_container_is_ready(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container)))
        webkit_chat_container_ready(self);

568
    gtk_widget_set_visible(priv->hbox_chat_info, TRUE);
569
}
Stepan Salenikovich's avatar
Stepan Salenikovich committed
570
571

GtkWidget *
572
chat_view_new (WebKitChatContainer* webkit_chat_container,
573
               AccountInfoPointer const & accountInfo,
574
               lrc::api::conversation::Info* conversation)
Stepan Salenikovich's avatar
Stepan Salenikovich committed
575
576
577
{
    ChatView *self = CHAT_VIEW(g_object_new(CHAT_VIEW_TYPE, NULL));

aviau's avatar
aviau committed
578
579
    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
    priv->webkit_chat_container = GTK_WIDGET(webkit_chat_container);
580
    priv->conversation_ = conversation;
581
    priv->accountInfo_ = &accountInfo;
aviau's avatar
aviau committed
582
583

    build_chat_view(self);
Stepan Salenikovich's avatar
Stepan Salenikovich committed
584
585
586
    return (GtkWidget *)self;
}

587
void
588
chat_view_update_temporary(ChatView* self, bool showAddButton, bool showInvitation)
589
{
590
    g_return_if_fail(IS_CHAT_VIEW(self));
591
592
    auto priv = CHAT_VIEW_GET_PRIVATE(self);

593
    priv->isTemporary_ = showAddButton;
594
595
596
597
    if (!priv->isTemporary_) {
        gtk_widget_hide(priv->button_add_to_conversations);
    }
    webkit_chat_container_set_temporary(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container), priv->isTemporary_);
598
    if (!priv->conversation_) return;
599
    auto contactUri = priv->conversation_->participants.front();
600
    try {
601
        auto contactInfo = (*priv->accountInfo_)->contactModel->getContact(contactUri);
602
603
604
605
606
607
        auto bestName = contactInfo.profileInfo.alias;
        if (bestName.empty())
            bestName = contactInfo.registeredName;
        if (bestName.empty())
            bestName = contactInfo.profileInfo.uri;
        webkit_chat_container_set_invitation(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
608
                                             showInvitation,
609
610
611
612
                                             bestName);
    } catch (const std::out_of_range&) {
        // ContactModel::getContact() exception
    }
613
614
}

615
616
bool
chat_view_get_temporary(ChatView *self)
617
{
618
    g_return_val_if_fail(IS_CHAT_VIEW(self), false);
619
    auto priv = CHAT_VIEW_GET_PRIVATE(self);
620
    return priv->isTemporary_;
621
622
}

623
624
lrc::api::conversation::Info
chat_view_get_conversation(ChatView *self)
625
{
626
    g_return_val_if_fail(IS_CHAT_VIEW(self), lrc::api::conversation::Info());
627
    auto priv = CHAT_VIEW_GET_PRIVATE(self);
628
    return *priv->conversation_;
629
}
630
631
632
633
634
635
636

void
chat_view_set_header_visible(ChatView *self, gboolean visible)
{
    auto priv = CHAT_VIEW_GET_PRIVATE(self);
    gtk_widget_set_visible(priv->hbox_chat_info, visible);
}