let conn = pool.get()?;
let community = Community::read(&conn, data.community_id)?;
- if community.local {
- let community_follower_form = CommunityFollowerForm {
- community_id: data.community_id,
- user_id,
- };
+ let community_follower_form = CommunityFollowerForm {
+ community_id: data.community_id,
+ user_id,
+ };
+ if community.local {
if data.follow {
match CommunityFollower::follow(&conn, &community_follower_form) {
Ok(user) => user,
};
}
} else {
- // TODO: still have to implement unfollow
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
}
.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 {
#[derive(Deserialize, Debug)]
pub enum CommunityAcceptedObjects {
Follow(Follow),
+ Undo(Undo),
}
// TODO Consolidate community and user inboxes into a single shared one
CommunityAcceptedObjects::Follow(f) => {
handle_follow(&f, &request, &community_name, db, chat_server)
}
+ CommunityAcceptedObjects::Undo(u) => {
+ handle_undo_follow(&u, &request, &community_name, db, chat_server)
+ }
}
}
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())
+}
// These two have default impls, since currently a community can't follow anything,
// and a user can't be followed (yet)
#[allow(unused_variables)]
- fn send_follow(&self, follow_actor_id: &str, conn: &PgConnection) -> Result<(), Error> {
- Err(format_err!("Follow not implemented."))
- }
+ fn send_follow(&self, follow_actor_id: &str, conn: &PgConnection) -> Result<(), Error>;
+ fn send_unfollow(&self, follow_actor_id: &str, conn: &PgConnection) -> Result<(), Error>;
#[allow(unused_variables)]
- fn send_accept_follow(&self, follow: &Follow, conn: &PgConnection) -> Result<(), Error> {
- Err(format_err!("Accept not implemented."))
- }
+ fn send_accept_follow(&self, follow: &Follow, 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_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.
- fn get_follower_inboxes(&self, conn: &PgConnection) -> Result<Vec<String>, Error> {
- Ok(vec![])
- }
+ fn get_follower_inboxes(&self, conn: &PgConnection) -> Result<Vec<String>, Error>;
// TODO move these to the db rows
fn get_inbox_url(&self) -> String {
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> {
unimplemented!()
}
fn send_undo_remove(&self, _creator: &User_, _conn: &PgConnection) -> Result<(), Error> {
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 {
).then(d => d.json());
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');
});
});