]> Untitled Git - lemmy.git/commitdiff
Add federated comment and post undo like.
authorDessalines <tyhou13@gmx.com>
Mon, 4 May 2020 00:34:04 +0000 (20:34 -0400)
committerDessalines <tyhou13@gmx.com>
Mon, 4 May 2020 00:34:04 +0000 (20:34 -0400)
server/src/api/comment.rs
server/src/api/post.rs
server/src/apub/comment.rs
server/src/apub/mod.rs
server/src/apub/post.rs
server/src/apub/shared_inbox.rs
ui/src/api_tests/api.spec.ts

index 2853beb3411c9ad3ee4348b268d59529d7cc606c..0660a52c8598dfdc5fac6ae88899db2a04233bb1 100644 (file)
@@ -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
index b9c4c083614cba91f6dcd13a01d8542000fd4ad8..42c350741933322c02787e4e832835ff72d6ce63 100644 (file)
@@ -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)) {
index 55dec23b63a56ec25e0b53e74dcfe57f7c2491d4..17da45a6bca857ab409e1fcf35b429d20fb3c7ff 100644 (file)
@@ -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(())
+  }
 }
index 0438f92e00555224ea48c159828426f4206e58a6..c5bd2ea4388a2882ffc07e48b46f3c1b7c6ea80e 100644 (file)
@@ -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 {
index 408164374cdcf7120e8b78562e80f7fe8ad00a5d..61fbf827c58dab05e9f4d0aa8202159c2f72df0e 100644 (file)
@@ -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(())
+  }
 }
index d77788e552f79b7d875fb4f457800fa0c6a816e6..4b29c397686d221a4633f48951c85ec88f8878ac 100644 (file)
@@ -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())
+}
index ed97174e864751c065ab84fab2d8f9df2ba63cbc..b25c8df5fe64c483a77a8ca83ce919e685046a85 100644 (file)
@@ -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';