[gnome-software] Add a power-user moderation view in gnome-software
- From: Richard Hughes <rhughes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software] Add a power-user moderation view in gnome-software
- Date: Fri, 12 Feb 2016 14:09:45 +0000 (UTC)
commit df79804d605574eaaf301dc32601e1c9f463181e
Author: Richard Hughes <richard hughsie com>
Date: Thu Feb 11 18:09:29 2016 +0000
Add a power-user moderation view in gnome-software
This is a prototype for power-users and is not fully complete or designed...
It is, however, hugely useful for me.
po/POTFILES.in | 1 +
src/Makefile.am | 3 +
src/gnome-software.gresource.xml | 1 +
src/gnome-software.ui | 8 +
src/gs-application.c | 2 +
src/gs-plugin-loader.c | 86 +++++++++
src/gs-plugin-loader.h | 8 +
src/gs-plugin.h | 9 +
src/gs-review-row.c | 17 ++-
src/gs-review-row.ui | 13 ++
src/gs-review.h | 1 +
src/gs-shell-moderate.c | 313 +++++++++++++++++++++++++++++++
src/gs-shell-moderate.h | 51 +++++
src/gs-shell-moderate.ui | 62 ++++++
src/gs-shell.c | 12 ++
src/gs-shell.h | 1 +
src/plugins/gs-plugin-xdg-app-reviews.c | 120 ++++++++----
17 files changed, 666 insertions(+), 42 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index fdf8ea3..8b40618 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -36,6 +36,7 @@ src/gs-shell-details.c
src/gs-shell-installed.c
[type: gettext/glade]src/gs-shell-installed.ui
src/gs-shell-overview.c
+[type: gettext/glade]src/gs-shell-moderate.ui
[type: gettext/glade]src/gs-shell-overview.ui
src/gs-shell-search.c
[type: gettext/glade]src/gs-shell-search.ui
diff --git a/src/Makefile.am b/src/Makefile.am
index d8b12bb..dfe3aa3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -44,6 +44,7 @@ UI_FILES = \
gs-shell-extras.ui \
gs-shell-details.ui \
gs-shell-installed.ui \
+ gs-shell-moderate.ui \
gs-shell-overview.ui \
gs-shell-search.ui \
gs-shell-updates.ui \
@@ -172,6 +173,8 @@ gnome_software_SOURCES = \
gs-shell-extras.h \
gs-shell-installed.c \
gs-shell-installed.h \
+ gs-shell-moderate.c \
+ gs-shell-moderate.h \
gs-shell-overview.c \
gs-shell-overview.h \
gs-shell-updates.c \
diff --git a/src/gnome-software.gresource.xml b/src/gnome-software.gresource.xml
index 261562f..9c29e61 100644
--- a/src/gnome-software.gresource.xml
+++ b/src/gnome-software.gresource.xml
@@ -20,6 +20,7 @@
<file preprocess="xml-stripblanks">gs-shell-extras.ui</file>
<file preprocess="xml-stripblanks">gs-shell-details.ui</file>
<file preprocess="xml-stripblanks">gs-shell-installed.ui</file>
+ <file preprocess="xml-stripblanks">gs-shell-moderate.ui</file>
<file preprocess="xml-stripblanks">gs-shell-overview.ui</file>
<file preprocess="xml-stripblanks">gs-shell-search.ui</file>
<file preprocess="xml-stripblanks">gs-shell-updates.ui</file>
diff --git a/src/gnome-software.ui b/src/gnome-software.ui
index 6392826..03d1ef2 100644
--- a/src/gnome-software.ui
+++ b/src/gnome-software.ui
@@ -407,6 +407,14 @@
</packing>
</child>
<child>
+ <object class="GsShellModerate" id="shell_moderate">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="name">moderate</property>
+ </packing>
+ </child>
+ <child>
<object class="GsShellSearch" id="shell_search">
<property name="visible">True</property>
</object>
diff --git a/src/gs-application.c b/src/gs-application.c
index 71f6f66..e811a15 100644
--- a/src/gs-application.c
+++ b/src/gs-application.c
@@ -513,6 +513,8 @@ set_mode_activated (GSimpleAction *action,
gs_shell_set_mode (app->shell, GS_SHELL_MODE_UPDATES);
} else if (g_strcmp0 (mode, "installed") == 0) {
gs_shell_set_mode (app->shell, GS_SHELL_MODE_INSTALLED);
+ } else if (g_strcmp0 (mode, "moderate") == 0) {
+ gs_shell_set_mode (app->shell, GS_SHELL_MODE_MODERATE);
} else if (g_strcmp0 (mode, "overview") == 0) {
gs_shell_set_mode (app->shell, GS_SHELL_MODE_OVERVIEW);
} else if (g_strcmp0 (mode, "updated") == 0) {
diff --git a/src/gs-plugin-loader.c b/src/gs-plugin-loader.c
index 21b17f9..9432c3e 100644
--- a/src/gs-plugin-loader.c
+++ b/src/gs-plugin-loader.c
@@ -1005,6 +1005,89 @@ gs_plugin_loader_get_distro_upgrades_finish (GsPluginLoader *plugin_loader,
/******************************************************************************/
/**
+ * gs_plugin_loader_get_unvoted_reviews_thread_cb:
+ **/
+static void
+gs_plugin_loader_get_unvoted_reviews_thread_cb (GTask *task,
+ gpointer object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ GsPluginLoaderAsyncState *state = (GsPluginLoaderAsyncState *) task_data;
+ GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (object);
+ GError *error = NULL;
+
+ state->list = gs_plugin_loader_run_results (plugin_loader,
+ "gs_plugin_add_unvoted_reviews",
+ state->flags,
+ cancellable,
+ &error);
+ if (error != NULL) {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ /* filter package list */
+ gs_plugin_list_filter_duplicates (&state->list);
+
+ /* dedupe applications we already know about */
+ gs_plugin_loader_list_dedupe (plugin_loader, state->list);
+
+ /* success */
+ g_task_return_pointer (task, gs_plugin_list_copy (state->list), (GDestroyNotify) gs_plugin_list_free);
+}
+
+/**
+ * gs_plugin_loader_get_unvoted_reviews_async:
+ *
+ * This method calls all plugins that implement the gs_plugin_add_unvoted_reviews()
+ * function.
+ **/
+void
+gs_plugin_loader_get_unvoted_reviews_async (GsPluginLoader *plugin_loader,
+ GsPluginRefineFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GsPluginLoaderAsyncState *state;
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (GS_IS_PLUGIN_LOADER (plugin_loader));
+ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+ /* save state */
+ state = g_slice_new0 (GsPluginLoaderAsyncState);
+ state->flags = flags;
+
+ /* run in a thread */
+ task = g_task_new (plugin_loader, cancellable, callback, user_data);
+ g_task_set_task_data (task, state, (GDestroyNotify) gs_plugin_loader_free_async_state);
+ g_task_set_return_on_cancel (task, TRUE);
+ g_task_run_in_thread (task, gs_plugin_loader_get_unvoted_reviews_thread_cb);
+}
+
+/**
+ * gs_plugin_loader_get_unvoted_reviews_finish:
+ *
+ * Return value: (element-type GsApp) (transfer full): A list of applications
+ **/
+GList *
+gs_plugin_loader_get_unvoted_reviews_finish (GsPluginLoader *plugin_loader,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (GS_IS_PLUGIN_LOADER (plugin_loader), NULL);
+ g_return_val_if_fail (G_IS_TASK (res), NULL);
+ g_return_val_if_fail (g_task_is_valid (res, plugin_loader), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+/******************************************************************************/
+
+/**
* gs_plugin_loader_get_sources_thread_cb:
**/
static void
@@ -2793,6 +2876,9 @@ gs_plugin_loader_review_action_async (GsPluginLoader *plugin_loader,
case GS_REVIEW_ACTION_REMOVE:
state->function_name = "gs_plugin_review_remove";
break;
+ case GS_REVIEW_ACTION_DISMISS:
+ state->function_name = "gs_plugin_review_dismiss";
+ break;
default:
g_assert_not_reached ();
break;
diff --git a/src/gs-plugin-loader.h b/src/gs-plugin-loader.h
index 3925dc9..2365cb0 100644
--- a/src/gs-plugin-loader.h
+++ b/src/gs-plugin-loader.h
@@ -95,6 +95,14 @@ void gs_plugin_loader_get_distro_upgrades_async (GsPluginLoader *plugin_loader
GList *gs_plugin_loader_get_distro_upgrades_finish (GsPluginLoader *plugin_loader,
GAsyncResult *res,
GError **error);
+void gs_plugin_loader_get_unvoted_reviews_async (GsPluginLoader *plugin_loader,
+ GsPluginRefineFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GList *gs_plugin_loader_get_unvoted_reviews_finish (GsPluginLoader *plugin_loader,
+ GAsyncResult *res,
+ GError **error);
void gs_plugin_loader_get_sources_async (GsPluginLoader *plugin_loader,
GsPluginRefineFlags flags,
GCancellable *cancellable,
diff --git a/src/gs-plugin.h b/src/gs-plugin.h
index 7b72782..878a8d3 100644
--- a/src/gs-plugin.h
+++ b/src/gs-plugin.h
@@ -251,6 +251,10 @@ gboolean gs_plugin_add_featured (GsPlugin *plugin,
GList **list,
GCancellable *cancellable,
GError **error);
+gboolean gs_plugin_add_unvoted_reviews (GsPlugin *plugin,
+ GList **list,
+ GCancellable *cancellable,
+ GError **error);
gboolean gs_plugin_refine (GsPlugin *plugin,
GList **list,
GsPluginRefineFlags flags,
@@ -309,6 +313,11 @@ gboolean gs_plugin_review_remove (GsPlugin *plugin,
GsReview *review,
GCancellable *cancellable,
GError **error);
+gboolean gs_plugin_review_dismiss (GsPlugin *plugin,
+ GsApp *app,
+ GsReview *review,
+ GCancellable *cancellable,
+ GError **error);
gboolean gs_plugin_refresh (GsPlugin *plugin,
guint cache_age,
GsPluginRefreshFlags flags,
diff --git a/src/gs-review-row.c b/src/gs-review-row.c
index 0e32275..496ca4f 100644
--- a/src/gs-review-row.c
+++ b/src/gs-review-row.c
@@ -40,6 +40,7 @@ typedef struct
GtkWidget *text_label;
GtkWidget *button_yes;
GtkWidget *button_no;
+ GtkWidget *button_dismiss;
GtkWidget *button_report;
GtkWidget *button_remove;
GtkWidget *box_vote_buttons;
@@ -83,7 +84,8 @@ gs_review_row_refresh (GsReviewRow *row)
/* set actions up */
if ((priv->actions & (1 << GS_REVIEW_ACTION_UPVOTE |
- 1 << GS_REVIEW_ACTION_DOWNVOTE)) == 0) {
+ 1 << GS_REVIEW_ACTION_DOWNVOTE |
+ 1 << GS_REVIEW_ACTION_DISMISS)) == 0) {
gtk_widget_set_visible (priv->box_vote_buttons, FALSE);
} else {
gtk_widget_set_visible (priv->box_vote_buttons, TRUE);
@@ -91,6 +93,8 @@ gs_review_row_refresh (GsReviewRow *row)
priv->actions & 1 << GS_REVIEW_ACTION_UPVOTE);
gtk_widget_set_visible (priv->button_no,
priv->actions & 1 << GS_REVIEW_ACTION_DOWNVOTE);
+ gtk_widget_set_visible (priv->button_dismiss,
+ priv->actions & 1 << GS_REVIEW_ACTION_DISMISS);
}
gtk_widget_set_visible (priv->button_remove,
priv->actions & 1 << GS_REVIEW_ACTION_REMOVE);
@@ -159,6 +163,7 @@ gs_review_row_class_init (GsReviewRowClass *klass)
gtk_widget_class_bind_template_child_private (widget_class, GsReviewRow, text_label);
gtk_widget_class_bind_template_child_private (widget_class, GsReviewRow, button_yes);
gtk_widget_class_bind_template_child_private (widget_class, GsReviewRow, button_no);
+ gtk_widget_class_bind_template_child_private (widget_class, GsReviewRow, button_dismiss);
gtk_widget_class_bind_template_child_private (widget_class, GsReviewRow, button_report);
gtk_widget_class_bind_template_child_private (widget_class, GsReviewRow, button_remove);
gtk_widget_class_bind_template_child_private (widget_class, GsReviewRow, box_vote_buttons);
@@ -186,6 +191,13 @@ gs_review_row_button_clicked_report_cb (GtkButton *button, GsReviewRow *row)
}
static void
+gs_review_row_button_clicked_dismiss_cb (GtkButton *button, GsReviewRow *row)
+{
+ g_signal_emit (row, signals[SIGNAL_BUTTON_CLICKED], 0,
+ GS_REVIEW_ACTION_DISMISS);
+}
+
+static void
gs_review_row_button_clicked_remove_cb (GtkButton *button, GsReviewRow *row)
{
g_signal_emit (row, signals[SIGNAL_BUTTON_CLICKED], 0,
@@ -235,6 +247,9 @@ gs_review_row_new (GsReview *review)
g_signal_connect_object (priv->button_no, "clicked",
G_CALLBACK (gs_review_row_button_clicked_downvote_cb),
row, 0);
+ g_signal_connect_object (priv->button_dismiss, "clicked",
+ G_CALLBACK (gs_review_row_button_clicked_dismiss_cb),
+ row, 0);
g_signal_connect_object (priv->button_report, "clicked",
G_CALLBACK (gs_review_row_button_clicked_report_cb),
row, 0);
diff --git a/src/gs-review-row.ui b/src/gs-review-row.ui
index 95f5e67..bd3d993 100644
--- a/src/gs-review-row.ui
+++ b/src/gs-review-row.ui
@@ -139,6 +139,19 @@
<property name="position">2</property>
</packing>
</child>
+ <child>
+ <object class="GtkButton" id="button_dismiss">
+ <property name="label" translatable="yes">Meh</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
</object>
<packing>
<property name="left_attach">0</property>
diff --git a/src/gs-review.h b/src/gs-review.h
index 2d2edd2..aadfd38 100644
--- a/src/gs-review.h
+++ b/src/gs-review.h
@@ -36,6 +36,7 @@ typedef enum {
GS_REVIEW_ACTION_DOWNVOTE,
GS_REVIEW_ACTION_REPORT,
GS_REVIEW_ACTION_REMOVE,
+ GS_REVIEW_ACTION_DISMISS,
GS_REVIEW_ACTION_LAST
} GsReviewAction;
diff --git a/src/gs-shell-moderate.c b/src/gs-shell-moderate.c
new file mode 100644
index 0000000..11580e4
--- /dev/null
+++ b/src/gs-shell-moderate.c
@@ -0,0 +1,313 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2013 Matthias Clasen <mclasen redhat com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+
+#include "gs-app.h"
+#include "gs-app-row.h"
+#include "gs-review-row.h"
+#include "gs-shell.h"
+#include "gs-shell-moderate.h"
+#include "gs-utils.h"
+
+struct _GsShellModerate
+{
+ GsPage parent_instance;
+
+ GsPluginLoader *plugin_loader;
+ GtkBuilder *builder;
+ GCancellable *cancellable;
+ GtkSizeGroup *sizegroup_image;
+ GtkSizeGroup *sizegroup_name;
+ GsShell *shell;
+
+ GtkWidget *list_box_install;
+ GtkWidget *scrolledwindow_install;
+ GtkWidget *spinner_install;
+ GtkWidget *stack_install;
+};
+
+G_DEFINE_TYPE (GsShellModerate, gs_shell_moderate, GS_TYPE_PAGE)
+
+/**
+ * gs_shell_moderate_app_set_review_cb:
+ **/
+static void
+gs_shell_moderate_app_set_review_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source);
+ g_autoptr(GError) error = NULL;
+
+ if (!gs_plugin_loader_app_action_finish (plugin_loader, res, &error)) {
+ g_warning ("failed to set review: %s", error->message);
+ return;
+ }
+}
+
+static void
+gs_shell_moderate_review_clicked_cb (GsReviewRow *row,
+ GsReviewAction action,
+ GsShellModerate *self)
+{
+ GsApp *app = g_object_get_data (G_OBJECT (row), "GsApp");
+ gs_plugin_loader_review_action_async (self->plugin_loader,
+ app,
+ gs_review_row_get_review (row),
+ action,
+ self->cancellable,
+ gs_shell_moderate_app_set_review_cb,
+ self);
+ gtk_widget_set_visible (GTK_WIDGET (row), FALSE);
+}
+
+static void
+gs_shell_moderate_add_app (GsShellModerate *self, GsApp *app)
+{
+ GPtrArray *reviews;
+ GtkWidget *app_row;
+ guint i;
+
+ /* this hides the action button */
+ gs_app_set_kind (app, GS_APP_KIND_UNKNOWN);
+ gs_app_set_kind (app, GS_APP_KIND_SYSTEM);
+
+ /* add top level app */
+ app_row = gs_app_row_new (app);
+ gs_app_row_set_colorful (GS_APP_ROW (app_row), FALSE);
+ gtk_container_add (GTK_CONTAINER (self->list_box_install), app_row);
+ gs_app_row_set_size_groups (GS_APP_ROW (app_row),
+ self->sizegroup_image,
+ self->sizegroup_name);
+
+ /* add reviews */
+ reviews = gs_app_get_reviews (app);
+ for (i = 0; i < reviews->len; i++) {
+ GsReview *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_shell_moderate_review_clicked_cb), self);
+ g_object_set_data_full (G_OBJECT (row), "GsApp",
+ g_object_ref (app),
+ (GDestroyNotify) g_object_unref);
+ gtk_container_add (GTK_CONTAINER (self->list_box_install), row);
+ }
+ gtk_widget_show (app_row);
+}
+
+/**
+ * gs_shell_moderate_get_unvoted_reviews_cb:
+ **/
+static void
+gs_shell_moderate_get_unvoted_reviews_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GList *l;
+ GsApp *app;
+ GsShellModerate *self = GS_SHELL_MODERATE (user_data);
+ GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source_object);
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GsAppList) list = NULL;
+
+ gs_stop_spinner (GTK_SPINNER (self->spinner_install));
+ gtk_stack_set_visible_child_name (GTK_STACK (self->stack_install), "view");
+
+ list = gs_plugin_loader_get_unvoted_reviews_finish (plugin_loader,
+ res,
+ &error);
+ if (list == NULL) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("failed to get moderate apps: %s", error->message);
+ return;
+ }
+ for (l = list; l != NULL; l = l->next) {
+ app = GS_APP (l->data);
+ gs_shell_moderate_add_app (self, app);
+ }
+}
+
+/**
+ * gs_shell_moderate_load:
+ */
+static void
+gs_shell_moderate_load (GsShellModerate *self)
+{
+ /* remove old entries */
+ gs_container_remove_all (GTK_CONTAINER (self->list_box_install));
+
+ /* get unvoted reviews as apps */
+ gs_plugin_loader_get_unvoted_reviews_async (self->plugin_loader,
+ GS_PLUGIN_REFINE_FLAGS_DEFAULT |
+ 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_LICENCE |
+ GS_PLUGIN_REFINE_FLAGS_REQUIRE_REVIEWS,
+ self->cancellable,
+ gs_shell_moderate_get_unvoted_reviews_cb,
+ self);
+ gs_start_spinner (GTK_SPINNER (self->spinner_install));
+ gtk_stack_set_visible_child_name (GTK_STACK (self->stack_install), "spinner");
+}
+
+/**
+ * gs_shell_moderate_reload:
+ */
+void
+gs_shell_moderate_reload (GsShellModerate *self)
+{
+ gs_shell_moderate_load (self);
+}
+
+/**
+ * gs_shell_moderate_switch_to:
+ **/
+void
+gs_shell_moderate_switch_to (GsShellModerate *self, gboolean scroll_up)
+{
+ 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_shell_moderate_load (self);
+}
+
+/**
+ * gs_shell_moderate_list_header_func
+ **/
+static void
+gs_shell_moderate_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);
+ }
+}
+
+/**
+ * gs_shell_moderate_setup:
+ */
+void
+gs_shell_moderate_setup (GsShellModerate *self,
+ GsShell *shell,
+ GsPluginLoader *plugin_loader,
+ GtkBuilder *builder,
+ GCancellable *cancellable)
+{
+ g_return_if_fail (GS_IS_SHELL_MODERATE (self));
+
+ self->shell = shell;
+ self->plugin_loader = g_object_ref (plugin_loader);
+ self->builder = g_object_ref (builder);
+ self->cancellable = g_object_ref (cancellable);
+
+ gtk_list_box_set_header_func (GTK_LIST_BOX (self->list_box_install),
+ gs_shell_moderate_list_header_func,
+ self, NULL);
+
+ /* chain up */
+ gs_page_setup (GS_PAGE (self), shell, plugin_loader, cancellable);
+}
+
+/**
+ * gs_shell_moderate_dispose:
+ **/
+static void
+gs_shell_moderate_dispose (GObject *object)
+{
+ GsShellModerate *self = GS_SHELL_MODERATE (object);
+
+ g_clear_object (&self->sizegroup_image);
+ g_clear_object (&self->sizegroup_name);
+
+ g_clear_object (&self->builder);
+ g_clear_object (&self->plugin_loader);
+ g_clear_object (&self->cancellable);
+
+ G_OBJECT_CLASS (gs_shell_moderate_parent_class)->dispose (object);
+}
+
+/**
+ * gs_shell_moderate_class_init:
+ **/
+static void
+gs_shell_moderate_class_init (GsShellModerateClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->dispose = gs_shell_moderate_dispose;
+
+ gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/Software/gs-shell-moderate.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, GsShellModerate, list_box_install);
+ gtk_widget_class_bind_template_child (widget_class, GsShellModerate, scrolledwindow_install);
+ gtk_widget_class_bind_template_child (widget_class, GsShellModerate, spinner_install);
+ gtk_widget_class_bind_template_child (widget_class, GsShellModerate, stack_install);
+}
+
+/**
+ * gs_shell_moderate_init:
+ **/
+static void
+gs_shell_moderate_init (GsShellModerate *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ self->sizegroup_image = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ self->sizegroup_name = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+}
+
+/**
+ * gs_shell_moderate_new:
+ **/
+GsShellModerate *
+gs_shell_moderate_new (void)
+{
+ GsShellModerate *self;
+ self = g_object_new (GS_TYPE_SHELL_MODERATE, NULL);
+ return GS_SHELL_MODERATE (self);
+}
+
+/* vim: set noexpandtab: */
diff --git a/src/gs-shell-moderate.h b/src/gs-shell-moderate.h
new file mode 100644
index 0000000..4a74492
--- /dev/null
+++ b/src/gs-shell-moderate.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __GS_SHELL_MODERATE_H
+#define __GS_SHELL_MODERATE_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "gs-page.h"
+#include "gs-plugin-loader.h"
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_SHELL_MODERATE (gs_shell_moderate_get_type ())
+
+G_DECLARE_FINAL_TYPE (GsShellModerate, gs_shell_moderate, GS, SHELL_MODERATE, GsPage)
+
+GsShellModerate *gs_shell_moderate_new (void);
+void gs_shell_moderate_switch_to (GsShellModerate *self,
+ gboolean scroll_up);
+void gs_shell_moderate_reload (GsShellModerate *self);
+void gs_shell_moderate_setup (GsShellModerate *self,
+ GsShell *shell,
+ GsPluginLoader *plugin_loader,
+ GtkBuilder *builder,
+ GCancellable *cancellable);
+
+G_END_DECLS
+
+#endif /* __GS_SHELL_MODERATE_H */
+
+/* vim: set noexpandtab: */
diff --git a/src/gs-shell-moderate.ui b/src/gs-shell-moderate.ui
new file mode 100644
index 0000000..a781526
--- /dev/null
+++ b/src/gs-shell-moderate.ui
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="3.10"/>
+ <template class="GsShellModerate" parent="GsPage">
+ <child internal-child="accessible">
+ <object class="AtkObject" id="moderate-accessible">
+ <property name="accessible-name" translatable="yes">Moderate page</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkStack" id="stack_install">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkSpinner" id="spinner_install">
+ <property name="visible">True</property>
+ <property name="width_request">32</property>
+ <property name="height_request">32</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ </object>
+ <packing>
+ <property name="name">spinner</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box_install">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow_install">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GsFixedSizeBin" id="gs_fixed_bin">
+ <property name="visible">True</property>
+ <property name="preferred-width">860</property>
+ <child>
+ <object class="GtkListBox" id="list_box_install">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="selection_mode">none</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="name">view</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/gs-shell.c b/src/gs-shell.c
index 62aff1b..37bcb14 100644
--- a/src/gs-shell.c
+++ b/src/gs-shell.c
@@ -29,6 +29,7 @@
#include "gs-shell.h"
#include "gs-shell-details.h"
#include "gs-shell-installed.h"
+#include "gs-shell-moderate.h"
#include "gs-shell-search.h"
#include "gs-shell-overview.h"
#include "gs-shell-updates.h"
@@ -46,6 +47,7 @@ static const gchar *page_name[] = {
"details",
"category",
"extras",
+ "moderate",
};
typedef struct {
@@ -63,6 +65,7 @@ typedef struct
GsShellMode mode;
GsShellOverview *shell_overview;
GsShellInstalled *shell_installed;
+ GsShellModerate *shell_moderate;
GsShellSearch *shell_search;
GsShellUpdates *shell_updates;
GsShellDetails *shell_details;
@@ -189,6 +192,9 @@ gs_shell_change_mode (GsShell *shell,
case GS_SHELL_MODE_INSTALLED:
gs_shell_installed_switch_to (priv->shell_installed, scroll_up);
break;
+ case GS_SHELL_MODE_MODERATE:
+ gs_shell_moderate_switch_to (priv->shell_moderate, scroll_up);
+ break;
case GS_SHELL_MODE_SEARCH:
widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "entry_search"));
text = gtk_entry_get_text (GTK_ENTRY (widget));
@@ -647,6 +653,12 @@ gs_shell_setup (GsShell *shell, GsPluginLoader *plugin_loader, GCancellable *can
priv->plugin_loader,
priv->builder,
priv->cancellable);
+ priv->shell_moderate = GS_SHELL_MODERATE (gtk_builder_get_object (priv->builder, "shell_moderate"));
+ gs_shell_moderate_setup (priv->shell_moderate,
+ shell,
+ priv->plugin_loader,
+ priv->builder,
+ priv->cancellable);
priv->shell_search = GS_SHELL_SEARCH (gtk_builder_get_object (priv->builder, "shell_search"));
gs_shell_search_setup (priv->shell_search,
shell,
diff --git a/src/gs-shell.h b/src/gs-shell.h
index 7f64277..ab41856 100644
--- a/src/gs-shell.h
+++ b/src/gs-shell.h
@@ -50,6 +50,7 @@ typedef enum {
GS_SHELL_MODE_DETAILS,
GS_SHELL_MODE_CATEGORY,
GS_SHELL_MODE_EXTRAS,
+ GS_SHELL_MODE_MODERATE,
GS_SHELL_MODE_LAST
} GsShellMode;
diff --git a/src/plugins/gs-plugin-xdg-app-reviews.c b/src/plugins/gs-plugin-xdg-app-reviews.c
index b86a157..e3a1f02 100644
--- a/src/plugins/gs-plugin-xdg-app-reviews.c
+++ b/src/plugins/gs-plugin-xdg-app-reviews.c
@@ -319,47 +319,6 @@ xdg_app_review_parse_success (const gchar *data,
return TRUE;
}
-#if 0
-/**
- * xdg_app_review_get_moderate:
- */
-static GPtrArray *
-xdg_app_review_get_moderate (SoupSession *session,
- const gchar *user_hash,
- GError **error)
-{
- guint status_code;
- g_autofree gchar *uri = NULL;
- g_autoptr(SoupMessage) msg = NULL;
- g_autoptr(GFile) cachefn_file = NULL;
- g_autoptr(GPtrArray) reviews = NULL;
-
- /* create the GET data *with* the machine hash so we can later
- * review the application ourselves */
- uri = g_strdup_printf ("%s/moderate/%s",
- plugin->priv->review_server,
- user_hash);
- msg = soup_message_new (SOUP_METHOD_GET, uri);
- status_code = soup_session_send_message (session, msg);
- if (status_code != SOUP_STATUS_OK) {
- if (!xdg_app_review_parse_success (msg->response_body->data,
- msg->response_body->length,
- error))
- return NULL;
- /* not sure what to do here */
- g_set_error_literal (error,
- GS_PLUGIN_ERROR,
- GS_PLUGIN_ERROR_FAILED,
- "status code invalid");
- return NULL;
- }
- g_debug ("xdg-app-review returned: %s", msg->response_body->data);
- return xdg_app_review_parse_reviews (msg->response_body->data,
- msg->response_body->length,
- error);
-}
-#endif
-
/**
* gs_plugin_xdg_app_reviews_json_post:
*/
@@ -970,6 +929,21 @@ gs_plugin_review_downvote (GsPlugin *plugin,
}
/**
+ * gs_plugin_review_dismiss:
+ */
+gboolean
+gs_plugin_review_dismiss (GsPlugin *plugin,
+ GsApp *app,
+ GsReview *review,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autofree gchar *uri = NULL;
+ uri = g_strdup_printf ("%s/dismiss", plugin->priv->review_server);
+ return gs_plugin_xdg_app_reviews_vote (plugin, review, uri, error);
+}
+
+/**
* gs_plugin_review_remove:
*/
gboolean
@@ -983,3 +957,67 @@ gs_plugin_review_remove (GsPlugin *plugin,
uri = g_strdup_printf ("%s/remove", plugin->priv->review_server);
return gs_plugin_xdg_app_reviews_vote (plugin, review, uri, error);
}
+
+/**
+ * gs_plugin_add_unvoted_reviews:
+ */
+gboolean
+gs_plugin_add_unvoted_reviews (GsPlugin *plugin,
+ GList **list,
+ GCancellable *cancellable,
+ GError **error)
+{
+ const gchar *app_id_last = NULL;
+ guint status_code;
+ guint i;
+ g_autofree gchar *uri = NULL;
+ g_autoptr(GFile) cachefn_file = NULL;
+ g_autoptr(GPtrArray) reviews = NULL;
+ g_autoptr(GsApp) app_current = NULL;
+ g_autoptr(SoupMessage) msg = NULL;
+
+ /* create the GET data *with* the machine hash so we can later
+ * review the application ourselves */
+ uri = g_strdup_printf ("%s/moderate/%s",
+ plugin->priv->review_server,
+ plugin->priv->user_hash);
+ msg = soup_message_new (SOUP_METHOD_GET, uri);
+ status_code = soup_session_send_message (plugin->soup_session, msg);
+ if (status_code != SOUP_STATUS_OK) {
+ if (!xdg_app_review_parse_success (msg->response_body->data,
+ msg->response_body->length,
+ error))
+ return FALSE;
+ /* not sure what to do here */
+ g_set_error_literal (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_FAILED,
+ "status code invalid");
+ return FALSE;
+ }
+ g_debug ("xdg-app-review returned: %s", msg->response_body->data);
+ reviews = xdg_app_review_parse_reviews (msg->response_body->data,
+ msg->response_body->length,
+ error);
+ if (reviews == NULL)
+ return FALSE;
+
+ /* look at all the reviews; faking application objects */
+ for (i = 0; i < reviews->len; i++) {
+ GsReview *review;
+ const gchar *app_id;
+
+ /* same app? */
+ review = g_ptr_array_index (reviews, i);
+ app_id = gs_review_get_metadata_item (review, "app_id");
+ if (g_strcmp0 (app_id, app_id_last) != 0) {
+ g_clear_object (&app_current);
+ app_current = gs_app_new (app_id);
+ gs_plugin_add_app (list, app_current);
+ app_id_last = app_id;
+ }
+ gs_app_add_review (app_current, review);
+ }
+
+ return TRUE;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]