chat_entry.c (7499B)
1 /* 2 This file is part of GNUnet. 3 Copyright (C) 2021--2024 GNUnet e.V. 4 5 GNUnet is free software: you can redistribute it and/or modify it 6 under the terms of the GNU Affero General Public License as published 7 by the Free Software Foundation, either version 3 of the License, 8 or (at your option) any later version. 9 10 GNUnet is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Affero General Public License for more details. 14 15 You should have received a copy of the GNU Affero General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 SPDX-License-Identifier: AGPL3.0-or-later 19 */ 20 /* 21 * @author Tobias Frisch 22 * @file ui/chat_entry.c 23 */ 24 25 #include "chat_entry.h" 26 27 #include "chat_title.h" 28 #include "message.h" 29 30 #include "../application.h" 31 #include "../contact.h" 32 #include "../discourse.h" 33 #include "../ui.h" 34 35 #include <glib-2.0/glib.h> 36 #include <gnunet/gnunet_chat_lib.h> 37 38 UI_CHAT_ENTRY_Handle* 39 ui_chat_entry_new(MESSENGER_Application *app, 40 struct GNUNET_CHAT_Context *context) 41 { 42 g_assert((app) && (context)); 43 44 UI_CHAT_ENTRY_Handle* handle = g_malloc(sizeof(UI_CHAT_ENTRY_Handle)); 45 46 memset(handle, 0, sizeof(*handle)); 47 48 handle->timestamp = ((time_t) -1); 49 handle->context = context; 50 51 handle->chat = ui_chat_new(app, handle->context); 52 handle->builder = ui_builder_from_resource( 53 application_get_resource_path(app, "ui/chat_entry.ui") 54 ); 55 56 handle->entry_box = GTK_WIDGET( 57 gtk_builder_get_object(handle->builder, "entry_box") 58 ); 59 60 handle->entry_avatar = HDY_AVATAR( 61 gtk_builder_get_object(handle->builder, "entry_avatar") 62 ); 63 64 handle->title_label = GTK_LABEL( 65 gtk_builder_get_object(handle->builder, "title_label") 66 ); 67 68 handle->timestamp_label = GTK_LABEL( 69 gtk_builder_get_object(handle->builder, "timestamp_label") 70 ); 71 72 handle->text_label = GTK_LABEL( 73 gtk_builder_get_object(handle->builder, "text_label") 74 ); 75 76 handle->read_receipt_image = GTK_IMAGE( 77 gtk_builder_get_object(handle->builder, "read_receipt_image") 78 ); 79 80 GNUNET_CHAT_context_set_user_pointer( 81 handle->context, 82 handle 83 ); 84 85 return handle; 86 } 87 88 static void 89 _chat_entry_update_contact(UI_CHAT_ENTRY_Handle *handle, 90 MESSENGER_Application *app, 91 struct GNUNET_CHAT_Contact* contact) 92 { 93 g_assert((handle) && (app)); 94 95 struct GNUNET_CHAT_Contact *prev = g_object_get_qdata( 96 G_OBJECT(handle->entry_avatar), 97 app->quarks.data 98 ); 99 100 if (prev) 101 { 102 contact_remove_name_label_from_info(contact, handle->title_label); 103 contact_remove_name_avatar_from_info(contact, handle->entry_avatar); 104 } 105 106 if (contact) 107 { 108 contact_add_name_label_to_info(contact, handle->title_label); 109 contact_add_name_avatar_to_info(contact, handle->entry_avatar); 110 } 111 112 g_object_set_qdata( 113 G_OBJECT(handle->entry_avatar), 114 app->quarks.data, 115 contact 116 ); 117 } 118 119 void 120 ui_chat_entry_update(UI_CHAT_ENTRY_Handle *handle, 121 MESSENGER_Application *app) 122 { 123 g_assert((handle) && (app)); 124 125 struct GNUNET_CHAT_Contact* contact; 126 struct GNUNET_CHAT_Group* group; 127 128 contact = GNUNET_CHAT_context_get_contact(handle->context); 129 group = GNUNET_CHAT_context_get_group(handle->context); 130 131 const char *icon = "action-unavailable-symbolic"; 132 133 _chat_entry_update_contact(handle, app, contact); 134 135 if (contact) 136 icon = "avatar-default-symbolic"; 137 else if (group) 138 { 139 const char *title = GNUNET_CHAT_group_get_name(group); 140 141 if ((title) && ('#' == *title)) 142 icon = "network-wired-symbolic"; 143 else 144 icon = "system-users-symbolic"; 145 146 ui_label_set_text(handle->title_label, title); 147 ui_avatar_set_text(handle->entry_avatar, title); 148 } 149 150 hdy_avatar_set_icon_name(handle->entry_avatar, icon); 151 152 if (!(handle->chat)) 153 return; 154 155 ui_chat_update(handle->chat, app); 156 157 GList *rows = gtk_container_get_children( 158 GTK_CONTAINER(handle->chat->messages_listbox) 159 ); 160 161 if (!rows) 162 return; 163 164 UI_MESSAGE_Handle *last_message = NULL; 165 for (GList *row = rows; row; row = row->next) 166 { 167 UI_MESSAGE_Handle *message = (UI_MESSAGE_Handle*) g_object_get_qdata( 168 G_OBJECT(row->data), app->quarks.ui 169 ); 170 171 if (!message) 172 continue; 173 174 last_message = message; 175 } 176 177 g_list_free(rows); 178 179 if (!last_message) 180 return; 181 182 handle->timestamp = last_message->timestamp; 183 184 GDateTime *dt_now = g_date_time_new_now_local(); 185 GDateTime *dt_message = g_date_time_new_from_unix_local( 186 (gint64) handle->timestamp 187 ); 188 189 GTimeSpan span = g_date_time_difference(dt_now, dt_message); 190 gchar *time = NULL; 191 192 if (span > 7 * G_TIME_SPAN_DAY) 193 time = g_date_time_format(dt_message, "%F"); 194 else if (span > 2 * G_TIME_SPAN_DAY) 195 time = g_date_time_format(dt_message, "%A"); 196 else if (span > G_TIME_SPAN_DAY) 197 time = g_date_time_format(dt_message, _("Yesterday")); 198 else 199 time = g_date_time_format(dt_message, "%R"); 200 201 g_date_time_unref(dt_now); 202 g_date_time_unref(dt_message); 203 204 const gchar *text = gtk_label_get_text(last_message->text_label); 205 206 if (group) 207 { 208 GString *message_text = g_string_new(NULL); 209 210 g_string_append_printf( 211 message_text, 212 "%s: %s", 213 gtk_label_get_text(last_message->sender_label), 214 text 215 ); 216 217 gtk_label_set_text(handle->text_label, message_text->str); 218 g_string_free(message_text, TRUE); 219 } 220 else 221 gtk_label_set_text(handle->text_label, text); 222 223 if (time) 224 { 225 gtk_label_set_text(handle->timestamp_label, time); 226 g_free(time); 227 } 228 229 gtk_widget_set_visible( 230 GTK_WIDGET(handle->read_receipt_image), 231 last_message->read_receipt_image? gtk_widget_is_visible( 232 GTK_WIDGET(last_message->read_receipt_image) 233 ) : FALSE 234 ); 235 236 gtk_list_box_invalidate_sort(app->ui.messenger.chats_listbox); 237 } 238 239 static enum GNUNET_GenericReturnValue 240 _ui_chat_entry_delete_discourses (void *cls, 241 UNUSED struct GNUNET_CHAT_Context *context, 242 struct GNUNET_CHAT_Discourse *discourse) 243 { 244 g_assert(discourse); 245 246 if (GNUNET_YES == GNUNET_CHAT_discourse_is_open(discourse)) 247 GNUNET_CHAT_discourse_close(discourse); 248 249 discourse_destroy_info(discourse); 250 return GNUNET_YES; 251 } 252 253 void 254 ui_chat_entry_delete(UI_CHAT_ENTRY_Handle *handle) 255 { 256 g_assert(handle); 257 258 ui_chat_delete(handle->chat); 259 260 GNUNET_CHAT_context_iterate_discourses( 261 handle->context, 262 _ui_chat_entry_delete_discourses, 263 NULL 264 ); 265 266 g_object_unref(handle->builder); 267 268 if (handle->update) 269 util_source_remove(handle->update); 270 271 if (handle->context) 272 GNUNET_CHAT_context_set_user_pointer(handle->context, NULL); 273 274 g_free(handle); 275 } 276 277 void 278 ui_chat_entry_dispose(UI_CHAT_ENTRY_Handle *handle, 279 MESSENGER_Application *app) 280 { 281 g_assert((handle) && (handle->entry_box)); 282 283 UI_MESSENGER_Handle *ui = &(app->ui.messenger); 284 285 util_source_remove_by_data(handle); 286 287 ui->chat_entries = g_list_remove(ui->chat_entries, handle); 288 289 gtk_container_remove( 290 GTK_CONTAINER(ui->chats_listbox), 291 gtk_widget_get_parent(handle->entry_box) 292 ); 293 294 _chat_entry_update_contact(handle, app, NULL); 295 296 gtk_container_remove( 297 GTK_CONTAINER(ui->chat_title_stack), 298 handle->chat->title->chat_title_box 299 ); 300 301 gtk_container_remove( 302 GTK_CONTAINER(ui->chats_stack), 303 handle->chat->chat_box 304 ); 305 306 ui_chat_entry_delete(handle); 307 }