Tue, 10 Oct 2023 01:21:52 -0500
Add an edited property to PurpleMessage
Testing Done:
Ran the unit tests under valgrind.
Reviewed at https://reviews.imfreedom.org/r/2640/
--- a/libpurple/purplemessage.c Tue Oct 10 01:20:37 2023 -0500 +++ b/libpurple/purplemessage.c Tue Oct 10 01:21:52 2023 -0500 @@ -46,6 +46,7 @@ GError *error; GDateTime *delivered_at; + GDateTime *edited_at; GHashTable *attachments; }; @@ -65,6 +66,8 @@ PROP_ERROR, PROP_DELIVERED, PROP_DELIVERED_AT, + PROP_EDITED, + PROP_EDITED_AT, N_PROPERTIES }; static GParamSpec *properties[N_PROPERTIES]; @@ -132,6 +135,12 @@ case PROP_DELIVERED_AT: g_value_set_boxed(value, purple_message_get_delivered_at(message)); break; + case PROP_EDITED: + g_value_set_boolean(value, purple_message_get_edited(message)); + break; + case PROP_EDITED_AT: + g_value_set_boxed(value, purple_message_get_edited_at(message)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec); break; @@ -185,6 +194,12 @@ case PROP_DELIVERED_AT: purple_message_set_delivered_at(message, g_value_get_boxed(value)); break; + case PROP_EDITED: + purple_message_set_edited(message, g_value_get_boolean(value)); + break; + case PROP_EDITED_AT: + purple_message_set_edited_at(message, g_value_get_boxed(value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec); break; @@ -204,11 +219,9 @@ g_clear_error(&message->error); - if(message->timestamp != NULL) { - g_date_time_unref(message->timestamp); - } - + g_clear_pointer(&message->timestamp, g_date_time_unref); g_clear_pointer(&message->delivered_at, g_date_time_unref); + g_clear_pointer(&message->edited_at, g_date_time_unref); g_hash_table_destroy(message->attachments); @@ -408,6 +421,36 @@ G_TYPE_DATE_TIME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + /** + * PurpleMessaged:edited: + * + * Whether or not this message has been edited. + * + * This should typically only be set by a protocol plugin. + * + * Since: 3.0.0 + */ + properties[PROP_EDITED] = g_param_spec_boolean( + "edited", "edited", + "Whether or not this message has been edited.", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * PurpleMessage:edit-at: + * + * The time that the message was last edited at. This is protocol dependent + * and possibly client dependent as well. So if this is %NULL that doesn't + * necessarily mean the message was not edited. + * + * Since: 3.0.0 + */ + properties[PROP_EDITED_AT] = g_param_spec_boxed( + "edited-at", "edited-at", + "The time that the message was last edited.", + G_TYPE_DATE_TIME, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_properties(obj_class, N_PROPERTIES, properties); } @@ -801,11 +844,6 @@ obj = G_OBJECT(message); - /* If there are any changes here, we need to manually change both delivered - * and delivered-at because we can't call purple_message_set_delivered as - * it would need to call us, making an infinite loop. - */ - if(datetime == NULL) { if(message->delivered_at == NULL) { return; @@ -830,3 +868,64 @@ g_object_notify_by_pspec(obj, properties[PROP_DELIVERED_AT]); g_object_thaw_notify(obj); } + +gboolean +purple_message_get_edited(PurpleMessage *message) { + g_return_val_if_fail(PURPLE_IS_MESSAGE(message), FALSE); + + return (message->edited_at != NULL); +} + +void +purple_message_set_edited(PurpleMessage *message, gboolean edited) { + GDateTime *datetime = NULL; + + g_return_if_fail(PURPLE_IS_MESSAGE(message)); + + if(edited) { + datetime = g_date_time_new_now_utc(); + } + + purple_message_set_edited_at(message, datetime); + g_clear_pointer(&datetime, g_date_time_unref); +} + +GDateTime * +purple_message_get_edited_at(PurpleMessage *message) { + g_return_val_if_fail(PURPLE_IS_MESSAGE(message), NULL); + + return message->edited_at; +} + +void +purple_message_set_edited_at(PurpleMessage *message, GDateTime *datetime) { + GObject *obj = NULL; + + g_return_if_fail(PURPLE_IS_MESSAGE(message)); + + obj = G_OBJECT(message); + + if(datetime == NULL) { + if(message->edited_at == NULL) { + return; + } + + g_clear_pointer(&message->edited_at, g_date_time_unref); + } else { + if(message->edited_at != NULL) { + if(g_date_time_equal(message->edited_at, datetime)) { + return; + } + + g_clear_pointer(&message->edited_at, g_date_time_unref); + message->edited_at = g_date_time_ref(datetime); + } else { + message->edited_at = g_date_time_ref(datetime); + } + } + + g_object_freeze_notify(obj); + g_object_notify_by_pspec(obj, properties[PROP_EDITED]); + g_object_notify_by_pspec(obj, properties[PROP_EDITED_AT]); + g_object_thaw_notify(obj); +}
--- a/libpurple/purplemessage.h Tue Oct 10 01:20:37 2023 -0500 +++ b/libpurple/purplemessage.h Tue Oct 10 01:21:52 2023 -0500 @@ -561,6 +561,60 @@ */ void purple_message_set_delivered_at(PurpleMessage *message, GDateTime *datetime); +/** + * purple_message_get_edited: + * @message: The instance. + * + * Gets whether or not @message has been edited. + * + * Returns: %TRUE if edited, otherwise %FALSE. + * + * Since: 3.0.0 + */ +gboolean purple_message_get_edited(PurpleMessage *message); + +/** + * purple_message_set_edited: + * @message: The instance. + * @edited: The new edited state. + * + * Sets the edited state of @message to @edited. + * + * > Note: Setting this will also set [property@Message:edited-at]. If + * @edited is %TRUE it will be set to the current time, otherwise it will be + * unset. + * + * Since: 3.0.0 + */ +void purple_message_set_edited(PurpleMessage *message, gboolean edited); + +/** + * purple_message_get_edited_at: + * @message: The instance. + * + * Gets the time that @message was last edited. If @message has never been + * edited this will be %NULL. + * + * Returns: (transfer none) (nullable): The last edit time of @message. + * + * Since: 3.0.0 + */ +GDateTime *purple_message_get_edited_at(PurpleMessage *message); + +/** + * purple_message_set_edited_at: + * @message: The instance. + * @datetime: (nullable): The time of the last edit. + * + * Sets the last edit time of @message to @datetime. + * + * > Note: Setting this will also set [property@Message:edited]. If + * @datetime is %NULL it will be set to %FALSE, otherwise %TRUE. + * + * Since: 3.0.0 + */ +void purple_message_set_edited_at(PurpleMessage *message, GDateTime *datetime); + G_END_DECLS #endif /* PURPLE_MESSAGE_H */
--- a/libpurple/tests/test_message.c Tue Oct 10 01:20:37 2023 -0500 +++ b/libpurple/tests/test_message.c Tue Oct 10 01:21:52 2023 -0500 @@ -44,6 +44,7 @@ GDateTime *timestamp = NULL; GDateTime *timestamp1 = NULL; GDateTime *delivered_at1 = NULL; + GDateTime *edited_at1 = NULL; GError *error = NULL; GError *error1 = NULL; char *id = NULL; @@ -54,6 +55,7 @@ char *contents = NULL; gboolean action = FALSE; gboolean delivered = FALSE; + gboolean edited = FALSE; timestamp = g_date_time_new_from_unix_utc(911347200); error = g_error_new(g_quark_from_static_string("test-message"), 0, @@ -71,6 +73,7 @@ "author-alias", "alias", "author-name-color", "purple", "delivered", TRUE, + "edited", TRUE, "recipient", "pidgy", "contents", "Now that is a big door", "content-type", PURPLE_MESSAGE_CONTENT_TYPE_MARKDOWN, @@ -88,6 +91,8 @@ "author-name-color", &author_name_color, "delivered", &delivered, "delivered-at", &delivered_at1, + "edited", &edited, + "edited-at", &edited_at1, "recipient", &recipient, "contents", &contents, "content-type", &content_type, @@ -103,6 +108,8 @@ g_assert_cmpstr(author_name_color, ==, "purple"); g_assert_true(delivered); g_assert_nonnull(delivered_at1); + g_assert_true(edited); + g_assert_nonnull(edited_at1); g_assert_cmpstr(recipient, ==, "pidgy"); g_assert_cmpstr(contents, ==, "Now that is a big door"); g_assert_cmpint(content_type, ==, PURPLE_MESSAGE_CONTENT_TYPE_MARKDOWN); @@ -115,6 +122,7 @@ g_clear_pointer(&author_alias, g_free); g_clear_pointer(&author_name_color, g_free); g_clear_pointer(&delivered_at1, g_date_time_unref); + g_clear_pointer(&edited_at1, g_date_time_unref); g_clear_pointer(&recipient, g_free); g_clear_pointer(&contents, g_free); g_clear_pointer(×tamp, g_date_time_unref); @@ -200,6 +208,83 @@ g_clear_object(&message); } +static void +test_purple_message_edited_set_edited_at(void) { + PurpleMessage *message = NULL; + guint edited_counter = 0; + guint edited_at_counter = 0; + + message = g_object_new(PURPLE_TYPE_MESSAGE, NULL); + g_signal_connect(message, "notify::edited", + G_CALLBACK(test_purple_message_notify_counter_cb), + &edited_counter); + g_signal_connect(message, "notify::edited-at", + G_CALLBACK(test_purple_message_notify_counter_cb), + &edited_at_counter); + + /* The default edit state is FALSE, so setting it to true, should call + * the notify signals and set edited-at. + */ + purple_message_set_edited(message, TRUE); + g_assert_true(purple_message_get_edited(message)); + g_assert_nonnull(purple_message_get_edited_at(message)); + g_assert_cmpuint(edited_counter, ==, 1); + g_assert_cmpuint(edited_at_counter, ==, 1); + + /* Now clear everything and verify it's empty. */ + edited_counter = 0; + edited_at_counter = 0; + + purple_message_set_edited(message, FALSE); + g_assert_false(purple_message_get_edited(message)); + g_assert_null(purple_message_get_edited_at(message)); + g_assert_cmpuint(edited_counter, ==, 1); + g_assert_cmpuint(edited_at_counter, ==, 1); + + g_clear_object(&message); +} + +static void +test_purple_message_edited_at_set_edited(void) { + PurpleMessage *message = NULL; + GDateTime *edited_at = NULL; + GDateTime *edited_at1 = NULL; + guint edited_counter = 0; + guint edited_at_counter = 0; + + message = g_object_new(PURPLE_TYPE_MESSAGE, NULL); + g_signal_connect(message, "notify::edited", + G_CALLBACK(test_purple_message_notify_counter_cb), + &edited_counter); + g_signal_connect(message, "notify::edited-at", + G_CALLBACK(test_purple_message_notify_counter_cb), + &edited_at_counter); + + /* The default value for edited-at is NULL, so setting it to non-null + * should emit the signals and everything. + */ + edited_at = g_date_time_new_now_utc(); + purple_message_set_edited_at(message, edited_at); + g_assert_true(purple_message_get_edited(message)); + edited_at1 = purple_message_get_edited_at(message); + g_assert_nonnull(edited_at1); + g_assert_true(g_date_time_equal(edited_at1, edited_at)); + g_assert_cmpuint(edited_counter, ==, 1); + g_assert_cmpuint(edited_at_counter, ==, 1); + + /* Now clear everything and make sure it's all good. */ + edited_counter = 0; + edited_at_counter = 0; + purple_message_set_edited_at(message, NULL); + g_assert_false(purple_message_get_edited(message)); + g_assert_null(purple_message_get_edited_at(message)); + g_assert_cmpuint(edited_counter, ==, 1); + g_assert_cmpuint(edited_at_counter, ==, 1); + + g_clear_pointer(&edited_at, g_date_time_unref); + g_clear_object(&message); +} + /****************************************************************************** * Main *****************************************************************************/ @@ -215,5 +300,10 @@ g_test_add_func("/message/delivered-at-sets-delivered", test_purple_message_delivered_at_set_delivered); + g_test_add_func("/message/edited-sets-edited-at", + test_purple_message_edited_set_edited_at); + g_test_add_func("/message/edited-at-sets-edited", + test_purple_message_edited_at_set_edited); + return g_test_run(); }