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

Generated by: LCOV version 2.0-1