blob: 80925f98d357e5cc99884e3b601cf3d7be55e7cc [file] [log] [blame]
Stepan Salenikovichd2cad062016-01-08 13:43:49 -05001/*
Guillaume Roguez77c579d2018-01-30 15:54:02 -05002 * Copyright (C) 2016-2018 Savoir-faire Linux Inc.
Stepan Salenikovichd2cad062016-01-08 13:43:49 -05003 * Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
aviau039001d2016-09-29 16:39:05 -04004 * Author: Alexandre Viau <alexandre.viau@savoirfairelinux.com>
Sébastien Blin55bff9d2017-10-03 15:15:23 -04005 * Author: Nicolas Jäger <nicolas.jager@savoirfairelinux.com>
6 * Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
Stepan Salenikovichd2cad062016-01-08 13:43:49 -05007 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23#include "chatview.h"
24
Sébastien Blin55bff9d2017-10-03 15:15:23 -040025// std
26#include <algorithm>
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050027
Guillaume Roguez5b137be2018-02-21 10:44:58 -050028// GTK
29#include <glib/gi18n.h>
30
Nicolas Jager759faea2017-03-22 16:22:00 -040031// LRC
Sébastien Blin55bff9d2017-10-03 15:15:23 -040032#include <api/contactmodel.h>
33#include <api/conversationmodel.h>
34#include <api/contact.h>
Nicolas Jager759faea2017-03-22 16:22:00 -040035
Sébastien Blin55bff9d2017-10-03 15:15:23 -040036// Client
37#include "utils/files.h"
Stepan Salenikovichce06adb2016-02-19 12:53:53 -050038
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050039struct _ChatView
40{
41 GtkBox parent;
42};
43
44struct _ChatViewClass
45{
46 GtkBoxClass parent_class;
47};
48
49typedef struct _ChatViewPrivate ChatViewPrivate;
50
51struct _ChatViewPrivate
52{
aviau039001d2016-09-29 16:39:05 -040053 GtkWidget *box_webkit_chat_container;
54 GtkWidget *webkit_chat_container;
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -050055 GtkWidget *hbox_chat_info;
56 GtkWidget *label_peer;
AmarOkcba03952017-07-17 10:13:49 -040057 GtkWidget *label_cm;
Stepan Salenikovich8043a562016-03-18 13:56:40 -040058 GtkWidget *button_close_chatview;
Stepan Salenikoviche9933242016-06-21 18:08:48 -040059 GtkWidget *button_placecall;
Sébastien Blin55bff9d2017-10-03 15:15:23 -040060 GtkWidget *button_add_to_conversations;
Nicolas Jagerd29ae952017-12-04 16:00:12 -050061 GtkWidget *button_place_audio_call;
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050062
Sébastien Blin70dc0b72017-07-31 16:24:41 -040063 GSettings *settings;
64
Sébastien Blin55bff9d2017-10-03 15:15:23 -040065 lrc::api::conversation::Info* conversation_;
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -040066 AccountInfoPointer const * accountInfo_;
Sébastien Blin55bff9d2017-10-03 15:15:23 -040067 bool isTemporary_;
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050068
Sébastien Blin55bff9d2017-10-03 15:15:23 -040069 QMetaObject::Connection new_interaction_connection;
70 QMetaObject::Connection update_interaction_connection;
71 QMetaObject::Connection update_add_to_conversations;
Stepan Salenikovich1f731212016-11-10 11:55:10 -050072
73 gulong webkit_ready;
AmarOkb4253242017-07-13 11:21:39 -040074 gulong webkit_send_text;
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050075};
76
77G_DEFINE_TYPE_WITH_PRIVATE(ChatView, chat_view, GTK_TYPE_BOX);
78
79#define CHAT_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CHAT_VIEW_TYPE, ChatViewPrivate))
80
81enum {
82 NEW_MESSAGES_DISPLAYED,
Stepan Salenikovich8043a562016-03-18 13:56:40 -040083 HIDE_VIEW_CLICKED,
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050084 LAST_SIGNAL
85};
86
87static guint chat_view_signals[LAST_SIGNAL] = { 0 };
88
89static void
90chat_view_dispose(GObject *object)
91{
92 ChatView *view;
93 ChatViewPrivate *priv;
94
95 view = CHAT_VIEW(object);
96 priv = CHAT_VIEW_GET_PRIVATE(view);
97
Sébastien Blin55bff9d2017-10-03 15:15:23 -040098 QObject::disconnect(priv->new_interaction_connection);
99 QObject::disconnect(priv->update_interaction_connection);
100 QObject::disconnect(priv->update_add_to_conversations);
aviau039001d2016-09-29 16:39:05 -0400101
102 /* Destroying the box will also destroy its children, and we wouldn't
103 * want that. So we remove the webkit_chat_container from the box. */
104 if (priv->webkit_chat_container) {
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500105 /* disconnect for webkit signals */
106 g_signal_handler_disconnect(priv->webkit_chat_container, priv->webkit_ready);
107 priv->webkit_ready = 0;
AmarOkb4253242017-07-13 11:21:39 -0400108 g_signal_handler_disconnect(priv->webkit_chat_container, priv->webkit_send_text);
109 priv->webkit_send_text = 0;
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500110
aviau039001d2016-09-29 16:39:05 -0400111 gtk_container_remove(
112 GTK_CONTAINER(priv->box_webkit_chat_container),
113 GTK_WIDGET(priv->webkit_chat_container)
114 );
115 priv->webkit_chat_container = nullptr;
116 }
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500117
118 G_OBJECT_CLASS(chat_view_parent_class)->dispose(object);
119}
120
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500121static void
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400122hide_chat_view(G_GNUC_UNUSED GtkWidget *widget, ChatView *self)
123{
124 g_signal_emit(G_OBJECT(self), chat_view_signals[HIDE_VIEW_CLICKED], 0);
125}
126
127static void
Sébastien Blin70dc0b72017-07-31 16:24:41 -0400128display_links_toggled(ChatView *self)
129{
130 auto priv = CHAT_VIEW_GET_PRIVATE(self);
131 if (priv->webkit_chat_container) {
132 webkit_chat_container_set_display_links(
133 WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
134 g_settings_get_boolean(priv->settings, "enable-display-links")
135 );
136 }
137}
138
139static void
Stepan Salenikoviche9933242016-06-21 18:08:48 -0400140placecall_clicked(ChatView *self)
141{
142 auto priv = CHAT_VIEW_GET_PRIVATE(self);
Sébastien Blin5851aaf2017-11-06 16:02:08 -0500143 if (!priv->conversation_) return;
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400144 (*priv->accountInfo_)->conversationModel->placeCall(priv->conversation_->uid);
Stepan Salenikoviche9933242016-06-21 18:08:48 -0400145}
146
147static void
Nicolas Jagerd29ae952017-12-04 16:00:12 -0500148place_audio_call_clicked(ChatView *self)
149{
150 auto priv = CHAT_VIEW_GET_PRIVATE(self);
151
152 if (!priv->conversation_)
153 return;
154
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400155 (*priv->accountInfo_)->conversationModel->placeAudioOnlyCall(priv->conversation_->uid);
Nicolas Jagerd29ae952017-12-04 16:00:12 -0500156}
157
158static void
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400159button_add_to_conversations_clicked(ChatView *self)
Nicolas Jager759faea2017-03-22 16:22:00 -0400160{
161 auto priv = CHAT_VIEW_GET_PRIVATE(self);
Sébastien Blin5851aaf2017-11-06 16:02:08 -0500162 if (!priv->conversation_) return;
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400163 (*priv->accountInfo_)->conversationModel->makePermanent(priv->conversation_->uid);
Nicolas Jager759faea2017-03-22 16:22:00 -0400164}
165
Guillaume Roguez5b137be2018-02-21 10:44:58 -0500166static gchar*
Sébastien Blin8fca81d2018-03-02 14:55:25 -0500167file_to_manipulate(GtkWindow* top_window, bool send)
Guillaume Roguez5b137be2018-02-21 10:44:58 -0500168{
169 GtkWidget* dialog;
170 GtkFileChooserAction action = send? GTK_FILE_CHOOSER_ACTION_OPEN : GTK_FILE_CHOOSER_ACTION_SAVE;
171 gint res;
172 gchar* filename = nullptr;
173
174 dialog = gtk_file_chooser_dialog_new(send? _("Send File") : _("Save File"),
175 top_window,
176 action,
177 _("_Cancel"),
178 GTK_RESPONSE_CANCEL,
179 send? _("_Open"): _("_Save"),
180 GTK_RESPONSE_ACCEPT,
181 nullptr);
182
183 res = gtk_dialog_run (GTK_DIALOG(dialog));
184
185 if (res == GTK_RESPONSE_ACCEPT) {
186 auto chooser = GTK_FILE_CHOOSER(dialog);
187 filename = gtk_file_chooser_get_filename(chooser);
188 }
189 gtk_widget_destroy (dialog);
190
191 return filename;
192}
193
Nicolas Jager759faea2017-03-22 16:22:00 -0400194static void
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400195webkit_chat_container_script_dialog(G_GNUC_UNUSED GtkWidget* webview, gchar *interaction, ChatView* self)
AmarOkb4253242017-07-13 11:21:39 -0400196{
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400197 auto priv = CHAT_VIEW_GET_PRIVATE(self);
198 auto order = std::string(interaction);
Sébastien Blin5851aaf2017-11-06 16:02:08 -0500199 if (!priv->conversation_) return;
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400200 if (order == "ACCEPT") {
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400201 (*priv->accountInfo_)->conversationModel->makePermanent(priv->conversation_->uid);
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400202 } else if (order == "REFUSE") {
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400203 (*priv->accountInfo_)->conversationModel->removeConversation(priv->conversation_->uid);
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400204 } else if (order == "BLOCK") {
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400205 (*priv->accountInfo_)->conversationModel->removeConversation(priv->conversation_->uid, true);
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400206 } else if (order.find("SEND:") == 0) {
207 // Get text body
208 auto toSend = order.substr(std::string("SEND:").size());
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400209 (*priv->accountInfo_)->conversationModel->sendMessage(priv->conversation_->uid, toSend);
Guillaume Roguez5b137be2018-02-21 10:44:58 -0500210 } else if (order.find("SEND_FILE") == 0) {
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400211 if (auto model = (*priv->accountInfo_)->conversationModel.get()) {
Guillaume Roguez5b137be2018-02-21 10:44:58 -0500212 if (auto filename = file_to_manipulate(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(self))), true))
213 model->sendFile(priv->conversation_->uid, filename, g_path_get_basename(filename));
214 }
215 } else if (order.find("ACCEPT_FILE:") == 0) {
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400216 if (auto model = (*priv->accountInfo_)->conversationModel.get()) {
Guillaume Roguez5b137be2018-02-21 10:44:58 -0500217 try {
218 auto interactionId = std::stoull(order.substr(std::string("ACCEPT_FILE:").size()));
Sébastien Blinafbc9592018-02-28 13:52:20 -0500219
220 lrc::api::datatransfer::Info info = {};
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400221 (*priv->accountInfo_)->conversationModel->getTransferInfo(interactionId, info);
Sébastien Blinafbc9592018-02-28 13:52:20 -0500222
Sébastien Blin8fca81d2018-03-02 14:55:25 -0500223 // get prefered directory destination.
224 auto* download_directory_variant = g_settings_get_value(priv->settings, "download-folder");
225 char* download_directory_value;
226 g_variant_get(download_directory_variant, "&s", &download_directory_value);
227 std::string default_download_dir = g_get_user_special_dir (G_USER_DIRECTORY_DOWNLOAD);
228 auto current_value = std::string(download_directory_value);
229 if (current_value.empty()) {
230 g_settings_set_value(priv->settings, "download-folder", g_variant_new("s", default_download_dir.c_str()));
231 }
232 // get full path
233 std::string filename = current_value.empty()? default_download_dir.c_str() : download_directory_value;
234 if (!filename.empty() && filename.back() != '/') filename += "/";
235 filename += info.displayName;
236
237 model->acceptTransfer(priv->conversation_->uid, interactionId, filename);
Guillaume Roguez5b137be2018-02-21 10:44:58 -0500238 } catch (...) {
239 // ignore
240 }
241 }
242 } else if (order.find("REFUSE_FILE:") == 0) {
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400243 if (auto model = (*priv->accountInfo_)->conversationModel.get()) {
Guillaume Roguez5b137be2018-02-21 10:44:58 -0500244 try {
245 auto interactionId = std::stoull(order.substr(std::string("REFUSE_FILE:").size()));
246 model->cancelTransfer(priv->conversation_->uid, interactionId);
247 } catch (...) {
248 // ignore
249 }
250 }
251 } else if (order.find("OPEN_FILE:") == 0) {
252 // Get text body
253 auto filename {"file://" + order.substr(std::string("OPEN_FILE:").size())};
254 filename.erase(std::find_if(filename.rbegin(), filename.rend(),
255 std::not1(std::ptr_fun<int, int>(std::isspace))).base(), filename.end());
256 GError* error = nullptr;
257 if (!gtk_show_uri(nullptr, filename.c_str(), GDK_CURRENT_TIME, &error)) {
258 g_debug("Could not open file: %s", error->message);
259 g_error_free(error);
260 }
AmarOkb4253242017-07-13 11:21:39 -0400261 }
262}
263
264static void
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500265chat_view_init(ChatView *view)
266{
267 gtk_widget_init_template(GTK_WIDGET(view));
268
269 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(view);
Sébastien Blin70dc0b72017-07-31 16:24:41 -0400270 priv->settings = g_settings_new_full(get_ring_schema(), NULL, NULL);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500271
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400272 g_signal_connect(priv->button_close_chatview, "clicked", G_CALLBACK(hide_chat_view), view);
Stepan Salenikoviche9933242016-06-21 18:08:48 -0400273 g_signal_connect_swapped(priv->button_placecall, "clicked", G_CALLBACK(placecall_clicked), view);
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400274 g_signal_connect_swapped(priv->button_add_to_conversations, "clicked", G_CALLBACK(button_add_to_conversations_clicked), view);
Nicolas Jagerd29ae952017-12-04 16:00:12 -0500275 g_signal_connect_swapped(priv->button_place_audio_call, "clicked", G_CALLBACK(place_audio_call_clicked), view);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500276}
277
278static void
279chat_view_class_init(ChatViewClass *klass)
280{
281 G_OBJECT_CLASS(klass)->dispose = chat_view_dispose;
282
283 gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
284 "/cx/ring/RingGnome/chatview.ui");
285
aviau039001d2016-09-29 16:39:05 -0400286 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, box_webkit_chat_container);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500287 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, hbox_chat_info);
288 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, label_peer);
AmarOkcba03952017-07-17 10:13:49 -0400289 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, label_cm);
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400290 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, button_close_chatview);
Stepan Salenikoviche9933242016-06-21 18:08:48 -0400291 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, button_placecall);
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400292 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, button_add_to_conversations);
Nicolas Jagerd29ae952017-12-04 16:00:12 -0500293 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, button_place_audio_call);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500294
295 chat_view_signals[NEW_MESSAGES_DISPLAYED] = g_signal_new (
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400296 "new-interactions-displayed",
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500297 G_TYPE_FROM_CLASS(klass),
298 (GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
299 0,
300 nullptr,
301 nullptr,
302 g_cclosure_marshal_VOID__VOID,
303 G_TYPE_NONE, 0);
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400304
305 chat_view_signals[HIDE_VIEW_CLICKED] = g_signal_new (
306 "hide-view-clicked",
307 G_TYPE_FROM_CLASS(klass),
308 (GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
309 0,
310 nullptr,
311 nullptr,
312 g_cclosure_marshal_VOID__VOID,
313 G_TYPE_NONE, 0);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500314}
315
316static void
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400317print_interaction_to_buffer(ChatView* self, uint64_t interactionId, const lrc::api::interaction::Info& interaction)
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500318{
aviau039001d2016-09-29 16:39:05 -0400319 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
320
Sébastien Blin5851aaf2017-11-06 16:02:08 -0500321 if (!priv->conversation_) return;
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400322 if (interaction.status == lrc::api::interaction::Status::UNREAD)
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400323 (*priv->accountInfo_)->conversationModel->setInteractionRead(priv->conversation_->uid, interactionId);
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400324
325 webkit_chat_container_print_new_interaction(
aviau039001d2016-09-29 16:39:05 -0400326 WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400327 *(*priv->accountInfo_)->conversationModel,
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400328 interactionId,
329 interaction
aviau039001d2016-09-29 16:39:05 -0400330 );
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500331}
332
aviaufc213552016-11-01 12:39:39 -0400333static void
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400334update_interaction(ChatView* self, uint64_t interactionId, const lrc::api::interaction::Info& interaction)
aviaufc213552016-11-01 12:39:39 -0400335{
336 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400337 webkit_chat_container_update_interaction(
338 WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400339 *(*priv->accountInfo_)->conversationModel,
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400340 interactionId,
341 interaction
aviaufc213552016-11-01 12:39:39 -0400342 );
aviaufc213552016-11-01 12:39:39 -0400343}
344
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500345static void
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400346load_participants_images(ChatView *self)
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500347{
348 g_return_if_fail(IS_CHAT_VIEW(self));
349 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
350
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400351 // Contact
Sébastien Blin5851aaf2017-11-06 16:02:08 -0500352 if (!priv->conversation_) return;
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400353 auto contactUri = priv->conversation_->participants.front();
Guillaume Roguezc2095922017-12-14 14:07:10 -0500354 try{
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400355 auto& contact = (*priv->accountInfo_)->contactModel->getContact(contactUri);
Guillaume Roguezc2095922017-12-14 14:07:10 -0500356 if (!contact.profileInfo.avatar.empty()) {
357 webkit_chat_container_set_sender_image(
358 WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400359 (*priv->accountInfo_)->contactModel->getContactProfileId(contactUri),
Guillaume Roguezc2095922017-12-14 14:07:10 -0500360 contact.profileInfo.avatar
361 );
362 }
363 } catch (const std::out_of_range&) {
364 // ContactModel::getContact() exception
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500365 }
366
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400367 // For this account
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400368 if (!(*priv->accountInfo_)->profileInfo.avatar.empty()) {
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400369 webkit_chat_container_set_sender_image(
370 WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400371 (*priv->accountInfo_)->contactModel->getContactProfileId((*priv->accountInfo_)->profileInfo.uri),
372 (*priv->accountInfo_)->profileInfo.avatar
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400373 );
374 }
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500375}
376
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500377static void
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400378print_text_recording(ChatView *self)
379{
380 g_return_if_fail(IS_CHAT_VIEW(self));
381 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
382
Sébastien Blin162eb0f2018-02-27 10:14:53 -0500383 // Read interactions
Sébastien Blin5851aaf2017-11-06 16:02:08 -0500384 if (!priv->conversation_) return;
Sébastien Blin162eb0f2018-02-27 10:14:53 -0500385 for (const auto& it: priv->conversation_->interactions) {
386 if (it.second.status == lrc::api::interaction::Status::UNREAD)
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400387 (*priv->accountInfo_)->conversationModel->setInteractionRead(priv->conversation_->uid, it.first);
Sébastien Blin162eb0f2018-02-27 10:14:53 -0500388 }
389
Sébastien Blin05317a72018-02-21 11:09:16 -0500390 webkit_chat_container_print_history(
391 WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400392 *(*priv->accountInfo_)->conversationModel,
Sébastien Blin05317a72018-02-21 11:09:16 -0500393 priv->conversation_->interactions
394 );
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400395
396 QObject::disconnect(priv->new_interaction_connection);
397}
398
399static void
Hugo Lefeuvre6bbfd872018-05-01 12:41:29 -0400400update_if_banned(ChatView *self)
401{
402 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
403
404 if (!priv->conversation_) return;
405 auto participant = priv->conversation_->participants[0];
406 try {
407 auto contactInfo = (*priv->accountInfo_)->contactModel->getContact(participant);
408 if (contactInfo.isBanned) {
409 gtk_widget_set_sensitive (priv->button_placecall, FALSE);
410 gtk_widget_set_tooltip_text(priv->button_placecall, _("Can't place call with banned contact"));
411 gtk_widget_set_sensitive (priv->button_add_to_conversations, FALSE);
412 gtk_widget_set_tooltip_text(priv->button_add_to_conversations, _("Can't start conversation with banned contact"));
413 gtk_widget_set_sensitive (priv->button_place_audio_call, FALSE);
414 gtk_widget_set_tooltip_text(priv->button_place_audio_call, _("Can't place audio call with banned contact"));
415 webkit_chat_hide_message_bar(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container), true);
416 } else {
417 gtk_widget_set_sensitive (priv->button_placecall, TRUE);
418 gtk_widget_set_sensitive (priv->button_add_to_conversations, TRUE);
419 gtk_widget_set_sensitive (priv->button_place_audio_call, TRUE);
420 webkit_chat_hide_message_bar(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container), false);
421 }
422 } catch (const std::out_of_range&) {
423 // ContactModel::getContact() exception
424 }
425}
426
427static void
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400428update_add_to_conversations(ChatView *self)
Sébastien Blinf3681aa2017-07-19 16:53:49 -0400429{
430 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
431
Sébastien Blin5851aaf2017-11-06 16:02:08 -0500432 if (!priv->conversation_) return;
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400433 auto participant = priv->conversation_->participants[0];
Guillaume Roguezc2095922017-12-14 14:07:10 -0500434 try {
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400435 auto contactInfo = (*priv->accountInfo_)->contactModel->getContact(participant);
Guillaume Roguezc2095922017-12-14 14:07:10 -0500436 if(contactInfo.profileInfo.type != lrc::api::profile::Type::TEMPORARY
437 && contactInfo.profileInfo.type != lrc::api::profile::Type::PENDING)
438 gtk_widget_hide(priv->button_add_to_conversations);
439 } catch (const std::out_of_range&) {
440 // ContactModel::getContact() exception
441 }
Stepan Salenikovich5039c9b2016-02-12 14:09:51 -0500442}
443
444static void
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500445update_contact_methods(ChatView *self)
446{
447 g_return_if_fail(IS_CHAT_VIEW(self));
448 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
Sébastien Blin5851aaf2017-11-06 16:02:08 -0500449 if (!priv->conversation_) return;
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400450 auto contactUri = priv->conversation_->participants.front();
Guillaume Roguezc2095922017-12-14 14:07:10 -0500451 try {
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400452 auto contactInfo = (*priv->accountInfo_)->contactModel->getContact(contactUri);
Guillaume Roguezc2095922017-12-14 14:07:10 -0500453 auto bestId = std::string(contactInfo.registeredName).empty() ? contactInfo.profileInfo.uri : contactInfo.registeredName;
454 if (contactInfo.profileInfo.alias == bestId) {
455 gtk_widget_hide(priv->label_cm);
456 } else {
457 gtk_label_set_text(GTK_LABEL(priv->label_cm), bestId.c_str());
458 gtk_widget_show(priv->label_cm);
459 }
460 } catch (const std::out_of_range&) {
461 // ContactModel::getContact() exception
AmarOkcba03952017-07-17 10:13:49 -0400462 }
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500463}
464
465static void
Sébastien Blinf3681aa2017-07-19 16:53:49 -0400466update_name(ChatView *self)
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500467{
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400468 g_return_if_fail(IS_CHAT_VIEW(self));
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500469 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
Sébastien Blin5851aaf2017-11-06 16:02:08 -0500470 if (!priv->conversation_) return;
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400471 auto contactUri = priv->conversation_->participants.front();
Guillaume Roguezc2095922017-12-14 14:07:10 -0500472 try {
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400473 auto contactInfo = (*priv->accountInfo_)->contactModel->getContact(contactUri);
Guillaume Roguezc2095922017-12-14 14:07:10 -0500474 auto alias = contactInfo.profileInfo.alias;
475 alias.erase(std::remove(alias.begin(), alias.end(), '\r'), alias.end());
476 gtk_label_set_text(GTK_LABEL(priv->label_peer), alias.c_str());
477 } catch (const std::out_of_range&) {
478 // ContactModel::getContact() exception
479 }
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500480}
481
aviau039001d2016-09-29 16:39:05 -0400482static void
aviau039001d2016-09-29 16:39:05 -0400483webkit_chat_container_ready(ChatView* self)
484{
485 /* The webkit chat container has loaded the javascript libraries, we can
486 * now use it. */
487
488 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
489
490 webkit_chat_container_clear(
491 WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container)
492 );
493
Sébastien Blin70dc0b72017-07-31 16:24:41 -0400494 display_links_toggled(self);
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400495 print_text_recording(self);
496 load_participants_images(self);
Sébastien Blin70dc0b72017-07-31 16:24:41 -0400497
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400498 priv->new_interaction_connection = QObject::connect(
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400499 &*(*priv->accountInfo_)->conversationModel, &lrc::api::ConversationModel::newInteraction,
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400500 [self, priv](const std::string& uid, uint64_t interactionId, lrc::api::interaction::Info interaction) {
Sébastien Blin5851aaf2017-11-06 16:02:08 -0500501 if (!priv->conversation_) return;
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400502 if(uid == priv->conversation_->uid) {
503 print_interaction_to_buffer(self, interactionId, interaction);
504 }
505 });
506
507 priv->update_interaction_connection = QObject::connect(
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400508 &*(*priv->accountInfo_)->conversationModel, &lrc::api::ConversationModel::interactionStatusUpdated,
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400509 [self, priv](const std::string& uid, uint64_t msgId, lrc::api::interaction::Info msg) {
Sébastien Blin5851aaf2017-11-06 16:02:08 -0500510 if (!priv->conversation_) return;
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400511 if(uid == priv->conversation_->uid) {
512 update_interaction(self, msgId, msg);
513 }
514 });
515
Sébastien Blin5851aaf2017-11-06 16:02:08 -0500516 if (!priv->conversation_) return;
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400517 auto contactUri = priv->conversation_->participants.front();
Guillaume Roguezc2095922017-12-14 14:07:10 -0500518 try {
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400519 auto contactInfo = (*priv->accountInfo_)->contactModel->getContact(contactUri);
Guillaume Roguezc2095922017-12-14 14:07:10 -0500520 priv->isTemporary_ = contactInfo.profileInfo.type == lrc::api::profile::Type::TEMPORARY
521 || contactInfo.profileInfo.type == lrc::api::profile::Type::PENDING;
522 webkit_chat_container_set_temporary(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container), priv->isTemporary_);
523 auto bestName = contactInfo.profileInfo.alias;
524 if (bestName.empty())
525 bestName = contactInfo.registeredName;
526 if (bestName.empty())
527 bestName = contactInfo.profileInfo.uri;
528 bestName.erase(std::remove(bestName.begin(), bestName.end(), '\r'), bestName.end());
529 webkit_chat_container_set_invitation(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
530 (contactInfo.profileInfo.type == lrc::api::profile::Type::PENDING),
531 bestName);
532 webkit_chat_disable_send_interaction(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
533 (contactInfo.profileInfo.type == lrc::api::profile::Type::SIP)
534 && priv->conversation_->callId.empty());
535 } catch (const std::out_of_range&) {
536 // ContactModel::getContact() exception
537 }
aviau039001d2016-09-29 16:39:05 -0400538}
539
540static void
541build_chat_view(ChatView* self)
542{
543 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
544
545 gtk_container_add(GTK_CONTAINER(priv->box_webkit_chat_container), priv->webkit_chat_container);
546 gtk_widget_show(priv->webkit_chat_container);
547
Sébastien Blinf3681aa2017-07-19 16:53:49 -0400548 update_name(self);
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400549 update_add_to_conversations(self);
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500550 update_contact_methods(self);
Hugo Lefeuvre6bbfd872018-05-01 12:41:29 -0400551 update_if_banned(self);
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500552
553 priv->webkit_ready = g_signal_connect_swapped(
aviau039001d2016-09-29 16:39:05 -0400554 priv->webkit_chat_container,
555 "ready",
556 G_CALLBACK(webkit_chat_container_ready),
557 self
558 );
559
AmarOkb4253242017-07-13 11:21:39 -0400560 priv->webkit_send_text = g_signal_connect(priv->webkit_chat_container,
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400561 "script-dialog",
562 G_CALLBACK(webkit_chat_container_script_dialog),
AmarOkb4253242017-07-13 11:21:39 -0400563 self);
564
aviau039001d2016-09-29 16:39:05 -0400565 if (webkit_chat_container_is_ready(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container)))
aviau039001d2016-09-29 16:39:05 -0400566 webkit_chat_container_ready(self);
aviau039001d2016-09-29 16:39:05 -0400567
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400568 gtk_widget_set_visible(priv->hbox_chat_info, TRUE);
aviau039001d2016-09-29 16:39:05 -0400569}
570
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500571GtkWidget *
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400572chat_view_new (WebKitChatContainer* webkit_chat_container,
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400573 AccountInfoPointer const & accountInfo,
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400574 lrc::api::conversation::Info* conversation)
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500575{
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500576 ChatView *self = CHAT_VIEW(g_object_new(CHAT_VIEW_TYPE, NULL));
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500577
aviau039001d2016-09-29 16:39:05 -0400578 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
579 priv->webkit_chat_container = GTK_WIDGET(webkit_chat_container);
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400580 priv->conversation_ = conversation;
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400581 priv->accountInfo_ = &accountInfo;
aviau039001d2016-09-29 16:39:05 -0400582
583 build_chat_view(self);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500584 return (GtkWidget *)self;
585}
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500586
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400587void
Sébastien Blin8ab4a812018-02-27 13:24:17 -0500588chat_view_update_temporary(ChatView* self, bool showAddButton, bool showInvitation)
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500589{
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400590 g_return_if_fail(IS_CHAT_VIEW(self));
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400591 auto priv = CHAT_VIEW_GET_PRIVATE(self);
592
Sébastien Blin8ab4a812018-02-27 13:24:17 -0500593 priv->isTemporary_ = showAddButton;
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400594 if (!priv->isTemporary_) {
595 gtk_widget_hide(priv->button_add_to_conversations);
596 }
597 webkit_chat_container_set_temporary(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container), priv->isTemporary_);
Sébastien Blin5851aaf2017-11-06 16:02:08 -0500598 if (!priv->conversation_) return;
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400599 auto contactUri = priv->conversation_->participants.front();
Guillaume Roguezc2095922017-12-14 14:07:10 -0500600 try {
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400601 auto contactInfo = (*priv->accountInfo_)->contactModel->getContact(contactUri);
Guillaume Roguezc2095922017-12-14 14:07:10 -0500602 auto bestName = contactInfo.profileInfo.alias;
603 if (bestName.empty())
604 bestName = contactInfo.registeredName;
605 if (bestName.empty())
606 bestName = contactInfo.profileInfo.uri;
607 webkit_chat_container_set_invitation(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
Sébastien Blin8ab4a812018-02-27 13:24:17 -0500608 showInvitation,
Guillaume Roguezc2095922017-12-14 14:07:10 -0500609 bestName);
610 } catch (const std::out_of_range&) {
611 // ContactModel::getContact() exception
612 }
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400613}
614
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400615bool
616chat_view_get_temporary(ChatView *self)
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400617{
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400618 g_return_val_if_fail(IS_CHAT_VIEW(self), false);
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400619 auto priv = CHAT_VIEW_GET_PRIVATE(self);
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400620 return priv->isTemporary_;
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400621}
622
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400623lrc::api::conversation::Info
624chat_view_get_conversation(ChatView *self)
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400625{
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400626 g_return_val_if_fail(IS_CHAT_VIEW(self), lrc::api::conversation::Info());
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400627 auto priv = CHAT_VIEW_GET_PRIVATE(self);
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400628 return *priv->conversation_;
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400629}
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -0400630
631void
632chat_view_set_header_visible(ChatView *self, gboolean visible)
633{
634 auto priv = CHAT_VIEW_GET_PRIVATE(self);
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -0400635 gtk_widget_set_visible(priv->hbox_chat_info, visible);
636}