mirror of
https://github.com/LemmyNet/lemmy
synced 2024-11-26 22:40:21 +00:00
Add federated comment and post undo like.
This commit is contained in:
parent
dfc9637230
commit
fab22e3d8a
7 changed files with 279 additions and 5 deletions
|
@ -561,7 +561,7 @@ impl Perform for Oper<CreateCommentLike> {
|
||||||
comment.send_dislike(&user, &conn)?;
|
comment.send_dislike(&user, &conn)?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// TODO tombstone the like
|
comment.send_undo_like(&user, &conn)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Have to refetch the comment to get the current state
|
// Have to refetch the comment to get the current state
|
||||||
|
|
|
@ -397,7 +397,7 @@ impl Perform for Oper<CreatePostLike> {
|
||||||
post.send_dislike(&user, &conn)?;
|
post.send_dislike(&user, &conn)?;
|
||||||
}
|
}
|
||||||
} else {
|
} 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)) {
|
let post_view = match PostView::read(&conn, data.post_id, Some(user_id)) {
|
||||||
|
|
|
@ -413,4 +413,51 @@ impl ApubLikeableType for Comment {
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -208,7 +208,7 @@ pub trait ApubObjectType {
|
||||||
pub trait ApubLikeableType {
|
pub trait ApubLikeableType {
|
||||||
fn send_like(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
|
fn send_like(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
|
||||||
fn send_dislike(&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 {
|
pub fn get_shared_inbox(actor_id: &str) -> String {
|
||||||
|
|
|
@ -416,4 +416,50 @@ impl ApubLikeableType for Post {
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,6 +91,9 @@ pub async fn shared_inbox(
|
||||||
(SharedAcceptedObjects::Undo(u), Some("Remove")) => {
|
(SharedAcceptedObjects::Undo(u), Some("Remove")) => {
|
||||||
receive_undo_remove(&u, &request, &conn, chat_server)
|
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.")),
|
_ => Err(format_err!("Unknown incoming activity type.")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1424,3 +1427,141 @@ fn receive_undo_remove_community(
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().finish())
|
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(¬e, &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())
|
||||||
|
}
|
||||||
|
|
44
ui/src/api_tests/api.spec.ts
vendored
44
ui/src/api_tests/api.spec.ts
vendored
|
@ -16,6 +16,8 @@ import {
|
||||||
CommunityForm,
|
CommunityForm,
|
||||||
GetCommunityForm,
|
GetCommunityForm,
|
||||||
GetCommunityResponse,
|
GetCommunityResponse,
|
||||||
|
CommentLikeForm,
|
||||||
|
CreatePostLikeForm,
|
||||||
} from '../interfaces';
|
} from '../interfaces';
|
||||||
|
|
||||||
let lemmyAlphaUrl = 'http://localhost:8540';
|
let lemmyAlphaUrl = 'http://localhost:8540';
|
||||||
|
@ -163,11 +165,28 @@ describe('main', () => {
|
||||||
}
|
}
|
||||||
).then(d => d.json());
|
).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.name).toBe(name);
|
||||||
expect(createResponse.post.community_local).toBe(false);
|
expect(createResponse.post.community_local).toBe(false);
|
||||||
expect(createResponse.post.creator_local).toBe(true);
|
expect(createResponse.post.creator_local).toBe(true);
|
||||||
expect(createResponse.post.score).toBe(1);
|
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 getPostUrl = `${lemmyBetaApiUrl}/post?id=2`;
|
||||||
let getPostRes: GetPostResponse = await fetch(getPostUrl, {
|
let getPostRes: GetPostResponse = await fetch(getPostUrl, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
@ -176,7 +195,7 @@ describe('main', () => {
|
||||||
expect(getPostRes.post.name).toBe(name);
|
expect(getPostRes.post.name).toBe(name);
|
||||||
expect(getPostRes.post.community_local).toBe(true);
|
expect(getPostRes.post.community_local).toBe(true);
|
||||||
expect(getPostRes.post.creator_local).toBe(false);
|
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.creator_local).toBe(true);
|
||||||
expect(createResponse.comment.score).toBe(1);
|
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 getPostUrl = `${lemmyBetaApiUrl}/post?id=2`;
|
||||||
let getPostRes: GetPostResponse = await fetch(getPostUrl, {
|
let getPostRes: GetPostResponse = await fetch(getPostUrl, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
@ -251,7 +291,7 @@ describe('main', () => {
|
||||||
expect(getPostRes.comments[0].content).toBe(content);
|
expect(getPostRes.comments[0].content).toBe(content);
|
||||||
expect(getPostRes.comments[0].community_local).toBe(true);
|
expect(getPostRes.comments[0].community_local).toBe(true);
|
||||||
expect(getPostRes.comments[0].creator_local).toBe(false);
|
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
|
// Now do beta replying to that comment, as a child comment
|
||||||
let contentBeta = 'A child federated comment from beta';
|
let contentBeta = 'A child federated comment from beta';
|
||||||
|
|
Loading…
Reference in a new issue