"eslint": "^7.30.0",
"eslint-plugin-jane": "^9.0.3",
"jest": "^27.0.6",
- "lemmy-js-client": "0.11.0-rc.3",
+ "lemmy-js-client": "0.11.4-rc.9",
"node-fetch": "^2.6.1",
"prettier": "^2.3.2",
"ts-jest": "^27.0.3",
test('Fetch in_reply_tos: A is unsubbed from B, B makes a post, and some embedded comments, A subs to B, B updates the lowest level comment, A fetches both the post and all the inreplyto comments for that post.', async () => {
// Unfollow all remote communities
- let followed = await unfollowRemotes(alpha);
+ let site = await unfollowRemotes(alpha);
expect(
- followed.communities.filter(c => c.community.local == false).length
+ site.my_user.follows.filter(c => c.community.local == false).length
).toBe(0);
// B creates a post, and two comments, should be invisible to A
setupLogins,
searchForBetaCommunity,
followCommunity,
- checkFollowedCommunities,
unfollowRemotes,
+ getSite,
} from './shared';
beforeAll(async () => {
expect(follow.community_view.community.name).toBe('main');
// Check it from local
- let followCheck = await checkFollowedCommunities(alpha);
- let remoteCommunityId = followCheck.communities.find(
+ let site = await getSite(alpha);
+ let remoteCommunityId = site.my_user.follows.find(
c => c.community.local == false
).community.id;
expect(remoteCommunityId).toBeDefined();
expect(unfollow.community_view.community.local).toBe(false);
// Make sure you are unsubbed locally
- let unfollowCheck = await checkFollowedCommunities(alpha);
- expect(unfollowCheck.communities.length).toBeGreaterThanOrEqual(1);
+ let siteUnfollowCheck = await getSite(alpha);
+ expect(siteUnfollowCheck.my_user.follows.length).toBeGreaterThanOrEqual(1);
});
SearchResponse,
FollowCommunity,
CommunityResponse,
- GetFollowedCommunitiesResponse,
GetPostResponse,
Register,
Comment,
CreatePostLike,
EditPrivateMessage,
DeletePrivateMessage,
- GetFollowedCommunities,
GetPrivateMessages,
GetSite,
GetPost,
return api.client.followCommunity(form);
}
-export async function checkFollowedCommunities(
- api: API
-): Promise<GetFollowedCommunitiesResponse> {
- let form: GetFollowedCommunities = {
- auth: api.auth,
- };
- return api.client.getFollowedCommunities(form);
-}
-
export async function likePost(
api: API,
score: number,
}
export async function saveUserSettingsBio(
- api: API,
- auth: string
+ api: API
): Promise<LoginResponse> {
let form: SaveUserSettings = {
show_nsfw: true,
show_avatars: true,
send_notifications_to_email: false,
bio: 'a changed bio',
- auth,
+ auth: api.auth,
};
return saveUserSettings(api, form);
}
}
export async function getSite(
- api: API,
- auth: string
+ api: API
): Promise<GetSiteResponse> {
let form: GetSite = {
- auth,
+ auth: api.auth,
};
return api.client.getSite(form);
}
export async function unfollowRemotes(
api: API
-): Promise<GetFollowedCommunitiesResponse> {
+): Promise<GetSiteResponse> {
// Unfollow all remote communities
- let followed = await checkFollowedCommunities(api);
- let remoteFollowed = followed.communities.filter(
+ let site = await getSite(api);
+ let remoteFollowed = site.my_user.follows.filter(
c => c.community.local == false
);
for (let cu of remoteFollowed) {
await followCommunity(api, false, cu.community.id);
}
- let followed2 = await checkFollowedCommunities(api);
- return followed2;
+ let siteRes = await getSite(api);
+ return siteRes;
}
export async function followBeta(api: API): Promise<CommunityResponse> {
ListingType,
} from 'lemmy-js-client';
-let auth: string;
let apShortname: string;
function assertUserFederation(userOne: PersonViewSafe, userTwo: PersonViewSafe) {
expect(userOne.person.name).toBe(userTwo.person.name);
- expect(userOne.person.preferred_username).toBe(userTwo.person.preferred_username);
+ expect(userOne.person.display_name).toBe(userTwo.person.display_name);
expect(userOne.person.bio).toBe(userTwo.person.bio);
expect(userOne.person.actor_id).toBe(userTwo.person.actor_id);
expect(userOne.person.avatar).toBe(userTwo.person.avatar);
test('Create user', async () => {
let userRes = await registerUser(alpha);
expect(userRes.jwt).toBeDefined();
- auth = userRes.jwt;
-
- let site = await getSite(alpha, auth);
+ alpha.auth = userRes.jwt;
+
+ let site = await getSite(alpha);
expect(site.my_user).toBeDefined();
- apShortname = `@${site.my_user.person.name}@lemmy-alpha:8541`;
+ apShortname = `@${site.my_user.local_user_view.person.name}@lemmy-alpha:8541`;
});
test('Set some user settings, check that they are federated', async () => {
lang: '',
avatar,
banner,
- preferred_username: 'user321',
+ display_name: 'user321',
show_avatars: false,
send_notifications_to_email: false,
bio,
- auth,
+ auth: alpha.auth,
};
await saveUserSettings(alpha, form);
dependencies:
language-subtag-registry "~0.3.2"
-lemmy-js-client@0.11.0-rc.3:
- version "0.11.0-rc.3"
- resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.11.0-rc.3.tgz#dd4727ca4d16fe9593368725aacfd9e7a8d52548"
- integrity sha512-16mgl+TS1+0UHiY+ZKPuqHfbrn93h8K8tJ+kKJ1pjT2WhG23o0B8dLahG1jDQPG+dkXpR6PZxYudMYjuO8WvjQ==
+lemmy-js-client@0.11.4-rc.9:
+ version "0.11.4-rc.9"
+ resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.11.4-rc.9.tgz#f7b3c73691e4c1600daf3840d22d9cfbddc5f363"
+ integrity sha512-zP8JxWzQU+yuyE8cMG0GzR8aR3lJ++G5zzbynsXwDevzAZXhOm0ObNNtJiA3Q5msStFVKVYa3GwZxBv4XiYshw==
leven@^3.1.0:
version "3.1.0"
blocking,
check_community_ban,
check_downvotes_enabled,
+ check_person_block,
comment::*,
get_local_user_view_from_jwt,
};
)
.await?;
+ check_person_block(
+ local_user_view.person.id,
+ orig_comment.get_recipient_id(),
+ context.pool(),
+ )
+ .await?;
+
// Add parent user to recipients
let recipient_id = orig_comment.get_recipient_id();
if let Ok(local_recipient) = blocking(context.pool(), move |conn| {
use lemmy_db_queries::{
source::{comment::Comment_, community::CommunityModerator_, post::Post_},
Bannable,
+ Blockable,
Crud,
Followable,
Joinable,
use lemmy_db_schema::source::{
comment::Comment,
community::*,
+ community_block::{CommunityBlock, CommunityBlockForm},
moderator::*,
person::Person,
post::Post,
}
}
+#[async_trait::async_trait(?Send)]
+impl Perform for BlockCommunity {
+ type Response = BlockCommunityResponse;
+
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<BlockCommunityResponse, LemmyError> {
+ let data: &BlockCommunity = self;
+ let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
+
+ let community_id = data.community_id;
+ let person_id = local_user_view.person.id;
+ let community_block_form = CommunityBlockForm {
+ person_id,
+ community_id,
+ };
+
+ if data.block {
+ let block = move |conn: &'_ _| CommunityBlock::block(conn, &community_block_form);
+ if blocking(context.pool(), block).await?.is_err() {
+ return Err(ApiError::err("community_block_already_exists").into());
+ }
+
+ // Also, unfollow the community, and send a federated unfollow
+ let community_follower_form = CommunityFollowerForm {
+ community_id: data.community_id,
+ person_id,
+ pending: false,
+ };
+ blocking(context.pool(), move |conn: &'_ _| {
+ CommunityFollower::unfollow(conn, &community_follower_form)
+ })
+ .await?
+ .ok();
+ let community = blocking(context.pool(), move |conn| {
+ Community::read(conn, community_id)
+ })
+ .await??;
+ UndoFollowCommunity::send(&local_user_view.person, &community, context).await?;
+ } else {
+ let unblock = move |conn: &'_ _| CommunityBlock::unblock(conn, &community_block_form);
+ if blocking(context.pool(), unblock).await?.is_err() {
+ return Err(ApiError::err("community_block_already_exists").into());
+ }
+ }
+
+ let community_view = blocking(context.pool(), move |conn| {
+ CommunityView::read(conn, community_id, Some(person_id))
+ })
+ .await??;
+
+ Ok(BlockCommunityResponse {
+ blocked: data.block,
+ community_view,
+ })
+ }
+}
+
#[async_trait::async_trait(?Send)]
impl Perform for BanFromCommunity {
type Response = BanFromCommunityResponse;
UserOperation::GetReplies => do_websocket_operation::<GetReplies>(context, id, op, data).await,
UserOperation::AddAdmin => do_websocket_operation::<AddAdmin>(context, id, op, data).await,
UserOperation::BanPerson => do_websocket_operation::<BanPerson>(context, id, op, data).await,
+ UserOperation::BlockPerson => {
+ do_websocket_operation::<BlockPerson>(context, id, op, data).await
+ }
UserOperation::GetPersonMentions => {
do_websocket_operation::<GetPersonMentions>(context, id, op, data).await
}
UserOperation::FollowCommunity => {
do_websocket_operation::<FollowCommunity>(context, id, op, data).await
}
- UserOperation::GetFollowedCommunities => {
- do_websocket_operation::<GetFollowedCommunities>(context, id, op, data).await
+ UserOperation::BlockCommunity => {
+ do_websocket_operation::<BlockCommunity>(context, id, op, data).await
}
UserOperation::BanFromCommunity => {
do_websocket_operation::<BanFromCommunity>(context, id, op, data).await
use lemmy_api_common::{
blocking,
collect_moderated_communities,
- community::{GetFollowedCommunities, GetFollowedCommunitiesResponse},
get_local_user_view_from_jwt,
is_admin,
password_length_check,
post::Post_,
private_message::PrivateMessage_,
},
+ Blockable,
Crud,
SortType,
};
moderator::*,
password_reset_request::*,
person::*,
+ person_block::{PersonBlock, PersonBlockForm},
person_mention::*,
post::Post,
private_message::PrivateMessage,
post_report_view::PostReportView,
};
use lemmy_db_views_actor::{
- community_follower_view::CommunityFollowerView,
community_moderator_view::CommunityModeratorView,
person_mention_view::{PersonMentionQueryBuilder, PersonMentionView},
person_view::PersonViewSafe,
}
}
+#[async_trait::async_trait(?Send)]
+impl Perform for BlockPerson {
+ type Response = BlockPersonResponse;
+
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<BlockPersonResponse, LemmyError> {
+ let data: &BlockPerson = self;
+ let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
+
+ let target_id = data.person_id;
+ let person_id = local_user_view.person.id;
+
+ // Don't let a person block themselves
+ if target_id == person_id {
+ return Err(ApiError::err("cant_block_yourself").into());
+ }
+
+ let person_block_form = PersonBlockForm {
+ person_id,
+ target_id,
+ };
+
+ if data.block {
+ let block = move |conn: &'_ _| PersonBlock::block(conn, &person_block_form);
+ if blocking(context.pool(), block).await?.is_err() {
+ return Err(ApiError::err("person_block_already_exists").into());
+ }
+ } else {
+ let unblock = move |conn: &'_ _| PersonBlock::unblock(conn, &person_block_form);
+ if blocking(context.pool(), unblock).await?.is_err() {
+ return Err(ApiError::err("person_block_already_exists").into());
+ }
+ }
+
+ // TODO does any federated stuff need to be done here?
+
+ let person_view = blocking(context.pool(), move |conn| {
+ PersonViewSafe::read(conn, target_id)
+ })
+ .await??;
+
+ let res = BlockPersonResponse {
+ person_view,
+ blocked: data.block,
+ };
+
+ Ok(res)
+ }
+}
+
#[async_trait::async_trait(?Send)]
impl Perform for GetReplies {
type Response = GetRepliesResponse;
Ok(res)
}
}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for GetFollowedCommunities {
- type Response = GetFollowedCommunitiesResponse;
-
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<GetFollowedCommunitiesResponse, LemmyError> {
- let data: &GetFollowedCommunities = self;
- let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
-
- let person_id = local_user_view.person.id;
- let communities = blocking(context.pool(), move |conn| {
- CommunityFollowerView::for_person(conn, person_id)
- })
- .await?
- .map_err(|_| ApiError::err("system_err_login"))?;
-
- // Return the jwt
- Ok(GetFollowedCommunitiesResponse { communities })
- }
-}
blocking,
check_community_ban,
check_downvotes_enabled,
+ check_person_block,
get_local_user_view_from_jwt,
is_mod_or_admin,
mark_post_as_read,
check_community_ban(local_user_view.person.id, post.community_id, context.pool()).await?;
+ check_person_block(local_user_view.person.id, post.creator_id, context.pool()).await?;
+
let like_form = PostLikeForm {
post_id: data.post_id,
person_id: local_user_view.person.id,
use lemmy_api_common::{
blocking,
build_federated_instances,
- get_local_user_settings_view_from_jwt,
get_local_user_view_from_jwt,
get_local_user_view_from_jwt_opt,
is_admin,
let banned = blocking(context.pool(), move |conn| PersonViewSafe::banned(conn)).await??;
let federated_instances = build_federated_instances(context.pool()).await?;
- let my_user = Some(get_local_user_settings_view_from_jwt(&data.auth, context.pool()).await?);
-
Ok(GetSiteResponse {
site_view: Some(site_view),
admins,
banned,
online: 0,
version: version::VERSION.to_string(),
- my_user,
+ my_user: None,
federated_instances,
})
}
use lemmy_db_schema::{CommunityId, PersonId};
use lemmy_db_views_actor::{
- community_follower_view::CommunityFollowerView,
community_moderator_view::CommunityModeratorView,
community_view::CommunityView,
person_view::PersonViewSafe,
}
#[derive(Deserialize)]
-pub struct GetFollowedCommunities {
+pub struct BlockCommunity {
+ pub community_id: CommunityId,
+ pub block: bool,
pub auth: String,
}
-#[derive(Serialize)]
-pub struct GetFollowedCommunitiesResponse {
- pub communities: Vec<CommunityFollowerView>,
+#[derive(Serialize, Clone)]
+pub struct BlockCommunityResponse {
+ pub community_view: CommunityView,
+ pub blocked: bool,
}
#[derive(Deserialize)]
use lemmy_db_queries::{
source::{
community::{CommunityModerator_, Community_},
+ person_block::PersonBlock_,
site::Site_,
},
Crud,
comment::Comment,
community::{Community, CommunityModerator},
person::Person,
+ person_block::PersonBlock,
person_mention::{PersonMention, PersonMentionForm},
post::{Post, PostRead, PostReadForm},
site::Site,
}
}
+pub async fn check_person_block(
+ my_id: PersonId,
+ potential_blocker_id: PersonId,
+ pool: &DbPool,
+) -> Result<(), LemmyError> {
+ let is_blocked = move |conn: &'_ _| PersonBlock::read(conn, potential_blocker_id, my_id).is_ok();
+ if blocking(pool, is_blocked).await? {
+ Err(ApiError::err("person_block").into())
+ } else {
+ Ok(())
+ }
+}
+
pub async fn check_downvotes_enabled(score: i16, pool: &DbPool) -> Result<(), LemmyError> {
if score == -1 {
let site = blocking(pool, move |conn| Site::read_simple(conn)).await??;
private_message_view::PrivateMessageView,
};
use lemmy_db_views_actor::{
- community_follower_view::CommunityFollowerView,
community_moderator_view::CommunityModeratorView,
person_mention_view::PersonMentionView,
person_view::PersonViewSafe,
#[derive(Serialize)]
pub struct GetPersonDetailsResponse {
pub person_view: PersonViewSafe,
- pub follows: Vec<CommunityFollowerView>,
- pub moderates: Vec<CommunityModeratorView>,
pub comments: Vec<CommentView>,
pub posts: Vec<PostView>,
+ pub moderates: Vec<CommunityModeratorView>,
}
#[derive(Serialize)]
pub banned: bool,
}
+#[derive(Deserialize)]
+pub struct BlockPerson {
+ pub person_id: PersonId,
+ pub block: bool,
+ pub auth: String,
+}
+
+#[derive(Serialize, Clone)]
+pub struct BlockPersonResponse {
+ pub person_view: PersonViewSafe,
+ pub blocked: bool,
+}
+
#[derive(Deserialize)]
pub struct GetReplies {
pub sort: Option<String>,
post_view::PostView,
site_view::SiteView,
};
-use lemmy_db_views_actor::{community_view::CommunityView, person_view::PersonViewSafe};
+use lemmy_db_views_actor::{
+ community_block_view::CommunityBlockView,
+ community_follower_view::CommunityFollowerView,
+ community_moderator_view::CommunityModeratorView,
+ community_view::CommunityView,
+ person_block_view::PersonBlockView,
+ person_view::PersonViewSafe,
+};
use lemmy_db_views_moderator::{
mod_add_community_view::ModAddCommunityView,
mod_add_view::ModAddView,
pub banned: Vec<PersonViewSafe>,
pub online: usize,
pub version: String,
- pub my_user: Option<LocalUserSettingsView>,
+ pub my_user: Option<MyUserInfo>,
pub federated_instances: Option<FederatedInstances>, // Federation may be disabled
}
+#[derive(Serialize)]
+pub struct MyUserInfo {
+ pub local_user_view: LocalUserSettingsView,
+ pub follows: Vec<CommunityFollowerView>,
+ pub moderates: Vec<CommunityModeratorView>,
+ pub community_blocks: Vec<CommunityBlockView>,
+ pub person_blocks: Vec<PersonBlockView>,
+}
+
#[derive(Deserialize)]
pub struct TransferSite {
pub person_id: PersonId,
use lemmy_api_common::{
blocking,
check_community_ban,
+ check_person_block,
comment::*,
get_local_user_view_from_jwt,
get_post,
check_community_ban(local_user_view.person.id, community_id, context.pool()).await?;
+ check_person_block(local_user_view.person.id, post.creator_id, context.pool()).await?;
+
// Check if post is locked, no new comments
if post.locked {
return Err(ApiError::err("locked").into());
let parent = blocking(context.pool(), move |conn| Comment::read(conn, parent_id))
.await?
.map_err(|_| ApiError::err("couldnt_create_comment"))?;
+
+ check_person_block(local_user_view.person.id, parent.creator_id, context.pool()).await?;
+
+ // Strange issue where sometimes the post ID is incorrect
if parent.post_id != post_id {
return Err(ApiError::err("couldnt_create_comment").into());
}
})
.await??;
+ // TODO is this necessary? It should really only need to check on create
check_community_ban(
local_user_view.person.id,
orig_comment.community.id,
use actix_web::web::Data;
use lemmy_api_common::{
blocking,
+ check_person_block,
get_local_user_view_from_jwt,
person::{CreatePrivateMessage, PrivateMessageResponse},
send_email_to_user,
let content_slurs_removed = remove_slurs(&data.content.to_owned());
+ check_person_block(local_user_view.person.id, data.recipient_id, context.pool()).await?;
+
let private_message_form = PrivateMessageForm {
content: content_slurs_removed.to_owned(),
creator_id: local_user_view.person.id,
site::*,
};
use lemmy_db_views::site_view::SiteView;
-use lemmy_db_views_actor::person_view::PersonViewSafe;
-use lemmy_utils::{settings::structs::Settings, version, ConnectionId, LemmyError};
+use lemmy_db_views_actor::{
+ community_block_view::CommunityBlockView,
+ community_follower_view::CommunityFollowerView,
+ community_moderator_view::CommunityModeratorView,
+ person_block_view::PersonBlockView,
+ person_view::PersonViewSafe,
+};
+use lemmy_utils::{settings::structs::Settings, version, ApiError, ConnectionId, LemmyError};
use lemmy_websocket::{messages::GetUsersOnline, LemmyContext};
use log::info;
.await
.unwrap_or(1);
- let my_user = get_local_user_settings_view_from_jwt_opt(&data.auth, context.pool()).await?;
+ // Build the local user
+ let my_user = if let Some(local_user_view) =
+ get_local_user_settings_view_from_jwt_opt(&data.auth, context.pool()).await?
+ {
+ let person_id = local_user_view.person.id;
+ let follows = blocking(context.pool(), move |conn| {
+ CommunityFollowerView::for_person(conn, person_id)
+ })
+ .await?
+ .map_err(|_| ApiError::err("system_err_login"))?;
+
+ let person_id = local_user_view.person.id;
+ let community_blocks = blocking(context.pool(), move |conn| {
+ CommunityBlockView::for_person(conn, person_id)
+ })
+ .await?
+ .map_err(|_| ApiError::err("system_err_login"))?;
+
+ let person_id = local_user_view.person.id;
+ let person_blocks = blocking(context.pool(), move |conn| {
+ PersonBlockView::for_person(conn, person_id)
+ })
+ .await?
+ .map_err(|_| ApiError::err("system_err_login"))?;
+
+ let moderates = blocking(context.pool(), move |conn| {
+ CommunityModeratorView::for_person(conn, person_id)
+ })
+ .await?
+ .map_err(|_| ApiError::err("system_err_login"))?;
+
+ Some(MyUserInfo {
+ local_user_view,
+ follows,
+ moderates,
+ community_blocks,
+ person_blocks,
+ })
+ } else {
+ None
+ };
+
let federated_instances = build_federated_instances(context.pool()).await?;
Ok(GetSiteResponse {
use lemmy_db_schema::source::person::*;
use lemmy_db_views::{comment_view::CommentQueryBuilder, post_view::PostQueryBuilder};
use lemmy_db_views_actor::{
- community_follower_view::CommunityFollowerView,
community_moderator_view::CommunityModeratorView,
person_view::PersonViewSafe,
};
})
.await??;
- let mut follows = vec![];
- if let Some(pid) = person_id {
- if pid == person_details_id {
- follows = blocking(context.pool(), move |conn| {
- CommunityFollowerView::for_person(conn, person_details_id)
- })
- .await??;
- }
- };
let moderates = blocking(context.pool(), move |conn| {
CommunityModeratorView::for_person(conn, person_details_id)
})
// Return the jwt
Ok(GetPersonDetailsResponse {
person_view,
- follows,
moderates,
comments,
posts,
Self: Sized;
}
+pub trait Blockable {
+ type Form;
+ fn block(conn: &PgConnection, form: &Self::Form) -> Result<Self, Error>
+ where
+ Self: Sized;
+ fn unblock(conn: &PgConnection, form: &Self::Form) -> Result<usize, Error>
+ where
+ Self: Sized;
+}
+
pub trait Readable {
type Form;
fn mark_as_read(conn: &PgConnection, form: &Self::Form) -> Result<Self, Error>
--- /dev/null
+use crate::Blockable;
+use diesel::{dsl::*, result::Error, *};
+use lemmy_db_schema::source::community_block::{CommunityBlock, CommunityBlockForm};
+
+impl Blockable for CommunityBlock {
+ type Form = CommunityBlockForm;
+ fn block(conn: &PgConnection, community_block_form: &Self::Form) -> Result<Self, Error> {
+ use lemmy_db_schema::schema::community_block::dsl::*;
+ insert_into(community_block)
+ .values(community_block_form)
+ .on_conflict((person_id, community_id))
+ .do_update()
+ .set(community_block_form)
+ .get_result::<Self>(conn)
+ }
+ fn unblock(conn: &PgConnection, community_block_form: &Self::Form) -> Result<usize, Error> {
+ use lemmy_db_schema::schema::community_block::dsl::*;
+ diesel::delete(
+ community_block
+ .filter(person_id.eq(community_block_form.person_id))
+ .filter(community_id.eq(community_block_form.community_id)),
+ )
+ .execute(conn)
+ }
+}
pub mod comment;
pub mod comment_report;
pub mod community;
+pub mod community_block;
pub mod local_user;
pub mod moderator;
pub mod password_reset_request;
pub mod person;
+pub mod person_block;
pub mod person_mention;
pub mod post;
pub mod post_report;
--- /dev/null
+use crate::Blockable;
+use diesel::{dsl::*, result::Error, *};
+use lemmy_db_schema::{
+ source::person_block::{PersonBlock, PersonBlockForm},
+ PersonId,
+};
+
+pub trait PersonBlock_ {
+ fn read(
+ conn: &PgConnection,
+ person_id: PersonId,
+ target_id: PersonId,
+ ) -> Result<PersonBlock, Error>;
+}
+
+impl PersonBlock_ for PersonBlock {
+ fn read(
+ conn: &PgConnection,
+ for_person_id: PersonId,
+ for_recipient_id: PersonId,
+ ) -> Result<Self, Error> {
+ use lemmy_db_schema::schema::person_block::dsl::*;
+ person_block
+ .filter(person_id.eq(for_person_id))
+ .filter(target_id.eq(for_recipient_id))
+ .first::<Self>(conn)
+ }
+}
+
+impl Blockable for PersonBlock {
+ type Form = PersonBlockForm;
+ fn block(conn: &PgConnection, person_block_form: &PersonBlockForm) -> Result<Self, Error> {
+ use lemmy_db_schema::schema::person_block::dsl::*;
+ insert_into(person_block)
+ .values(person_block_form)
+ .on_conflict((person_id, target_id))
+ .do_update()
+ .set(person_block_form)
+ .get_result::<Self>(conn)
+ }
+ fn unblock(conn: &PgConnection, person_block_form: &Self::Form) -> Result<usize, Error> {
+ use lemmy_db_schema::schema::person_block::dsl::*;
+ diesel::delete(
+ person_block
+ .filter(person_id.eq(person_block_form.person_id))
+ .filter(target_id.eq(person_block_form.target_id)),
+ )
+ .execute(conn)
+ }
+}
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, DieselNewType)]
pub struct PersonMentionId(i32);
+#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, DieselNewType)]
+pub struct PersonBlockId(i32);
+
+#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, DieselNewType)]
+pub struct CommunityBlockId(i32);
+
#[repr(transparent)]
#[derive(Clone, PartialEq, Serialize, Deserialize, Debug, AsExpression, FromSqlRow)]
#[sql_type = "Text"]
}
}
+table! {
+ person_block (id) {
+ id -> Int4,
+ person_id -> Int4,
+ target_id -> Int4,
+ published -> Timestamp,
+ }
+}
+
+table! {
+ community_block (id) {
+ id -> Int4,
+ person_id -> Int4,
+ community_id -> Int4,
+ published -> Timestamp,
+ }
+}
+
// These are necessary since diesel doesn't have self joins / aliases
table! {
comment_alias_1 (id) {
joinable!(post_report -> person_alias_2 (resolver_id));
joinable!(comment_report -> person_alias_2 (resolver_id));
+joinable!(person_block -> person (person_id));
+joinable!(person_block -> person_alias_1 (target_id));
+
joinable!(comment -> person (creator_id));
joinable!(comment -> post (post_id));
joinable!(comment_aggregates -> comment (comment_id));
joinable!(comment_saved -> comment (comment_id));
joinable!(comment_saved -> person (person_id));
joinable!(community_aggregates -> community (community_id));
+joinable!(community_block -> community (community_id));
+joinable!(community_block -> person (person_id));
joinable!(community_follower -> community (community_id));
joinable!(community_follower -> person (person_id));
joinable!(community_moderator -> community (community_id));
activity,
comment,
comment_aggregates,
+ community_block,
comment_like,
comment_report,
comment_saved,
person,
person_aggregates,
person_ban,
+ person_block,
person_mention,
post,
post_aggregates,
--- /dev/null
+use crate::{
+ schema::community_block,
+ source::community::Community,
+ CommunityBlockId,
+ CommunityId,
+ PersonId,
+};
+use serde::Serialize;
+
+#[derive(Clone, Queryable, Associations, Identifiable, PartialEq, Debug, Serialize)]
+#[table_name = "community_block"]
+#[belongs_to(Community)]
+pub struct CommunityBlock {
+ pub id: CommunityBlockId,
+ pub person_id: PersonId,
+ pub community_id: CommunityId,
+ pub published: chrono::NaiveDateTime,
+}
+
+#[derive(Insertable, AsChangeset)]
+#[table_name = "community_block"]
+pub struct CommunityBlockForm {
+ pub person_id: PersonId,
+ pub community_id: CommunityId,
+}
pub mod comment;
pub mod comment_report;
pub mod community;
+pub mod community_block;
pub mod local_user;
pub mod moderator;
pub mod password_reset_request;
pub mod person;
+pub mod person_block;
pub mod person_mention;
pub mod post;
pub mod post_report;
--- /dev/null
+use crate::{schema::person_block, PersonBlockId, PersonId};
+use serde::Serialize;
+
+#[derive(Clone, Queryable, Associations, Identifiable, PartialEq, Debug, Serialize)]
+#[table_name = "person_block"]
+pub struct PersonBlock {
+ pub id: PersonBlockId,
+ pub person_id: PersonId,
+ pub target_id: PersonId,
+ pub published: chrono::NaiveDateTime,
+}
+
+#[derive(Insertable, AsChangeset)]
+#[table_name = "person_block"]
+pub struct PersonBlockForm {
+ pub person_id: PersonId,
+ pub target_id: PersonId,
+}
comment_like,
comment_saved,
community,
+ community_block,
community_follower,
community_person_ban,
person,
person_alias_1,
+ person_block,
post,
},
source::{
comment::{Comment, CommentAlias1, CommentSaved},
community::{Community, CommunityFollower, CommunityPersonBan, CommunitySafe},
person::{Person, PersonAlias1, PersonSafe, PersonSafeAlias1},
+ person_block::PersonBlock,
post::Post,
},
CommentId,
pub creator_banned_from_community: bool, // Left Join to CommunityPersonBan
pub subscribed: bool, // Left join to CommunityFollower
pub saved: bool, // Left join to CommentSaved
+ pub creator_blocked: bool, // Left join to PersonBlock
pub my_vote: Option<i16>, // Left join to CommentLike
}
Option<CommunityPersonBan>,
Option<CommunityFollower>,
Option<CommentSaved>,
+ Option<PersonBlock>,
Option<i16>,
);
creator_banned_from_community,
subscribed,
saved,
+ creator_blocked,
comment_like,
) = comment::table
.find(comment_id)
.and(comment_saved::person_id.eq(person_id_join)),
),
)
+ .left_join(
+ person_block::table.on(
+ comment::creator_id
+ .eq(person_block::target_id)
+ .and(person_block::person_id.eq(person_id_join)),
+ ),
+ )
.left_join(
comment_like::table.on(
comment::id
community_person_ban::all_columns.nullable(),
community_follower::all_columns.nullable(),
comment_saved::all_columns.nullable(),
+ person_block::all_columns.nullable(),
comment_like::score.nullable(),
))
.first::<CommentViewTuple>(conn)?;
creator_banned_from_community: creator_banned_from_community.is_some(),
subscribed: subscribed.is_some(),
saved: saved.is_some(),
+ creator_blocked: creator_blocked.is_some(),
my_vote,
})
}
.and(comment_saved::person_id.eq(person_id_join)),
),
)
+ .left_join(
+ person_block::table.on(
+ comment::creator_id
+ .eq(person_block::target_id)
+ .and(person_block::person_id.eq(person_id_join)),
+ ),
+ )
+ .left_join(
+ community_block::table.on(
+ community::id
+ .eq(community_block::community_id)
+ .and(community_block::person_id.eq(person_id_join)),
+ ),
+ )
.left_join(
comment_like::table.on(
comment::id
community_person_ban::all_columns.nullable(),
community_follower::all_columns.nullable(),
comment_saved::all_columns.nullable(),
+ person_block::all_columns.nullable(),
comment_like::score.nullable(),
))
.into_boxed();
.order_by(comment_aggregates::score.desc()),
};
+ // Don't show blocked communities or persons
+ if self.my_person_id.is_some() {
+ query = query.filter(community_block::person_id.is_null());
+ query = query.filter(person_block::person_id.is_null());
+ }
+
let (limit, offset) = limit_and_offset(self.page, self.limit);
// Note: deleted and removed comments are done on the front side
creator_banned_from_community: a.7.is_some(),
subscribed: a.8.is_some(),
saved: a.9.is_some(),
- my_vote: a.10,
+ creator_blocked: a.10.is_some(),
+ my_vote: a.11,
})
.collect::<Vec<Self>>()
}
use lemmy_db_queries::{
aggregates::comment_aggregates::CommentAggregates,
establish_unpooled_connection,
+ Blockable,
Crud,
Likeable,
};
- use lemmy_db_schema::source::{comment::*, community::*, person::*, post::*};
+ use lemmy_db_schema::source::{
+ comment::*,
+ community::*,
+ person::*,
+ person_block::PersonBlockForm,
+ post::*,
+ };
use serial_test::serial;
#[test]
let inserted_person = Person::create(&conn, &new_person).unwrap();
+ let new_person_2 = PersonForm {
+ name: "sara".into(),
+ ..PersonForm::default()
+ };
+
+ let inserted_person_2 = Person::create(&conn, &new_person_2).unwrap();
+
let new_community = CommunityForm {
name: "test community 5".to_string(),
title: "nada".to_owned(),
let inserted_comment = Comment::create(&conn, &comment_form).unwrap();
+ let comment_form_2 = CommentForm {
+ content: "A test blocked comment".into(),
+ creator_id: inserted_person_2.id,
+ post_id: inserted_post.id,
+ parent_id: Some(inserted_comment.id),
+ ..CommentForm::default()
+ };
+
+ let inserted_comment_2 = Comment::create(&conn, &comment_form_2).unwrap();
+
+ let timmy_blocks_sara_form = PersonBlockForm {
+ person_id: inserted_person.id,
+ target_id: inserted_person_2.id,
+ };
+
+ let inserted_block = PersonBlock::block(&conn, &timmy_blocks_sara_form).unwrap();
+
+ let expected_block = PersonBlock {
+ id: inserted_block.id,
+ person_id: inserted_person.id,
+ target_id: inserted_person_2.id,
+ published: inserted_block.published,
+ };
+
+ assert_eq!(expected_block, inserted_block);
+
let comment_like_form = CommentLikeForm {
comment_id: inserted_comment.id,
post_id: inserted_post.id,
my_vote: None,
subscribed: false,
saved: false,
+ creator_blocked: false,
comment: Comment {
id: inserted_comment.id,
content: "A test comment 32".into(),
.list()
.unwrap();
+ let read_comment_from_blocked_person =
+ CommentView::read(&conn, inserted_comment_2.id, Some(inserted_person.id)).unwrap();
+
let like_removed = CommentLike::remove(&conn, inserted_person.id, inserted_comment.id).unwrap();
let num_deleted = Comment::delete(&conn, inserted_comment.id).unwrap();
+ Comment::delete(&conn, inserted_comment_2.id).unwrap();
Post::delete(&conn, inserted_post.id).unwrap();
Community::delete(&conn, inserted_community.id).unwrap();
Person::delete(&conn, inserted_person.id).unwrap();
+ Person::delete(&conn, inserted_person_2.id).unwrap();
+
+ // Make sure its 1, not showing the blocked comment
+ assert_eq!(1, read_comment_views_with_person.len());
assert_eq!(
expected_comment_view_no_person,
- read_comment_views_no_person[0]
+ read_comment_views_no_person[1]
);
assert_eq!(
expected_comment_view_with_person,
read_comment_views_with_person[0]
);
+
+ // Make sure block set the creator blocked
+ assert_eq!(true, read_comment_from_blocked_person.creator_blocked);
+
assert_eq!(1, num_deleted);
assert_eq!(1, like_removed);
}
use lemmy_db_schema::{
schema::{
community,
+ community_block,
community_follower,
community_person_ban,
person,
+ person_block,
post,
post_aggregates,
post_like,
source::{
community::{Community, CommunityFollower, CommunityPersonBan, CommunitySafe},
person::{Person, PersonSafe},
+ person_block::PersonBlock,
post::{Post, PostRead, PostSaved},
},
CommunityId,
pub community: CommunitySafe,
pub creator_banned_from_community: bool, // Left Join to CommunityPersonBan
pub counts: PostAggregates,
- pub subscribed: bool, // Left join to CommunityFollower
- pub saved: bool, // Left join to PostSaved
- pub read: bool, // Left join to PostRead
- pub my_vote: Option<i16>, // Left join to PostLike
+ pub subscribed: bool, // Left join to CommunityFollower
+ pub saved: bool, // Left join to PostSaved
+ pub read: bool, // Left join to PostRead
+ pub creator_blocked: bool, // Left join to PersonBlock
+ pub my_vote: Option<i16>, // Left join to PostLike
}
type PostViewTuple = (
Option<CommunityFollower>,
Option<PostSaved>,
Option<PostRead>,
+ Option<PersonBlock>,
Option<i16>,
);
follower,
saved,
read,
+ creator_blocked,
post_like,
) = post::table
.find(post_id)
.and(post_read::person_id.eq(person_id_join)),
),
)
+ .left_join(
+ person_block::table.on(
+ post::creator_id
+ .eq(person_block::target_id)
+ .and(person_block::person_id.eq(person_id_join)),
+ ),
+ )
.left_join(
post_like::table.on(
post::id
community_follower::all_columns.nullable(),
post_saved::all_columns.nullable(),
post_read::all_columns.nullable(),
+ person_block::all_columns.nullable(),
post_like::score.nullable(),
))
.first::<PostViewTuple>(conn)?;
subscribed: follower.is_some(),
saved: saved.is_some(),
read: read.is_some(),
+ creator_blocked: creator_blocked.is_some(),
my_vote,
})
}
.and(post_read::person_id.eq(person_id_join)),
),
)
+ .left_join(
+ person_block::table.on(
+ post::creator_id
+ .eq(person_block::target_id)
+ .and(person_block::person_id.eq(person_id_join)),
+ ),
+ )
+ .left_join(
+ community_block::table.on(
+ community::id
+ .eq(community_block::community_id)
+ .and(community_block::person_id.eq(person_id_join)),
+ ),
+ )
.left_join(
post_like::table.on(
post::id
community_follower::all_columns.nullable(),
post_saved::all_columns.nullable(),
post_read::all_columns.nullable(),
+ person_block::all_columns.nullable(),
post_like::score.nullable(),
))
.into_boxed();
query = query.filter(post_saved::id.is_not_null());
};
+ // Don't show blocked communities or persons
+ if self.my_person_id.is_some() {
+ query = query.filter(community_block::person_id.is_null());
+ query = query.filter(person_block::person_id.is_null());
+ }
+
query = match self.sort.unwrap_or(SortType::Hot) {
SortType::Active => query
.then_order_by(
subscribed: a.5.is_some(),
saved: a.6.is_some(),
read: a.7.is_some(),
- my_vote: a.8,
+ creator_blocked: a.8.is_some(),
+ my_vote: a.9,
})
.collect::<Vec<Self>>()
}
use lemmy_db_queries::{
aggregates::post_aggregates::PostAggregates,
establish_unpooled_connection,
+ Blockable,
Crud,
Likeable,
ListingType,
SortType,
};
- use lemmy_db_schema::source::{community::*, person::*, post::*};
+ use lemmy_db_schema::source::{
+ community::*,
+ community_block::{CommunityBlock, CommunityBlockForm},
+ person::*,
+ person_block::{PersonBlock, PersonBlockForm},
+ post::*,
+ };
use serial_test::serial;
#[test]
let inserted_community = Community::create(&conn, &new_community).unwrap();
+ // Test a person block, make sure the post query doesn't include their post
+ let blocked_person = PersonForm {
+ name: person_name.to_owned(),
+ ..PersonForm::default()
+ };
+
+ let inserted_blocked_person = Person::create(&conn, &blocked_person).unwrap();
+
+ let post_from_blocked_person = PostForm {
+ name: "blocked_person_post".to_string(),
+ creator_id: inserted_blocked_person.id,
+ community_id: inserted_community.id,
+ ..PostForm::default()
+ };
+
+ Post::create(&conn, &post_from_blocked_person).unwrap();
+
+ // block that person
+ let person_block = PersonBlockForm {
+ person_id: inserted_person.id,
+ target_id: inserted_blocked_person.id,
+ };
+
+ PersonBlock::block(&conn, &person_block).unwrap();
+
+ // A sample post
let new_post = PostForm {
name: post_name.to_owned(),
creator_id: inserted_person.id,
subscribed: false,
read: false,
saved: false,
+ creator_blocked: false,
+ };
+
+ // Test a community block
+ let community_block = CommunityBlockForm {
+ person_id: inserted_person.id,
+ community_id: inserted_community.id,
};
+ CommunityBlock::block(&conn, &community_block).unwrap();
+
+ let read_post_listings_with_person_after_block = PostQueryBuilder::create(&conn)
+ .listing_type(ListingType::Community)
+ .sort(SortType::New)
+ .show_bot_accounts(false)
+ .community_id(inserted_community.id)
+ .my_person_id(inserted_person.id)
+ .list()
+ .unwrap();
// TODO More needs to be added here
let mut expected_post_listing_with_user = expected_post_listing_no_person.to_owned();
let like_removed = PostLike::remove(&conn, inserted_person.id, inserted_post.id).unwrap();
let num_deleted = Post::delete(&conn, inserted_post.id).unwrap();
+ PersonBlock::unblock(&conn, &person_block).unwrap();
+ CommunityBlock::unblock(&conn, &community_block).unwrap();
Community::delete(&conn, inserted_community.id).unwrap();
Person::delete(&conn, inserted_person.id).unwrap();
Person::delete(&conn, inserted_bot.id).unwrap();
+ Person::delete(&conn, inserted_blocked_person.id).unwrap();
// The with user
assert_eq!(
read_post_listing_with_person
);
- // Should be only one person, IE the bot post should be missing
+ // Should be only one person, IE the bot post, and blocked should be missing
assert_eq!(1, read_post_listings_with_person.len());
// Without the user
);
assert_eq!(expected_post_listing_no_person, read_post_listing_no_person);
- // Should be 2 posts, with the bot post
- assert_eq!(2, read_post_listings_no_person.len());
+ // Should be 2 posts, with the bot post, and the blocked
+ assert_eq!(3, read_post_listings_no_person.len());
+
+ // Should be 0 posts after the community block
+ assert_eq!(0, read_post_listings_with_person_after_block.len());
assert_eq!(expected_post_like, inserted_post_like);
assert_eq!(1, like_removed);
--- /dev/null
+use diesel::{result::Error, *};
+use lemmy_db_queries::{ToSafe, ViewToVec};
+use lemmy_db_schema::{
+ schema::{community, community_block, person},
+ source::{
+ community::{Community, CommunitySafe},
+ person::{Person, PersonSafe},
+ },
+ PersonId,
+};
+use serde::Serialize;
+
+#[derive(Debug, Serialize, Clone)]
+pub struct CommunityBlockView {
+ pub person: PersonSafe,
+ pub community: CommunitySafe,
+}
+
+type CommunityBlockViewTuple = (PersonSafe, CommunitySafe);
+
+impl CommunityBlockView {
+ pub fn for_person(conn: &PgConnection, person_id: PersonId) -> Result<Vec<Self>, Error> {
+ let res = community_block::table
+ .inner_join(person::table)
+ .inner_join(community::table)
+ .select((
+ Person::safe_columns_tuple(),
+ Community::safe_columns_tuple(),
+ ))
+ .filter(community_block::person_id.eq(person_id))
+ .order_by(community_block::published)
+ .load::<CommunityBlockViewTuple>(conn)?;
+
+ Ok(Self::from_tuple_to_vec(res))
+ }
+}
+
+impl ViewToVec for CommunityBlockView {
+ type DbTuple = CommunityBlockViewTuple;
+ fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
+ items
+ .iter()
+ .map(|a| Self {
+ person: a.0.to_owned(),
+ community: a.1.to_owned(),
+ })
+ .collect::<Vec<Self>>()
+ }
+}
ViewToVec,
};
use lemmy_db_schema::{
- schema::{community, community_aggregates, community_follower},
- source::community::{Community, CommunityFollower, CommunitySafe},
+ schema::{community, community_aggregates, community_block, community_follower},
+ source::{
+ community::{Community, CommunityFollower, CommunitySafe},
+ community_block::CommunityBlock,
+ },
CommunityId,
PersonId,
};
pub struct CommunityView {
pub community: CommunitySafe,
pub subscribed: bool,
+ pub blocked: bool,
pub counts: CommunityAggregates,
}
CommunitySafe,
CommunityAggregates,
Option<CommunityFollower>,
+ Option<CommunityBlock>,
);
impl CommunityView {
// The left join below will return None in this case
let person_id_join = my_person_id.unwrap_or(PersonId(-1));
- let (community, counts, follower) = community::table
+ let (community, counts, follower, blocked) = community::table
.find(community_id)
.inner_join(community_aggregates::table)
.left_join(
.and(community_follower::person_id.eq(person_id_join)),
),
)
+ .left_join(
+ community_block::table.on(
+ community::id
+ .eq(community_block::community_id)
+ .and(community_block::person_id.eq(person_id_join)),
+ ),
+ )
.select((
Community::safe_columns_tuple(),
community_aggregates::all_columns,
community_follower::all_columns.nullable(),
+ community_block::all_columns.nullable(),
))
.first::<CommunityViewTuple>(conn)?;
Ok(CommunityView {
community,
subscribed: follower.is_some(),
+ blocked: blocked.is_some(),
counts,
})
}
.and(community_follower::person_id.eq(person_id_join)),
),
)
+ .left_join(
+ community_block::table.on(
+ community::id
+ .eq(community_block::community_id)
+ .and(community_block::person_id.eq(person_id_join)),
+ ),
+ )
.select((
Community::safe_columns_tuple(),
community_aggregates::all_columns,
community_follower::all_columns.nullable(),
+ community_block::all_columns.nullable(),
))
.into_boxed();
};
}
+ // Don't show blocked communities
+ if self.my_person_id.is_some() {
+ query = query.filter(community_block::person_id.is_null());
+ }
+
let (limit, offset) = limit_and_offset(self.page, self.limit);
let res = query
.limit(limit)
community: a.0.to_owned(),
counts: a.1.to_owned(),
subscribed: a.2.is_some(),
+ blocked: a.3.is_some(),
})
.collect::<Vec<Self>>()
}
+pub mod community_block_view;
pub mod community_follower_view;
pub mod community_moderator_view;
pub mod community_person_ban_view;
pub mod community_view;
+pub mod person_block_view;
pub mod person_mention_view;
pub mod person_view;
--- /dev/null
+use diesel::{result::Error, *};
+use lemmy_db_queries::{ToSafe, ViewToVec};
+use lemmy_db_schema::{
+ schema::{person, person_alias_1, person_block},
+ source::person::{Person, PersonAlias1, PersonSafe, PersonSafeAlias1},
+ PersonId,
+};
+use serde::Serialize;
+
+#[derive(Debug, Serialize, Clone)]
+pub struct PersonBlockView {
+ pub person: PersonSafe,
+ pub target: PersonSafeAlias1,
+}
+
+type PersonBlockViewTuple = (PersonSafe, PersonSafeAlias1);
+
+impl PersonBlockView {
+ pub fn for_person(conn: &PgConnection, person_id: PersonId) -> Result<Vec<Self>, Error> {
+ let res = person_block::table
+ .inner_join(person::table)
+ .inner_join(person_alias_1::table) // TODO I dont know if this will be smart abt the column
+ .select((
+ Person::safe_columns_tuple(),
+ PersonAlias1::safe_columns_tuple(),
+ ))
+ .filter(person_block::person_id.eq(person_id))
+ .order_by(person_block::published)
+ .load::<PersonBlockViewTuple>(conn)?;
+
+ Ok(Self::from_tuple_to_vec(res))
+ }
+}
+
+impl ViewToVec for PersonBlockView {
+ type DbTuple = PersonBlockViewTuple;
+ fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
+ items
+ .iter()
+ .map(|a| Self {
+ person: a.0.to_owned(),
+ target: a.1.to_owned(),
+ })
+ .collect::<Vec<Self>>()
+ }
+}
community_person_ban,
person,
person_alias_1,
+ person_block,
person_mention,
post,
},
comment::{Comment, CommentSaved},
community::{Community, CommunityFollower, CommunityPersonBan, CommunitySafe},
person::{Person, PersonAlias1, PersonSafe, PersonSafeAlias1},
+ person_block::PersonBlock,
person_mention::PersonMention,
post::Post,
},
pub creator_banned_from_community: bool, // Left Join to CommunityPersonBan
pub subscribed: bool, // Left join to CommunityFollower
pub saved: bool, // Left join to CommentSaved
+ pub creator_blocked: bool, // Left join to PersonBlock
pub my_vote: Option<i16>, // Left join to CommentLike
}
Option<CommunityPersonBan>,
Option<CommunityFollower>,
Option<CommentSaved>,
+ Option<PersonBlock>,
Option<i16>,
);
creator_banned_from_community,
subscribed,
saved,
+ creator_blocked,
my_vote,
) = person_mention::table
.find(person_mention_id)
.and(comment_saved::person_id.eq(person_id_join)),
),
)
+ .left_join(
+ person_block::table.on(
+ comment::creator_id
+ .eq(person_block::target_id)
+ .and(person_block::person_id.eq(person_id_join)),
+ ),
+ )
.left_join(
comment_like::table.on(
comment::id
community_person_ban::all_columns.nullable(),
community_follower::all_columns.nullable(),
comment_saved::all_columns.nullable(),
+ person_block::all_columns.nullable(),
comment_like::score.nullable(),
))
.first::<PersonMentionViewTuple>(conn)?;
creator_banned_from_community: creator_banned_from_community.is_some(),
subscribed: subscribed.is_some(),
saved: saved.is_some(),
+ creator_blocked: creator_blocked.is_some(),
my_vote,
})
}
.and(comment_saved::person_id.eq(person_id_join)),
),
)
+ .left_join(
+ person_block::table.on(
+ comment::creator_id
+ .eq(person_block::target_id)
+ .and(person_block::person_id.eq(person_id_join)),
+ ),
+ )
.left_join(
comment_like::table.on(
comment::id
community_person_ban::all_columns.nullable(),
community_follower::all_columns.nullable(),
comment_saved::all_columns.nullable(),
+ person_block::all_columns.nullable(),
comment_like::score.nullable(),
))
.into_boxed();
creator_banned_from_community: a.7.is_some(),
subscribed: a.8.is_some(),
saved: a.9.is_some(),
- my_vote: a.10,
+ creator_blocked: a.10.is_some(),
+ my_vote: a.11,
})
.collect::<Vec<Self>>()
}
ListPostReports,
GetReportCount,
FollowCommunity,
- GetFollowedCommunities,
GetReplies,
GetPersonMentions,
MarkPersonMentionAsRead,
ModJoin,
ChangePassword,
GetSiteMetadata,
+ BlockCommunity,
+ BlockPerson,
}
#[derive(EnumString, ToString, Debug, Clone)]
--- /dev/null
+drop table person_block;
+drop table community_block;
--- /dev/null
+create table person_block (
+ id serial primary key,
+ person_id int references person on update cascade on delete cascade not null,
+ target_id int references person on update cascade on delete cascade not null,
+ published timestamp not null default now(),
+ unique(person_id, target_id)
+);
+
+create table community_block (
+ id serial primary key,
+ person_id int references person on update cascade on delete cascade not null,
+ community_id int references community on update cascade on delete cascade not null,
+ published timestamp not null default now(),
+ unique(person_id, community_id)
+);
.route("", web::put().to(route_post_crud::<EditCommunity>))
.route("/list", web::get().to(route_get_crud::<ListCommunities>))
.route("/follow", web::post().to(route_post::<FollowCommunity>))
+ .route("/block", web::post().to(route_post::<BlockCommunity>))
.route(
"/delete",
web::post().to(route_post_crud::<DeleteCommunity>),
web::post().to(route_post::<MarkPersonMentionAsRead>),
)
.route("/replies", web::get().to(route_get::<GetReplies>))
- .route(
- "/followed_communities",
- web::get().to(route_get::<GetFollowedCommunities>),
- )
.route("/join", web::post().to(route_post::<UserJoin>))
// Admin action. I don't like that it's in /user
.route("/ban", web::post().to(route_post::<BanPerson>))
+ .route("/block", web::post().to(route_post::<BlockPerson>))
// Account actions. I don't like that they're in /user maybe /accounts
.route("/login", web::post().to(route_post::<Login>))
.route("/get_captcha", web::get().to(route_get::<GetCaptcha>))