From 6f0f7d1b40a8fa8d46a2d6f4317600001cdbbb18 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:57:27 +0200 Subject: Adding upstream version 43.5. Signed-off-by: Daniel Baumann --- src/gs-moderate-page.c | 471 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 471 insertions(+) create mode 100644 src/gs-moderate-page.c (limited to 'src/gs-moderate-page.c') diff --git a/src/gs-moderate-page.c b/src/gs-moderate-page.c new file mode 100644 index 0000000..5461791 --- /dev/null +++ b/src/gs-moderate-page.c @@ -0,0 +1,471 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * vi:set noexpandtab tabstop=8 shiftwidth=8: + * + * Copyright (C) 2013-2017 Richard Hughes + * Copyright (C) 2013 Matthias Clasen + * Copyright (C) 2016-2018 Kalev Lember + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include "config.h" + +#include +#include +#include + +#include "gs-app-row.h" +#include "gs-review-row.h" +#include "gs-shell.h" +#include "gs-moderate-page.h" +#include "gs-common.h" + +struct _GsModeratePage +{ + GsPage parent_instance; + + GsPluginLoader *plugin_loader; + GCancellable *cancellable; + GtkSizeGroup *sizegroup_name; + GtkSizeGroup *sizegroup_button_label; + GtkSizeGroup *sizegroup_button_image; + GsShell *shell; + GsOdrsProvider *odrs_provider; + + GtkWidget *list_box_install; + GtkWidget *scrolledwindow_install; + GtkWidget *spinner_install; + GtkWidget *stack_install; +}; + +G_DEFINE_TYPE (GsModeratePage, gs_moderate_page, GS_TYPE_PAGE) + +typedef enum { + PROP_ODRS_PROVIDER = 1, +} GsModeratePageProperty; + +static GParamSpec *obj_props[PROP_ODRS_PROVIDER + 1] = { NULL, }; + +static void +gs_moderate_page_perhaps_hide_app_row (GsModeratePage *self, GsApp *app) +{ + GtkWidget *child; + GsAppRow *app_row = NULL; + gboolean is_visible = FALSE; + + for (child = gtk_widget_get_first_child (self->list_box_install); + child != NULL; + child = gtk_widget_get_next_sibling (child)) { + if (!gtk_widget_get_visible (child)) + continue; + if (GS_IS_APP_ROW (child)) { + GsApp *app_tmp = gs_app_row_get_app (GS_APP_ROW (child)); + if (g_strcmp0 (gs_app_get_id (app), + gs_app_get_id (app_tmp)) == 0) { + app_row = GS_APP_ROW (child); + continue; + } + } + if (GS_IS_REVIEW_ROW (child)) { + GsApp *app_tmp = g_object_get_data (G_OBJECT (child), "GsApp"); + if (g_strcmp0 (gs_app_get_id (app), + gs_app_get_id (app_tmp)) == 0) { + is_visible = TRUE; + break; + } + } + } + if (!is_visible && app_row != NULL) + gs_app_row_unreveal (app_row); +} + +static void +gs_moderate_page_review_clicked_cb (GsReviewRow *row, + GsReviewAction action, + GsModeratePage *self) +{ + GsApp *app = g_object_get_data (G_OBJECT (row), "GsApp"); + AsReview *review = gs_review_row_get_review (row); + g_autoptr(GError) local_error = NULL; + + g_assert (self->odrs_provider != NULL); + + /* FIXME: Make this async */ + switch (action) { + case GS_REVIEW_ACTION_UPVOTE: + gs_odrs_provider_upvote_review (self->odrs_provider, app, + review, self->cancellable, + &local_error); + break; + case GS_REVIEW_ACTION_DOWNVOTE: + gs_odrs_provider_downvote_review (self->odrs_provider, app, + review, self->cancellable, + &local_error); + break; + case GS_REVIEW_ACTION_REPORT: + gs_odrs_provider_report_review (self->odrs_provider, app, + review, self->cancellable, + &local_error); + break; + case GS_REVIEW_ACTION_DISMISS: + gs_odrs_provider_dismiss_review (self->odrs_provider, app, + review, self->cancellable, + &local_error); + break; + case GS_REVIEW_ACTION_REMOVE: + gs_odrs_provider_remove_review (self->odrs_provider, app, + review, self->cancellable, + &local_error); + break; + default: + g_assert_not_reached (); + } + + gtk_widget_set_visible (GTK_WIDGET (row), FALSE); + + /* if there are no more visible rows, hide the app */ + gs_moderate_page_perhaps_hide_app_row (self, app); + + if (local_error != NULL) { + g_warning ("failed to set review on %s: %s", + gs_app_get_id (app), local_error->message); + return; + } +} + +static void +gs_moderate_page_selection_changed_cb (GtkListBox *listbox, + GsAppRow *app_row, + GsModeratePage *self) +{ + g_autofree gchar *tmp = NULL; + tmp = gs_app_to_string (gs_app_row_get_app (app_row)); + g_print ("%s", tmp); +} + +static void +gs_moderate_page_add_app (GsModeratePage *self, GsApp *app) +{ + GPtrArray *reviews; + GtkWidget *app_row; + guint i; + + /* this hides the action button */ + gs_app_add_quirk (app, GS_APP_QUIRK_COMPULSORY); + + /* add top level app */ + app_row = gs_app_row_new (app); + gs_app_row_set_show_buttons (GS_APP_ROW (app_row), TRUE); + gtk_list_box_append (GTK_LIST_BOX (self->list_box_install), app_row); + gs_app_row_set_size_groups (GS_APP_ROW (app_row), + self->sizegroup_name, + self->sizegroup_button_label, + self->sizegroup_button_image); + + /* add reviews */ + reviews = gs_app_get_reviews (app); + for (i = 0; i < reviews->len; i++) { + AsReview *review = g_ptr_array_index (reviews, i); + GtkWidget *row = gs_review_row_new (review); + gtk_widget_set_margin_start (row, 250); + gtk_widget_set_margin_end (row, 250); + gs_review_row_set_actions (GS_REVIEW_ROW (row), + 1 << GS_REVIEW_ACTION_UPVOTE | + 1 << GS_REVIEW_ACTION_DOWNVOTE | + 1 << GS_REVIEW_ACTION_DISMISS | + 1 << GS_REVIEW_ACTION_REPORT); + g_signal_connect (row, "button-clicked", + G_CALLBACK (gs_moderate_page_review_clicked_cb), self); + g_object_set_data_full (G_OBJECT (row), "GsApp", + g_object_ref (app), + (GDestroyNotify) g_object_unref); + gtk_list_box_append (GTK_LIST_BOX (self->list_box_install), row); + } + gtk_widget_show (app_row); +} + +static void +gs_moderate_page_refine_unvoted_reviews_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + guint i; + GsApp *app; + GsModeratePage *self = GS_MODERATE_PAGE (user_data); + GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source_object); + g_autoptr(GError) error = NULL; + g_autoptr(GsAppList) list = NULL; + + gtk_spinner_stop (GTK_SPINNER (self->spinner_install)); + gtk_stack_set_visible_child_name (GTK_STACK (self->stack_install), "view"); + + 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_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("failed to get moderate apps: %s", error->message); + return; + } + + /* no results */ + if (gs_app_list_length (list) == 0) { + gtk_stack_set_visible_child_name (GTK_STACK (self->stack_install), + "uptodate"); + return; + } + + for (i = 0; i < gs_app_list_length (list); i++) { + app = gs_app_list_index (list, i); + gs_moderate_page_add_app (self, app); + } +} + +static void +gs_moderate_page_load (GsModeratePage *self) +{ + g_autoptr(GsPluginJob) plugin_job = NULL; + g_autoptr(GsAppList) list = gs_app_list_new (); + g_autoptr(GError) local_error = NULL; + + /* remove old entries */ + gs_widget_remove_all (self->list_box_install, (GsRemoveFunc) gtk_list_box_remove); + + /* get unvoted reviews as apps */ + if (!gs_odrs_provider_add_unvoted_reviews (self->odrs_provider, list, + self->cancellable, &local_error)) { + if (!g_error_matches (local_error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_CANCELLED) && + !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("failed to get moderate apps: %s", local_error->message); + return; + } + + plugin_job = gs_plugin_job_refine_new (list, + GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON | + GS_PLUGIN_REFINE_FLAGS_REQUIRE_SETUP_ACTION | + GS_PLUGIN_REFINE_FLAGS_REQUIRE_VERSION | + GS_PLUGIN_REFINE_FLAGS_REQUIRE_PROVENANCE | + GS_PLUGIN_REFINE_FLAGS_REQUIRE_DESCRIPTION | + GS_PLUGIN_REFINE_FLAGS_REQUIRE_LICENSE | + GS_PLUGIN_REFINE_FLAGS_REQUIRE_REVIEWS); + gs_plugin_job_set_interactive (plugin_job, TRUE); + gs_plugin_loader_job_process_async (self->plugin_loader, plugin_job, + self->cancellable, + gs_moderate_page_refine_unvoted_reviews_cb, + self); + gtk_spinner_start (GTK_SPINNER (self->spinner_install)); + gtk_stack_set_visible_child_name (GTK_STACK (self->stack_install), "spinner"); +} + +static void +gs_moderate_page_reload (GsPage *page) +{ + GsModeratePage *self = GS_MODERATE_PAGE (page); + if (gs_shell_get_mode (self->shell) == GS_SHELL_MODE_MODERATE) + gs_moderate_page_load (self); +} + +static void +gs_moderate_page_switch_to (GsPage *page) +{ + GsModeratePage *self = GS_MODERATE_PAGE (page); + + if (gs_shell_get_mode (self->shell) != GS_SHELL_MODE_MODERATE) { + g_warning ("Called switch_to(moderate) when in mode %s", + gs_shell_get_mode_string (self->shell)); + return; + } + if (gs_shell_get_mode (self->shell) == GS_SHELL_MODE_MODERATE) + gs_grab_focus_when_mapped (self->scrolledwindow_install); + gs_moderate_page_load (self); +} + +static void +gs_moderate_page_list_header_func (GtkListBoxRow *row, + GtkListBoxRow *before, + gpointer user_data) +{ + GtkWidget *header; + gtk_list_box_row_set_header (row, NULL); + if (before == NULL) + return; + if (GS_IS_REVIEW_ROW (before) && GS_IS_APP_ROW (row)) { + header = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL); + gtk_list_box_row_set_header (row, header); + } +} + +static gboolean +gs_moderate_page_setup (GsPage *page, + GsShell *shell, + GsPluginLoader *plugin_loader, + GCancellable *cancellable, + GError **error) +{ + GsModeratePage *self = GS_MODERATE_PAGE (page); + + g_return_val_if_fail (GS_IS_MODERATE_PAGE (self), TRUE); + + self->shell = shell; + self->plugin_loader = g_object_ref (plugin_loader); + self->cancellable = g_object_ref (cancellable); + + gtk_list_box_set_header_func (GTK_LIST_BOX (self->list_box_install), + gs_moderate_page_list_header_func, + self, NULL); + + return TRUE; +} + +static void +gs_moderate_page_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GsModeratePage *self = GS_MODERATE_PAGE (object); + + switch ((GsModeratePageProperty) prop_id) { + case PROP_ODRS_PROVIDER: + g_value_set_object (value, gs_moderate_page_get_odrs_provider (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gs_moderate_page_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GsModeratePage *self = GS_MODERATE_PAGE (object); + + switch ((GsModeratePageProperty) prop_id) { + case PROP_ODRS_PROVIDER: + gs_moderate_page_set_odrs_provider (self, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gs_moderate_page_dispose (GObject *object) +{ + GsModeratePage *self = GS_MODERATE_PAGE (object); + + g_clear_object (&self->sizegroup_name); + g_clear_object (&self->sizegroup_button_label); + g_clear_object (&self->sizegroup_button_image); + + g_clear_object (&self->plugin_loader); + g_clear_object (&self->cancellable); + g_clear_object (&self->odrs_provider); + + G_OBJECT_CLASS (gs_moderate_page_parent_class)->dispose (object); +} + +static void +gs_moderate_page_class_init (GsModeratePageClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GsPageClass *page_class = GS_PAGE_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->get_property = gs_moderate_page_get_property; + object_class->set_property = gs_moderate_page_set_property; + object_class->dispose = gs_moderate_page_dispose; + page_class->switch_to = gs_moderate_page_switch_to; + page_class->reload = gs_moderate_page_reload; + page_class->setup = gs_moderate_page_setup; + + /** + * GsModeratePage:odrs-provider: (nullable) + * + * An ODRS provider to give access to ratings and reviews information + * for the apps being displayed. + * + * If this is %NULL, ratings and reviews will be disabled and the page + * will be effectively useless. + * + * Since: 41 + */ + obj_props[PROP_ODRS_PROVIDER] = + g_param_spec_object ("odrs-provider", NULL, NULL, + GS_TYPE_ODRS_PROVIDER, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, G_N_ELEMENTS (obj_props), obj_props); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Software/gs-moderate-page.ui"); + + gtk_widget_class_bind_template_child (widget_class, GsModeratePage, list_box_install); + gtk_widget_class_bind_template_child (widget_class, GsModeratePage, scrolledwindow_install); + gtk_widget_class_bind_template_child (widget_class, GsModeratePage, spinner_install); + gtk_widget_class_bind_template_child (widget_class, GsModeratePage, stack_install); +} + +static void +gs_moderate_page_init (GsModeratePage *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); + + g_signal_connect (self->list_box_install, "row-activated", + G_CALLBACK (gs_moderate_page_selection_changed_cb), self); + + self->sizegroup_name = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + self->sizegroup_button_label = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + self->sizegroup_button_image = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); +} + +GsModeratePage * +gs_moderate_page_new (void) +{ + GsModeratePage *self; + self = g_object_new (GS_TYPE_MODERATE_PAGE, NULL); + return GS_MODERATE_PAGE (self); +} + +/** + * gs_moderate_page_get_odrs_provider: + * @self: a #GsModeratePage + * + * Get the value of #GsModeratePage:odrs-provider. + * + * Returns: (nullable) (transfer none): a #GsOdrsProvider, or %NULL if unset + * Since: 41 + */ +GsOdrsProvider * +gs_moderate_page_get_odrs_provider (GsModeratePage *self) +{ + g_return_val_if_fail (GS_IS_MODERATE_PAGE (self), NULL); + + return self->odrs_provider; +} + +/** + * gs_moderate_page_set_odrs_provider: + * @self: a #GsModeratePage + * @odrs_provider: (nullable) (transfer none): new #GsOdrsProvider or %NULL + * + * Set the value of #GsModeratePage:odrs-provider. + * + * Since: 41 + */ +void +gs_moderate_page_set_odrs_provider (GsModeratePage *self, + GsOdrsProvider *odrs_provider) +{ + g_return_if_fail (GS_IS_MODERATE_PAGE (self)); + g_return_if_fail (odrs_provider == NULL || GS_IS_ODRS_PROVIDER (odrs_provider)); + + if (g_set_object (&self->odrs_provider, odrs_provider)) { + gs_moderate_page_reload (GS_PAGE (self)); + g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ODRS_PROVIDER]); + } +} -- cgit v1.2.3