chatview.cpp 23.4 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>
Hugo Lefeuvre's avatar
Hugo Lefeuvre committed
7
 *  Author: Hugo Lefeuvre <hugo.lefeuvre@savoirfairelinux.com>
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.
 */

#include "chatview.h"

26
27
// std
#include <algorithm>
28

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

32
33
34
35
// LRC
#include <api/contactmodel.h>
#include <api/conversationmodel.h>
#include <api/contact.h>
36
37
#include <api/newcallmodel.h>
#include <api/call.h>
38

39
40
// Client
#include "utils/files.h"
41

42
43
44
45
46
47
48
49
50
51
52
53
54
55
struct _ChatView
{
    GtkBox parent;
};

struct _ChatViewClass
{
    GtkBoxClass parent_class;
};

typedef struct _ChatViewPrivate ChatViewPrivate;

struct _ChatViewPrivate
{
aviau's avatar
aviau committed
56
57
    GtkWidget *box_webkit_chat_container;
    GtkWidget *webkit_chat_container;
Stepan Salenikovich's avatar
Stepan Salenikovich committed
58

59
60
    GSettings *settings;

61
    lrc::api::conversation::Info* conversation_;
62
    AccountInfoPointer const * accountInfo_;
63

64
    QMetaObject::Connection new_interaction_connection;
65
    QMetaObject::Connection interaction_removed;
66
67
    QMetaObject::Connection update_interaction_connection;
    QMetaObject::Connection update_add_to_conversations;
68
69

    gulong webkit_ready;
70
    gulong webkit_send_text;
71
    gulong webkit_drag_drop;
72
73
74
75
76
77
78
79
};

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,
80
    HIDE_VIEW_CLICKED,
81
82
83
84
85
86
87
88
89
90
91
92
93
94
    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);

95
96
    QObject::disconnect(priv->new_interaction_connection);
    QObject::disconnect(priv->update_interaction_connection);
97
    QObject::disconnect(priv->interaction_removed);
98
    QObject::disconnect(priv->update_add_to_conversations);
aviau's avatar
aviau committed
99
100
101
102

    /* 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) {
103
104
105
        /* disconnect for webkit signals */
        g_signal_handler_disconnect(priv->webkit_chat_container, priv->webkit_ready);
        priv->webkit_ready = 0;
106
107
        g_signal_handler_disconnect(priv->webkit_chat_container, priv->webkit_send_text);
        priv->webkit_send_text = 0;
108
109
        g_signal_handler_disconnect(priv->webkit_chat_container, priv->webkit_drag_drop);
        priv->webkit_drag_drop = 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;
}

Hugo Lefeuvre's avatar
Hugo Lefeuvre committed
194
195
static void update_chatview_frame(ChatView *self);

196
static void
Hugo Lefeuvre's avatar
Hugo Lefeuvre committed
197
webkit_chat_container_script_dialog(GtkWidget* webview, gchar *interaction, ChatView* self)
198
{
199
200
    auto priv = CHAT_VIEW_GET_PRIVATE(self);
    auto order = std::string(interaction);
201
    if (!priv->conversation_) return;
202
    if (order == "ACCEPT") {
203
        (*priv->accountInfo_)->conversationModel->makePermanent(priv->conversation_->uid);
204
    } else if (order == "REFUSE") {
205
        (*priv->accountInfo_)->conversationModel->removeConversation(priv->conversation_->uid);
206
    } else if (order == "BLOCK") {
207
        (*priv->accountInfo_)->conversationModel->removeConversation(priv->conversation_->uid, true);
Hugo Lefeuvre's avatar
Hugo Lefeuvre committed
208
209
210
211
212
    } else if (order == "UNBLOCK") {
        try {
            auto contactUri = priv->conversation_->participants.front();
            auto& contact = (*priv->accountInfo_)->contactModel->getContact(contactUri);
            (*priv->accountInfo_)->contactModel->addContact(contact);
213
        } catch (std::out_of_range&) {
Hugo Lefeuvre's avatar
Hugo Lefeuvre committed
214
215
216
217
218
219
220
221
            g_debug("webkit_chat_container_script_dialog: oor while retrieving invalid contact info. Chatview bug ?");
        }
    } else if (order.find("PLACE_CALL") == 0) {
        placecall_clicked(self);
    } else if (order.find("PLACE_AUDIO_CALL") == 0) {
        place_audio_call_clicked(self);
    } else if (order.find("CLOSE_CHATVIEW") == 0) {
        hide_chat_view(webview, self);
222
223
224
    } else if (order.find("SEND:") == 0) {
        // Get text body
        auto toSend = order.substr(std::string("SEND:").size());
225
        (*priv->accountInfo_)->conversationModel->sendMessage(priv->conversation_->uid, toSend);
226
    } else if (order.find("SEND_FILE") == 0) {
227
        if (auto model = (*priv->accountInfo_)->conversationModel.get()) {
228
229
230
231
            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) {
232
        if (auto model = (*priv->accountInfo_)->conversationModel.get()) {
233
234
            try {
                auto interactionId = std::stoull(order.substr(std::string("ACCEPT_FILE:").size()));
235
236

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

239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
                // 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);
254
255
256
257
258
            } catch (...) {
                // ignore
            }
        }
    } else if (order.find("REFUSE_FILE:") == 0) {
259
        if (auto model = (*priv->accountInfo_)->conversationModel.get()) {
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
            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);
        }
Hugo Lefeuvre's avatar
Hugo Lefeuvre committed
277
278
    } else if (order.find("ADD_TO_CONVERSATIONS") == 0) {
        button_add_to_conversations_clicked(self);
279
280
281
282
283
284
285
286
    } else if (order.find("DELETE_INTERACTION:") == 0) {
        try {
            auto interactionId = std::stoull(order.substr(std::string("DELETE_INTERACTION:").size()));
            if (!priv->conversation_) return;
            (*priv->accountInfo_)->conversationModel->clearInteractionFromConversation(priv->conversation_->uid, interactionId);
        } catch (...) {
            g_warning("delete interaction failed: can't find %s", order.substr(std::string("DELETE_INTERACTION:").size()).c_str());
        }
287
288
289
290
291
292
293
294
    } else if (order.find("RETRY_INTERACTION:") == 0) {
        try {
            auto interactionId = std::stoull(order.substr(std::string("RETRY_INTERACTION:").size()));
            if (!priv->conversation_) return;
            (*priv->accountInfo_)->conversationModel->retryInteraction(priv->conversation_->uid, interactionId);
        } catch (...) {
            g_warning("delete interaction failed: can't find %s", order.substr(std::string("RETRY_INTERACTION:").size()).c_str());
        }
295
296
297
    }
}

298
299
300
301
302
303
static void
chat_view_init(ChatView *view)
{
    gtk_widget_init_template(GTK_WIDGET(view));

    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(view);
304
    priv->settings = g_settings_new_full(get_ring_schema(), NULL, NULL);
305
306
307
308
309
310
311
312
313
314
}

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
315
    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, box_webkit_chat_container);
316
317

    chat_view_signals[NEW_MESSAGES_DISPLAYED] = g_signal_new (
318
        "new-interactions-displayed",
319
320
321
322
323
324
325
        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);
326
327
328
329
330
331
332
333
334
335

    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);
336
337
}

aviau's avatar
aviau committed
338
static void
339
print_interaction_to_buffer(ChatView* self, uint64_t interactionId, const lrc::api::interaction::Info& interaction)
aviau's avatar
aviau committed
340
341
342
{
    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);

343
    if (!priv->conversation_) return;
344
    if (interaction.status == lrc::api::interaction::Status::UNREAD)
345
        (*priv->accountInfo_)->conversationModel->setInteractionRead(priv->conversation_->uid, interactionId);
346
347

    webkit_chat_container_print_new_interaction(
aviau's avatar
aviau committed
348
        WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
349
        *(*priv->accountInfo_)->conversationModel,
350
351
        interactionId,
        interaction
aviau's avatar
aviau committed
352
    );
353
354
}

355
static void
356
update_interaction(ChatView* self, uint64_t interactionId, const lrc::api::interaction::Info& interaction)
357
358
{
    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
359
360
    webkit_chat_container_update_interaction(
        WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
361
        *(*priv->accountInfo_)->conversationModel,
362
363
        interactionId,
        interaction
364
    );
365
}
366

367
368
369
370
371
372
373
374
375
376
static void
remove_interaction(ChatView* self, uint64_t interactionId)
{
    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
    webkit_chat_container_remove_interaction(
        WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
        interactionId
    );
}

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

383
    // Contact
384
    if (!priv->conversation_) return;
385
    auto contactUri = priv->conversation_->participants.front();
386
    try{
387
        auto& contact = (*priv->accountInfo_)->contactModel->getContact(contactUri);
388
389
390
        if (!contact.profileInfo.avatar.empty()) {
            webkit_chat_container_set_sender_image(
                WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
391
                (*priv->accountInfo_)->contactModel->getContactProfileId(contactUri),
392
393
394
395
396
                contact.profileInfo.avatar
                );
        }
    } catch (const std::out_of_range&) {
        // ContactModel::getContact() exception
397
398
    }

399
    // For this account
400
    if (!(*priv->accountInfo_)->profileInfo.avatar.empty()) {
401
402
        webkit_chat_container_set_sender_image(
            WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
403
404
            (*priv->accountInfo_)->contactModel->getContactProfileId((*priv->accountInfo_)->profileInfo.uri),
            (*priv->accountInfo_)->profileInfo.avatar
405
        );
406
407
408
    }
}

409
static void
410
print_text_recording(ChatView *self)
411
412
413
414
{
    g_return_if_fail(IS_CHAT_VIEW(self));
    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);

415
    // Read interactions
416
    if (!priv->conversation_) return;
417
418
    for (const auto& it: priv->conversation_->interactions) {
        if (it.second.status == lrc::api::interaction::Status::UNREAD)
419
            (*priv->accountInfo_)->conversationModel->setInteractionRead(priv->conversation_->uid, it.first);
420
421
    }

422
423
    webkit_chat_container_print_history(
        WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
424
        *(*priv->accountInfo_)->conversationModel,
425
426
        priv->conversation_->interactions
    );
427

428
    QObject::disconnect(priv->new_interaction_connection);
429
430
}

aviau's avatar
aviau committed
431
432
433
434
435
436
437
438
439
440
441
442
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)
    );

443
    display_links_toggled(self);
444
445
446
447
    print_text_recording(self);
    load_participants_images(self);

    priv->new_interaction_connection = QObject::connect(
448
    &*(*priv->accountInfo_)->conversationModel, &lrc::api::ConversationModel::newInteraction,
449
    [self, priv](const std::string& uid, uint64_t interactionId, lrc::api::interaction::Info interaction) {
450
        if (!priv->conversation_) return;
451
        if (uid == priv->conversation_->uid) {
452
453
454
            print_interaction_to_buffer(self, interactionId, interaction);
        }
    });
455

456
    priv->update_interaction_connection = QObject::connect(
457
    &*(*priv->accountInfo_)->conversationModel, &lrc::api::ConversationModel::interactionStatusUpdated,
458
    [self, priv](const std::string& uid, uint64_t msgId, lrc::api::interaction::Info msg) {
459
        if (!priv->conversation_) return;
460
        if (uid == priv->conversation_->uid) {
461
462
463
464
            update_interaction(self, msgId, msg);
        }
    });

465
466
467
468
469
470
471
472
473
    priv->interaction_removed = QObject::connect(
    &*(*priv->accountInfo_)->conversationModel, &lrc::api::ConversationModel::interactionRemoved,
    [self, priv](const std::string& convUid, uint64_t interactionId) {
        if (!priv->conversation_) return;
        if (convUid == priv->conversation_->uid) {
            remove_interaction(self, interactionId);
        }
    });

474
    if (!priv->conversation_) return;
475
    auto contactUri = priv->conversation_->participants.front();
476
    try {
477
        auto contactInfo = (*priv->accountInfo_)->contactModel->getContact(contactUri);
478
479
480
481
482
483
484
485
486
487
488
489
        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_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
    }
Hugo Lefeuvre's avatar
Hugo Lefeuvre committed
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521

    update_chatview_frame(self);
}

static void
update_chatview_frame(ChatView* self)
{
    g_return_if_fail(IS_CHAT_VIEW(self));
    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
    if (!priv->conversation_) return;

    auto contactUri = priv->conversation_->participants.front();

    lrc::api::contact::Info contactInfo;
    try {
        contactInfo = (*priv->accountInfo_)->contactModel->getContact(contactUri);
    } catch (const std::out_of_range&) {
        g_debug("update_chatview_frame: failed to retrieve contactInfo");
        return;
    }

    // get alias and bestName
    auto alias = contactInfo.profileInfo.alias;
    auto bestName = contactInfo.registeredName;
    if (bestName.empty())
        bestName = contactInfo.profileInfo.uri;
    bestName.erase(std::remove(bestName.begin(), bestName.end(), '\r'), bestName.end());
    alias.erase(std::remove(alias.begin(), alias.end(), '\r'), alias.end());

    // get temporary status
    bool temp = contactInfo.profileInfo.type == lrc::api::profile::Type::TEMPORARY || contactInfo.profileInfo.type == lrc::api::profile::Type::PENDING;

522
523
524
    webkit_chat_update_chatview_frame(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
                                     (*priv->accountInfo_)->enabled,
                                     contactInfo.isBanned, temp, alias.c_str(), bestName.c_str());
Hugo Lefeuvre's avatar
Hugo Lefeuvre committed
525
526
527
528

    webkit_chat_container_set_invitation(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
                                             (contactInfo.profileInfo.type == lrc::api::profile::Type::PENDING),
                                             bestName);
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550

    // hide navbar if we are in call
    try {
        std::string callId;
        if (priv->conversation_->confId.empty()) {
            callId = priv->conversation_->callId;
        } else {
            callId = priv->conversation_->confId;
        }

        if (*priv->accountInfo_) {
            const lrc::api::call::Status& status = (*priv->accountInfo_)->callModel->getCall(callId).status;
            if (status != lrc::api::call::Status::ENDED &&
                status != lrc::api::call::Status::INVALID &&
                status != lrc::api::call::Status::TERMINATING) {
                g_debug("call has status %s, hiding", lrc::api::call::to_string(status).c_str());
                chat_view_set_header_visible(self, FALSE);
            } else {
                chat_view_set_header_visible(self, TRUE);
            }
        }
    } catch (const std::out_of_range&) {}
aviau's avatar
aviau committed
551
552
}

553
554
555
556
557
558
559
560
static void
on_webkit_drag_drop(GtkWidget*, gchar* data, ChatView* self)
{
    g_return_if_fail(IS_CHAT_VIEW(self));
    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
    if (!priv->conversation_) return;
    if (!data) return;

561
562
563
564
565
566
567
    GError *error = nullptr;
    std::string data_str = g_filename_from_uri(data, nullptr, &error);
    if (error) {
        g_warning("Unable to exec g_filename_from_uri on %s", data);
        g_error_free(error);
        return;
    }
568
569
570
    // Only take files
    if (data_str.find("\r\n") == std::string::npos) return;
    const auto LEN_END = std::string("\r\n").length();
571
572
573
574
575
576
    if (data_str.length() > LEN_END) {
        // remove \r\n from the string
        data_str = data_str.substr(0, data_str.length() - LEN_END);
    } else {
        // Nothing valid to drop, abort.
        return;
577
578
579
580
581
582
583
    }

    if (auto model = (*priv->accountInfo_)->conversationModel.get()) {
        model->sendFile(priv->conversation_->uid, data_str, g_path_get_basename(data_str.c_str()));
    }
}

aviau's avatar
aviau committed
584
585
586
587
588
589
590
591
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);

Hugo Lefeuvre's avatar
Hugo Lefeuvre committed
592
    update_chatview_frame(self);
aviau's avatar
aviau committed
593

594
    priv->webkit_ready = g_signal_connect_swapped(
aviau's avatar
aviau committed
595
596
597
598
599
600
        priv->webkit_chat_container,
        "ready",
        G_CALLBACK(webkit_chat_container_ready),
        self
    );

601
    priv->webkit_send_text = g_signal_connect(priv->webkit_chat_container,
602
603
        "script-dialog",
        G_CALLBACK(webkit_chat_container_script_dialog),
604
605
        self);

606
607
608
609
610
611
612
    priv->webkit_drag_drop = g_signal_connect(
        priv->webkit_chat_container,
        "data-dropped",
        G_CALLBACK(on_webkit_drag_drop),
        self
    );

aviau's avatar
aviau committed
613
614
    if (webkit_chat_container_is_ready(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container)))
        webkit_chat_container_ready(self);
615
}
Stepan Salenikovich's avatar
Stepan Salenikovich committed
616
617

GtkWidget *
618
chat_view_new (WebKitChatContainer* webkit_chat_container,
619
               AccountInfoPointer const & accountInfo,
620
               lrc::api::conversation::Info* conversation)
Stepan Salenikovich's avatar
Stepan Salenikovich committed
621
622
623
{
    ChatView *self = CHAT_VIEW(g_object_new(CHAT_VIEW_TYPE, NULL));

aviau's avatar
aviau committed
624
625
    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
    priv->webkit_chat_container = GTK_WIDGET(webkit_chat_container);
626
    priv->conversation_ = conversation;
627
    priv->accountInfo_ = &accountInfo;
aviau's avatar
aviau committed
628
629

    build_chat_view(self);
Stepan Salenikovich's avatar
Stepan Salenikovich committed
630
631
632
    return (GtkWidget *)self;
}

633
void
634
chat_view_update_temporary(ChatView* self, bool showAddButton, bool showInvitation)
635
{
636
    g_return_if_fail(IS_CHAT_VIEW(self));
Hugo Lefeuvre's avatar
Hugo Lefeuvre committed
637
    update_chatview_frame(self);
638
639
}

640
641
lrc::api::conversation::Info
chat_view_get_conversation(ChatView *self)
642
{
643
    g_return_val_if_fail(IS_CHAT_VIEW(self), lrc::api::conversation::Info());
644
    auto priv = CHAT_VIEW_GET_PRIVATE(self);
645
    return *priv->conversation_;
646
}
647
648
649
650
651

void
chat_view_set_header_visible(ChatView *self, gboolean visible)
{
    auto priv = CHAT_VIEW_GET_PRIVATE(self);
Hugo Lefeuvre's avatar
Hugo Lefeuvre committed
652
    webkit_chat_set_header_visible(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container), visible);
653
}