~tristan957/tllt-cp

f12202e48f45bb0fff58a40f3786bca9d1ed3c75 — Tristan Partin 1 year, 6 months ago a0126bd
Present user with dialog for feedback on recipe

Bug exists however in that if you manually cancel the recipe, you still see the dialog
A lib/dto/tllt-cp-user-feedback-dto.c => lib/dto/tllt-cp-user-feedback-dto.c +81 -0
@@ 0,0 1,81 @@
#include <glib-object.h>
#include <glib/gi18n.h>

#include "tllt-cp-user-feedback-dto.h"
#include "tllt-cp-user.h"

struct _TlltCpUserFeedbackDto
{
	GObject parent_instance;
};

typedef struct TlltCpUserFeedbackDtoPrivate
{
	TlltCpUserFeedback feedback;
} TlltCpUserFeedbackDtoPrivate;

G_DEFINE_TYPE_WITH_PRIVATE(TlltCpUserFeedbackDto, tllt_cp_user_feedback_dto, G_TYPE_OBJECT)

typedef enum TlltCpUserFeedbackDtoProps
{
	PROP_FEEDBACK = 1,
	N_PROPS,
} TlltCpUserFeedbackDtoProps;

static GParamSpec *obj_properties[N_PROPS];

static void
tllt_cp_user_feedback_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *pspec)
{
	TlltCpUserFeedbackDto *self		   = TLLT_CP_USER_FEEDBACK_DTO(obj);
	TlltCpUserFeedbackDtoPrivate *priv = tllt_cp_user_feedback_dto_get_instance_private(self);

	switch (prop_id) {
	case PROP_FEEDBACK:
		g_value_set_uint(val, priv->feedback);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
	}
}

static void
tllt_cp_user_feedback_set_property(GObject *obj, guint prop_id, const GValue *val,
								   GParamSpec *pspec)
{
	TlltCpUserFeedbackDto *self		   = TLLT_CP_USER_FEEDBACK_DTO(obj);
	TlltCpUserFeedbackDtoPrivate *priv = tllt_cp_user_feedback_dto_get_instance_private(self);

	switch (prop_id) {
	case PROP_FEEDBACK:
		priv->feedback = g_value_get_uint(val);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
	}
}

static void
tllt_cp_user_feedback_dto_class_init(TlltCpUserFeedbackDtoClass *klass)
{
	GObjectClass *obj_class = G_OBJECT_CLASS(klass);

	obj_class->get_property = tllt_cp_user_feedback_get_property;
	obj_class->set_property = tllt_cp_user_feedback_set_property;

	obj_properties[PROP_FEEDBACK] =
		g_param_spec_uint("feedback", _("Feedback"), _("User feedback"), 0, MORE_TOASTY, 0,
						  G_PARAM_PRIVATE | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);

	g_object_class_install_properties(obj_class, N_PROPS, obj_properties);
}

static void
tllt_cp_user_feedback_dto_init(G_GNUC_UNUSED TlltCpUserFeedbackDto *self)
{}

TlltCpUserFeedbackDto *
tllt_cp_user_feedback_new(const TlltCpUserFeedback feedback)
{
	return g_object_new(TLLT_CP_TYPE_USER_FEEDBACK_DTO, "feedback", feedback, NULL);
}

A lib/dto/tllt-cp-user-feedback-dto.h => lib/dto/tllt-cp-user-feedback-dto.h +19 -0
@@ 0,0 1,19 @@
#pragma once

#if !defined(__TLLT_CP_HEADER_INTERNAL__) && !defined(TLLT_CP_COMPILATION)
#	error "Only <tllt-cp.h> can be included directly."
#endif

#include <glib-object.h>

#include "tllt-cp-user.h"

G_BEGIN_DECLS

#define TLLT_CP_TYPE_USER_FEEDBACK_DTO (tllt_cp_user_feedback_dto_get_type())
G_DECLARE_FINAL_TYPE(TlltCpUserFeedbackDto, tllt_cp_user_feedback_dto, TLLT_CP, USER_FEEDBACK_DTO,
					 GObject)

TlltCpUserFeedbackDto *tllt_cp_user_feedback_new(const TlltCpUserFeedback);

G_END_DECLS

M lib/meson.build => lib/meson.build +2 -0
@@ 28,6 28,7 @@ sources = [
	'dto/tllt-cp-cooking-details-dto.c',
	'dto/tllt-cp-create-recipe-dto.c',
	'dto/tllt-cp-create-user-dto.c',
	'dto/tllt-cp-user-feedback-dto.c',
	'tllt-cp-client.c',
	'tllt-cp-error.c',
	'tllt-cp-recipe.c',


@@ 39,6 40,7 @@ public_headers = [
	'dto/tllt-cp-cooking-details-dto.h',
	'dto/tllt-cp-create-recipe-dto.h',
	'dto/tllt-cp-create-user-dto.h',
	'dto/tllt-cp-user-feedback-dto.h',
	'tllt-cp.h',
	'tllt-cp-client.h',
	'tllt-cp-error.h',

M lib/tllt-cp-client.c => lib/tllt-cp-client.c +8 -1
@@ 228,9 228,11 @@ tllt_cp_client_post_request(TlltCpClient *self, const GType type, const char *en
			g_free(deserialized);
			goto on_error;
		}

		g_free(deserialized);
	}

	// if (type != G_TYPE_NONE) {
	if ((req_code = curl_easy_setopt(self->handle, CURLOPT_WRITEFUNCTION,
									 tllt_cp_client_write_cb)) != CURLE_OK) {
		g_set_error(err, PACKAGE_DOMAIN, ERROR_CURL, "Failed to set CURLOPT_WRITEFUNCTION: %s",


@@ 242,6 244,8 @@ tllt_cp_client_post_request(TlltCpClient *self, const GType type, const char *en
					curl_easy_strerror(req_code));
		goto on_error;
	}
	// }

	if ((req_code = curl_easy_setopt(self->handle, CURLOPT_USERAGENT, "libcurl/tllt-cp")) !=
		CURLE_OK) {
		g_set_error(err, PACKAGE_DOMAIN, ERROR_CURL, "Failed to set CURLOPT_USERAGENT: %s",


@@ 288,7 292,10 @@ tllt_cp_client_post_request(TlltCpClient *self, const GType type, const char *en
		goto on_error;
	}

	GObject *obj = json_gobject_from_data(type, buffer.buf, -1, err);
	GObject *obj = NULL;
	if (type != G_TYPE_NONE) {
		obj = json_gobject_from_data(type, buffer.buf, -1, err);
	}

	curl_slist_free_all(headers);
	curl_easy_reset(self->handle);

M lib/tllt-cp-client.h => lib/tllt-cp-client.h +2 -1
@@ 18,6 18,7 @@ typedef struct TlltCpBuffer
struct _TlltCpClient
{
	GObject parent_instance;

	CURL *handle;
	char *server;
};


@@ 29,7 30,7 @@ TlltCpClient *tllt_cp_client_new_from_environment();
GObject *tllt_cp_client_get_request(TlltCpClient *self, const GType type, const char *endpoint,
									GError **err) G_GNUC_WARN_UNUSED_RESULT;
GObject *tllt_cp_client_post_request(TlltCpClient *self, const GType type, const char *endpoint,
									 GObject *data, GError **err) G_GNUC_WARN_UNUSED_RESULT;
									 GObject *data, GError **err);
size_t tllt_cp_client_write_cb(char *ptr, size_t size, size_t nmemb, void *user_data);

G_END_DECLS

M lib/tllt-cp-user.c => lib/tllt-cp-user.c +18 -0
@@ 8,6 8,7 @@
#include "dto/tllt-cp-authentication-dto.h"
#include "dto/tllt-cp-create-recipe-dto.h"
#include "dto/tllt-cp-create-user-dto.h"
#include "dto/tllt-cp-user-feedback-dto.h"
#include "tllt-cp-client.h"
#include "tllt-cp-recipe.h"
#include "tllt-cp-user.h"


@@ 306,3 307,20 @@ tllt_cp_user_get_cooking_details_for_recipe(TlltCpUser *self, TlltCpClient *clie

	return TLLT_CP_COOKING_DETAILS_DTO(obj);
}

void
tllt_cp_user_adjust_scale(TlltCpUser *self, TlltCpClient *client, const TlltCpUserFeedback choice,
						  GError **err)
{
	g_return_if_fail(err == NULL || *err == NULL);

	TlltCpUserPrivate *priv = tllt_cp_user_get_instance_private(self);

	g_autoptr(GString) endpoint = g_string_new(client->server);
	g_string_append_printf(endpoint, "/users/%u/userScale", priv->id);
	g_autoptr(TlltCpUserFeedbackDto) dto = tllt_cp_user_feedback_new(choice);

	tllt_cp_client_post_request(client, G_TYPE_NONE, endpoint->str, G_OBJECT(dto), err);

	g_return_if_fail(err == NULL || *err == NULL);
}

M lib/tllt-cp-user.h => lib/tllt-cp-user.h +9 -0
@@ 20,6 20,13 @@ struct _TlltCpUser
	GObject parent_instance;
};

typedef enum TlltCpUserFeedback
{
	LESS_TOASTY = 1,
	THE_SAME,
	MORE_TOASTY,
} TlltCpUserFeedback;

TlltCpUser *tllt_cp_user_get_by_id(TlltCpClient *client, unsigned int id, GError **err);
TlltCpUser *tllt_cp_user_authenticate(TlltCpClient *client, const char *email, const char *password,
									  GError **err);


@@ 36,5 43,7 @@ TlltCpCookingDetailsDto *tllt_cp_user_get_cooking_details_for_recipe(TlltCpUser 
																	 TlltCpClient *client,
																	 TlltCpRecipe *recipe,
																	 GError **err);
void tllt_cp_user_adjust_scale(TlltCpUser *self, TlltCpClient *client,
							   const TlltCpUserFeedback choice, GError **err);

G_END_DECLS

M src/tllt-cp-feedback-dialog.c => src/tllt-cp-feedback-dialog.c +45 -8
@@ 13,6 13,7 @@ struct _TlltCpFeedbackDialog
typedef struct TlltCpFeedbackDialogPrivate
{
	TlltCpUser *user;
	TlltCpClient *client;
} TlltCpFeedbackDialogPrivate;

G_DEFINE_TYPE_WITH_PRIVATE(TlltCpFeedbackDialog, tllt_cp_feedback_dialog, GTK_TYPE_MESSAGE_DIALOG)


@@ 20,6 21,7 @@ G_DEFINE_TYPE_WITH_PRIVATE(TlltCpFeedbackDialog, tllt_cp_feedback_dialog, GTK_TY
typedef enum TlltCpFeedbackDialogProps
{
	PROP_USER = 1,
	PROP_CLIENT,
	N_PROPS,
} TlltCpFeedbackDialogProps;



@@ 32,6 34,7 @@ tllt_cp_feedback_dialog_finalize(GObject *obj)
	TlltCpFeedbackDialogPrivate *priv = tllt_cp_feedback_dialog_get_instance_private(self);

	g_object_unref(priv->user);
	g_object_unref(priv->client);

	G_OBJECT_CLASS(tllt_cp_feedback_dialog_parent_class)->finalize(obj);
}


@@ 46,6 49,9 @@ tllt_cp_feedback_dialog_get_property(GObject *obj, guint prop_id, GValue *val, G
	case PROP_USER:
		g_value_set_object(val, priv->user);
		break;
	case PROP_CLIENT:
		g_value_set_object(val, priv->client);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
	}


@@ 60,8 66,17 @@ tllt_cp_feedback_dialog_set_property(GObject *obj, guint prop_id, const GValue *

	switch (prop_id) {
	case PROP_USER:
		if (priv->user != NULL) {
			g_object_unref(priv->user);
		}
		priv->user = g_value_dup_object(val);
		break;
	case PROP_CLIENT:
		if (priv->client != NULL) {
			g_object_unref(priv->client);
		}
		priv->client = g_value_dup_object(val);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
	}


@@ 70,9 85,15 @@ tllt_cp_feedback_dialog_set_property(GObject *obj, guint prop_id, const GValue *
static void
on_less_toasty_clicked(G_GNUC_UNUSED GtkButton *widget, G_GNUC_UNUSED gpointer user_data)
{
	TlltCpFeedbackDialog *self = TLLT_CP_FEEDBACK_DIALOG(user_data);
	TlltCpFeedbackDialog *self		  = TLLT_CP_FEEDBACK_DIALOG(user_data);
	TlltCpFeedbackDialogPrivate *priv = tllt_cp_feedback_dialog_get_instance_private(self);

	g_print("less toasty\n");
	g_autoptr(GError) err = NULL;
	tllt_cp_user_adjust_scale(priv->user, priv->client, LESS_TOASTY, &err);

	if (err != NULL) {
		g_printerr("%s\n", err->message);
	}

	gtk_window_close(GTK_WINDOW(self));
}


@@ 80,9 101,15 @@ on_less_toasty_clicked(G_GNUC_UNUSED GtkButton *widget, G_GNUC_UNUSED gpointer u
static void
on_the_same_clicked(G_GNUC_UNUSED GtkButton *widget, G_GNUC_UNUSED gpointer user_data)
{
	TlltCpFeedbackDialog *self = TLLT_CP_FEEDBACK_DIALOG(user_data);
	TlltCpFeedbackDialog *self		  = TLLT_CP_FEEDBACK_DIALOG(user_data);
	TlltCpFeedbackDialogPrivate *priv = tllt_cp_feedback_dialog_get_instance_private(self);

	g_print("the same\n");
	g_autoptr(GError) err = NULL;
	tllt_cp_user_adjust_scale(priv->user, priv->client, THE_SAME, &err);

	if (err != NULL) {
		g_printerr("%s\n", err->message);
	}

	gtk_window_close(GTK_WINDOW(self));
}


@@ 90,9 117,15 @@ on_the_same_clicked(G_GNUC_UNUSED GtkButton *widget, G_GNUC_UNUSED gpointer user
static void
on_more_toasty_clicked(G_GNUC_UNUSED GtkButton *widget, G_GNUC_UNUSED gpointer user_data)
{
	TlltCpFeedbackDialog *self = TLLT_CP_FEEDBACK_DIALOG(user_data);
	TlltCpFeedbackDialog *self		  = TLLT_CP_FEEDBACK_DIALOG(user_data);
	TlltCpFeedbackDialogPrivate *priv = tllt_cp_feedback_dialog_get_instance_private(self);

	g_print("more toasty\n");
	g_autoptr(GError) err = NULL;
	tllt_cp_user_adjust_scale(priv->user, priv->client, MORE_TOASTY, &err);

	if (err != NULL) {
		g_printerr("%s\n", err->message);
	}

	gtk_window_close(GTK_WINDOW(self));
}


@@ 110,6 143,9 @@ tllt_cp_feedback_dialog_class_init(TlltCpFeedbackDialogClass *klass)
	obj_properties[PROP_USER] =
		g_param_spec_object("user", _("User"), _("User providing the feedback"), TLLT_CP_TYPE_USER,
							G_PARAM_PRIVATE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE);
	obj_properties[PROP_CLIENT] = g_param_spec_object(
		"client", _("Client"), _("Client to talk to the server"), TLLT_CP_TYPE_CLIENT,
		G_PARAM_PRIVATE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE);

	g_object_class_install_properties(obj_class, N_PROPS, obj_properties);



@@ 127,7 163,8 @@ tllt_cp_feedback_dialog_init(TlltCpFeedbackDialog *self)
}

TlltCpFeedbackDialog *
tllt_cp_feedback_dialog_new(TlltCpUser *user)
tllt_cp_feedback_dialog_new(GtkWindow *parent, TlltCpUser *user, TlltCpClient *client)
{
	return g_object_new(TLLT_CP_TYPE_FEEDBACK_DIALOG, "user", user, NULL);
	return g_object_new(TLLT_CP_TYPE_FEEDBACK_DIALOG, "transient-for", parent, "user", user,
						"client", client, NULL);
}

M src/tllt-cp-feedback-dialog.h => src/tllt-cp-feedback-dialog.h +2 -1
@@ 11,6 11,7 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE(TlltCpFeedbackDialog, tllt_cp_feedback_dialog, TLLT_CP, FEEDBACK_DIALOG,
					 GtkMessageDialog)

TlltCpFeedbackDialog *tllt_cp_feedback_dialog_new(TlltCpUser *user);
TlltCpFeedbackDialog *tllt_cp_feedback_dialog_new(GtkWindow *parent, TlltCpUser *user,
												  TlltCpClient *client);

G_END_DECLS

M src/tllt-cp-new-recipe-window.c => src/tllt-cp-new-recipe-window.c +8 -0
@@ 69,9 69,15 @@ tllt_cp_new_recipe_window_set_property(GObject *obj, guint prop_id, const GValue

	switch (prop_id) {
	case PROP_CLIENT:
		if (priv->client != NULL) {
			g_object_unref(priv->client);
		}
		priv->client = g_value_dup_object(val);
		break;
	case PROP_AUTHOR:
		if (priv->author != NULL) {
			g_object_unref(priv->author);
		}
		priv->author = g_value_dup_object(val);
		break;
	default:


@@ 102,6 108,8 @@ on_create_recipe_button_clicked(G_GNUC_UNUSED GtkButton *widget, gpointer user_d
	if (err != NULL) {
		g_printerr("%s\n", err->message);
	}

	gtk_window_close(GTK_WINDOW(self));
}

static void

M src/tllt-cp-window.c => src/tllt-cp-window.c +6 -4
@@ 363,14 363,16 @@ on_toaster_stopped(G_GNUC_UNUSED TlltToaster *toaster, gpointer user_data)
	TlltCpWindow *self		  = TLLT_CP_WINDOW(user_data);
	TlltCpWindowPrivate *priv = tllt_cp_window_get_instance_private(self);

	priv->toaster_user			   = NULL;
	priv->currently_running_recipe = NULL;

	if (priv->toaster_user != NULL && priv->currently_running_recipe != NULL) {
		TlltCpFeedbackDialog *dialog = tllt_cp_feedback_dialog_new(priv->toaster_user);
		TlltCpFeedbackDialog *dialog =
			tllt_cp_feedback_dialog_new(GTK_WINDOW(self), priv->toaster_user, priv->client);
		gtk_dialog_run(GTK_DIALOG(dialog));
		gtk_widget_destroy(GTK_WIDGET(dialog));
	}

	priv->toaster_user			   = NULL;
	priv->currently_running_recipe = NULL;

	g_object_unref(self);	// Refers to ref when toaster is started

	gtk_widget_set_visible(GTK_WIDGET(priv->toasting_status_box), FALSE);