From 837a3b62f9912a64a4f7d80e7850f435ee130ec5 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Thu, 16 Feb 2023 09:08:50 -0600 Subject: [PATCH] Add support for receiving federated quote posts and displaying quote posts (can't source them yet) --- .gitignore | 2 +- src/ap | 2 +- src/model/status.c | 82 +++++++++++++++++++++++++++++++++++-------- src/model/status.h | 8 +++++ src/view/api/Status.c | 46 ++++++++++++++++++++++-- 5 files changed, 121 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index 9beb3d0..b75d110 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ src.bin debug release data/ -assets/soapbox/ +assets/soapbox* backup/ vgcore.* apogee* diff --git a/src/ap b/src/ap index 7f73d77..9b6e772 160000 --- a/src/ap +++ b/src/ap @@ -1 +1 @@ -Subproject commit 7f73d77dd53ce619d2000bec8b5a706b3ba1b366 +Subproject commit 9b6e7725a6356f2a6e776dde60c18201a82d6f88 diff --git a/src/model/status.c b/src/model/status.c index c4d5cfa..1d4a1b5 100644 --- a/src/model/status.c +++ b/src/model/status.c @@ -46,6 +46,7 @@ static struct json_object_field status_layout[] = { JSON_FIELD_DATETIME( published, false ), JSON_FIELD_INTEGER( in_reply_to, false ), + JSON_FIELD_INTEGER( quote_id, false ), JSON_FIELD_INTEGER( repost_id, false ), JSON_FIELD_INTEGER( root_status_id, false ), JSON_FIELD_INTEGER( reposted_status_id, false ), @@ -57,6 +58,7 @@ static struct json_object_field status_layout[] = { JSON_FIELD_ARRAY_OF_INTS( likes, false ), JSON_FIELD_ARRAY_OF_INTS( replies, false ), JSON_FIELD_ARRAY_OF_INTS( reposts, false ), + JSON_FIELD_ARRAY_OF_INTS( quotes, false ), JSON_FIELD_ARRAY_OF_INTS( mentions, false ), JSON_FIELD_ARRAY_OF_TYPE( emoji, false, emoji_type ), @@ -229,7 +231,31 @@ void status_add_mention( struct status* s, int id ) } void status_add_repost( struct status* s, struct status* repost ) { - // TODO: implement + if( repost->id == 0 ) { return; } + + for( int i = 0; i < s->reposts.count; ++i ) { + if( s->reposts.items[i] == repost->id ) { + return; + } + } + + int id = repost->id; + array_append( &s->reposts, sizeof(id), &id ); + status_save(s); +} +void status_add_quote( struct status* s, struct status* quote ) +{ + if( quote->id == 0 ) { return; } + + for( int i = 0; i < s->quotes.count; ++i ) { + if( s->quotes.items[i] == quote->id ) { + return; + } + } + + int id = quote->id; + array_append( &s->quotes, sizeof(id), &id ); + status_save(s); } bool status_sync_from_activity_pub( struct status* s, struct ap_object* act ) { @@ -260,6 +286,14 @@ bool status_sync_from_activity_pub( struct status* s, struct ap_object* act ) status_free(parent); } } + if( act->quote_url ) { + struct status* parent = status_from_uri_or_fetch( act->quote_url ); + if( parent ) { + status_make_quote_of( s, parent->id ); + // DO NOT SAVE parent! This is done inside status_make_quote_of + status_free(parent); + } + } for( int i = 0; i < s->emoji.count; ++i ) { emoji_free( s->emoji.items[i] ); @@ -588,6 +622,7 @@ void status_free( struct status* s ) free(s->likes.items); free(s->replies.items); free(s->reposts.items); + free(s->quotes.items); free(s->mentions.items); for( int i = 0; i < s->emoji.count; ++i ) { @@ -634,26 +669,45 @@ void status_make_reply_to( struct status* s, int in_reply_to_id ) struct status* in_reply_to = status_from_id( in_reply_to_id ); if( !in_reply_to ) { s->in_reply_to = 0; - } else { - // Setup this status's reply fields - s->in_reply_to = in_reply_to_id; - s->root_status_id = in_reply_to->root_status_id; + return; + } - // Record in parent as reply - status_add_reply( in_reply_to, s ); + // Setup this status's reply fields + s->in_reply_to = in_reply_to_id; + s->root_status_id = in_reply_to->root_status_id; - // Mention the account that made the post being replied to - status_add_mention( s, in_reply_to->account_id ); + // Record in parent as reply + status_add_reply( in_reply_to, s ); - // Mention everytone else in that post - for( int i = 0; i < in_reply_to->mentions.count; ++i ) { - status_add_mention( s, in_reply_to->mentions.items[i] ); - } + // Mention the account that made the post being replied to + status_add_mention( s, in_reply_to->account_id ); - status_save(in_reply_to); + // Mention everyone else in that post + for( int i = 0; i < in_reply_to->mentions.count; ++i ) { + status_add_mention( s, in_reply_to->mentions.items[i] ); } + + status_save(in_reply_to); status_free(in_reply_to); } +void status_make_quote_of( struct status* s, int id_quote_of ) +{ + struct status* quoted_post = status_from_id( id_quote_of ); + if( !quoted_post ) { + s->quote_id = 0; + return; + } + + // Set quoted id + s->quote_id = id_quote_of; + + // Record quote in parent + status_add_quote( quoted_post, s ); + + // Save and cleanup + status_save( quoted_post ); + status_free( quoted_post ); +} void status_get_context( struct status* s, void* ancestors_ptr, void* replies_ptr ) { struct array_of_status { diff --git a/src/model/status.h b/src/model/status.h index 8c571e9..ab4eff3 100644 --- a/src/model/status.h +++ b/src/model/status.h @@ -26,6 +26,7 @@ struct status time_t published; int in_reply_to; + int quote_id; int repost_id; int root_status_id; int reposted_status_id; // if this post was reposted, this will be the id of that status @@ -60,6 +61,11 @@ struct status int count; } reposts; + struct { + int* items; + int count; + } quotes; + struct { int* items; int count; @@ -101,7 +107,9 @@ void status_get_context( struct status* s, void* ancestors, void* replies ); void status_add_reply( struct status* s, struct status* child ); void status_add_mention( struct status* s, int id ); void status_add_repost( struct status* s, struct status* repost ); +void status_add_quote( struct status* s, struct status* quote ); void status_make_reply_to( struct status* s, int in_reply_to ); +void status_make_quote_of( struct status* s, int id_quote_of ); void status_add_react( struct status* s, const char* react, struct account* a ); void status_remove_react( struct status* s, const char* react, struct account* a ); void status_add_like( struct status* s, struct account* a ); diff --git a/src/view/api/Status.c b/src/view/api/Status.c index ba44eb4..f40492f 100644 --- a/src/view/api/Status.c +++ b/src/view/api/Status.c @@ -278,6 +278,27 @@ bool is_local_callback( void* field_data, bool is_read, bool* val ) *val = !s->remote; return true; } +bool quote_url_callback( void* field_data, bool is_read, char** val ) +{ + if( is_read ) { return false; } + + struct status* s = field_data; + if( s->quote_id == 0 ) { return false; } + + struct status* quoted_post = status_from_id(s->quote_id); + *val = strdup(quoted_post->url ); + status_free(quoted_post); + return true; +} +bool quote_visible_callback( void* field_data, bool is_read, bool* val ) +{ + if( is_read ) { return false; } + + struct status* s = field_data; + + *val = ( s->quote_id != 0 ); + return true; +} #define OBJ_TYPE struct status static struct json_object_field content_layout[] = { @@ -332,9 +353,28 @@ static struct json_object_field pleroma_layout[] = { }, JSON_FIELD_FIXED_BOOL( parent_visible, true ), JSON_FIELD_FIXED_NULL( pinned_at ), - JSON_FIELD_FIXED_NULL( quote ), - JSON_FIELD_FIXED_NULL( quote_url ), - JSON_FIELD_FIXED_BOOL( quote_visible, false ), + { + .key = "quote", + .offset = offsetof( OBJ_TYPE, quote_id ), + .type = &Status_reference_type, + }, + { + .key = "quote_url", + .offset = 0, + .type = &json_field_callback_string, + .string_callback = quote_url_callback, + }, + { + .key = "quote_visible", + .offset = 0, + .type = &json_field_bool_callback, + .bool_callback = quote_visible_callback, + }, + { + .key = "quotes_count", + .offset = offsetof( OBJ_TYPE, quotes.count ), + .type = &json_field_integer, + }, /* "spoiler_text": { "text/plain": ""