messenger-gtk

Gtk+3 graphical user interfaces for GNUnet Messenger
Log | Files | Refs | Submodules | README | LICENSE

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 }