Send Undo(Like) activity when post is unfavourited
This commit is contained in:
parent
99f32e8202
commit
cf69ac6eb2
5 changed files with 96 additions and 22 deletions
|
@ -92,6 +92,7 @@ The following activities are supported:
|
||||||
- Create(Note)
|
- Create(Note)
|
||||||
- Delete(Note)
|
- Delete(Note)
|
||||||
- Like(Note)
|
- Like(Note)
|
||||||
|
- Undo(Like)
|
||||||
- Announce(Note)
|
- Announce(Note)
|
||||||
- Follow(Person)
|
- Follow(Person)
|
||||||
- Update(Person)
|
- Update(Person)
|
||||||
|
|
|
@ -125,7 +125,7 @@ fn create_activity(
|
||||||
instance_url: &str,
|
instance_url: &str,
|
||||||
actor_name: &str,
|
actor_name: &str,
|
||||||
activity_type: &str,
|
activity_type: &str,
|
||||||
activity_uuid: Option<Uuid>,
|
internal_activity_id: Option<&Uuid>,
|
||||||
object: impl Serialize,
|
object: impl Serialize,
|
||||||
recipients: Vec<String>,
|
recipients: Vec<String>,
|
||||||
) -> Activity {
|
) -> Activity {
|
||||||
|
@ -135,7 +135,7 @@ fn create_activity(
|
||||||
);
|
);
|
||||||
let activity_id = get_object_url(
|
let activity_id = get_object_url(
|
||||||
instance_url,
|
instance_url,
|
||||||
&activity_uuid.unwrap_or(new_uuid()),
|
internal_activity_id.unwrap_or(&new_uuid()),
|
||||||
);
|
);
|
||||||
Activity {
|
Activity {
|
||||||
context: json!(AP_CONTEXT),
|
context: json!(AP_CONTEXT),
|
||||||
|
@ -248,19 +248,39 @@ pub fn create_activity_note(
|
||||||
pub fn create_activity_like(
|
pub fn create_activity_like(
|
||||||
instance_url: &str,
|
instance_url: &str,
|
||||||
actor_profile: &DbActorProfile,
|
actor_profile: &DbActorProfile,
|
||||||
object_id: &str,
|
note_id: &str,
|
||||||
|
reaction_id: &Uuid,
|
||||||
) -> Activity {
|
) -> Activity {
|
||||||
let activity = create_activity(
|
let activity = create_activity(
|
||||||
instance_url,
|
instance_url,
|
||||||
&actor_profile.username,
|
&actor_profile.username,
|
||||||
LIKE,
|
LIKE,
|
||||||
None,
|
Some(reaction_id),
|
||||||
object_id,
|
note_id,
|
||||||
vec![AP_PUBLIC.to_string()],
|
vec![AP_PUBLIC.to_string()],
|
||||||
);
|
);
|
||||||
activity
|
activity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_activity_undo_like(
|
||||||
|
instance_url: &str,
|
||||||
|
actor_profile: &DbActorProfile,
|
||||||
|
reaction_id: &Uuid,
|
||||||
|
) -> Activity {
|
||||||
|
let object_id = get_object_url(
|
||||||
|
instance_url,
|
||||||
|
reaction_id,
|
||||||
|
);
|
||||||
|
create_activity(
|
||||||
|
instance_url,
|
||||||
|
&actor_profile.username,
|
||||||
|
UNDO,
|
||||||
|
None,
|
||||||
|
object_id,
|
||||||
|
vec![AP_PUBLIC.to_string()],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn create_activity_announce(
|
pub fn create_activity_announce(
|
||||||
instance_url: &str,
|
instance_url: &str,
|
||||||
actor_profile: &DbActorProfile,
|
actor_profile: &DbActorProfile,
|
||||||
|
@ -317,7 +337,7 @@ pub fn create_activity_follow(
|
||||||
instance_url,
|
instance_url,
|
||||||
&actor_profile.username,
|
&actor_profile.username,
|
||||||
FOLLOW,
|
FOLLOW,
|
||||||
Some(*follow_request_id),
|
Some(follow_request_id),
|
||||||
object,
|
object,
|
||||||
vec![target_actor_id.to_string()],
|
vec![target_actor_id.to_string()],
|
||||||
);
|
);
|
||||||
|
|
|
@ -24,7 +24,11 @@ use crate::models::profiles::queries::{
|
||||||
update_profile,
|
update_profile,
|
||||||
};
|
};
|
||||||
use crate::models::profiles::types::{DbActorProfile, ProfileUpdateData};
|
use crate::models::profiles::types::{DbActorProfile, ProfileUpdateData};
|
||||||
use crate::models::reactions::queries::create_reaction;
|
use crate::models::reactions::queries::{
|
||||||
|
create_reaction,
|
||||||
|
get_reaction_by_activity_id,
|
||||||
|
delete_reaction,
|
||||||
|
};
|
||||||
use crate::models::relationships::queries::{
|
use crate::models::relationships::queries::{
|
||||||
follow_request_accepted,
|
follow_request_accepted,
|
||||||
follow_request_rejected,
|
follow_request_rejected,
|
||||||
|
@ -433,6 +437,16 @@ pub async fn receive_activity(
|
||||||
let target_profile = get_profile_by_acct(db_client, &target_username).await?;
|
let target_profile = get_profile_by_acct(db_client, &target_username).await?;
|
||||||
unfollow(db_client, &source_profile.id, &target_profile.id).await?;
|
unfollow(db_client, &source_profile.id, &target_profile.id).await?;
|
||||||
},
|
},
|
||||||
|
(UNDO, _) => {
|
||||||
|
// Undo(Like)
|
||||||
|
let object_id = get_object_id(activity.object)?;
|
||||||
|
let reaction = get_reaction_by_activity_id(db_client, &object_id).await?;
|
||||||
|
delete_reaction(
|
||||||
|
db_client,
|
||||||
|
&reaction.author_id,
|
||||||
|
&reaction.post_id,
|
||||||
|
).await?;
|
||||||
|
},
|
||||||
(UPDATE, PERSON) => {
|
(UPDATE, PERSON) => {
|
||||||
let actor: Actor = serde_json::from_value(activity.object)
|
let actor: Actor = serde_json::from_value(activity.object)
|
||||||
.map_err(|_| ValidationError("invalid actor data"))?;
|
.map_err(|_| ValidationError("invalid actor data"))?;
|
||||||
|
|
|
@ -7,6 +7,7 @@ use uuid::Uuid;
|
||||||
use crate::activitypub::activity::{
|
use crate::activitypub::activity::{
|
||||||
create_activity_note,
|
create_activity_note,
|
||||||
create_activity_like,
|
create_activity_like,
|
||||||
|
create_activity_undo_like,
|
||||||
create_activity_announce,
|
create_activity_announce,
|
||||||
create_activity_delete_note,
|
create_activity_delete_note,
|
||||||
};
|
};
|
||||||
|
@ -232,20 +233,20 @@ async fn favourite(
|
||||||
if !can_view_post(Some(¤t_user), &post) {
|
if !can_view_post(Some(¤t_user), &post) {
|
||||||
return Err(HttpError::NotFoundError("post"));
|
return Err(HttpError::NotFoundError("post"));
|
||||||
};
|
};
|
||||||
let reaction_created = match create_reaction(
|
let maybe_reaction_created = match create_reaction(
|
||||||
db_client, ¤t_user.id, &status_id, None,
|
db_client, ¤t_user.id, &status_id, None,
|
||||||
).await {
|
).await {
|
||||||
Ok(_) => {
|
Ok(reaction) => {
|
||||||
post.reaction_count += 1;
|
post.reaction_count += 1;
|
||||||
true
|
Some(reaction)
|
||||||
},
|
},
|
||||||
Err(DatabaseError::AlreadyExists(_)) => false, // post already favourited
|
Err(DatabaseError::AlreadyExists(_)) => None, // post already favourited
|
||||||
Err(other_error) => return Err(other_error.into()),
|
Err(other_error) => return Err(other_error.into()),
|
||||||
};
|
};
|
||||||
get_reposted_posts(db_client, vec![&mut post]).await?;
|
get_reposted_posts(db_client, vec![&mut post]).await?;
|
||||||
get_actions_for_posts(db_client, ¤t_user.id, vec![&mut post]).await?;
|
get_actions_for_posts(db_client, ¤t_user.id, vec![&mut post]).await?;
|
||||||
|
|
||||||
if reaction_created {
|
if let Some(reaction) = maybe_reaction_created {
|
||||||
let maybe_remote_actor = post.author.remote_actor()
|
let maybe_remote_actor = post.author.remote_actor()
|
||||||
.map_err(|_| HttpError::InternalError)?;
|
.map_err(|_| HttpError::InternalError)?;
|
||||||
if let Some(remote_actor) = maybe_remote_actor {
|
if let Some(remote_actor) = maybe_remote_actor {
|
||||||
|
@ -255,6 +256,7 @@ async fn favourite(
|
||||||
&config.instance_url(),
|
&config.instance_url(),
|
||||||
¤t_user.profile,
|
¤t_user.profile,
|
||||||
object_id,
|
object_id,
|
||||||
|
&reaction.id,
|
||||||
);
|
);
|
||||||
deliver_activity(&config, ¤t_user, activity, vec![remote_actor]);
|
deliver_activity(&config, ¤t_user, activity, vec![remote_actor]);
|
||||||
}
|
}
|
||||||
|
@ -277,13 +279,33 @@ async fn unfavourite(
|
||||||
if !can_view_post(Some(¤t_user), &post) {
|
if !can_view_post(Some(¤t_user), &post) {
|
||||||
return Err(HttpError::NotFoundError("post"));
|
return Err(HttpError::NotFoundError("post"));
|
||||||
};
|
};
|
||||||
match delete_reaction(db_client, ¤t_user.id, &status_id).await {
|
let maybe_reaction_deleted = match delete_reaction(
|
||||||
Ok(_) => post.reaction_count -= 1,
|
db_client, ¤t_user.id, &status_id,
|
||||||
Err(DatabaseError::NotFound(_)) => (), // post not favourited
|
).await {
|
||||||
|
Ok(reaction_id) => {
|
||||||
|
post.reaction_count -= 1;
|
||||||
|
Some(reaction_id)
|
||||||
|
},
|
||||||
|
Err(DatabaseError::NotFound(_)) => None, // post not favourited
|
||||||
Err(other_error) => return Err(other_error.into()),
|
Err(other_error) => return Err(other_error.into()),
|
||||||
}
|
};
|
||||||
get_reposted_posts(db_client, vec![&mut post]).await?;
|
get_reposted_posts(db_client, vec![&mut post]).await?;
|
||||||
get_actions_for_posts(db_client, ¤t_user.id, 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 {
|
||||||
|
let maybe_remote_actor = post.author.remote_actor()
|
||||||
|
.map_err(|_| HttpError::InternalError)?;
|
||||||
|
if let Some(remote_actor) = maybe_remote_actor {
|
||||||
|
// Federate
|
||||||
|
let activity = create_activity_undo_like(
|
||||||
|
&config.instance_url(),
|
||||||
|
¤t_user.profile,
|
||||||
|
&reaction_id,
|
||||||
|
);
|
||||||
|
deliver_activity(&config, ¤t_user, activity, vec![remote_actor]);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
let status = Status::from_post(post, &config.instance_url());
|
let status = Status::from_post(post, &config.instance_url());
|
||||||
Ok(HttpResponse::Ok().json(status))
|
Ok(HttpResponse::Ok().json(status))
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,25 +42,42 @@ pub async fn create_reaction(
|
||||||
Ok(reaction)
|
Ok(reaction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_reaction_by_activity_id(
|
||||||
|
db_client: &impl GenericClient,
|
||||||
|
activity_id: &str,
|
||||||
|
) -> Result<DbReaction, DatabaseError> {
|
||||||
|
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(
|
pub async fn delete_reaction(
|
||||||
db_client: &mut impl GenericClient,
|
db_client: &mut impl GenericClient,
|
||||||
author_id: &Uuid,
|
author_id: &Uuid,
|
||||||
post_id: &Uuid,
|
post_id: &Uuid,
|
||||||
) -> Result<(), DatabaseError> {
|
) -> Result<Uuid, DatabaseError> {
|
||||||
let transaction = db_client.transaction().await?;
|
let transaction = db_client.transaction().await?;
|
||||||
let deleted_count = transaction.execute(
|
let maybe_row = transaction.query_opt(
|
||||||
"
|
"
|
||||||
DELETE FROM post_reaction
|
DELETE FROM post_reaction
|
||||||
WHERE author_id = $1 AND post_id = $2
|
WHERE author_id = $1 AND post_id = $2
|
||||||
|
RETURNING post_reaction.id
|
||||||
",
|
",
|
||||||
&[&author_id, &post_id],
|
&[&author_id, &post_id],
|
||||||
).await?;
|
).await?;
|
||||||
if deleted_count == 0 {
|
let row = maybe_row.ok_or(DatabaseError::NotFound("reaction"))?;
|
||||||
return Err(DatabaseError::NotFound("reaction"));
|
let reaction_id = row.try_get("id")?;
|
||||||
}
|
|
||||||
update_reaction_count(&transaction, post_id, -1).await?;
|
update_reaction_count(&transaction, post_id, -1).await?;
|
||||||
transaction.commit().await?;
|
transaction.commit().await?;
|
||||||
Ok(())
|
Ok(reaction_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finds favourites among given posts and returns their IDs
|
/// Finds favourites among given posts and returns their IDs
|
||||||
|
|
Loading…
Reference in a new issue