use uuid::Uuid; use fedimovies_utils::id::generate_ulid; use crate::database::{catch_unique_violation, DatabaseClient, DatabaseError}; use crate::notifications::queries::create_reaction_notification; use crate::posts::queries::{get_post_author, update_reaction_count}; use super::types::DbReaction; pub async fn create_reaction( db_client: &mut impl DatabaseClient, author_id: &Uuid, post_id: &Uuid, activity_id: Option<&String>, ) -> Result { let transaction = db_client.transaction().await?; let reaction_id = generate_ulid(); // Reactions to reposts are not allowed let maybe_row = transaction .query_opt( " INSERT INTO post_reaction (id, author_id, post_id, activity_id) SELECT $1, $2, $3, $4 WHERE NOT EXISTS ( SELECT 1 FROM post WHERE post.id = $3 AND post.repost_of_id IS NOT NULL ) RETURNING post_reaction ", &[&reaction_id, &author_id, &post_id, &activity_id], ) .await .map_err(catch_unique_violation("reaction"))?; let row = maybe_row.ok_or(DatabaseError::NotFound("post"))?; let reaction: DbReaction = row.try_get("post_reaction")?; update_reaction_count(&transaction, post_id, 1).await?; let post_author = get_post_author(&transaction, post_id).await?; if post_author.is_local() && post_author.id != *author_id { create_reaction_notification(&transaction, author_id, &post_author.id, post_id).await?; }; transaction.commit().await?; Ok(reaction) } pub async fn get_reaction_by_remote_activity_id( db_client: &impl DatabaseClient, activity_id: &str, ) -> Result { let maybe_row = db_client .query_opt( " SELECT post_reaction FROM post_reaction WHERE activity_id = $1 ", &[&activity_id], ) .await?; let row = maybe_row.ok_or(DatabaseError::NotFound("reaction"))?; let reaction = row.try_get("post_reaction")?; Ok(reaction) } pub async fn delete_reaction( db_client: &mut impl DatabaseClient, author_id: &Uuid, post_id: &Uuid, ) -> Result { let transaction = db_client.transaction().await?; let maybe_row = transaction .query_opt( " DELETE FROM post_reaction WHERE author_id = $1 AND post_id = $2 RETURNING post_reaction.id ", &[&author_id, &post_id], ) .await?; let row = maybe_row.ok_or(DatabaseError::NotFound("reaction"))?; let reaction_id = row.try_get("id")?; update_reaction_count(&transaction, post_id, -1).await?; transaction.commit().await?; Ok(reaction_id) } /// Finds favourites among given posts and returns their IDs pub async fn find_favourited_by_user( db_client: &impl DatabaseClient, user_id: &Uuid, posts_ids: &[Uuid], ) -> Result, DatabaseError> { let rows = db_client .query( " SELECT post_id FROM post_reaction WHERE author_id = $1 AND post_id = ANY($2) ", &[&user_id, &posts_ids], ) .await?; let favourites: Vec = rows .iter() .map(|row| row.try_get("post_id")) .collect::>()?; Ok(favourites) }