diff --git a/src/mastodon_api/accounts/views.rs b/src/mastodon_api/accounts/views.rs index 49bd505..d64f487 100644 --- a/src/mastodon_api/accounts/views.rs +++ b/src/mastodon_api/accounts/views.rs @@ -21,10 +21,7 @@ use crate::ethereum::identity::{ }; use crate::ethereum::subscriptions::create_subscription_signature; use crate::mastodon_api::oauth::auth::get_current_user; -use crate::models::posts::helpers::{ - get_actions_for_posts, - get_reposted_posts, -}; +use crate::mastodon_api::statuses::helpers::build_status_list; use crate::mastodon_api::statuses::types::Status; use crate::models::posts::queries::get_posts_by_author; use crate::models::profiles::queries::{ @@ -442,7 +439,7 @@ async fn get_account_statuses( }; let profile = get_profile_by_id(db_client, &account_id).await?; // Include reposts but not replies - let mut posts = get_posts_by_author( + let posts = get_posts_by_author( db_client, &profile.id, maybe_current_user.as_ref().map(|user| &user.id), @@ -451,17 +448,12 @@ async fn get_account_statuses( query_params.max_id, query_params.limit, ).await?; - get_reposted_posts(db_client, posts.iter_mut().collect()).await?; - if let Some(user) = maybe_current_user { - get_actions_for_posts( - db_client, - &user.id, - posts.iter_mut().collect(), - ).await?; - } - let statuses: Vec = posts.into_iter() - .map(|post| Status::from_post(post, &config.instance_url())) - .collect(); + let statuses = build_status_list( + db_client, + &config.instance_url(), + maybe_current_user.as_ref(), + posts, + ).await?; Ok(HttpResponse::Ok().json(statuses)) } diff --git a/src/mastodon_api/search/helpers.rs b/src/mastodon_api/search/helpers.rs index a2334ac..e43010d 100644 --- a/src/mastodon_api/search/helpers.rs +++ b/src/mastodon_api/search/helpers.rs @@ -10,11 +10,8 @@ use crate::activitypub::fetcher::helpers::{ use crate::config::Config; use crate::errors::{ValidationError, HttpError}; use crate::mastodon_api::accounts::types::Account; -use crate::mastodon_api::statuses::types::Status; -use crate::models::posts::helpers::{ - can_view_post, - get_actions_for_posts, -}; +use crate::mastodon_api::statuses::helpers::build_status_list; +use crate::models::posts::helpers::can_view_post; use crate::models::posts::types::Post; use crate::models::profiles::queries::{ search_profile, @@ -158,9 +155,11 @@ pub async fn search( let accounts: Vec = profiles.into_iter() .map(|profile| Account::from_profile(profile, &config.instance_url())) .collect(); - get_actions_for_posts(db_client, ¤t_user.id, posts.iter_mut().collect()).await?; - let statuses: Vec = posts.into_iter() - .map(|post| Status::from_post(post, &config.instance_url())) - .collect(); + let statuses = build_status_list( + db_client, + &config.instance_url(), + Some(current_user), + posts, + ).await?; Ok(SearchResults { accounts, statuses }) } diff --git a/src/mastodon_api/statuses/helpers.rs b/src/mastodon_api/statuses/helpers.rs index 4efe379..c10718f 100644 --- a/src/mastodon_api/statuses/helpers.rs +++ b/src/mastodon_api/statuses/helpers.rs @@ -2,10 +2,15 @@ use tokio_postgres::GenericClient; use crate::activitypub::actor::Actor; use crate::errors::DatabaseError; +use crate::models::posts::helpers::{ + add_user_actions, + add_reposted_posts, +}; use crate::models::posts::queries::get_post_author; use crate::models::posts::types::{Post, Visibility}; use crate::models::relationships::queries::{get_followers, get_subscribers}; use crate::models::users::types::User; +use super::types::Status; pub async fn get_note_recipients( db_client: &impl GenericClient, @@ -77,3 +82,35 @@ pub async fn get_announce_recipients( }; Ok(Audience { recipients, primary_recipient }) } + +/// Load related objects and build status for API response +pub async fn build_status( + db_client: &impl GenericClient, + instance_url: &str, + user: Option<&User>, + mut post: Post, +) -> Result { + add_reposted_posts(db_client, vec![&mut post]).await?; + if let Some(user) = user { + add_user_actions(db_client, &user.id, vec![&mut post]).await?; + }; + let status = Status::from_post(post, instance_url); + Ok(status) +} + +pub async fn build_status_list( + db_client: &impl GenericClient, + instance_url: &str, + user: Option<&User>, + mut posts: Vec, +) -> Result, DatabaseError> { + add_reposted_posts(db_client, posts.iter_mut().collect()).await?; + if let Some(user) = user { + add_user_actions(db_client, &user.id, posts.iter_mut().collect()).await?; + }; + let statuses: Vec = posts + .into_iter() + .map(|post| Status::from_post(post, instance_url)) + .collect(); + Ok(statuses) +} diff --git a/src/mastodon_api/statuses/mod.rs b/src/mastodon_api/statuses/mod.rs index 328cdc2..e681737 100644 --- a/src/mastodon_api/statuses/mod.rs +++ b/src/mastodon_api/statuses/mod.rs @@ -1,3 +1,3 @@ -mod helpers; +pub mod helpers; pub mod types; pub mod views; diff --git a/src/mastodon_api/statuses/views.rs b/src/mastodon_api/statuses/views.rs index 01b1453..36a0ea6 100644 --- a/src/mastodon_api/statuses/views.rs +++ b/src/mastodon_api/statuses/views.rs @@ -26,10 +26,6 @@ use crate::models::attachments::queries::set_attachment_ipfs_cid; use crate::models::posts::helpers::can_view_post; use crate::models::posts::mentions::{find_mentioned_profiles, replace_mentions}; use crate::models::posts::tags::{find_tags, replace_tags}; -use crate::models::posts::helpers::{ - get_actions_for_posts, - get_reposted_posts, -}; use crate::models::posts::queries::{ create_post, get_post_by_id, @@ -44,6 +40,8 @@ use crate::models::reactions::queries::{ delete_reaction, }; use super::helpers::{ + build_status, + build_status_list, get_announce_recipients, get_like_recipients, get_note_recipients, @@ -128,6 +126,7 @@ async fn create_status( ); let recipients = get_note_recipients(db_client, ¤t_user, &post).await?; deliver_activity(&config, ¤t_user, activity, recipients); + let status = Status::from_post(post, &instance.url()); Ok(HttpResponse::Created().json(status)) } @@ -144,15 +143,16 @@ async fn get_status( Some(auth) => Some(get_current_user(db_client, auth.token()).await?), None => None, }; - let mut post = get_post_by_id(db_client, &status_id).await?; + let post = get_post_by_id(db_client, &status_id).await?; if !can_view_post(db_client, maybe_current_user.as_ref(), &post).await? { return Err(HttpError::NotFoundError("post")); }; - get_reposted_posts(db_client, vec![&mut post]).await?; - if let Some(user) = maybe_current_user { - get_actions_for_posts(db_client, &user.id, vec![&mut post]).await?; - } - let status = Status::from_post(post, &config.instance_url()); + let status = build_status( + db_client, + &config.instance_url(), + maybe_current_user.as_ref(), + post, + ).await?; Ok(HttpResponse::Ok().json(status)) } @@ -197,23 +197,17 @@ async fn get_context( Some(auth) => Some(get_current_user(db_client, auth.token()).await?), None => None, }; - let mut posts = get_thread( + let posts = get_thread( db_client, &status_id, maybe_current_user.as_ref().map(|user| &user.id), ).await?; - get_reposted_posts(db_client, posts.iter_mut().collect()).await?; - if let Some(user) = maybe_current_user { - get_actions_for_posts( - db_client, - &user.id, - posts.iter_mut().collect(), - ).await?; - } - let statuses: Vec = posts - .into_iter() - .map(|post| Status::from_post(post, &config.instance_url())) - .collect(); + let statuses = build_status_list( + db_client, + &config.instance_url(), + maybe_current_user.as_ref(), + posts, + ).await?; Ok(HttpResponse::Ok().json(statuses)) } @@ -240,8 +234,6 @@ async fn favourite( Err(DatabaseError::AlreadyExists(_)) => None, // post already favourited Err(other_error) => return Err(other_error.into()), }; - get_reposted_posts(db_client, vec![&mut post]).await?; - get_actions_for_posts(db_client, ¤t_user.id, vec![&mut post]).await?; if let Some(reaction) = maybe_reaction_created { // Federate @@ -256,9 +248,14 @@ async fn favourite( &primary_recipient, ); deliver_activity(&config, ¤t_user, activity, recipients); - } + }; - let status = Status::from_post(post, &config.instance_url()); + let status = build_status( + db_client, + &config.instance_url(), + Some(¤t_user), + post, + ).await?; Ok(HttpResponse::Ok().json(status)) } @@ -282,8 +279,6 @@ async fn unfavourite( Err(DatabaseError::NotFound(_)) => None, // post not favourited Err(other_error) => return Err(other_error.into()), }; - get_reposted_posts(db_client, vec![&mut post]).await?; - get_actions_for_posts(db_client, ¤t_user.id, vec![&mut post]).await?; if let Some(reaction_id) = maybe_reaction_deleted { // Federate @@ -298,7 +293,12 @@ async fn unfavourite( deliver_activity(&config, ¤t_user, activity, recipients); }; - let status = Status::from_post(post, &config.instance_url()); + let status = build_status( + db_client, + &config.instance_url(), + Some(¤t_user), + post, + ).await?; Ok(HttpResponse::Ok().json(status)) } @@ -319,10 +319,8 @@ async fn reblog( repost_of_id: Some(status_id.into_inner()), ..Default::default() }; - let mut repost = create_post(db_client, ¤t_user.id, repost_data).await?; + let repost = create_post(db_client, ¤t_user.id, repost_data).await?; post.repost_count += 1; - get_reposted_posts(db_client, vec![&mut post]).await?; - get_actions_for_posts(db_client, ¤t_user.id, vec![&mut post]).await?; // Federate let Audience { recipients, .. } = @@ -335,8 +333,12 @@ async fn reblog( ); deliver_activity(&config, ¤t_user, activity, recipients); - repost.repost_of = Some(Box::new(post)); - let status = Status::from_post(repost, &config.instance_url()); + let status = build_status( + db_client, + &config.instance_url(), + Some(¤t_user), + repost, + ).await?; Ok(HttpResponse::Ok().json(status)) } @@ -357,9 +359,7 @@ async fn unreblog( let repost_id = reposts.first().ok_or(HttpError::NotFoundError("post"))?; // Ignore returned data because reposts don't have attached files delete_post(db_client, repost_id).await?; - let mut post = get_post_by_id(db_client, &status_id).await?; - get_reposted_posts(db_client, vec![&mut post]).await?; - get_actions_for_posts(db_client, ¤t_user.id, vec![&mut post]).await?; + let post = get_post_by_id(db_client, &status_id).await?; // Federate let Audience { recipients, primary_recipient } = @@ -372,7 +372,12 @@ async fn unreblog( ); deliver_activity(&config, ¤t_user, activity, recipients); - let status = Status::from_post(post, &config.instance_url()); + let status = build_status( + db_client, + &config.instance_url(), + Some(¤t_user), + post, + ).await?; Ok(HttpResponse::Ok().json(status)) } @@ -426,9 +431,13 @@ async fn make_permanent( // Update post post.ipfs_cid = Some(post_metadata_cid); update_post(db_client, &post).await?; - get_reposted_posts(db_client, vec![&mut post]).await?; - get_actions_for_posts(db_client, ¤t_user.id, vec![&mut post]).await?; - let status = Status::from_post(post, &config.instance_url()); + + let status = build_status( + db_client, + &config.instance_url(), + Some(¤t_user), + post, + ).await?; Ok(HttpResponse::Ok().json(status)) } @@ -481,9 +490,13 @@ async fn token_minted( }; post.token_tx_id = Some(transaction_data.into_inner().transaction_id); update_post(db_client, &post).await?; - get_reposted_posts(db_client, vec![&mut post]).await?; - get_actions_for_posts(db_client, ¤t_user.id, vec![&mut post]).await?; - let status = Status::from_post(post, &config.instance_url()); + + let status = build_status( + db_client, + &config.instance_url(), + Some(¤t_user), + post, + ).await?; Ok(HttpResponse::Ok().json(status)) } diff --git a/src/mastodon_api/timelines/views.rs b/src/mastodon_api/timelines/views.rs index ace49ee..4654ede 100644 --- a/src/mastodon_api/timelines/views.rs +++ b/src/mastodon_api/timelines/views.rs @@ -6,11 +6,7 @@ use crate::config::Config; use crate::database::{Pool, get_database_client}; use crate::errors::HttpError; use crate::mastodon_api::oauth::auth::get_current_user; -use crate::mastodon_api::statuses::types::Status; -use crate::models::posts::helpers::{ - get_actions_for_posts, - get_reposted_posts, -}; +use crate::mastodon_api::statuses::helpers::build_status_list; use crate::models::posts::queries::{get_home_timeline, get_posts_by_tag}; use super::types::TimelineQueryParams; @@ -23,22 +19,18 @@ async fn home_timeline( ) -> Result { let db_client = &**get_database_client(&db_pool).await?; let current_user = get_current_user(db_client, auth.token()).await?; - let mut posts = get_home_timeline( + let posts = get_home_timeline( db_client, ¤t_user.id, query_params.max_id, query_params.limit, ).await?; - get_reposted_posts(db_client, posts.iter_mut().collect()).await?; - get_actions_for_posts( + let statuses = build_status_list( db_client, - ¤t_user.id, - posts.iter_mut().collect(), + &config.instance_url(), + Some(¤t_user), + posts, ).await?; - let statuses: Vec = posts - .into_iter() - .map(|post| Status::from_post(post, &config.instance_url())) - .collect(); Ok(HttpResponse::Ok().json(statuses)) } @@ -55,25 +47,19 @@ async fn hashtag_timeline( Some(auth) => Some(get_current_user(db_client, auth.token()).await?), None => None, }; - let mut posts = get_posts_by_tag( + let posts = get_posts_by_tag( db_client, &hashtag, maybe_current_user.as_ref().map(|user| &user.id), query_params.max_id, query_params.limit, ).await?; - get_reposted_posts(db_client, posts.iter_mut().collect()).await?; - if let Some(user) = maybe_current_user { - get_actions_for_posts( - db_client, - &user.id, - posts.iter_mut().collect(), - ).await?; - }; - let statuses: Vec = posts - .into_iter() - .map(|post| Status::from_post(post, &config.instance_url())) - .collect(); + let statuses = build_status_list( + db_client, + &config.instance_url(), + maybe_current_user.as_ref(), + posts, + ).await?; Ok(HttpResponse::Ok().json(statuses)) } diff --git a/src/models/notifications/queries.rs b/src/models/notifications/queries.rs index c881d3f..6422971 100644 --- a/src/models/notifications/queries.rs +++ b/src/models/notifications/queries.rs @@ -4,7 +4,7 @@ use tokio_postgres::GenericClient; use uuid::Uuid; use crate::errors::DatabaseError; -use crate::models::posts::helpers::get_actions_for_posts; +use crate::models::posts::helpers::add_user_actions; use crate::models::posts::queries::{ RELATED_ATTACHMENTS, RELATED_MENTIONS, @@ -133,6 +133,6 @@ pub async fn get_notifications( let posts = notifications.iter_mut() .filter_map(|item| item.post.as_mut()) .collect(); - get_actions_for_posts(db_client, recipient_id, posts).await?; + add_user_actions(db_client, recipient_id, posts).await?; Ok(notifications) } diff --git a/src/models/posts/helpers.rs b/src/models/posts/helpers.rs index ec25a02..05022e9 100644 --- a/src/models/posts/helpers.rs +++ b/src/models/posts/helpers.rs @@ -9,13 +9,16 @@ use crate::models::users::types::User; use super::queries::{get_posts, find_reposted_by_user}; use super::types::{Post, PostActions, Visibility}; -pub async fn get_reposted_posts( +pub async fn add_reposted_posts( db_client: &impl GenericClient, posts: Vec<&mut Post>, ) -> Result<(), DatabaseError> { let reposted_ids: Vec = posts.iter() .filter_map(|post| post.repost_of_id) .collect(); + if reposted_ids.is_empty() { + return Ok(()); + }; let reposted = get_posts(db_client, reposted_ids).await?; for post in posts { if let Some(ref repost_of_id) = post.repost_of_id { @@ -29,7 +32,7 @@ pub async fn get_reposted_posts( Ok(()) } -pub async fn get_actions_for_posts( +pub async fn add_user_actions( db_client: &impl GenericClient, user_id: &Uuid, posts: Vec<&mut Post>, diff --git a/src/models/profiles/queries.rs b/src/models/profiles/queries.rs index 3241c99..e0c0cf1 100644 --- a/src/models/profiles/queries.rs +++ b/src/models/profiles/queries.rs @@ -134,7 +134,7 @@ pub async fn get_profile_by_actor_id( pub async fn get_profile_by_acct( db_client: &impl GenericClient, - account_uri: &str, + acct: &str, ) -> Result { let result = db_client.query_opt( " @@ -142,7 +142,7 @@ pub async fn get_profile_by_acct( FROM actor_profile WHERE actor_profile.acct = $1 ", - &[&account_uri], + &[&acct], ).await?; let profile = match result { Some(row) => row.try_get("actor_profile")?,