2025-03-24 22:42:33 -04:00
use super ::* ;
use crate ::cache ::Cache ;
2025-03-25 18:18:33 -04:00
use crate ::model ::{
Error , Result ,
2025-03-29 23:51:13 -04:00
auth ::{ Notification , User } ,
2025-03-25 18:18:33 -04:00
permissions ::FinePermission ,
reactions ::{ AssetType , Reaction } ,
} ;
2025-04-03 15:07:57 -04:00
use crate ::{ auto_method , execute , get , query_row , params } ;
2025-03-24 22:42:33 -04:00
#[ cfg(feature = " sqlite " ) ]
use rusqlite ::Row ;
#[ cfg(feature = " postgres " ) ]
use tokio_postgres ::Row ;
impl DataManager {
/// Get a [`Reaction`] from an SQL row.
pub ( crate ) fn get_reaction_from_row (
#[ cfg(feature = " sqlite " ) ] x : & Row < '_ > ,
#[ cfg(feature = " postgres " ) ] x : & Row ,
) -> Reaction {
Reaction {
2025-04-03 13:52:29 -04:00
id : get ! ( x ->0 ( i64 ) ) as usize ,
created : get ! ( x ->1 ( i64 ) ) as usize ,
owner : get ! ( x ->2 ( i64 ) ) as usize ,
asset : get ! ( x ->3 ( i64 ) ) as usize ,
2025-03-24 22:42:33 -04:00
asset_type : serde_json ::from_str ( & get! ( x ->4 ( String ) ) ) . unwrap ( ) ,
2025-04-03 15:07:57 -04:00
is_like : if get! ( x ->5 ( i64 ) ) as i8 = = 1 {
true
} else {
false
} ,
2025-03-24 22:42:33 -04:00
}
}
auto_method! ( get_reaction_by_id ( ) @ get_reaction_from_row -> " SELECT * FROM reactions WHERE id = $1 " - - name = " reaction " - - returns = Reaction - - cache - key - tmpl = " atto.reaction:{} " ) ;
/// Get a reaction by `owner` and `asset`.
pub async fn get_reaction_by_owner_asset (
& self ,
owner : usize ,
asset : usize ,
) -> Result < Reaction > {
let conn = match self . connect ( ) . await {
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = query_row! (
& conn ,
" SELECT * FROM reactions WHERE owner = $1 AND asset = $2 " ,
2025-04-03 13:52:29 -04:00
& [ & ( owner as i64 ) , & ( asset as i64 ) ] ,
2025-03-24 22:42:33 -04:00
| x | { Ok ( Self ::get_reaction_from_row ( x ) ) }
) ;
if res . is_err ( ) {
2025-03-25 21:19:55 -04:00
return Err ( Error ::GeneralNotFound ( " reaction " . to_string ( ) ) ) ;
2025-03-24 22:42:33 -04:00
}
Ok ( res . unwrap ( ) )
}
2025-03-31 15:39:49 -04:00
/// Create a new reaction in the database.
2025-03-24 22:42:33 -04:00
///
/// # Arguments
/// * `data` - a mock [`Reaction`] object to insert
2025-03-29 23:51:13 -04:00
pub async fn create_reaction ( & self , data : Reaction , user : & User ) -> Result < ( ) > {
2025-03-24 22:42:33 -04:00
let conn = match self . connect ( ) . await {
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = execute! (
& conn ,
2025-03-24 22:49:15 -04:00
" INSERT INTO reactions VALUES ($1, $2, $3, $4, $5, $6) " ,
2025-04-03 15:07:57 -04:00
params! [
& ( data . id as i64 ) ,
& ( data . created as i64 ) ,
& ( data . owner as i64 ) ,
& ( data . asset as i64 ) ,
2025-03-24 22:42:33 -04:00
& serde_json ::to_string ( & data . asset_type ) . unwrap ( ) . as_str ( ) ,
2025-04-03 15:07:57 -04:00
& ( if data . is_like { 1 } else { 0 } as i64 )
2025-03-24 22:42:33 -04:00
]
) ;
if let Err ( e ) = res {
return Err ( Error ::DatabaseError ( e . to_string ( ) ) ) ;
}
// incr corresponding
match data . asset_type {
2025-03-29 22:27:57 -04:00
AssetType ::Community = > {
if let Err ( e ) = {
if data . is_like {
self . incr_community_likes ( data . asset ) . await
} else {
self . incr_community_dislikes ( data . asset ) . await
}
} {
2025-03-24 22:42:33 -04:00
return Err ( e ) ;
2025-03-29 23:51:13 -04:00
} else if data . is_like {
2025-04-01 15:03:56 -04:00
let community = self . get_community_by_id_no_void ( data . asset ) . await . unwrap ( ) ;
2025-03-29 23:51:13 -04:00
if community . owner ! = user . id {
if let Err ( e ) = self
. create_notification ( Notification ::new (
" Your community has received a like! " . to_string ( ) ,
format! (
" [@{}](/api/v1/auth/profile/find/{}) has liked your community! " ,
user . username , user . id
) ,
community . owner ,
) )
. await
{
return Err ( e ) ;
}
}
2025-03-24 22:42:33 -04:00
}
}
2025-03-29 22:27:57 -04:00
AssetType ::Post = > {
if let Err ( e ) = {
if data . is_like {
self . incr_post_likes ( data . asset ) . await
} else {
self . incr_post_dislikes ( data . asset ) . await
}
} {
2025-03-24 22:42:33 -04:00
return Err ( e ) ;
2025-03-29 23:51:13 -04:00
} else if data . is_like {
let post = self . get_post_by_id ( data . asset ) . await . unwrap ( ) ;
if post . owner ! = user . id {
if let Err ( e ) = self
. create_notification ( Notification ::new (
" Your post has received a like! " . to_string ( ) ,
format! (
" [@{}](/api/v1/auth/profile/find/{}) has liked your post! " ,
user . username , user . id
) ,
post . owner ,
) )
. await
{
return Err ( e ) ;
}
}
2025-03-24 22:42:33 -04:00
}
}
2025-04-02 11:39:51 -04:00
AssetType ::User = > {
return Err ( Error ::NotAllowed ) ;
}
2025-03-24 22:42:33 -04:00
} ;
// return
Ok ( ( ) )
}
2025-03-29 22:27:57 -04:00
pub async fn delete_reaction ( & self , id : usize , user : & User ) -> Result < ( ) > {
2025-03-24 22:42:33 -04:00
let reaction = self . get_reaction_by_id ( id ) . await ? ;
if user . id ! = reaction . owner {
if ! user . permissions . check ( FinePermission ::MANAGE_REACTIONS ) {
return Err ( Error ::NotAllowed ) ;
}
}
let conn = match self . connect ( ) . await {
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = execute! (
& conn ,
" DELETE FROM reactions WHERE id = $1 " ,
2025-04-03 15:07:57 -04:00
& [ & ( id as i64 ) ]
2025-03-24 22:42:33 -04:00
) ;
if let Err ( e ) = res {
return Err ( Error ::DatabaseError ( e . to_string ( ) ) ) ;
}
self . 2. remove ( format! ( " atto.reaction: {} " , id ) ) . await ;
// decr corresponding
match reaction . asset_type {
2025-03-29 22:27:57 -04:00
AssetType ::Community = > {
if let Err ( e ) = {
if reaction . is_like {
self . decr_community_likes ( reaction . asset ) . await
} else {
self . decr_community_dislikes ( reaction . asset ) . await
}
} {
2025-03-24 22:42:33 -04:00
return Err ( e ) ;
}
}
2025-03-29 22:27:57 -04:00
AssetType ::Post = > {
if let Err ( e ) = {
if reaction . is_like {
self . decr_post_likes ( reaction . asset ) . await
} else {
self . decr_post_dislikes ( reaction . asset ) . await
}
} {
2025-03-24 22:42:33 -04:00
return Err ( e ) ;
}
}
2025-04-02 11:39:51 -04:00
AssetType ::User = > {
return Err ( Error ::NotAllowed ) ;
}
2025-03-24 22:42:33 -04:00
} ;
// return
Ok ( ( ) )
}
}