LCOV - code coverage report
Current view: top level - src/plugins/gnome - valent-messages-window.c (source / functions) Coverage Total Hit
Test: Code Coverage Lines: 27.9 % 358 100
Test Date: 2025-10-29 18:02:35 Functions: 35.7 % 28 10
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 11.5 % 182 21

             Branch data     Line data    Source code
       1                 :             : // SPDX-License-Identifier: GPL-3.0-or-later
       2                 :             : // SPDX-FileCopyrightText: Andy Holmes <andrew.g.r.holmes@gmail.com>
       3                 :             : 
       4                 :             : #define G_LOG_DOMAIN "valent-messages-window"
       5                 :             : 
       6                 :             : #include "config.h"
       7                 :             : 
       8                 :             : #include <inttypes.h>
       9                 :             : 
      10                 :             : #include <adwaita.h>
      11                 :             : #include <glib/gi18n.h>
      12                 :             : #include <gtk/gtk.h>
      13                 :             : #include <libebook-contacts/libebook-contacts.h>
      14                 :             : #include <valent.h>
      15                 :             : 
      16                 :             : #include "valent-contact-page.h"
      17                 :             : #include "valent-contact-row.h"
      18                 :             : #include "valent-conversation-page.h"
      19                 :             : #include "valent-ui-utils-private.h"
      20                 :             : #include "valent-message-row.h"
      21                 :             : 
      22                 :             : #include "valent-messages-window.h"
      23                 :             : 
      24                 :             : 
      25                 :             : struct _ValentMessagesWindow
      26                 :             : {
      27                 :             :   AdwApplicationWindow    parent_instance;
      28                 :             : 
      29                 :             :   GListModel             *contacts;
      30                 :             :   ValentContactsAdapter  *contacts_adapter;
      31                 :             :   GListModel             *messages;
      32                 :             :   ValentMessagesAdapter  *messages_adapter;
      33                 :             :   GCancellable           *search;
      34                 :             : 
      35                 :             :   /* template */
      36                 :             :   AdwNavigationSplitView *main_view;
      37                 :             :   AdwHeaderBar           *sidebar_header;
      38                 :             :   GtkListBox             *sidebar_list;
      39                 :             :   AdwNavigationView      *content_view;
      40                 :             :   AdwNavigationPage      *search_page;
      41                 :             :   GtkWidget              *search_entry;
      42                 :             :   GtkListBox             *search_list;
      43                 :             :   AdwNavigationPage      *contact_page;
      44                 :             :   AdwDialog              *details_dialog;
      45                 :             :   GtkListBox             *medium_list;
      46                 :             : };
      47                 :             : 
      48                 :             : void   valent_messages_window_set_active_message (ValentMessagesWindow *window,
      49                 :             :                                                   ValentMessage        *message);
      50                 :             : void   valent_messages_window_set_active_thread  (ValentMessagesWindow *window,
      51                 :             :                                                   const char           *iri);
      52                 :             : 
      53   [ +  +  +  - ]:           5 : G_DEFINE_FINAL_TYPE (ValentMessagesWindow, valent_messages_window, ADW_TYPE_APPLICATION_WINDOW)
      54                 :             : 
      55                 :             : typedef enum {
      56                 :             :   PROP_MESSAGES = 1,
      57                 :             : } ValentMessagesWindowProperty;
      58                 :             : 
      59                 :             : static GParamSpec *properties[PROP_MESSAGES + 1] = { NULL, };
      60                 :             : 
      61                 :             : 
      62                 :             : /*
      63                 :             :  * Contact Lookup
      64                 :             :  */
      65                 :             : static void
      66                 :           0 : lookup_contact_cb (ValentContactsAdapter *adapter,
      67                 :             :                    GAsyncResult          *result,
      68                 :             :                    ValentMessageRow      *row)
      69                 :             : {
      70                 :           0 :   g_autoptr (EContact) contact = NULL;
      71                 :           0 :   g_autoptr (GError) error = NULL;
      72                 :             : 
      73                 :           0 :   contact = valent_contacts_adapter_reverse_lookup_finish (adapter, result, &error);
      74         [ #  # ]:           0 :   if (contact == NULL)
      75                 :             :     {
      76         [ #  # ]:           0 :       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
      77                 :           0 :         g_warning ("%s(): %s", G_STRFUNC, error->message);
      78                 :             : 
      79         [ #  # ]:           0 :       return;
      80                 :             :     }
      81                 :             : 
      82         [ #  # ]:           0 :   valent_message_row_set_contact (row, contact);
      83                 :             : }
      84                 :             : 
      85                 :             : static void
      86                 :           0 : search_contacts_cb (ValentContactsAdapter *adapter,
      87                 :             :                     GAsyncResult          *result,
      88                 :             :                     ValentMessagesWindow  *self)
      89                 :             : {
      90                 :           0 :   g_autoptr (GListModel) contacts = NULL;
      91                 :           0 :   unsigned int n_contacts;
      92   [ #  #  #  # ]:           0 :   g_autoptr (GError) error = NULL;
      93                 :             : 
      94                 :           0 :   contacts = valent_contacts_adapter_search_finish (adapter, result, &error);
      95         [ #  # ]:           0 :   if (error != NULL)
      96                 :             :     {
      97         [ #  # ]:           0 :       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
      98                 :           0 :         g_warning ("%s(): %s", G_STRFUNC, error->message);
      99                 :             : 
     100         [ #  # ]:           0 :       return;
     101                 :             :     }
     102                 :             : 
     103                 :           0 :   n_contacts = g_list_model_get_n_items (contacts);
     104         [ #  # ]:           0 :   for (unsigned int i = 0; i < n_contacts; i++)
     105                 :             :     {
     106                 :           0 :       g_autoptr (EContact) contact = g_list_model_get_item (contacts, i);
     107                 :           0 :       GtkWidget *row;
     108   [ #  #  #  # ]:           0 :       g_autolist (EVCardAttribute) attrs = NULL;
     109                 :           0 :       g_autofree char *number = NULL;
     110                 :           0 :       unsigned int n_attrs;
     111                 :             : 
     112                 :             : #if EDS_CHECK_VERSION (3, 59, 0)
     113                 :             :       attrs = e_vcard_get_attributes_by_name (E_VCARD (contact), EVC_TEL);
     114                 :             : #else
     115                 :           0 :       attrs = e_contact_get_attributes (contact, E_CONTACT_TEL);
     116                 :             : #endif
     117                 :           0 :       n_attrs = g_list_length (attrs);
     118         [ #  # ]:           0 :       if (n_attrs == 0)
     119                 :           0 :         continue;
     120                 :             : 
     121                 :           0 :       g_object_get (contact, "primary-phone", &number, NULL);
     122   [ #  #  #  # ]:           0 :       if (number == NULL || *number == '\0')
     123                 :             :         {
     124                 :           0 :           g_free (number);
     125                 :           0 :           number = e_vcard_attribute_get_value ((EVCardAttribute *)attrs->data);
     126                 :             :         }
     127                 :             : 
     128         [ #  # ]:           0 :       if (n_attrs > 1)
     129                 :             :         {
     130                 :           0 :           g_autofree char *tmp = g_steal_pointer (&number);
     131                 :             : 
     132                 :           0 :           number = g_strdup_printf (ngettext ("%s and %u more…",
     133                 :             :                                               "%s and %u more…",
     134                 :             :                                               n_attrs - 1),
     135                 :             :                                     tmp, n_attrs - 1);
     136                 :             :         }
     137                 :             : 
     138                 :           0 :       row = g_object_new (VALENT_TYPE_CONTACT_ROW,
     139                 :             :                           "contact",        contact,
     140                 :             :                           "contact-medium", number,
     141                 :             :                           NULL);
     142                 :             : 
     143         [ #  # ]:           0 :       if (n_attrs > 1)
     144                 :             :         {
     145                 :           0 :           gtk_accessible_update_state (GTK_ACCESSIBLE (row),
     146                 :             :                                        GTK_ACCESSIBLE_STATE_EXPANDED, FALSE,
     147                 :             :                                        -1);
     148                 :             :         }
     149                 :             : 
     150                 :           0 :       gtk_list_box_insert (self->search_list, row, -1);
     151                 :             :     }
     152                 :             : }
     153                 :             : 
     154                 :             : static void
     155                 :           0 : search_messages_cb (ValentMessagesAdapter *adapter,
     156                 :             :                     GAsyncResult          *result,
     157                 :             :                     ValentMessagesWindow  *self)
     158                 :             : {
     159                 :           0 :   g_autoptr (GListModel) messages = NULL;
     160                 :           0 :   unsigned int n_messages;
     161                 :           0 :   g_autoptr (GError) error = NULL;
     162                 :             : 
     163                 :           0 :   messages = valent_messages_adapter_search_finish (adapter, result, &error);
     164         [ #  # ]:           0 :   if (messages == NULL)
     165                 :             :     {
     166         [ #  # ]:           0 :       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
     167                 :           0 :         g_warning ("%s(): %s", G_STRFUNC, error->message);
     168                 :             : 
     169         [ #  # ]:           0 :       return;
     170                 :             :     }
     171                 :             : 
     172                 :           0 :   n_messages = g_list_model_get_n_items (messages);
     173         [ #  # ]:           0 :   for (unsigned int i = 0; i < n_messages; i++)
     174                 :             :     {
     175                 :           0 :       g_autoptr (ValentMessage) message = g_list_model_get_item (messages, i);
     176                 :           0 :       GtkWidget *row;
     177                 :           0 :       const char *medium;
     178                 :             : 
     179                 :           0 :       row = g_object_new (VALENT_TYPE_MESSAGE_ROW,
     180                 :             :                           "message", message,
     181                 :             :                           NULL);
     182                 :           0 :       gtk_list_box_insert (self->search_list, row, -1);
     183                 :             : 
     184                 :           0 :       medium = valent_message_get_sender (message);
     185   [ #  #  #  # ]:           0 :       if (medium == NULL || *medium == '\0')
     186                 :             :         {
     187                 :           0 :           const char * const *recipients = NULL;
     188                 :             : 
     189                 :           0 :           recipients = valent_message_get_recipients (message);
     190         [ #  # ]:           0 :           if (recipients != NULL)
     191                 :           0 :             medium = recipients[0];
     192                 :             :         }
     193                 :             : 
     194   [ #  #  #  # ]:           0 :       if (medium != NULL && *medium != '\0')
     195                 :             :         {
     196         [ #  # ]:           0 :           g_autoptr (GCancellable) cancellable = NULL;
     197                 :             : 
     198                 :           0 :           cancellable = g_cancellable_new ();
     199                 :           0 :           g_signal_connect_object (row,
     200                 :             :                                    "destroy",
     201                 :             :                                    G_CALLBACK (g_cancellable_cancel),
     202                 :             :                                    cancellable,
     203                 :             :                                    G_CONNECT_SWAPPED);
     204         [ #  # ]:           0 :           valent_contacts_adapter_reverse_lookup (self->contacts_adapter,
     205                 :             :                                                   medium,
     206                 :             :                                                   cancellable,
     207                 :             :                                                   (GAsyncReadyCallback) lookup_contact_cb,
     208                 :             :                                                   row);
     209                 :             :         }
     210                 :             :     }
     211                 :             : }
     212                 :             : 
     213                 :             : /*
     214                 :             :  * Message Search
     215                 :             :  */
     216                 :             : static void
     217                 :           0 : search_header_func (GtkListBoxRow *row,
     218                 :             :                     GtkListBoxRow *before,
     219                 :             :                     gpointer       user_data)
     220                 :             : {
     221         [ #  # ]:           0 :   if (VALENT_IS_MESSAGE_ROW (row))
     222                 :             :     {
     223   [ #  #  #  # ]:           0 :       if (before == NULL || !VALENT_IS_MESSAGE_ROW (before))
     224                 :             :         {
     225                 :           0 :           GtkWidget *label;
     226                 :             : 
     227                 :           0 :           label = g_object_new (GTK_TYPE_LABEL,
     228                 :             :                                 "label",        _("Conversations"),
     229                 :             :                                 "halign",       GTK_ALIGN_START,
     230                 :             :                                 "margin-bottom", 6,
     231                 :             :                                 "margin-end",    6,
     232                 :             :                                 "margin-start",  6,
     233                 :             :                                 "margin-top",    6,
     234                 :             :                                 NULL);
     235                 :           0 :           gtk_widget_add_css_class (label, "dim-label");
     236                 :           0 :           gtk_widget_add_css_class (label, "caption-heading");
     237                 :           0 :           gtk_list_box_row_set_header (row, label);
     238                 :             :         }
     239                 :             :     }
     240         [ #  # ]:           0 :   else if (!VALENT_IS_CONTACT_ROW (before))
     241                 :             :     {
     242                 :           0 :       GtkWidget *label;
     243                 :             : 
     244                 :           0 :       label = g_object_new (GTK_TYPE_LABEL,
     245                 :             :                             "label",        _("Contacts"),
     246                 :             :                             "halign",       GTK_ALIGN_START,
     247                 :             :                             "margin-bottom", 6,
     248                 :             :                             "margin-end",    6,
     249                 :             :                             "margin-start",  6,
     250                 :             :                             "margin-top",    12, // +6 for section spacing
     251                 :             :                             NULL);
     252                 :           0 :       gtk_widget_add_css_class (label, "dim-label");
     253                 :           0 :       gtk_widget_add_css_class (label, "caption-heading");
     254                 :           0 :       gtk_list_box_row_set_header (row, label);
     255                 :             :     }
     256                 :           0 : }
     257                 :             : 
     258                 :             : static void
     259                 :           0 : on_search_changed (GtkSearchEntry       *entry,
     260                 :             :                    ValentMessagesWindow *self)
     261                 :             : {
     262                 :           0 :   GtkWidget *child;
     263                 :           0 :   const char *search_query;
     264                 :             : 
     265                 :             :   /* Clear previous results
     266                 :             :    */
     267                 :           0 :   g_cancellable_cancel (self->search);
     268         [ #  # ]:           0 :   g_clear_object (&self->search);
     269                 :             : 
     270         [ #  # ]:           0 :   while ((child = gtk_widget_get_first_child (GTK_WIDGET (self->search_list))))
     271                 :           0 :     gtk_list_box_remove (self->search_list, child);
     272                 :             : 
     273                 :           0 :   search_query = gtk_editable_get_text (GTK_EDITABLE (entry));
     274   [ #  #  #  # ]:           0 :   if (search_query == NULL || *search_query == '\0')
     275                 :             :     return;
     276                 :             : 
     277                 :             :   /* Search messages
     278                 :             :    */
     279                 :           0 :   self->search = g_cancellable_new ();
     280                 :           0 :   valent_messages_adapter_search (self->messages_adapter,
     281                 :             :                                   search_query,
     282                 :             :                                   self->search,
     283                 :             :                                   (GAsyncReadyCallback)search_messages_cb,
     284                 :             :                                   self);
     285                 :           0 :   valent_contacts_adapter_search (self->contacts_adapter,
     286                 :             :                                   search_query,
     287                 :             :                                   self->search,
     288                 :             :                                   (GAsyncReadyCallback)search_contacts_cb,
     289                 :             :                                   self);
     290                 :             : }
     291                 :             : 
     292                 :             : static void
     293                 :           0 : lookup_thread_cb (ValentMessagesAdapter *adapter,
     294                 :             :                   GAsyncResult          *result,
     295                 :             :                   ValentMessagesWindow  *self)
     296                 :             : {
     297                 :           0 :   g_autofree char *iri = NULL;
     298                 :           0 :   g_autoptr (GError) error = NULL;
     299                 :             : 
     300                 :           0 :   iri = g_task_propagate_pointer (G_TASK (result), &error);
     301         [ #  # ]:           0 :   if (iri == NULL)
     302                 :             :     {
     303         [ #  # ]:           0 :       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
     304                 :           0 :         g_warning ("%s(): %s", G_STRFUNC, error->message);
     305                 :             :       else
     306                 :           0 :         g_debug ("%s(): no thread found for contact", G_STRFUNC);
     307                 :             : 
     308         [ #  # ]:           0 :       return;
     309                 :             :     }
     310                 :             : 
     311                 :           0 :   adw_navigation_view_pop (self->content_view);
     312         [ #  # ]:           0 :   valent_messages_window_set_active_thread (self, iri);
     313                 :             : }
     314                 :             : 
     315                 :             : static void
     316                 :           0 : on_contact_selected (ValentContactPage    *page,
     317                 :             :                      EContact             *contact,
     318                 :             :                      const char           *target,
     319                 :             :                      ValentMessagesWindow *self)
     320                 :             : {
     321                 :           0 :   g_autoptr (GCancellable) cancellable = NULL;
     322                 :             : 
     323                 :           0 :   cancellable = g_cancellable_new ();
     324                 :           0 :   g_signal_connect_object (self,
     325                 :             :                            "destroy",
     326                 :             :                            G_CALLBACK (g_cancellable_cancel),
     327                 :             :                            cancellable,
     328                 :             :                            G_CONNECT_SWAPPED);
     329                 :             : 
     330         [ #  # ]:           0 :   valent_messages_adapter_lookup_thread (self->messages_adapter,
     331                 :           0 :                                          ((const char * const []){ target, NULL }),
     332                 :             :                                          cancellable,
     333                 :             :                                          (GAsyncReadyCallback) lookup_thread_cb,
     334                 :             :                                          self);
     335                 :             : 
     336                 :             :   // FIXME: loading indicator
     337                 :           0 : }
     338                 :             : 
     339                 :             : static void
     340                 :           0 : on_contact_medium_selected (GtkListBoxRow        *row,
     341                 :             :                             ValentMessagesWindow *self)
     342                 :             : {
     343                 :           0 :   g_autoptr (GCancellable) cancellable = NULL;
     344         [ #  # ]:           0 :   g_autofree char *medium = NULL;
     345                 :             : 
     346         [ #  # ]:           0 :   g_assert (VALENT_IS_CONTACT_ROW (row));
     347                 :             : 
     348                 :           0 :   cancellable = g_cancellable_new ();
     349                 :           0 :   g_signal_connect_object (self,
     350                 :             :                            "destroy",
     351                 :             :                            G_CALLBACK (g_cancellable_cancel),
     352                 :             :                            cancellable,
     353                 :             :                            G_CONNECT_SWAPPED);
     354                 :             : 
     355                 :           0 :   g_object_get (row, "contact-medium", &medium, NULL);
     356                 :           0 :   valent_messages_adapter_lookup_thread (self->messages_adapter,
     357                 :           0 :                                          ((const char * const []){ medium, NULL }),
     358                 :             :                                          cancellable,
     359                 :             :                                          (GAsyncReadyCallback) lookup_thread_cb,
     360                 :             :                                          self);
     361                 :             : 
     362                 :           0 :   adw_dialog_close (self->details_dialog);
     363                 :           0 : }
     364                 :             : 
     365                 :             : static void
     366                 :           0 : on_contact_row_collapsed (AdwDialog *dialog,
     367                 :             :                           GtkWidget *row)
     368                 :             : {
     369                 :           0 :   gtk_accessible_reset_relation (GTK_ACCESSIBLE (row),
     370                 :             :                                  GTK_ACCESSIBLE_RELATION_CONTROLS);
     371                 :           0 :   gtk_accessible_update_state (GTK_ACCESSIBLE (row),
     372                 :             :                                GTK_ACCESSIBLE_STATE_EXPANDED, FALSE,
     373                 :             :                                -1);
     374                 :           0 :   g_signal_handlers_disconnect_by_func (dialog, on_contact_row_collapsed, row);
     375                 :           0 : }
     376                 :             : 
     377                 :             : static void
     378                 :           0 : on_search_selected (GtkListBox           *box,
     379                 :             :                     GtkListBoxRow        *row,
     380                 :             :                     ValentMessagesWindow *self)
     381                 :             : {
     382         [ #  # ]:           0 :   g_assert (VALENT_IS_MESSAGES_WINDOW (self));
     383                 :             : 
     384         [ #  # ]:           0 :   if (VALENT_IS_MESSAGE_ROW (row))
     385                 :             :     {
     386                 :           0 :       ValentMessage *message;
     387                 :             : 
     388                 :           0 :       message = valent_message_row_get_message (VALENT_MESSAGE_ROW (row));
     389                 :           0 :       valent_messages_window_set_active_message (self, message);
     390                 :             : 
     391                 :             :       /* Reset the search
     392                 :             :        */
     393                 :           0 :       adw_navigation_view_pop (self->content_view);
     394                 :           0 :       gtk_editable_set_text (GTK_EDITABLE (self->search_entry), "");
     395                 :             :     }
     396         [ #  # ]:           0 :   else if (VALENT_IS_CONTACT_ROW (row))
     397                 :             :     {
     398                 :           0 :       g_autolist (EVCardAttribute) attrs = NULL;
     399                 :           0 :       EContact *contact;
     400                 :             : 
     401                 :           0 :       contact = valent_contact_row_get_contact (VALENT_CONTACT_ROW (row));
     402                 :             : #if EDS_CHECK_VERSION (3, 59, 0)
     403                 :             :       attrs = e_vcard_get_attributes_by_name (E_VCARD (contact), EVC_TEL);
     404                 :             : #else
     405                 :           0 :       attrs = e_contact_get_attributes (contact, E_CONTACT_TEL);
     406                 :             : #endif
     407         [ #  # ]:           0 :       if (g_list_length (attrs) == 1)
     408                 :             :         {
     409                 :           0 :           on_contact_medium_selected (row, self);
     410                 :             : 
     411                 :             :           /* Reset the search
     412                 :             :            */
     413                 :           0 :           adw_navigation_view_pop (self->content_view);
     414                 :           0 :           gtk_editable_set_text (GTK_EDITABLE (self->search_entry), "");
     415                 :             :         }
     416                 :             :       else
     417                 :             :         {
     418                 :           0 :           gtk_list_box_remove_all (GTK_LIST_BOX (self->medium_list));
     419         [ #  # ]:           0 :           for (const GList *iter = attrs; iter; iter = iter->next)
     420                 :             :             {
     421                 :           0 :               EVCardAttribute *attr = iter->data;
     422                 :           0 :               GtkWidget *medium_row;
     423                 :           0 :               g_autofree char *number = NULL;
     424                 :           0 :               const char *type_ = NULL;
     425                 :             : 
     426         [ #  # ]:           0 :               if (e_vcard_attribute_has_type (attr, "WORK"))
     427                 :           0 :                 type_ = _("Work");
     428         [ #  # ]:           0 :               else if (e_vcard_attribute_has_type (attr, "CELL"))
     429                 :           0 :                 type_ = _("Mobile");
     430         [ #  # ]:           0 :               else if (e_vcard_attribute_has_type (attr, "HOME"))
     431                 :           0 :                 type_ = _("Home");
     432                 :             :               else
     433                 :           0 :                 type_ = _("Other");
     434                 :             : 
     435                 :           0 :               number = e_vcard_attribute_get_value (attr);
     436                 :           0 :               medium_row = g_object_new (ADW_TYPE_ACTION_ROW,
     437                 :             :                                          "activatable", TRUE,
     438                 :             :                                          "title",       number,
     439                 :             :                                          "subtitle",    type_,
     440                 :             :                                          NULL);
     441                 :           0 :               g_object_set_data_full (G_OBJECT (medium_row),
     442                 :             :                                       "contact",
     443                 :             :                                       g_object_ref (contact),
     444                 :             :                                       g_object_unref);
     445                 :           0 :               g_signal_connect_object (medium_row,
     446                 :             :                                        "activated",
     447                 :             :                                        G_CALLBACK (on_contact_medium_selected),
     448                 :             :                                        self,
     449                 :             :                                        G_CONNECT_DEFAULT);
     450                 :             : 
     451                 :           0 :               gtk_list_box_insert (self->medium_list, medium_row, -1);
     452                 :             :             }
     453                 :             : 
     454                 :             :           /* Present the dialog and match the expanded state
     455                 :             :            */
     456                 :           0 :           gtk_accessible_update_state (GTK_ACCESSIBLE (row),
     457                 :             :                                        GTK_ACCESSIBLE_STATE_EXPANDED, TRUE,
     458                 :             :                                        -1);
     459                 :           0 :           gtk_accessible_update_relation (GTK_ACCESSIBLE (row),
     460                 :             :                                           GTK_ACCESSIBLE_RELATION_CONTROLS, self->details_dialog, NULL,
     461                 :             :                                           -1);
     462                 :           0 :           g_signal_connect_object (self->details_dialog,
     463                 :             :                                    "closed",
     464                 :             :                                    G_CALLBACK (on_contact_row_collapsed),
     465                 :             :                                    row,
     466                 :             :                                    G_CONNECT_DEFAULT);
     467                 :           0 :           adw_dialog_present (self->details_dialog, GTK_WIDGET (self));
     468                 :             :         }
     469                 :             :     }
     470                 :           0 : }
     471                 :             : 
     472                 :             : /*
     473                 :             :  * Sidebar
     474                 :             :  */
     475                 :             : static GtkWidget *
     476                 :           1 : sidebar_list_create (gpointer item,
     477                 :             :                      gpointer user_data)
     478                 :             : {
     479                 :           1 :   ValentMessagesWindow *window = VALENT_MESSAGES_WINDOW (user_data);
     480                 :           1 :   GListModel *thread = G_LIST_MODEL (item);
     481                 :           2 :   g_autoptr (ValentMessage) message = NULL;
     482                 :           1 :   ValentMessageBox box = VALENT_MESSAGE_BOX_ALL;
     483                 :           1 :   const char * const *recipients = NULL;
     484                 :           1 :   const char *medium = NULL;
     485                 :           1 :   GtkWidget *row;
     486                 :             : 
     487                 :           1 :   g_object_get (thread, "latest-message", &message, NULL);
     488                 :           1 :   row = g_object_new (VALENT_TYPE_MESSAGE_ROW,
     489                 :             :                       "message", message,
     490                 :             :                       NULL);
     491                 :           1 :   g_object_bind_property (thread, "latest-message",
     492                 :             :                           row,    "message",
     493                 :             :                           G_BINDING_DEFAULT);
     494                 :           1 :   g_object_set_data_full (G_OBJECT (row),
     495                 :             :                           "valent-message-thread",
     496                 :             :                           g_object_ref (thread),
     497                 :             :                           g_object_unref);
     498                 :             : 
     499                 :             :   // TODO: participant-based avatar for sidebar rows
     500         [ +  - ]:           1 :   if (message != NULL)
     501                 :           1 :     box = valent_message_get_box (message);
     502                 :             : 
     503         [ -  + ]:           1 :   if (box == VALENT_MESSAGE_BOX_INBOX)
     504                 :             :     {
     505                 :           0 :       medium = valent_message_get_sender (message);
     506                 :             :     }
     507         [ -  + ]:           1 :   else if (box == VALENT_MESSAGE_BOX_SENT)
     508                 :             :     {
     509                 :           1 :       recipients = valent_message_get_recipients (message);
     510         [ -  + ]:           1 :       if (recipients != NULL)
     511                 :           1 :         medium = recipients[0];
     512                 :             :     }
     513                 :             : 
     514   [ -  +  -  - ]:           1 :   if (window->contacts_adapter != NULL &&
     515         [ #  # ]:           0 :       medium != NULL && *medium != '\0')
     516                 :             :     {
     517         [ +  - ]:           1 :       g_autoptr (GCancellable) cancellable = NULL;
     518                 :             : 
     519                 :           0 :       cancellable = g_cancellable_new ();
     520                 :           0 :       g_signal_connect_object (row,
     521                 :             :                                "destroy",
     522                 :             :                                G_CALLBACK (g_cancellable_cancel),
     523                 :             :                                cancellable,
     524                 :             :                                G_CONNECT_SWAPPED);
     525         [ #  # ]:           0 :       valent_contacts_adapter_reverse_lookup (window->contacts_adapter,
     526                 :             :                                               medium,
     527                 :             :                                               cancellable,
     528                 :             :                                               (GAsyncReadyCallback) lookup_contact_cb,
     529                 :             :                                               row);
     530                 :             :     }
     531                 :             :   else
     532                 :             :     {
     533                 :           2 :       g_autoptr (EContact) contact = NULL;
     534                 :             : 
     535                 :           1 :       contact = e_contact_new ();
     536                 :           1 :       e_contact_set (contact, E_CONTACT_FULL_NAME, _("Unknown"));
     537                 :           1 :       e_contact_set (contact, E_CONTACT_PHONE_OTHER, _("Unknown sender"));
     538         [ +  - ]:           1 :       valent_message_row_set_contact (VALENT_MESSAGE_ROW (row), contact);
     539                 :             :     }
     540                 :             : 
     541         [ +  - ]:           1 :   return row;
     542                 :             : }
     543                 :             : 
     544                 :             : static GtkWidget *
     545                 :           0 : valent_messages_window_ensure_conversation (ValentMessagesWindow *window,
     546                 :             :                                             const char           *thread_iri)
     547                 :             : {
     548                 :           0 :   AdwNavigationPage *conversation;
     549                 :             : 
     550                 :           0 :   conversation = adw_navigation_view_find_page (window->content_view, thread_iri);
     551         [ #  # ]:           0 :   if (conversation == NULL)
     552                 :             :     {
     553                 :           0 :       conversation = g_object_new (VALENT_TYPE_CONVERSATION_PAGE,
     554                 :             :                                    "tag",      thread_iri,
     555                 :             :                                    "contacts", window->contacts_adapter,
     556                 :             :                                    "messages", window->messages_adapter,
     557                 :             :                                    "iri",      thread_iri,
     558                 :             :                                    NULL);
     559                 :           0 :       adw_navigation_view_push (window->content_view, conversation);
     560                 :             :     }
     561                 :             :   else
     562                 :             :     {
     563                 :           0 :       adw_navigation_view_pop_to_page (window->content_view, conversation);
     564                 :             :     }
     565                 :             : 
     566                 :           0 :   return GTK_WIDGET (conversation);
     567                 :             : }
     568                 :             : 
     569                 :             : static void
     570                 :           0 : on_conversation_activated (GtkListBox           *box,
     571                 :             :                            GtkListBoxRow        *row,
     572                 :             :                            ValentMessagesWindow *self)
     573                 :             : {
     574                 :           0 :   GtkWidget *page;
     575                 :           0 :   EContact *contact;
     576                 :           0 :   ValentMessage *message;
     577                 :           0 :   const char *sender;
     578                 :           0 :   int64_t thread_id;
     579                 :           0 :   const char *adapter_urn = NULL;
     580                 :           0 :   g_autofree char *thread_urn = NULL;
     581                 :             : 
     582         [ #  # ]:           0 :   g_assert (VALENT_IS_MESSAGES_WINDOW (self));
     583         [ #  # ]:           0 :   g_assert (VALENT_IS_MESSAGE_ROW (row));
     584                 :             : 
     585         [ #  # ]:           0 :   if (row == NULL)
     586                 :           0 :     return;
     587                 :             : 
     588                 :             :   // TODO: use IRI
     589                 :           0 :   contact = valent_message_row_get_contact (VALENT_MESSAGE_ROW (row));
     590                 :           0 :   message = valent_message_row_get_message (VALENT_MESSAGE_ROW (row));
     591                 :           0 :   thread_id = valent_message_get_thread_id (message);
     592                 :           0 :   adapter_urn = valent_resource_get_iri (VALENT_RESOURCE (self->messages_adapter));
     593                 :           0 :   thread_urn = g_strdup_printf ("%s:%"PRId64, adapter_urn, thread_id);
     594                 :             : 
     595                 :           0 :   page = valent_messages_window_ensure_conversation (self, thread_urn);
     596                 :           0 :   sender = valent_message_get_sender (message);
     597   [ #  #  #  # ]:           0 :   if (sender != NULL && *sender != '\0')
     598                 :             :     {
     599                 :           0 :       valent_conversation_page_add_participant (VALENT_CONVERSATION_PAGE (page),
     600                 :             :                                                 contact,
     601                 :             :                                                 sender);
     602                 :             :     }
     603                 :             : 
     604                 :           0 :   adw_navigation_split_view_set_show_content (self->main_view, TRUE);
     605                 :             : }
     606                 :             : 
     607                 :             : /*
     608                 :             :  * Message Source
     609                 :             :  */
     610                 :             : static void
     611                 :           1 : on_selected_item (GObject              *object,
     612                 :             :                   GParamSpec           *pspec,
     613                 :             :                   ValentMessagesWindow *self)
     614                 :             : {
     615                 :           1 :   ValentMessagesAdapter *adapter = NULL;
     616                 :           1 :   GObject *owner = NULL;
     617                 :           1 :   unsigned int n_items = 0;
     618                 :             : 
     619         [ -  + ]:           1 :   g_assert (VALENT_IS_MESSAGES_WINDOW (self));
     620                 :             : 
     621                 :           1 :   adapter = gtk_drop_down_get_selected_item (GTK_DROP_DOWN (object));
     622         [ +  - ]:           1 :   if (!g_set_object (&self->messages_adapter, adapter))
     623                 :             :     return;
     624                 :             : 
     625                 :             :   // HACK: try to find a matching contacts adapter
     626                 :           1 :   owner = valent_resource_get_source (VALENT_RESOURCE (adapter));
     627                 :           1 :   n_items = g_list_model_get_n_items (self->contacts);
     628         [ +  + ]:           2 :   for (unsigned int i = 0; i < n_items; i++)
     629                 :             :     {
     630                 :           1 :       g_autoptr (ValentContactsAdapter) item = NULL;
     631                 :           1 :       GObject *item_owner = NULL;
     632                 :             : 
     633                 :           1 :       item = g_list_model_get_item (self->contacts, i);
     634                 :           1 :       item_owner = valent_resource_get_source (VALENT_RESOURCE (item));
     635         [ -  + ]:           1 :       if (item_owner == owner)
     636                 :             :         {
     637                 :           0 :           g_set_object (&self->contacts_adapter, item);
     638         [ #  # ]:           0 :           break;
     639                 :             :         }
     640                 :             :     }
     641                 :             : 
     642                 :           1 :   gtk_list_box_bind_model (self->sidebar_list,
     643                 :             :                            G_LIST_MODEL (adapter),
     644                 :             :                            sidebar_list_create,
     645                 :             :                            self,
     646                 :             :                            NULL);
     647                 :             : }
     648                 :             : 
     649                 :             : /*
     650                 :             :  * GActions
     651                 :             :  */
     652                 :             : static void
     653                 :           0 : sms_new_action (GtkWidget  *widget,
     654                 :             :                 const char *action_name,
     655                 :             :                 GVariant   *parameter)
     656                 :             : {
     657                 :           0 :   ValentMessagesWindow *self = VALENT_MESSAGES_WINDOW (widget);
     658                 :             : 
     659         [ #  # ]:           0 :   g_assert (VALENT_IS_MESSAGES_WINDOW (self));
     660                 :             : 
     661                 :           0 :   gtk_list_box_select_row (self->sidebar_list, NULL);
     662                 :             : 
     663         [ #  # ]:           0 :   if (self->contact_page == NULL)
     664                 :             :     {
     665                 :           0 :       self->contact_page = g_object_new (VALENT_TYPE_CONTACT_PAGE,
     666                 :             :                                          "tag",      "contacts",
     667                 :             :                                          "contacts", self->contacts_adapter,
     668                 :             :                                          NULL);
     669                 :           0 :       g_signal_connect_object (self->contact_page,
     670                 :             :                                "selected",
     671                 :             :                                G_CALLBACK (on_contact_selected),
     672                 :             :                                self,
     673                 :             :                                G_CONNECT_DEFAULT);
     674                 :           0 :       adw_navigation_view_push (self->content_view, self->contact_page);
     675                 :             :     }
     676                 :             :   else
     677                 :             :     {
     678                 :           0 :       adw_navigation_view_pop_to_page (self->content_view, self->contact_page);
     679                 :             :     }
     680                 :             : 
     681                 :           0 :   adw_navigation_split_view_set_show_content (self->main_view, TRUE);
     682                 :           0 : }
     683                 :             : 
     684                 :             : static void
     685                 :           0 : sms_search_action (GtkWidget  *widget,
     686                 :             :                    const char *action_name,
     687                 :             :                    GVariant   *parameter)
     688                 :             : {
     689                 :           0 :   ValentMessagesWindow *self = VALENT_MESSAGES_WINDOW (widget);
     690                 :             : 
     691         [ #  # ]:           0 :   g_assert (VALENT_IS_MESSAGES_WINDOW (self));
     692                 :             : 
     693         [ #  # ]:           0 :   if (self->search_page == NULL)
     694                 :           0 :     adw_navigation_view_push_by_tag (self->content_view, "search");
     695                 :             :   else
     696                 :           0 :     adw_navigation_view_pop_to_page (self->content_view, self->search_page);
     697                 :             : 
     698                 :           0 :   gtk_widget_grab_focus (self->search_entry);
     699                 :           0 :   adw_navigation_split_view_set_show_content (self->main_view, TRUE);
     700                 :           0 : }
     701                 :             : 
     702                 :             : /*
     703                 :             :  * AdwNavigationPage
     704                 :             :  */
     705                 :             : static void
     706                 :           0 : on_page_popped (AdwNavigationView    *view,
     707                 :             :                 AdwNavigationPage    *page,
     708                 :             :                 ValentMessagesWindow *self)
     709                 :             : {
     710         [ #  # ]:           0 :   if (self->contact_page == page)
     711                 :           0 :     self->contact_page = NULL;
     712         [ #  # ]:           0 :   else if (self->search_page == page)
     713                 :           0 :     self->search_page = NULL;
     714                 :           0 : }
     715                 :             : 
     716                 :             : static void
     717                 :           0 : on_page_pushed (AdwNavigationView    *view,
     718                 :             :                 ValentMessagesWindow *self)
     719                 :             : {
     720                 :           0 :   AdwNavigationPage *page = adw_navigation_view_get_visible_page (view);
     721                 :           0 :   const char *tag = adw_navigation_page_get_tag (page);
     722                 :             : 
     723         [ #  # ]:           0 :   if (g_strcmp0 (tag, "contacts") == 0)
     724                 :           0 :     self->contact_page = page;
     725         [ #  # ]:           0 :   else if (g_strcmp0 (tag, "search") == 0)
     726                 :           0 :     self->search_page = page;
     727                 :           0 : }
     728                 :             : 
     729                 :             : /*
     730                 :             :  * GObject
     731                 :             :  */
     732                 :             : static void
     733                 :           1 : valent_messages_window_dispose (GObject *object)
     734                 :             : {
     735                 :           1 :   GtkWidget *widget = GTK_WIDGET (object);
     736                 :             : 
     737                 :           1 :   gtk_widget_dispose_template (widget, VALENT_TYPE_MESSAGES_WINDOW);
     738                 :             : 
     739                 :           1 :   G_OBJECT_CLASS (valent_messages_window_parent_class)->dispose (object);
     740                 :           1 : }
     741                 :             : 
     742                 :             : static void
     743                 :           1 : valent_messages_window_finalize (GObject *object)
     744                 :             : {
     745                 :           1 :   ValentMessagesWindow *self = VALENT_MESSAGES_WINDOW (object);
     746                 :             : 
     747         [ +  - ]:           1 :   g_clear_object (&self->contacts);
     748         [ -  + ]:           1 :   g_clear_object (&self->contacts_adapter);
     749         [ +  - ]:           1 :   g_clear_object (&self->messages);
     750         [ +  - ]:           1 :   g_clear_object (&self->messages_adapter);
     751                 :             : 
     752                 :           1 :   G_OBJECT_CLASS (valent_messages_window_parent_class)->finalize (object);
     753                 :           1 : }
     754                 :             : 
     755                 :             : static void
     756                 :           3 : valent_messages_window_get_property (GObject    *object,
     757                 :             :                                      guint       prop_id,
     758                 :             :                                      GValue     *value,
     759                 :             :                                      GParamSpec *pspec)
     760                 :             : {
     761                 :           3 :   ValentMessagesWindow *self = VALENT_MESSAGES_WINDOW (object);
     762                 :             : 
     763         [ +  - ]:           3 :   switch (prop_id)
     764                 :             :     {
     765                 :           3 :     case PROP_MESSAGES:
     766                 :           3 :       g_value_set_object (value, self->messages);
     767                 :           3 :       break;
     768                 :             : 
     769                 :           0 :     default:
     770                 :           0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     771                 :             :     }
     772                 :           3 : }
     773                 :             : 
     774                 :             : static void
     775                 :           1 : valent_messages_window_class_init (ValentMessagesWindowClass *klass)
     776                 :             : {
     777                 :           1 :   GObjectClass *object_class = G_OBJECT_CLASS (klass);
     778                 :           1 :   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
     779                 :             : 
     780                 :           1 :   object_class->dispose = valent_messages_window_dispose;
     781                 :           1 :   object_class->finalize = valent_messages_window_finalize;
     782                 :           1 :   object_class->get_property = valent_messages_window_get_property;
     783                 :             : 
     784                 :           1 :   gtk_widget_class_set_template_from_resource (widget_class, "/plugins/gnome/valent-messages-window.ui");
     785                 :           1 :   gtk_widget_class_bind_template_child (widget_class, ValentMessagesWindow, main_view);
     786                 :           1 :   gtk_widget_class_bind_template_child (widget_class, ValentMessagesWindow, sidebar_header);
     787                 :           1 :   gtk_widget_class_bind_template_child (widget_class, ValentMessagesWindow, sidebar_list);
     788                 :           1 :   gtk_widget_class_bind_template_child (widget_class, ValentMessagesWindow, content_view);
     789                 :           1 :   gtk_widget_class_bind_template_child (widget_class, ValentMessagesWindow, search_entry);
     790                 :           1 :   gtk_widget_class_bind_template_child (widget_class, ValentMessagesWindow, search_list);
     791                 :           1 :   gtk_widget_class_bind_template_child (widget_class, ValentMessagesWindow, details_dialog);
     792                 :           1 :   gtk_widget_class_bind_template_child (widget_class, ValentMessagesWindow, medium_list);
     793                 :           1 :   gtk_widget_class_bind_template_callback (widget_class, on_conversation_activated);
     794                 :           1 :   gtk_widget_class_bind_template_callback (widget_class, on_page_popped);
     795                 :           1 :   gtk_widget_class_bind_template_callback (widget_class, on_page_pushed);
     796                 :           1 :   gtk_widget_class_bind_template_callback (widget_class, on_search_changed);
     797                 :           1 :   gtk_widget_class_bind_template_callback (widget_class, on_contact_selected);
     798                 :           1 :   gtk_widget_class_bind_template_callback (widget_class, on_search_selected);
     799                 :           1 :   gtk_widget_class_bind_template_callback (widget_class, on_selected_item);
     800                 :             : 
     801                 :           1 :   gtk_widget_class_install_action (widget_class, "sms.new", NULL, sms_new_action);
     802                 :           1 :   gtk_widget_class_install_action (widget_class, "sms.search", NULL, sms_search_action);
     803                 :             : 
     804                 :           2 :   properties [PROP_MESSAGES] =
     805                 :           1 :     g_param_spec_object ("messages", NULL, NULL,
     806                 :             :                          VALENT_TYPE_MESSAGES,
     807                 :             :                          (G_PARAM_READABLE |
     808                 :             :                           G_PARAM_STATIC_STRINGS));
     809                 :             : 
     810                 :           1 :   g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
     811                 :             : 
     812                 :           1 :   g_type_ensure (VALENT_TYPE_CONTACT_PAGE);
     813                 :           1 : }
     814                 :             : 
     815                 :             : static void
     816                 :           1 : valent_messages_window_init (ValentMessagesWindow *self)
     817                 :             : {
     818                 :           1 :   gtk_widget_init_template (GTK_WIDGET (self));
     819                 :             : 
     820                 :           1 :   g_set_object (&self->contacts, G_LIST_MODEL (valent_contacts_get_default ()));
     821                 :           1 :   g_set_object (&self->messages, G_LIST_MODEL (valent_messages_get_default ()));
     822                 :           1 :   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MESSAGES]);
     823                 :             : 
     824                 :           1 :   gtk_list_box_set_header_func (self->search_list,
     825                 :             :                                 search_header_func,
     826                 :             :                                 self, NULL);
     827                 :           1 : }
     828                 :             : 
     829                 :             : /*< private >
     830                 :             :  * valent_messages_window_set_active_message:
     831                 :             :  * @window: a `ValentMessagesWindow`
     832                 :             :  * @message: a `ValentMessage`
     833                 :             :  *
     834                 :             :  * Set the active conversation to the thread of @message scroll to @message.
     835                 :             :  */
     836                 :             : void
     837                 :           0 : valent_messages_window_set_active_message (ValentMessagesWindow *window,
     838                 :             :                                            ValentMessage        *message)
     839                 :             : {
     840                 :           0 :   GtkWidget *widget;
     841                 :           0 :   ValentConversationPage *conversation;
     842                 :           0 :   int64_t thread_id;
     843                 :           0 :   const char *adapter_urn = NULL;
     844                 :           0 :   g_autofree char *thread_urn = NULL;
     845                 :             : 
     846         [ #  # ]:           0 :   g_return_if_fail (VALENT_IS_MESSAGES_WINDOW (window));
     847                 :             : 
     848                 :           0 :   thread_id = valent_message_get_thread_id (message);
     849                 :           0 :   adapter_urn = valent_resource_get_iri (VALENT_RESOURCE (window->messages_adapter));
     850                 :           0 :   thread_urn = g_strdup_printf ("%s:%"PRId64, adapter_urn, thread_id);
     851                 :             : 
     852                 :           0 :   widget = valent_messages_window_ensure_conversation (window, thread_urn);
     853                 :           0 :   conversation = VALENT_CONVERSATION_PAGE (widget);
     854                 :           0 :   valent_conversation_page_scroll_to_message (conversation, message);
     855                 :             : }
     856                 :             : 
     857                 :             : /**
     858                 :             :  * valent_messages_window_set_active_thread:
     859                 :             :  * @window: a `ValentMessagesWindow`
     860                 :             :  * @iri: a thread IRI
     861                 :             :  *
     862                 :             :  * Set the active conversation
     863                 :             :  */
     864                 :             : void
     865                 :           0 : valent_messages_window_set_active_thread (ValentMessagesWindow *window,
     866                 :             :                                           const char           *iri)
     867                 :             : {
     868         [ #  # ]:           0 :   g_return_if_fail (VALENT_IS_MESSAGES_WINDOW (window));
     869         [ #  # ]:           0 :   g_return_if_fail (iri != NULL);
     870                 :             : 
     871                 :           0 :   valent_messages_window_ensure_conversation (window, iri);
     872                 :             : }
     873                 :             : 
        

Generated by: LCOV version 2.0-1