summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDessalines <tyhou13@gmx.com>2020-05-03 20:34:04 -0400
committerDessalines <tyhou13@gmx.com>2020-05-03 20:34:04 -0400
commitfab22e3d8a44ecfd4ccb5a8762ea16845b1b4e1b (patch)
treefbed1bf394dca6a0976bda74839fa9ef489b3409
parentdfc9637230907df73c50dddf5fd565159bb38ec5 (diff)
Add federated comment and post undo like.
-rw-r--r--server/src/api/comment.rs2
-rw-r--r--server/src/api/post.rs2
-rw-r--r--server/src/apub/comment.rs47
-rw-r--r--server/src/apub/mod.rs2
-rw-r--r--server/src/apub/post.rs46
-rw-r--r--server/src/apub/shared_inbox.rs141
-rw-r--r--ui/src/api_tests/api.spec.ts44
7 files changed, 279 insertions, 5 deletions
diff --git a/server/src/api/comment.rs b/server/src/api/comment.rs
index 2853beb3..0660a52c 100644
--- a/server/src/api/comment.rs
+++ b/server/src/api/comment.rs
@@ -561,7 +561,7 @@ impl Perform for Oper<CreateCommentLike> {
comment.send_dislike(&user, &conn)?;
}
} else {
- // TODO tombstone the like
+ comment.send_undo_like(&user, &conn)?;
}
// Have to refetch the comment to get the current state
diff --git a/server/src/api/post.rs b/server/src/api/post.rs
index b9c4c083..42c35074 100644
--- a/server/src/api/post.rs
+++ b/server/src/api/post.rs
@@ -397,7 +397,7 @@ impl Perform for Oper<CreatePostLike> {
post.send_dislike(&user, &conn)?;
}
} else {
- // TODO tombstone the post like
+ post.send_undo_like(&user, &conn)?;
}
let post_view = match PostView::read(&conn, data.post_id, Some(user_id)) {
diff --git a/server/src/apub/comment.rs b/server/src/apub/comment.rs
index 55dec23b..17da45a6 100644
--- a/server/src/apub/comment.rs
+++ b/server/src/apub/comment.rs
@@ -413,4 +413,51 @@ impl ApubLikeableType for Comment {
)?;
Ok(())
}
+
+ fn send_undo_like(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
+ let note = self.to_apub(&conn)?;
+ let post = Post::read(&conn, self.post_id)?;
+ let community = Community::read(&conn, post.community_id)?;
+ let id = format!("{}/dislike/{}", self.ap_id, uuid::Uuid::new_v4());
+
+ let mut like = Like::new();
+ populate_object_props(&mut like.object_props, &community.get_followers_url(), &id)?;
+ like
+ .like_props
+ .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
+ .set_object_base_box(note)?;
+
+ // TODO
+ // Undo that fake activity
+ let undo_id = format!("{}/undo/like/{}", self.ap_id, uuid::Uuid::new_v4());
+ let mut undo = Undo::default();
+
+ populate_object_props(
+ &mut undo.object_props,
+ &community.get_followers_url(),
+ &undo_id,
+ )?;
+
+ undo
+ .undo_props
+ .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
+ .set_object_base_box(like)?;
+
+ // Insert the sent activity into the activity table
+ let activity_form = activity::ActivityForm {
+ user_id: creator.id,
+ data: serde_json::to_value(&undo)?,
+ local: true,
+ updated: None,
+ };
+ activity::Activity::create(&conn, &activity_form)?;
+
+ send_activity(
+ &undo,
+ &creator.private_key.as_ref().unwrap(),
+ &creator.actor_id,
+ community.get_follower_inboxes(&conn)?,
+ )?;
+ Ok(())
+ }
}
diff --git a/server/src/apub/mod.rs b/server/src/apub/mod.rs
index 0438f92e..c5bd2ea4 100644
--- a/server/src/apub/mod.rs
+++ b/server/src/apub/mod.rs
@@ -208,7 +208,7 @@ pub trait ApubObjectType {
pub trait ApubLikeableType {
fn send_like(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
fn send_dislike(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
- // TODO add send_undo_like / undo_dislike
+ fn send_undo_like(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
}
pub fn get_shared_inbox(actor_id: &str) -> String {
diff --git a/server/src/apub/post.rs b/server/src/apub/post.rs
index 40816437..61fbf827 100644
--- a/server/src/apub/post.rs
+++ b/server/src/apub/post.rs
@@ -416,4 +416,50 @@ impl ApubLikeableType for Post {
)?;
Ok(())
}
+
+ fn send_undo_like(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
+ let page = self.to_apub(conn)?;
+ let community = Community::read(conn, self.community_id)?;
+ let id = format!("{}/like/{}", self.ap_id, uuid::Uuid::new_v4());
+
+ let mut like = Like::new();
+ populate_object_props(&mut like.object_props, &community.get_followers_url(), &id)?;
+ like
+ .like_props
+ .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
+ .set_object_base_box(page)?;
+
+ // TODO
+ // Undo that fake activity
+ let undo_id = format!("{}/undo/like/{}", self.ap_id, uuid::Uuid::new_v4());
+ let mut undo = Undo::default();
+
+ populate_object_props(
+ &mut undo.object_props,
+ &community.get_followers_url(),
+ &undo_id,
+ )?;
+
+ undo
+ .undo_props
+ .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
+ .set_object_base_box(like)?;
+
+ // Insert the sent activity into the activity table
+ let activity_form = activity::ActivityForm {
+ user_id: creator.id,
+ data: serde_json::to_value(&undo)?,
+ local: true,
+ updated: None,
+ };
+ activity::Activity::create(&conn, &activity_form)?;
+
+ send_activity(
+ &undo,
+ &creator.private_key.as_ref().unwrap(),
+ &creator.actor_id,
+ community.get_follower_inboxes(&conn)?,
+ )?;
+ Ok(())
+ }
}
diff --git a/server/src/apub/shared_inbox.rs b/server/src/apub/shared_inbox.rs
index d77788e5..4b29c397 100644
--- a/server/src/apub/shared_inbox.rs
+++ b/server/src/apub/shared_inbox.rs
@@ -91,6 +91,9 @@ pub async fn shared_inbox(
(SharedAcceptedObjects::Undo(u), Some("Remove")) => {
receive_undo_remove(&u, &request, &conn, chat_server)
}
+ (SharedAcceptedObjects::Undo(u), Some("Like")) => {
+ receive_undo_like(&u, &request, &conn, chat_server)
+ }
_ => Err(format_err!("Unknown incoming activity type.")),
}
}
@@ -1424,3 +1427,141 @@ fn receive_undo_remove_community(
Ok(HttpResponse::Ok().finish())
}
+
+fn receive_undo_like(
+ undo: &Undo,
+ request: &HttpRequest,
+ conn: &PgConnection,
+ chat_server: ChatServerParam,
+) -> Result<HttpResponse, Error> {
+ let like = undo
+ .undo_props
+ .get_object_base_box()
+ .to_owned()
+ .unwrap()
+ .to_owned()
+ .into_concrete::<Like>()?;
+
+ let type_ = like
+ .like_props
+ .get_object_base_box()
+ .to_owned()
+ .unwrap()
+ .kind()
+ .unwrap();
+
+ match type_ {
+ "Note" => receive_undo_like_comment(&like, &request, &conn, chat_server),
+ "Page" => receive_undo_like_post(&like, &request, &conn, chat_server),
+ d => Err(format_err!("Undo Delete type {} not supported", d)),
+ }
+}
+
+fn receive_undo_like_comment(
+ like: &Like,
+ request: &HttpRequest,
+ conn: &PgConnection,
+ chat_server: ChatServerParam,
+) -> Result<HttpResponse, Error> {
+ let note = like
+ .like_props
+ .get_object_base_box()
+ .to_owned()
+ .unwrap()
+ .to_owned()
+ .into_concrete::<Note>()?;
+
+ let user_uri = like.like_props.get_actor_xsd_any_uri().unwrap().to_string();
+
+ let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
+ verify(request, &user.public_key.unwrap())?;
+
+ // Insert the received activity into the activity table
+ let activity_form = activity::ActivityForm {
+ user_id: user.id,
+ data: serde_json::to_value(&like)?,
+ local: false,
+ updated: None,
+ };
+ activity::Activity::create(&conn, &activity_form)?;
+
+ let comment = CommentForm::from_apub(&note, &conn)?;
+ let comment_id = Comment::read_from_apub_id(conn, &comment.ap_id)?.id;
+ let like_form = CommentLikeForm {
+ comment_id,
+ post_id: comment.post_id,
+ user_id: user.id,
+ score: 0,
+ };
+ CommentLike::remove(&conn, &like_form)?;
+
+ // Refetch the view
+ let comment_view = CommentView::read(&conn, comment_id, None)?;
+
+ // TODO get those recipient actor ids from somewhere
+ let recipient_ids = vec![];
+ let res = CommentResponse {
+ comment: comment_view,
+ recipient_ids,
+ };
+
+ chat_server.do_send(SendComment {
+ op: UserOperation::CreateCommentLike,
+ comment: res,
+ my_id: None,
+ });
+
+ Ok(HttpResponse::Ok().finish())
+}
+
+fn receive_undo_like_post(
+ like: &Like,
+ request: &HttpRequest,
+ conn: &PgConnection,
+ chat_server: ChatServerParam,
+) -> Result<HttpResponse, Error> {
+ let page = like
+ .like_props
+ .get_object_base_box()
+ .to_owned()
+ .unwrap()
+ .to_owned()
+ .into_concrete::<Page>()?;
+
+ let user_uri = like.like_props.get_actor_xsd_any_uri().unwrap().to_string();
+
+ let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
+ verify(request, &user.public_key.unwrap())?;
+
+ // Insert the received activity into the activity table
+ let activity_form = activity::ActivityForm {
+ user_id: user.id,
+ data: serde_json::to_value(&like)?,
+ local: false,
+ updated: None,
+ };
+ activity::Activity::create(&conn, &activity_form)?;
+
+ let post = PostForm::from_apub(&page, conn)?;
+ let post_id = Post::read_from_apub_id(conn, &post.ap_id)?.id;
+
+ let like_form = PostLikeForm {
+ post_id,
+ user_id: user.id,
+ score: 1,
+ };
+ PostLike::remove(&conn, &like_form)?;
+
+ // Refetch the view
+ let post_view = PostView::read(&conn, post_id, None)?;
+
+ let res = PostResponse { post: post_view };
+
+ chat_server.do_send(SendPost {
+ op: UserOperation::CreatePostLike,
+ post: res,
+ my_id: None,
+ });
+
+ Ok(HttpResponse::Ok().finish())
+}
diff --git a/ui/src/api_tests/api.spec.ts b/ui/src/api_tests/api.spec.ts
index ed97174e..b25c8df5 100644
--- a/ui/src/api_tests/api.spec.ts
+++ b/ui/src/api_tests/api.spec.ts
@@ -16,6 +16,8 @@ import {
CommunityForm,
GetCommunityForm,
GetCommunityResponse,
+ CommentLikeForm,
+ CreatePostLikeForm,
} from '../interfaces';
let lemmyAlphaUrl = 'http://localhost:8540';
@@ -163,11 +165,28 @@ describe('main', () => {
}
).then(d => d.json());
+ let unlikePostForm: CreatePostLikeForm = {
+ post_id: createResponse.post.id,
+ score: 0,
+ auth: lemmyAlphaAuth,
+ };
expect(createResponse.post.name).toBe(name);
expect(createResponse.post.community_local).toBe(false);
expect(createResponse.post.creator_local).toBe(true);
expect(createResponse.post.score).toBe(1);
+ let unlikePostRes: PostResponse = await fetch(
+ `${lemmyAlphaApiUrl}/post/like`,
+ {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: wrapper(unlikePostForm),
+ }
+ ).then(d => d.json());
+ expect(unlikePostRes.post.score).toBe(0);
+
let getPostUrl = `${lemmyBetaApiUrl}/post?id=2`;
let getPostRes: GetPostResponse = await fetch(getPostUrl, {
method: 'GET',
@@ -176,7 +195,7 @@ describe('main', () => {
expect(getPostRes.post.name).toBe(name);
expect(getPostRes.post.community_local).toBe(true);
expect(getPostRes.post.creator_local).toBe(false);
- expect(getPostRes.post.score).toBe(1);
+ expect(getPostRes.post.score).toBe(0);
});
});
@@ -243,6 +262,27 @@ describe('main', () => {
expect(createResponse.comment.creator_local).toBe(true);
expect(createResponse.comment.score).toBe(1);
+ // Do an unlike, to test it
+ let unlikeCommentForm: CommentLikeForm = {
+ comment_id: createResponse.comment.id,
+ score: 0,
+ post_id: 2,
+ auth: lemmyAlphaAuth,
+ };
+
+ let unlikeCommentRes: CommentResponse = await fetch(
+ `${lemmyAlphaApiUrl}/comment/like`,
+ {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: wrapper(unlikeCommentForm),
+ }
+ ).then(d => d.json());
+
+ expect(unlikeCommentRes.comment.score).toBe(0);
+
let getPostUrl = `${lemmyBetaApiUrl}/post?id=2`;
let getPostRes: GetPostResponse = await fetch(getPostUrl, {
method: 'GET',
@@ -251,7 +291,7 @@ describe('main', () => {
expect(getPostRes.comments[0].content).toBe(content);
expect(getPostRes.comments[0].community_local).toBe(true);
expect(getPostRes.comments[0].creator_local).toBe(false);
- expect(getPostRes.comments[0].score).toBe(1);
+ expect(getPostRes.comments[0].score).toBe(0);
// Now do beta replying to that comment, as a child comment
let contentBeta = 'A child federated comment from beta';