[gnome-software/1409-add-available-for-fedora-section-to-the-explore-page: 44/44] gs-overview-page: Add a deployment-featured section




commit 3db4410b2f4b8f589aeefa1471b2e9d376685532
Author: Milan Crha <mcrha redhat com>
Date:   Fri May 6 13:34:17 2022 +0200

    gs-overview-page: Add a deployment-featured section
    
    The deployment-featured section shows applications, which are tagged
    with "GnomeSoftware::DeploymentFeatured" key. This can be done with an appstream
    data file saved into /usr/share/swcatalog/xml/.  An example file can be found
    at contrib/org.gnome.Software.DeploymentFeatured.xml . The applications can be
    tagged with this key by other means, this is only one way to do it.
    
    The section is not shown when there are not tagged enough applications for it.
    
    Closes https://gitlab.gnome.org/GNOME/gnome-software/-/issues/1409

 contrib/org.gnome.Software.DeploymentFeatured.xml |  11 ++
 src/gs-overview-page.c                            | 139 ++++++++++++++++++++++
 src/gs-overview-page.ui                           |  27 +++++
 3 files changed, 177 insertions(+)
---
diff --git a/contrib/org.gnome.Software.DeploymentFeatured.xml 
b/contrib/org.gnome.Software.DeploymentFeatured.xml
new file mode 100644
index 000000000..b3ba38578
--- /dev/null
+++ b/contrib/org.gnome.Software.DeploymentFeatured.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This is only an example file, what the deployment-featured file can look like;
+     it should be installed beside the org.gnome.Software.Featured.xml file. -->
+<components>
+  <component merge="append">
+    <id>org.gnome.Software.desktop</id>
+    <custom>
+      <value key="GnomeSoftware::DeploymentFeatured">foocorp</value>
+    </custom>
+  </component>
+</components>
diff --git a/src/gs-overview-page.c b/src/gs-overview-page.c
index 0b8115dc4..103d0b999 100644
--- a/src/gs-overview-page.c
+++ b/src/gs-overview-page.c
@@ -36,6 +36,7 @@ struct _GsOverviewPage
        gint                     action_cnt;
        gboolean                 loading_featured;
        gboolean                 loading_curated;
+       gboolean                 loading_deployment_featured;
        gboolean                 loading_recent;
        gboolean                 loading_categories;
        gboolean                 empty;
@@ -50,11 +51,13 @@ struct _GsOverviewPage
        GtkWidget               *box_overview;
        GtkWidget               *box_curated;
        GtkWidget               *box_recent;
+       GtkWidget               *box_deployment_featured;
        GtkWidget               *flowbox_categories;
        GtkWidget               *flowbox_iconless_categories;
        GtkWidget               *iconless_categories_heading;
        GtkWidget               *curated_heading;
        GtkWidget               *recent_heading;
+       GtkWidget               *deployment_featured_heading;
        GtkWidget               *scrolledwindow_overview;
        GtkWidget               *stack_overview;
 };
@@ -114,6 +117,7 @@ gs_overview_page_decrement_action_cnt (GsOverviewPage *self)
        self->cache_valid = TRUE;
        g_signal_emit (self, signals[SIGNAL_REFRESHED], 0);
        self->loading_categories = FALSE;
+       self->loading_deployment_featured = FALSE;
        self->loading_featured = FALSE;
        self->loading_curated = FALSE;
        self->loading_recent = FALSE;
@@ -313,6 +317,56 @@ out:
        gs_overview_page_decrement_action_cnt (self);
 }
 
+static void
+gs_overview_page_get_deployment_featured_cb (GObject *source_object,
+                                            GAsyncResult *res,
+                                            gpointer user_data)
+{
+       GsOverviewPage *self = GS_OVERVIEW_PAGE (user_data);
+       GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source_object);
+       guint i;
+       GsApp *app;
+       GtkWidget *tile;
+       g_autoptr(GError) error = NULL;
+       g_autoptr(GsAppList) list = NULL;
+
+       /* get deployment-featured apps */
+       list = gs_plugin_loader_job_process_finish (plugin_loader, res, &error);
+       if (list == NULL) {
+               if (!g_error_matches (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_CANCELLED))
+                       g_warning ("failed to get deployment-featured apps: %s", error->message);
+               goto out;
+       }
+
+       /* not enough to show */
+       if (gs_app_list_length (list) < N_TILES) {
+               g_warning ("Only %u apps for deployment-featured list, hiding",
+                          gs_app_list_length (list));
+               gtk_widget_set_visible (self->box_deployment_featured, FALSE);
+               gtk_widget_set_visible (self->deployment_featured_heading, FALSE);
+               goto out;
+       }
+
+       gs_app_list_randomize (list);
+
+       gs_widget_remove_all (self->box_deployment_featured, (GsRemoveFunc) gtk_flow_box_remove);
+
+       for (i = 0; i < gs_app_list_length (list) && i < N_TILES; i++) {
+               app = gs_app_list_index (list, i);
+               tile = gs_summary_tile_new (app);
+               g_signal_connect (tile, "clicked",
+                         G_CALLBACK (app_tile_clicked), self);
+               gtk_flow_box_insert (GTK_FLOW_BOX (self->box_deployment_featured), tile, -1);
+       }
+       gtk_widget_set_visible (self->box_deployment_featured, TRUE);
+       gtk_widget_set_visible (self->deployment_featured_heading, TRUE);
+
+       self->empty = FALSE;
+
+ out:
+       gs_overview_page_decrement_action_cnt (self);
+}
+
 static void
 category_tile_clicked (GsCategoryTile *tile, gpointer data)
 {
@@ -489,6 +543,53 @@ fedora_third_party_disable (GsOverviewPage *self)
        gs_fedora_third_party_opt_out (self->third_party, self->cancellable, 
fedora_third_party_disable_done_cb, g_object_ref (self));
 }
 
+static gchar *
+gs_overview_page_read_deployment_featured_key (const gchar *key,
+                                              gboolean localized)
+{
+       static gboolean file_exists = TRUE;
+       g_autoptr(GKeyFile) key_file = NULL;
+       g_autoptr(GError) error = NULL;
+
+       if (!file_exists)
+               return NULL;
+
+       #define FILENAME "/etc/gnome-software/deployment-featured.ini"
+
+       key_file = g_key_file_new ();
+       if (!g_key_file_load_from_file (key_file, FILENAME, G_KEY_FILE_NONE, &error)) {
+               file_exists = FALSE;
+               g_debug ("Failed to read '" FILENAME "': %s", error ? error->message : "Unknown error");
+               return NULL;
+       }
+
+       #undef FILENAME
+
+       if (localized)
+               return g_key_file_get_locale_string (key_file, "Deployment Featured Apps", key, NULL, NULL);
+
+       return g_key_file_get_string (key_file, "Deployment Featured Apps", key, NULL);
+}
+
+static gchar *
+gs_overview_page_get_deployment_featured_label (void)
+{
+       return gs_overview_page_read_deployment_featured_key ("Label", TRUE);
+}
+
+static gchar **
+gs_overview_page_get_featured_deployments (void)
+{
+       g_autofree gchar *selector = gs_overview_page_read_deployment_featured_key ("Selector", FALSE);
+       g_auto(GStrv) deployments = NULL;
+       if (selector == NULL || *selector == '\0')
+               return NULL;
+       deployments = g_strsplit (selector, ",", -1);
+       if (deployments != NULL && deployments[0] != NULL && *deployments[0] != '\0')
+               return g_steal_pointer (&deployments);
+       return NULL;
+}
+
 static void
 gs_overview_page_load (GsOverviewPage *self)
 {
@@ -513,6 +614,36 @@ gs_overview_page_load (GsOverviewPage *self)
                self->action_cnt++;
        }
 
+       if (!self->loading_deployment_featured) {
+               g_auto(GStrv) deployments = gs_overview_page_get_featured_deployments ();
+
+               self->loading_deployment_featured = TRUE;
+
+               if (deployments != NULL) {
+                       g_autoptr(GsPluginJob) plugin_job = NULL;
+                       g_autoptr(GsAppQuery) query = NULL;
+                       GsPluginListAppsFlags flags = GS_PLUGIN_LIST_APPS_FLAGS_INTERACTIVE;
+
+                       query = gs_app_query_new ("deployment-featured", deployments,
+                                                 "max-results", N_TILES,
+                                                 "refine-flags", GS_PLUGIN_REFINE_FLAGS_REQUIRE_RATING |
+                                                                 GS_PLUGIN_REFINE_FLAGS_REQUIRE_CATEGORIES |
+                                                                 GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON,
+                                                 "dedupe-flags", GS_APP_LIST_FILTER_FLAG_PREFER_INSTALLED |
+                                                                 GS_APP_LIST_FILTER_FLAG_KEY_ID_PROVIDES,
+                                                 NULL);
+
+                       plugin_job = gs_plugin_job_list_apps_new (query, flags);
+
+                       gs_plugin_loader_job_process_async (self->plugin_loader,
+                                                           plugin_job,
+                                                           self->cancellable,
+                                                           gs_overview_page_get_deployment_featured_cb,
+                                                           self);
+                       self->action_cnt++;
+               }
+       }
+
        if (!self->loading_curated) {
                g_autoptr(GsPluginJob) plugin_job = NULL;
                g_autoptr(GsAppQuery) query = NULL;
@@ -721,11 +852,17 @@ refreshed_cb (GsOverviewPage *self, gpointer user_data)
 static void
 gs_overview_page_init (GsOverviewPage *self)
 {
+       g_autofree gchar *tmp_label = NULL;
+
        gtk_widget_init_template (GTK_WIDGET (self));
 
        gs_featured_carousel_set_apps (GS_FEATURED_CAROUSEL (self->featured_carousel), NULL);
 
        g_signal_connect (self, "refreshed", G_CALLBACK (refreshed_cb), self);
+
+       tmp_label = gs_overview_page_get_deployment_featured_label ();
+       if (tmp_label != NULL)
+               gtk_label_set_text (GTK_LABEL (self->deployment_featured_heading), tmp_label);
 }
 
 static void
@@ -813,11 +950,13 @@ gs_overview_page_class_init (GsOverviewPageClass *klass)
        gtk_widget_class_bind_template_child (widget_class, GsOverviewPage, box_overview);
        gtk_widget_class_bind_template_child (widget_class, GsOverviewPage, box_curated);
        gtk_widget_class_bind_template_child (widget_class, GsOverviewPage, box_recent);
+       gtk_widget_class_bind_template_child (widget_class, GsOverviewPage, box_deployment_featured);
        gtk_widget_class_bind_template_child (widget_class, GsOverviewPage, flowbox_categories);
        gtk_widget_class_bind_template_child (widget_class, GsOverviewPage, flowbox_iconless_categories);
        gtk_widget_class_bind_template_child (widget_class, GsOverviewPage, iconless_categories_heading);
        gtk_widget_class_bind_template_child (widget_class, GsOverviewPage, curated_heading);
        gtk_widget_class_bind_template_child (widget_class, GsOverviewPage, recent_heading);
+       gtk_widget_class_bind_template_child (widget_class, GsOverviewPage, deployment_featured_heading);
        gtk_widget_class_bind_template_child (widget_class, GsOverviewPage, scrolledwindow_overview);
        gtk_widget_class_bind_template_child (widget_class, GsOverviewPage, stack_overview);
        gtk_widget_class_bind_template_callback (widget_class, featured_carousel_app_clicked_cb);
diff --git a/src/gs-overview-page.ui b/src/gs-overview-page.ui
index a556aa04e..55c9dd795 100644
--- a/src/gs-overview-page.ui
+++ b/src/gs-overview-page.ui
@@ -151,6 +151,33 @@
                                   </object>
                                 </child>
 
+                                <child>
+                                  <object class="GtkLabel" id="deployment_featured_heading">
+                                    <property name="visible">False</property>
+                                    <property name="xalign">0</property>
+                                    <property name="label">Available for Deployment</property> <!-- 
placeholder, set in the code -->
+                                    <property name="margin-top">21</property>
+                                    <property name="margin-bottom">6</property>
+                                    <style>
+                                      <class name="heading"/>
+                                    </style>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkFlowBox" id="box_deployment_featured">
+                                    <property name="visible">False</property>
+                                    <property name="row_spacing">14</property>
+                                    <property name="column_spacing">14</property>
+                                    <property name="homogeneous">True</property>
+                                    <property name="min_children_per_line">2</property>
+                                    <property name="max_children_per_line">3</property>
+                                    <property name="selection_mode">none</property>
+                                    <accessibility>
+                                      <relation name="labelled-by">deployment_featured_heading</relation>
+                                    </accessibility>
+                                  </object>
+                                </child>
+
                                 <child>
                                   <object class="GtkLabel" id="iconless_categories_heading">
                                     <property name="xalign">0</property>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]