summaryrefslogtreecommitdiffstats
path: root/src/gs-review-row.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gs-review-row.c')
-rw-r--r--src/gs-review-row.c323
1 files changed, 323 insertions, 0 deletions
diff --git a/src/gs-review-row.c b/src/gs-review-row.c
new file mode 100644
index 0000000..9afb484
--- /dev/null
+++ b/src/gs-review-row.c
@@ -0,0 +1,323 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ * vi:set noexpandtab tabstop=8 shiftwidth=8:
+ *
+ * Copyright (C) 2016 Canonical Ltd.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include "gs-review-row.h"
+#include "gs-star-widget.h"
+
+typedef struct
+{
+ AsReview *review;
+ gboolean network_available;
+ guint64 actions;
+ GtkWidget *stars;
+ GtkWidget *summary_label;
+ GtkWidget *author_label;
+ GtkWidget *date_label;
+ GtkWidget *text_label;
+ GtkWidget *button_yes;
+ GtkWidget *button_no;
+ GtkWidget *button_dismiss;
+ GtkWidget *button_report;
+ GtkWidget *button_remove;
+ GtkWidget *box_voting;
+} GsReviewRowPrivate;
+
+enum {
+ SIGNAL_BUTTON_CLICKED,
+ SIGNAL_LAST
+};
+
+static guint signals [SIGNAL_LAST] = { 0 };
+
+G_DEFINE_TYPE_WITH_PRIVATE (GsReviewRow, gs_review_row, GTK_TYPE_LIST_BOX_ROW)
+
+static void
+gs_review_row_refresh (GsReviewRow *row)
+{
+ GsReviewRowPrivate *priv = gs_review_row_get_instance_private (row);
+ const gchar *reviewer;
+ GDateTime *date;
+ g_autofree gchar *text = NULL;
+
+ gs_star_widget_set_rating (GS_STAR_WIDGET (priv->stars),
+ as_review_get_rating (priv->review));
+ reviewer = as_review_get_reviewer_name (priv->review);
+ if (reviewer == NULL) {
+ /* TRANSLATORS: this is when a user doesn't specify a name */
+ reviewer = C_("Reviewer name", "Unknown");
+ }
+ gtk_label_set_text (GTK_LABEL (priv->author_label), reviewer);
+ date = as_review_get_date (priv->review);
+ if (date != NULL)
+ /* TRANSLATORS: This is the date string with: day number, month name, year.
+ i.e. "25 May 2012" */
+ text = g_date_time_format (date, _("%e %B %Y"));
+ else
+ text = g_strdup ("");
+ gtk_label_set_text (GTK_LABEL (priv->date_label), text);
+ gtk_label_set_text (GTK_LABEL (priv->summary_label),
+ as_review_get_summary (priv->review));
+ gtk_label_set_text (GTK_LABEL (priv->text_label),
+ as_review_get_description (priv->review));
+
+ /* if we voted, we can't do any actions */
+ if (as_review_get_flags (priv->review) & AS_REVIEW_FLAG_VOTED)
+ priv->actions = 0;
+
+ /* set actions up */
+ if ((priv->actions & (1 << GS_REVIEW_ACTION_UPVOTE |
+ 1 << GS_REVIEW_ACTION_DOWNVOTE |
+ 1 << GS_REVIEW_ACTION_DISMISS)) == 0) {
+ gtk_widget_set_visible (priv->box_voting, FALSE);
+ } else {
+ gtk_widget_set_visible (priv->box_voting, TRUE);
+ gtk_widget_set_visible (priv->button_yes,
+ 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);
+ gtk_widget_set_visible (priv->button_report,
+ priv->actions & 1 << GS_REVIEW_ACTION_REPORT);
+
+ /* mark insensitive if no network */
+ if (priv->network_available) {
+ gtk_widget_set_sensitive (priv->button_yes, TRUE);
+ gtk_widget_set_sensitive (priv->button_no, TRUE);
+ gtk_widget_set_sensitive (priv->button_remove, TRUE);
+ gtk_widget_set_sensitive (priv->button_report, TRUE);
+ } else {
+ gtk_widget_set_sensitive (priv->button_yes, FALSE);
+ gtk_widget_set_sensitive (priv->button_no, FALSE);
+ gtk_widget_set_sensitive (priv->button_remove, FALSE);
+ gtk_widget_set_sensitive (priv->button_report, FALSE);
+ }
+}
+
+void
+gs_review_row_set_network_available (GsReviewRow *review_row, gboolean network_available)
+{
+ GsReviewRowPrivate *priv = gs_review_row_get_instance_private (review_row);
+ priv->network_available = network_available;
+ gs_review_row_refresh (review_row);
+}
+
+static gboolean
+gs_review_row_refresh_idle (gpointer user_data)
+{
+ GsReviewRow *row = GS_REVIEW_ROW (user_data);
+
+ gs_review_row_refresh (row);
+
+ g_object_unref (row);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+gs_review_row_notify_props_changed_cb (GsApp *app,
+ GParamSpec *pspec,
+ GsReviewRow *row)
+{
+ g_idle_add (gs_review_row_refresh_idle, g_object_ref (row));
+}
+
+static void
+gs_review_row_init (GsReviewRow *row)
+{
+ GsReviewRowPrivate *priv = gs_review_row_get_instance_private (row);
+ priv->network_available = TRUE;
+ gtk_widget_init_template (GTK_WIDGET (row));
+}
+
+static void
+gs_review_row_dispose (GObject *object)
+{
+ GsReviewRow *row = GS_REVIEW_ROW (object);
+ GsReviewRowPrivate *priv = gs_review_row_get_instance_private (row);
+
+ g_clear_object (&priv->review);
+
+ G_OBJECT_CLASS (gs_review_row_parent_class)->dispose (object);
+}
+
+static void
+gs_review_row_class_init (GsReviewRowClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->dispose = gs_review_row_dispose;
+
+ signals [SIGNAL_BUTTON_CLICKED] =
+ g_signal_new ("button-clicked",
+ G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GsReviewRowClass, button_clicked),
+ NULL, NULL, g_cclosure_marshal_VOID__UINT,
+ G_TYPE_NONE, 1, G_TYPE_UINT);
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Software/gs-review-row.ui");
+
+ gtk_widget_class_bind_template_child_private (widget_class, GsReviewRow, stars);
+ gtk_widget_class_bind_template_child_private (widget_class, GsReviewRow, summary_label);
+ gtk_widget_class_bind_template_child_private (widget_class, GsReviewRow, author_label);
+ gtk_widget_class_bind_template_child_private (widget_class, GsReviewRow, date_label);
+ 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_voting);
+}
+
+static void
+gs_review_row_button_clicked_upvote_cb (GtkButton *button, GsReviewRow *row)
+{
+ g_signal_emit (row, signals[SIGNAL_BUTTON_CLICKED], 0,
+ GS_REVIEW_ACTION_UPVOTE);
+}
+
+static void
+gs_review_row_button_clicked_downvote_cb (GtkButton *button, GsReviewRow *row)
+{
+ g_signal_emit (row, signals[SIGNAL_BUTTON_CLICKED], 0,
+ GS_REVIEW_ACTION_DOWNVOTE);
+}
+
+static void
+gs_review_row_confirm_cb (GtkDialog *dialog, gint response_id, GsReviewRow *row)
+{
+ if (response_id == GTK_RESPONSE_YES) {
+ g_signal_emit (row, signals[SIGNAL_BUTTON_CLICKED], 0,
+ GS_REVIEW_ACTION_REPORT);
+ }
+ gtk_window_destroy (GTK_WINDOW (dialog));
+}
+
+static void
+gs_review_row_button_clicked_report_cb (GtkButton *button, GsReviewRow *row)
+{
+ GtkWidget *dialog;
+ GtkRoot *root;
+ GtkWidget *widget;
+ g_autoptr(GString) str = NULL;
+
+ str = g_string_new ("");
+
+ /* TRANSLATORS: we explain what the action is going to do */
+ g_string_append (str, _("You can report reviews for abusive, rude, or "
+ "discriminatory behavior."));
+ g_string_append (str, " ");
+
+ /* TRANSLATORS: we ask the user if they really want to do this */
+ g_string_append (str, _("Once reported, a review will be hidden until "
+ "it has been checked by an administrator."));
+
+ root = gtk_widget_get_root (GTK_WIDGET (button));
+ dialog = gtk_message_dialog_new (GTK_WINDOW (root),
+ GTK_DIALOG_MODAL |
+ GTK_DIALOG_DESTROY_WITH_PARENT |
+ GTK_DIALOG_USE_HEADER_BAR,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_CANCEL,
+ "%s",
+ /* TRANSLATORS: window title when
+ * reporting a user-submitted review
+ * for moderation */
+ _("Report Review?"));
+ widget = gtk_dialog_add_button (GTK_DIALOG (dialog),
+ /* TRANSLATORS: button text when
+ * sending a review for moderation */
+ _("Report"),
+ GTK_RESPONSE_YES);
+ gtk_style_context_add_class (gtk_widget_get_style_context (widget),
+ "destructive-action");
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ "%s", str->str);
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (gs_review_row_confirm_cb), row);
+ gtk_window_present (GTK_WINDOW (dialog));
+}
+
+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,
+ GS_REVIEW_ACTION_REMOVE);
+}
+
+AsReview *
+gs_review_row_get_review (GsReviewRow *review_row)
+{
+ GsReviewRowPrivate *priv = gs_review_row_get_instance_private (review_row);
+ return priv->review;
+}
+
+void
+gs_review_row_set_actions (GsReviewRow *review_row, guint64 actions)
+{
+ GsReviewRowPrivate *priv = gs_review_row_get_instance_private (review_row);
+ priv->actions = actions;
+ gs_review_row_refresh (review_row);
+}
+
+/**
+ * gs_review_row_new:
+ * @review: The review to show
+ *
+ * Create a widget suitable for showing an application review.
+ *
+ * Return value: A new @GsReviewRow.
+ **/
+GtkWidget *
+gs_review_row_new (AsReview *review)
+{
+ GsReviewRow *row;
+ GsReviewRowPrivate *priv;
+
+ g_return_val_if_fail (AS_IS_REVIEW (review), NULL);
+
+ row = g_object_new (GS_TYPE_REVIEW_ROW, NULL);
+ priv = gs_review_row_get_instance_private (row);
+ priv->review = g_object_ref (review);
+ g_signal_connect_object (priv->review, "notify::state",
+ G_CALLBACK (gs_review_row_notify_props_changed_cb),
+ row, 0);
+ g_signal_connect_object (priv->button_yes, "clicked",
+ G_CALLBACK (gs_review_row_button_clicked_upvote_cb),
+ row, 0);
+ 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);
+ g_signal_connect_object (priv->button_remove, "clicked",
+ G_CALLBACK (gs_review_row_button_clicked_remove_cb),
+ row, 0);
+ gs_review_row_refresh (row);
+
+ return GTK_WIDGET (row);
+}