Adding undo follow community.

This commit is contained in:
Dessalines 2020-05-03 22:41:45 -04:00
parent cf427543e5
commit 4d82b89947
6 changed files with 186 additions and 18 deletions

View file

@ -483,12 +483,12 @@ impl Perform for Oper<FollowCommunity> {
let conn = pool.get()?; let conn = pool.get()?;
let community = Community::read(&conn, data.community_id)?; let community = Community::read(&conn, data.community_id)?;
if community.local { let community_follower_form = CommunityFollowerForm {
let community_follower_form = CommunityFollowerForm { community_id: data.community_id,
community_id: data.community_id, user_id,
user_id, };
};
if community.local {
if data.follow { if data.follow {
match CommunityFollower::follow(&conn, &community_follower_form) { match CommunityFollower::follow(&conn, &community_follower_form) {
Ok(user) => user, Ok(user) => user,
@ -501,9 +501,19 @@ impl Perform for Oper<FollowCommunity> {
}; };
} }
} else { } else {
// TODO: still have to implement unfollow
let user = User_::read(&conn, user_id)?; let user = User_::read(&conn, user_id)?;
user.send_follow(&community.actor_id, &conn)?;
if data.follow {
// Dont actually add to the community followers here, because you need
// to wait for the accept
user.send_follow(&community.actor_id, &conn)?;
} else {
user.send_unfollow(&community.actor_id, &conn)?;
match CommunityFollower::ignore(&conn, &community_follower_form) {
Ok(user) => user,
Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
};
}
// TODO: this needs to return a "pending" state, until Accept is received from the remote server // TODO: this needs to return a "pending" state, until Accept is received from the remote server
} }

View file

@ -289,6 +289,14 @@ impl ActorType for Community {
.collect(), .collect(),
) )
} }
fn send_follow(&self, _follow_actor_id: &str, _conn: &PgConnection) -> Result<(), Error> {
unimplemented!()
}
fn send_unfollow(&self, _follow_actor_id: &str, _conn: &PgConnection) -> Result<(), Error> {
unimplemented!()
}
} }
impl FromApub for CommunityForm { impl FromApub for CommunityForm {

View file

@ -4,6 +4,7 @@ use super::*;
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub enum CommunityAcceptedObjects { pub enum CommunityAcceptedObjects {
Follow(Follow), Follow(Follow),
Undo(Undo),
} }
// TODO Consolidate community and user inboxes into a single shared one // TODO Consolidate community and user inboxes into a single shared one
@ -25,6 +26,9 @@ pub async fn community_inbox(
CommunityAcceptedObjects::Follow(f) => { CommunityAcceptedObjects::Follow(f) => {
handle_follow(&f, &request, &community_name, db, chat_server) handle_follow(&f, &request, &community_name, db, chat_server)
} }
CommunityAcceptedObjects::Undo(u) => {
handle_undo_follow(&u, &request, &community_name, db, chat_server)
}
} }
} }
@ -76,3 +80,56 @@ fn handle_follow(
Ok(HttpResponse::Ok().finish()) Ok(HttpResponse::Ok().finish())
} }
fn handle_undo_follow(
undo: &Undo,
request: &HttpRequest,
community_name: &str,
db: DbPoolParam,
_chat_server: ChatServerParam,
) -> Result<HttpResponse, Error> {
let follow = undo
.undo_props
.get_object_base_box()
.to_owned()
.unwrap()
.to_owned()
.into_concrete::<Follow>()?;
let user_uri = follow
.follow_props
.get_actor_xsd_any_uri()
.unwrap()
.to_string();
let _community_uri = follow
.follow_props
.get_object_xsd_any_uri()
.unwrap()
.to_string();
let conn = db.get()?;
let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
let community = Community::read_from_name(&conn, &community_name)?;
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(&follow)?,
local: false,
updated: None,
};
activity::Activity::create(&conn, &activity_form)?;
let community_follower_form = CommunityFollowerForm {
community_id: community.id,
user_id: user.id,
};
CommunityFollower::ignore(&conn, &community_follower_form).ok();
Ok(HttpResponse::Ok().finish())
}

View file

@ -233,14 +233,11 @@ pub trait ActorType {
// These two have default impls, since currently a community can't follow anything, // These two have default impls, since currently a community can't follow anything,
// and a user can't be followed (yet) // and a user can't be followed (yet)
#[allow(unused_variables)] #[allow(unused_variables)]
fn send_follow(&self, follow_actor_id: &str, conn: &PgConnection) -> Result<(), Error> { fn send_follow(&self, follow_actor_id: &str, conn: &PgConnection) -> Result<(), Error>;
Err(format_err!("Follow not implemented.")) fn send_unfollow(&self, follow_actor_id: &str, conn: &PgConnection) -> Result<(), Error>;
}
#[allow(unused_variables)] #[allow(unused_variables)]
fn send_accept_follow(&self, follow: &Follow, conn: &PgConnection) -> Result<(), Error> { fn send_accept_follow(&self, follow: &Follow, conn: &PgConnection) -> Result<(), Error>;
Err(format_err!("Accept not implemented."))
}
fn send_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>; fn send_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
fn send_undo_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>; fn send_undo_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
@ -248,12 +245,8 @@ pub trait ActorType {
fn send_remove(&self, mod_: &User_, conn: &PgConnection) -> Result<(), Error>; fn send_remove(&self, mod_: &User_, conn: &PgConnection) -> Result<(), Error>;
fn send_undo_remove(&self, mod_: &User_, conn: &PgConnection) -> Result<(), Error>; fn send_undo_remove(&self, mod_: &User_, conn: &PgConnection) -> Result<(), Error>;
// TODO default because there is no user following yet.
#[allow(unused_variables)]
/// For a given community, returns the inboxes of all followers. /// For a given community, returns the inboxes of all followers.
fn get_follower_inboxes(&self, conn: &PgConnection) -> Result<Vec<String>, Error> { fn get_follower_inboxes(&self, conn: &PgConnection) -> Result<Vec<String>, Error>;
Ok(vec![])
}
// TODO move these to the db rows // TODO move these to the db rows
fn get_inbox_url(&self) -> String { fn get_inbox_url(&self) -> String {

View file

@ -91,6 +91,54 @@ impl ActorType for User_ {
Ok(()) Ok(())
} }
fn send_unfollow(&self, follow_actor_id: &str, conn: &PgConnection) -> Result<(), Error> {
let mut follow = Follow::new();
let id = format!("{}/follow/{}", self.actor_id, uuid::Uuid::new_v4());
follow
.object_props
.set_context_xsd_any_uri(context())?
.set_id(id)?;
follow
.follow_props
.set_actor_xsd_any_uri(self.actor_id.to_owned())?
.set_object_xsd_any_uri(follow_actor_id)?;
let to = format!("{}/inbox", follow_actor_id);
// TODO
// Undo that fake activity
let undo_id = format!("{}/undo/follow/{}", self.actor_id, uuid::Uuid::new_v4());
let mut undo = Undo::default();
undo
.object_props
.set_context_xsd_any_uri(context())?
.set_id(undo_id)?;
undo
.undo_props
.set_actor_xsd_any_uri(self.actor_id.to_owned())?
.set_object_base_box(follow)?;
// Insert the sent activity into the activity table
let activity_form = activity::ActivityForm {
user_id: self.id,
data: serde_json::to_value(&undo)?,
local: true,
updated: None,
};
activity::Activity::create(&conn, &activity_form)?;
send_activity(
&undo,
&self.private_key.as_ref().unwrap(),
&follow_actor_id,
vec![to],
)?;
Ok(())
}
fn send_delete(&self, _creator: &User_, _conn: &PgConnection) -> Result<(), Error> { fn send_delete(&self, _creator: &User_, _conn: &PgConnection) -> Result<(), Error> {
unimplemented!() unimplemented!()
} }
@ -106,6 +154,14 @@ impl ActorType for User_ {
fn send_undo_remove(&self, _creator: &User_, _conn: &PgConnection) -> Result<(), Error> { fn send_undo_remove(&self, _creator: &User_, _conn: &PgConnection) -> Result<(), Error> {
unimplemented!() unimplemented!()
} }
fn send_accept_follow(&self, _follow: &Follow, _conn: &PgConnection) -> Result<(), Error> {
unimplemented!()
}
fn get_follower_inboxes(&self, _conn: &PgConnection) -> Result<Vec<String>, Error> {
unimplemented!()
}
} }
impl FromApub for UserForm { impl FromApub for UserForm {

View file

@ -140,6 +140,50 @@ describe('main', () => {
).then(d => d.json()); ).then(d => d.json());
expect(followedCommunitiesRes.communities[1].community_local).toBe(false); expect(followedCommunitiesRes.communities[1].community_local).toBe(false);
// Test out unfollowing
let unfollowForm: FollowCommunityForm = {
community_id: searchResponse.communities[0].id,
follow: false,
auth: lemmyAlphaAuth,
};
let unfollowRes: CommunityResponse = await fetch(
`${lemmyAlphaApiUrl}/community/follow`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: wrapper(unfollowForm),
}
).then(d => d.json());
// Check that you are unsubscribed to it locally
let followedCommunitiesResAgain: GetFollowedCommunitiesResponse = await fetch(
followedCommunitiesUrl,
{
method: 'GET',
}
).then(d => d.json());
expect(followedCommunitiesResAgain.communities.length).toBe(1);
// Follow again, for other tests
let followResAgain: CommunityResponse = await fetch(
`${lemmyAlphaApiUrl}/community/follow`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: wrapper(followForm),
}
).then(d => d.json());
// Make sure the follow response went through
expect(followResAgain.community.local).toBe(false);
expect(followResAgain.community.name).toBe('main');
}); });
}); });