mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-12-23 11:21:32 +00:00
* A first pass at user / community blocking. #426 * Adding unit tests for person and community block. * Moving migration * Fixing creator_blocked for comment queries, added tests. * Don't let a person block themselves * Fix post creator_blocked * Adding creator_blocked to PersonMentionView * Moving blocked and follows to MyUserInfo * Rename to local_user_view * Add moderates to MyUserInfo * Adding BlockCommunityResponse * Fixing name, and check_person_block * Fixing tests. * Using type in Blockable trait. * Changing recipient to target, adding unfollow to block action.
This commit is contained in:
parent
6af75492a9
commit
2016afc9db
41 changed files with 808 additions and 119 deletions
|
@ -16,7 +16,7 @@
|
||||||
"eslint": "^7.30.0",
|
"eslint": "^7.30.0",
|
||||||
"eslint-plugin-jane": "^9.0.3",
|
"eslint-plugin-jane": "^9.0.3",
|
||||||
"jest": "^27.0.6",
|
"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",
|
"node-fetch": "^2.6.1",
|
||||||
"prettier": "^2.3.2",
|
"prettier": "^2.3.2",
|
||||||
"ts-jest": "^27.0.3",
|
"ts-jest": "^27.0.3",
|
||||||
|
|
|
@ -332,9 +332,9 @@ test('A and G subscribe to B (center) A posts, G mentions B, it gets announced t
|
||||||
|
|
||||||
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 () => {
|
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
|
// Unfollow all remote communities
|
||||||
let followed = await unfollowRemotes(alpha);
|
let site = await unfollowRemotes(alpha);
|
||||||
expect(
|
expect(
|
||||||
followed.communities.filter(c => c.community.local == false).length
|
site.my_user.follows.filter(c => c.community.local == false).length
|
||||||
).toBe(0);
|
).toBe(0);
|
||||||
|
|
||||||
// B creates a post, and two comments, should be invisible to A
|
// B creates a post, and two comments, should be invisible to A
|
||||||
|
|
|
@ -4,8 +4,8 @@ import {
|
||||||
setupLogins,
|
setupLogins,
|
||||||
searchForBetaCommunity,
|
searchForBetaCommunity,
|
||||||
followCommunity,
|
followCommunity,
|
||||||
checkFollowedCommunities,
|
|
||||||
unfollowRemotes,
|
unfollowRemotes,
|
||||||
|
getSite,
|
||||||
} from './shared';
|
} from './shared';
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
|
@ -29,8 +29,8 @@ test('Follow federated community', async () => {
|
||||||
expect(follow.community_view.community.name).toBe('main');
|
expect(follow.community_view.community.name).toBe('main');
|
||||||
|
|
||||||
// Check it from local
|
// Check it from local
|
||||||
let followCheck = await checkFollowedCommunities(alpha);
|
let site = await getSite(alpha);
|
||||||
let remoteCommunityId = followCheck.communities.find(
|
let remoteCommunityId = site.my_user.follows.find(
|
||||||
c => c.community.local == false
|
c => c.community.local == false
|
||||||
).community.id;
|
).community.id;
|
||||||
expect(remoteCommunityId).toBeDefined();
|
expect(remoteCommunityId).toBeDefined();
|
||||||
|
@ -40,6 +40,6 @@ test('Follow federated community', async () => {
|
||||||
expect(unfollow.community_view.community.local).toBe(false);
|
expect(unfollow.community_view.community.local).toBe(false);
|
||||||
|
|
||||||
// Make sure you are unsubbed locally
|
// Make sure you are unsubbed locally
|
||||||
let unfollowCheck = await checkFollowedCommunities(alpha);
|
let siteUnfollowCheck = await getSite(alpha);
|
||||||
expect(unfollowCheck.communities.length).toBeGreaterThanOrEqual(1);
|
expect(siteUnfollowCheck.my_user.follows.length).toBeGreaterThanOrEqual(1);
|
||||||
});
|
});
|
||||||
|
|
|
@ -12,7 +12,6 @@ import {
|
||||||
SearchResponse,
|
SearchResponse,
|
||||||
FollowCommunity,
|
FollowCommunity,
|
||||||
CommunityResponse,
|
CommunityResponse,
|
||||||
GetFollowedCommunitiesResponse,
|
|
||||||
GetPostResponse,
|
GetPostResponse,
|
||||||
Register,
|
Register,
|
||||||
Comment,
|
Comment,
|
||||||
|
@ -30,7 +29,6 @@ import {
|
||||||
CreatePostLike,
|
CreatePostLike,
|
||||||
EditPrivateMessage,
|
EditPrivateMessage,
|
||||||
DeletePrivateMessage,
|
DeletePrivateMessage,
|
||||||
GetFollowedCommunities,
|
|
||||||
GetPrivateMessages,
|
GetPrivateMessages,
|
||||||
GetSite,
|
GetSite,
|
||||||
GetPost,
|
GetPost,
|
||||||
|
@ -336,15 +334,6 @@ export async function followCommunity(
|
||||||
return api.client.followCommunity(form);
|
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(
|
export async function likePost(
|
||||||
api: API,
|
api: API,
|
||||||
score: number,
|
score: number,
|
||||||
|
@ -543,8 +532,7 @@ export async function registerUser(
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function saveUserSettingsBio(
|
export async function saveUserSettingsBio(
|
||||||
api: API,
|
api: API
|
||||||
auth: string
|
|
||||||
): Promise<LoginResponse> {
|
): Promise<LoginResponse> {
|
||||||
let form: SaveUserSettings = {
|
let form: SaveUserSettings = {
|
||||||
show_nsfw: true,
|
show_nsfw: true,
|
||||||
|
@ -555,7 +543,7 @@ export async function saveUserSettingsBio(
|
||||||
show_avatars: true,
|
show_avatars: true,
|
||||||
send_notifications_to_email: false,
|
send_notifications_to_email: false,
|
||||||
bio: 'a changed bio',
|
bio: 'a changed bio',
|
||||||
auth,
|
auth: api.auth,
|
||||||
};
|
};
|
||||||
return saveUserSettings(api, form);
|
return saveUserSettings(api, form);
|
||||||
}
|
}
|
||||||
|
@ -568,11 +556,10 @@ export async function saveUserSettings(
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getSite(
|
export async function getSite(
|
||||||
api: API,
|
api: API
|
||||||
auth: string
|
|
||||||
): Promise<GetSiteResponse> {
|
): Promise<GetSiteResponse> {
|
||||||
let form: GetSite = {
|
let form: GetSite = {
|
||||||
auth,
|
auth: api.auth,
|
||||||
};
|
};
|
||||||
return api.client.getSite(form);
|
return api.client.getSite(form);
|
||||||
}
|
}
|
||||||
|
@ -590,17 +577,17 @@ export async function listPrivateMessages(
|
||||||
|
|
||||||
export async function unfollowRemotes(
|
export async function unfollowRemotes(
|
||||||
api: API
|
api: API
|
||||||
): Promise<GetFollowedCommunitiesResponse> {
|
): Promise<GetSiteResponse> {
|
||||||
// Unfollow all remote communities
|
// Unfollow all remote communities
|
||||||
let followed = await checkFollowedCommunities(api);
|
let site = await getSite(api);
|
||||||
let remoteFollowed = followed.communities.filter(
|
let remoteFollowed = site.my_user.follows.filter(
|
||||||
c => c.community.local == false
|
c => c.community.local == false
|
||||||
);
|
);
|
||||||
for (let cu of remoteFollowed) {
|
for (let cu of remoteFollowed) {
|
||||||
await followCommunity(api, false, cu.community.id);
|
await followCommunity(api, false, cu.community.id);
|
||||||
}
|
}
|
||||||
let followed2 = await checkFollowedCommunities(api);
|
let siteRes = await getSite(api);
|
||||||
return followed2;
|
return siteRes;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function followBeta(api: API): Promise<CommunityResponse> {
|
export async function followBeta(api: API): Promise<CommunityResponse> {
|
||||||
|
|
|
@ -14,12 +14,11 @@ import {
|
||||||
ListingType,
|
ListingType,
|
||||||
} from 'lemmy-js-client';
|
} from 'lemmy-js-client';
|
||||||
|
|
||||||
let auth: string;
|
|
||||||
let apShortname: string;
|
let apShortname: string;
|
||||||
|
|
||||||
function assertUserFederation(userOne: PersonViewSafe, userTwo: PersonViewSafe) {
|
function assertUserFederation(userOne: PersonViewSafe, userTwo: PersonViewSafe) {
|
||||||
expect(userOne.person.name).toBe(userTwo.person.name);
|
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.bio).toBe(userTwo.person.bio);
|
||||||
expect(userOne.person.actor_id).toBe(userTwo.person.actor_id);
|
expect(userOne.person.actor_id).toBe(userTwo.person.actor_id);
|
||||||
expect(userOne.person.avatar).toBe(userTwo.person.avatar);
|
expect(userOne.person.avatar).toBe(userTwo.person.avatar);
|
||||||
|
@ -30,11 +29,11 @@ function assertUserFederation(userOne: PersonViewSafe, userTwo: PersonViewSafe)
|
||||||
test('Create user', async () => {
|
test('Create user', async () => {
|
||||||
let userRes = await registerUser(alpha);
|
let userRes = await registerUser(alpha);
|
||||||
expect(userRes.jwt).toBeDefined();
|
expect(userRes.jwt).toBeDefined();
|
||||||
auth = userRes.jwt;
|
alpha.auth = userRes.jwt;
|
||||||
|
|
||||||
let site = await getSite(alpha, auth);
|
let site = await getSite(alpha);
|
||||||
expect(site.my_user).toBeDefined();
|
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 () => {
|
test('Set some user settings, check that they are federated', async () => {
|
||||||
|
@ -49,11 +48,11 @@ test('Set some user settings, check that they are federated', async () => {
|
||||||
lang: '',
|
lang: '',
|
||||||
avatar,
|
avatar,
|
||||||
banner,
|
banner,
|
||||||
preferred_username: 'user321',
|
display_name: 'user321',
|
||||||
show_avatars: false,
|
show_avatars: false,
|
||||||
send_notifications_to_email: false,
|
send_notifications_to_email: false,
|
||||||
bio,
|
bio,
|
||||||
auth,
|
auth: alpha.auth,
|
||||||
};
|
};
|
||||||
await saveUserSettings(alpha, form);
|
await saveUserSettings(alpha, form);
|
||||||
|
|
||||||
|
|
|
@ -3076,10 +3076,10 @@ language-tags@^1.0.5:
|
||||||
dependencies:
|
dependencies:
|
||||||
language-subtag-registry "~0.3.2"
|
language-subtag-registry "~0.3.2"
|
||||||
|
|
||||||
lemmy-js-client@0.11.0-rc.3:
|
lemmy-js-client@0.11.4-rc.9:
|
||||||
version "0.11.0-rc.3"
|
version "0.11.4-rc.9"
|
||||||
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.11.0-rc.3.tgz#dd4727ca4d16fe9593368725aacfd9e7a8d52548"
|
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.11.4-rc.9.tgz#f7b3c73691e4c1600daf3840d22d9cfbddc5f363"
|
||||||
integrity sha512-16mgl+TS1+0UHiY+ZKPuqHfbrn93h8K8tJ+kKJ1pjT2WhG23o0B8dLahG1jDQPG+dkXpR6PZxYudMYjuO8WvjQ==
|
integrity sha512-zP8JxWzQU+yuyE8cMG0GzR8aR3lJ++G5zzbynsXwDevzAZXhOm0ObNNtJiA3Q5msStFVKVYa3GwZxBv4XiYshw==
|
||||||
|
|
||||||
leven@^3.1.0:
|
leven@^3.1.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
|
|
|
@ -4,6 +4,7 @@ use lemmy_api_common::{
|
||||||
blocking,
|
blocking,
|
||||||
check_community_ban,
|
check_community_ban,
|
||||||
check_downvotes_enabled,
|
check_downvotes_enabled,
|
||||||
|
check_person_block,
|
||||||
comment::*,
|
comment::*,
|
||||||
get_local_user_view_from_jwt,
|
get_local_user_view_from_jwt,
|
||||||
};
|
};
|
||||||
|
@ -151,6 +152,13 @@ impl Perform for CreateCommentLike {
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
check_person_block(
|
||||||
|
local_user_view.person.id,
|
||||||
|
orig_comment.get_recipient_id(),
|
||||||
|
context.pool(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
// Add parent user to recipients
|
// Add parent user to recipients
|
||||||
let recipient_id = orig_comment.get_recipient_id();
|
let recipient_id = orig_comment.get_recipient_id();
|
||||||
if let Ok(local_recipient) = blocking(context.pool(), move |conn| {
|
if let Ok(local_recipient) = blocking(context.pool(), move |conn| {
|
||||||
|
|
|
@ -18,6 +18,7 @@ use lemmy_apub::{
|
||||||
use lemmy_db_queries::{
|
use lemmy_db_queries::{
|
||||||
source::{comment::Comment_, community::CommunityModerator_, post::Post_},
|
source::{comment::Comment_, community::CommunityModerator_, post::Post_},
|
||||||
Bannable,
|
Bannable,
|
||||||
|
Blockable,
|
||||||
Crud,
|
Crud,
|
||||||
Followable,
|
Followable,
|
||||||
Joinable,
|
Joinable,
|
||||||
|
@ -25,6 +26,7 @@ use lemmy_db_queries::{
|
||||||
use lemmy_db_schema::source::{
|
use lemmy_db_schema::source::{
|
||||||
comment::Comment,
|
comment::Comment,
|
||||||
community::*,
|
community::*,
|
||||||
|
community_block::{CommunityBlock, CommunityBlockForm},
|
||||||
moderator::*,
|
moderator::*,
|
||||||
person::Person,
|
person::Person,
|
||||||
post::Post,
|
post::Post,
|
||||||
|
@ -107,6 +109,66 @@ impl Perform for FollowCommunity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl Perform for BanFromCommunity {
|
impl Perform for BanFromCommunity {
|
||||||
type Response = BanFromCommunityResponse;
|
type Response = BanFromCommunityResponse;
|
||||||
|
|
|
@ -39,6 +39,9 @@ pub async fn match_websocket_operation(
|
||||||
UserOperation::GetReplies => do_websocket_operation::<GetReplies>(context, id, op, data).await,
|
UserOperation::GetReplies => do_websocket_operation::<GetReplies>(context, id, op, data).await,
|
||||||
UserOperation::AddAdmin => do_websocket_operation::<AddAdmin>(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::BanPerson => do_websocket_operation::<BanPerson>(context, id, op, data).await,
|
||||||
|
UserOperation::BlockPerson => {
|
||||||
|
do_websocket_operation::<BlockPerson>(context, id, op, data).await
|
||||||
|
}
|
||||||
UserOperation::GetPersonMentions => {
|
UserOperation::GetPersonMentions => {
|
||||||
do_websocket_operation::<GetPersonMentions>(context, id, op, data).await
|
do_websocket_operation::<GetPersonMentions>(context, id, op, data).await
|
||||||
}
|
}
|
||||||
|
@ -95,8 +98,8 @@ pub async fn match_websocket_operation(
|
||||||
UserOperation::FollowCommunity => {
|
UserOperation::FollowCommunity => {
|
||||||
do_websocket_operation::<FollowCommunity>(context, id, op, data).await
|
do_websocket_operation::<FollowCommunity>(context, id, op, data).await
|
||||||
}
|
}
|
||||||
UserOperation::GetFollowedCommunities => {
|
UserOperation::BlockCommunity => {
|
||||||
do_websocket_operation::<GetFollowedCommunities>(context, id, op, data).await
|
do_websocket_operation::<BlockCommunity>(context, id, op, data).await
|
||||||
}
|
}
|
||||||
UserOperation::BanFromCommunity => {
|
UserOperation::BanFromCommunity => {
|
||||||
do_websocket_operation::<BanFromCommunity>(context, id, op, data).await
|
do_websocket_operation::<BanFromCommunity>(context, id, op, data).await
|
||||||
|
|
|
@ -7,7 +7,6 @@ use chrono::Duration;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
blocking,
|
blocking,
|
||||||
collect_moderated_communities,
|
collect_moderated_communities,
|
||||||
community::{GetFollowedCommunities, GetFollowedCommunitiesResponse},
|
|
||||||
get_local_user_view_from_jwt,
|
get_local_user_view_from_jwt,
|
||||||
is_admin,
|
is_admin,
|
||||||
password_length_check,
|
password_length_check,
|
||||||
|
@ -27,6 +26,7 @@ use lemmy_db_queries::{
|
||||||
post::Post_,
|
post::Post_,
|
||||||
private_message::PrivateMessage_,
|
private_message::PrivateMessage_,
|
||||||
},
|
},
|
||||||
|
Blockable,
|
||||||
Crud,
|
Crud,
|
||||||
SortType,
|
SortType,
|
||||||
};
|
};
|
||||||
|
@ -39,6 +39,7 @@ use lemmy_db_schema::{
|
||||||
moderator::*,
|
moderator::*,
|
||||||
password_reset_request::*,
|
password_reset_request::*,
|
||||||
person::*,
|
person::*,
|
||||||
|
person_block::{PersonBlock, PersonBlockForm},
|
||||||
person_mention::*,
|
person_mention::*,
|
||||||
post::Post,
|
post::Post,
|
||||||
private_message::PrivateMessage,
|
private_message::PrivateMessage,
|
||||||
|
@ -52,7 +53,6 @@ use lemmy_db_views::{
|
||||||
post_report_view::PostReportView,
|
post_report_view::PostReportView,
|
||||||
};
|
};
|
||||||
use lemmy_db_views_actor::{
|
use lemmy_db_views_actor::{
|
||||||
community_follower_view::CommunityFollowerView,
|
|
||||||
community_moderator_view::CommunityModeratorView,
|
community_moderator_view::CommunityModeratorView,
|
||||||
person_mention_view::{PersonMentionQueryBuilder, PersonMentionView},
|
person_mention_view::{PersonMentionQueryBuilder, PersonMentionView},
|
||||||
person_view::PersonViewSafe,
|
person_view::PersonViewSafe,
|
||||||
|
@ -471,6 +471,59 @@ impl Perform for BanPerson {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl Perform for GetReplies {
|
impl Perform for GetReplies {
|
||||||
type Response = GetRepliesResponse;
|
type Response = GetRepliesResponse;
|
||||||
|
@ -778,27 +831,3 @@ impl Perform for GetReportCount {
|
||||||
Ok(res)
|
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 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ use lemmy_api_common::{
|
||||||
blocking,
|
blocking,
|
||||||
check_community_ban,
|
check_community_ban,
|
||||||
check_downvotes_enabled,
|
check_downvotes_enabled,
|
||||||
|
check_person_block,
|
||||||
get_local_user_view_from_jwt,
|
get_local_user_view_from_jwt,
|
||||||
is_mod_or_admin,
|
is_mod_or_admin,
|
||||||
mark_post_as_read,
|
mark_post_as_read,
|
||||||
|
@ -48,6 +49,8 @@ impl Perform for CreatePostLike {
|
||||||
|
|
||||||
check_community_ban(local_user_view.person.id, post.community_id, context.pool()).await?;
|
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 {
|
let like_form = PostLikeForm {
|
||||||
post_id: data.post_id,
|
post_id: data.post_id,
|
||||||
person_id: local_user_view.person.id,
|
person_id: local_user_view.person.id,
|
||||||
|
|
|
@ -4,7 +4,6 @@ use anyhow::Context;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
blocking,
|
blocking,
|
||||||
build_federated_instances,
|
build_federated_instances,
|
||||||
get_local_user_settings_view_from_jwt,
|
|
||||||
get_local_user_view_from_jwt,
|
get_local_user_view_from_jwt,
|
||||||
get_local_user_view_from_jwt_opt,
|
get_local_user_view_from_jwt_opt,
|
||||||
is_admin,
|
is_admin,
|
||||||
|
@ -422,15 +421,13 @@ impl Perform for TransferSite {
|
||||||
let banned = blocking(context.pool(), move |conn| PersonViewSafe::banned(conn)).await??;
|
let banned = blocking(context.pool(), move |conn| PersonViewSafe::banned(conn)).await??;
|
||||||
let federated_instances = build_federated_instances(context.pool()).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 {
|
Ok(GetSiteResponse {
|
||||||
site_view: Some(site_view),
|
site_view: Some(site_view),
|
||||||
admins,
|
admins,
|
||||||
banned,
|
banned,
|
||||||
online: 0,
|
online: 0,
|
||||||
version: version::VERSION.to_string(),
|
version: version::VERSION.to_string(),
|
||||||
my_user,
|
my_user: None,
|
||||||
federated_instances,
|
federated_instances,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use lemmy_db_schema::{CommunityId, PersonId};
|
use lemmy_db_schema::{CommunityId, PersonId};
|
||||||
use lemmy_db_views_actor::{
|
use lemmy_db_views_actor::{
|
||||||
community_follower_view::CommunityFollowerView,
|
|
||||||
community_moderator_view::CommunityModeratorView,
|
community_moderator_view::CommunityModeratorView,
|
||||||
community_view::CommunityView,
|
community_view::CommunityView,
|
||||||
person_view::PersonViewSafe,
|
person_view::PersonViewSafe,
|
||||||
|
@ -117,13 +116,16 @@ pub struct FollowCommunity {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct GetFollowedCommunities {
|
pub struct BlockCommunity {
|
||||||
|
pub community_id: CommunityId,
|
||||||
|
pub block: bool,
|
||||||
pub auth: String,
|
pub auth: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize, Clone)]
|
||||||
pub struct GetFollowedCommunitiesResponse {
|
pub struct BlockCommunityResponse {
|
||||||
pub communities: Vec<CommunityFollowerView>,
|
pub community_view: CommunityView,
|
||||||
|
pub blocked: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
|
|
@ -10,6 +10,7 @@ use diesel::PgConnection;
|
||||||
use lemmy_db_queries::{
|
use lemmy_db_queries::{
|
||||||
source::{
|
source::{
|
||||||
community::{CommunityModerator_, Community_},
|
community::{CommunityModerator_, Community_},
|
||||||
|
person_block::PersonBlock_,
|
||||||
site::Site_,
|
site::Site_,
|
||||||
},
|
},
|
||||||
Crud,
|
Crud,
|
||||||
|
@ -21,6 +22,7 @@ use lemmy_db_schema::{
|
||||||
comment::Comment,
|
comment::Comment,
|
||||||
community::{Community, CommunityModerator},
|
community::{Community, CommunityModerator},
|
||||||
person::Person,
|
person::Person,
|
||||||
|
person_block::PersonBlock,
|
||||||
person_mention::{PersonMention, PersonMentionForm},
|
person_mention::{PersonMention, PersonMentionForm},
|
||||||
post::{Post, PostRead, PostReadForm},
|
post::{Post, PostRead, PostReadForm},
|
||||||
site::Site,
|
site::Site,
|
||||||
|
@ -353,6 +355,19 @@ pub async fn check_community_ban(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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> {
|
pub async fn check_downvotes_enabled(score: i16, pool: &DbPool) -> Result<(), LemmyError> {
|
||||||
if score == -1 {
|
if score == -1 {
|
||||||
let site = blocking(pool, move |conn| Site::read_simple(conn)).await??;
|
let site = blocking(pool, move |conn| Site::read_simple(conn)).await??;
|
||||||
|
|
|
@ -4,7 +4,6 @@ use lemmy_db_views::{
|
||||||
private_message_view::PrivateMessageView,
|
private_message_view::PrivateMessageView,
|
||||||
};
|
};
|
||||||
use lemmy_db_views_actor::{
|
use lemmy_db_views_actor::{
|
||||||
community_follower_view::CommunityFollowerView,
|
|
||||||
community_moderator_view::CommunityModeratorView,
|
community_moderator_view::CommunityModeratorView,
|
||||||
person_mention_view::PersonMentionView,
|
person_mention_view::PersonMentionView,
|
||||||
person_view::PersonViewSafe,
|
person_view::PersonViewSafe,
|
||||||
|
@ -96,10 +95,9 @@ pub struct GetPersonDetails {
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct GetPersonDetailsResponse {
|
pub struct GetPersonDetailsResponse {
|
||||||
pub person_view: PersonViewSafe,
|
pub person_view: PersonViewSafe,
|
||||||
pub follows: Vec<CommunityFollowerView>,
|
|
||||||
pub moderates: Vec<CommunityModeratorView>,
|
|
||||||
pub comments: Vec<CommentView>,
|
pub comments: Vec<CommentView>,
|
||||||
pub posts: Vec<PostView>,
|
pub posts: Vec<PostView>,
|
||||||
|
pub moderates: Vec<CommunityModeratorView>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
|
@ -145,6 +143,19 @@ pub struct BanPersonResponse {
|
||||||
pub banned: bool,
|
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)]
|
#[derive(Deserialize)]
|
||||||
pub struct GetReplies {
|
pub struct GetReplies {
|
||||||
pub sort: Option<String>,
|
pub sort: Option<String>,
|
||||||
|
|
|
@ -5,7 +5,14 @@ use lemmy_db_views::{
|
||||||
post_view::PostView,
|
post_view::PostView,
|
||||||
site_view::SiteView,
|
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::{
|
use lemmy_db_views_moderator::{
|
||||||
mod_add_community_view::ModAddCommunityView,
|
mod_add_community_view::ModAddCommunityView,
|
||||||
mod_add_view::ModAddView,
|
mod_add_view::ModAddView,
|
||||||
|
@ -110,10 +117,19 @@ pub struct GetSiteResponse {
|
||||||
pub banned: Vec<PersonViewSafe>,
|
pub banned: Vec<PersonViewSafe>,
|
||||||
pub online: usize,
|
pub online: usize,
|
||||||
pub version: String,
|
pub version: String,
|
||||||
pub my_user: Option<LocalUserSettingsView>,
|
pub my_user: Option<MyUserInfo>,
|
||||||
pub federated_instances: Option<FederatedInstances>, // Federation may be disabled
|
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)]
|
#[derive(Deserialize)]
|
||||||
pub struct TransferSite {
|
pub struct TransferSite {
|
||||||
pub person_id: PersonId,
|
pub person_id: PersonId,
|
||||||
|
|
|
@ -3,6 +3,7 @@ use actix_web::web::Data;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
blocking,
|
blocking,
|
||||||
check_community_ban,
|
check_community_ban,
|
||||||
|
check_person_block,
|
||||||
comment::*,
|
comment::*,
|
||||||
get_local_user_view_from_jwt,
|
get_local_user_view_from_jwt,
|
||||||
get_post,
|
get_post,
|
||||||
|
@ -49,6 +50,8 @@ impl PerformCrud for CreateComment {
|
||||||
|
|
||||||
check_community_ban(local_user_view.person.id, community_id, context.pool()).await?;
|
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
|
// Check if post is locked, no new comments
|
||||||
if post.locked {
|
if post.locked {
|
||||||
return Err(ApiError::err("locked").into());
|
return Err(ApiError::err("locked").into());
|
||||||
|
@ -60,6 +63,10 @@ impl PerformCrud for CreateComment {
|
||||||
let parent = blocking(context.pool(), move |conn| Comment::read(conn, parent_id))
|
let parent = blocking(context.pool(), move |conn| Comment::read(conn, parent_id))
|
||||||
.await?
|
.await?
|
||||||
.map_err(|_| ApiError::err("couldnt_create_comment"))?;
|
.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 {
|
if parent.post_id != post_id {
|
||||||
return Err(ApiError::err("couldnt_create_comment").into());
|
return Err(ApiError::err("couldnt_create_comment").into());
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ impl PerformCrud for EditComment {
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
|
|
||||||
|
// TODO is this necessary? It should really only need to check on create
|
||||||
check_community_ban(
|
check_community_ban(
|
||||||
local_user_view.person.id,
|
local_user_view.person.id,
|
||||||
orig_comment.community.id,
|
orig_comment.community.id,
|
||||||
|
|
|
@ -2,6 +2,7 @@ use crate::PerformCrud;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
blocking,
|
blocking,
|
||||||
|
check_person_block,
|
||||||
get_local_user_view_from_jwt,
|
get_local_user_view_from_jwt,
|
||||||
person::{CreatePrivateMessage, PrivateMessageResponse},
|
person::{CreatePrivateMessage, PrivateMessageResponse},
|
||||||
send_email_to_user,
|
send_email_to_user,
|
||||||
|
@ -34,6 +35,8 @@ impl PerformCrud for CreatePrivateMessage {
|
||||||
|
|
||||||
let content_slurs_removed = remove_slurs(&data.content.to_owned());
|
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 {
|
let private_message_form = PrivateMessageForm {
|
||||||
content: content_slurs_removed.to_owned(),
|
content: content_slurs_removed.to_owned(),
|
||||||
creator_id: local_user_view.person.id,
|
creator_id: local_user_view.person.id,
|
||||||
|
|
|
@ -8,8 +8,14 @@ use lemmy_api_common::{
|
||||||
site::*,
|
site::*,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::site_view::SiteView;
|
use lemmy_db_views::site_view::SiteView;
|
||||||
use lemmy_db_views_actor::person_view::PersonViewSafe;
|
use lemmy_db_views_actor::{
|
||||||
use lemmy_utils::{settings::structs::Settings, version, ConnectionId, LemmyError};
|
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 lemmy_websocket::{messages::GetUsersOnline, LemmyContext};
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
|
@ -83,7 +89,48 @@ impl PerformCrud for GetSite {
|
||||||
.await
|
.await
|
||||||
.unwrap_or(1);
|
.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?;
|
let federated_instances = build_federated_instances(context.pool()).await?;
|
||||||
|
|
||||||
Ok(GetSiteResponse {
|
Ok(GetSiteResponse {
|
||||||
|
|
|
@ -6,7 +6,6 @@ use lemmy_db_queries::{from_opt_str_to_opt_enum, ApubObject, SortType};
|
||||||
use lemmy_db_schema::source::person::*;
|
use lemmy_db_schema::source::person::*;
|
||||||
use lemmy_db_views::{comment_view::CommentQueryBuilder, post_view::PostQueryBuilder};
|
use lemmy_db_views::{comment_view::CommentQueryBuilder, post_view::PostQueryBuilder};
|
||||||
use lemmy_db_views_actor::{
|
use lemmy_db_views_actor::{
|
||||||
community_follower_view::CommunityFollowerView,
|
|
||||||
community_moderator_view::CommunityModeratorView,
|
community_moderator_view::CommunityModeratorView,
|
||||||
person_view::PersonViewSafe,
|
person_view::PersonViewSafe,
|
||||||
};
|
};
|
||||||
|
@ -103,15 +102,6 @@ impl PerformCrud for GetPersonDetails {
|
||||||
})
|
})
|
||||||
.await??;
|
.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| {
|
let moderates = blocking(context.pool(), move |conn| {
|
||||||
CommunityModeratorView::for_person(conn, person_details_id)
|
CommunityModeratorView::for_person(conn, person_details_id)
|
||||||
})
|
})
|
||||||
|
@ -120,7 +110,6 @@ impl PerformCrud for GetPersonDetails {
|
||||||
// Return the jwt
|
// Return the jwt
|
||||||
Ok(GetPersonDetailsResponse {
|
Ok(GetPersonDetailsResponse {
|
||||||
person_view,
|
person_view,
|
||||||
follows,
|
|
||||||
moderates,
|
moderates,
|
||||||
comments,
|
comments,
|
||||||
posts,
|
posts,
|
||||||
|
|
|
@ -108,6 +108,16 @@ pub trait Saveable {
|
||||||
Self: Sized;
|
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 {
|
pub trait Readable {
|
||||||
type Form;
|
type Form;
|
||||||
fn mark_as_read(conn: &PgConnection, form: &Self::Form) -> Result<Self, Error>
|
fn mark_as_read(conn: &PgConnection, form: &Self::Form) -> Result<Self, Error>
|
||||||
|
|
25
crates/db_queries/src/source/community_block.rs
Normal file
25
crates/db_queries/src/source/community_block.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,10 +2,12 @@ pub mod activity;
|
||||||
pub mod comment;
|
pub mod comment;
|
||||||
pub mod comment_report;
|
pub mod comment_report;
|
||||||
pub mod community;
|
pub mod community;
|
||||||
|
pub mod community_block;
|
||||||
pub mod local_user;
|
pub mod local_user;
|
||||||
pub mod moderator;
|
pub mod moderator;
|
||||||
pub mod password_reset_request;
|
pub mod password_reset_request;
|
||||||
pub mod person;
|
pub mod person;
|
||||||
|
pub mod person_block;
|
||||||
pub mod person_mention;
|
pub mod person_mention;
|
||||||
pub mod post;
|
pub mod post;
|
||||||
pub mod post_report;
|
pub mod post_report;
|
||||||
|
|
50
crates/db_queries/src/source/person_block.rs
Normal file
50
crates/db_queries/src/source/person_block.rs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -67,6 +67,12 @@ impl fmt::Display for PrivateMessageId {
|
||||||
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, DieselNewType)]
|
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, DieselNewType)]
|
||||||
pub struct PersonMentionId(i32);
|
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)]
|
#[repr(transparent)]
|
||||||
#[derive(Clone, PartialEq, Serialize, Deserialize, Debug, AsExpression, FromSqlRow)]
|
#[derive(Clone, PartialEq, Serialize, Deserialize, Debug, AsExpression, FromSqlRow)]
|
||||||
#[sql_type = "Text"]
|
#[sql_type = "Text"]
|
||||||
|
|
|
@ -465,6 +465,24 @@ table! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
// These are necessary since diesel doesn't have self joins / aliases
|
||||||
table! {
|
table! {
|
||||||
comment_alias_1 (id) {
|
comment_alias_1 (id) {
|
||||||
|
@ -542,6 +560,9 @@ joinable!(comment -> person_alias_1 (creator_id));
|
||||||
joinable!(post_report -> person_alias_2 (resolver_id));
|
joinable!(post_report -> person_alias_2 (resolver_id));
|
||||||
joinable!(comment_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 -> person (creator_id));
|
||||||
joinable!(comment -> post (post_id));
|
joinable!(comment -> post (post_id));
|
||||||
joinable!(comment_aggregates -> comment (comment_id));
|
joinable!(comment_aggregates -> comment (comment_id));
|
||||||
|
@ -552,6 +573,8 @@ joinable!(comment_report -> comment (comment_id));
|
||||||
joinable!(comment_saved -> comment (comment_id));
|
joinable!(comment_saved -> comment (comment_id));
|
||||||
joinable!(comment_saved -> person (person_id));
|
joinable!(comment_saved -> person (person_id));
|
||||||
joinable!(community_aggregates -> community (community_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 -> community (community_id));
|
||||||
joinable!(community_follower -> person (person_id));
|
joinable!(community_follower -> person (person_id));
|
||||||
joinable!(community_moderator -> community (community_id));
|
joinable!(community_moderator -> community (community_id));
|
||||||
|
@ -594,6 +617,7 @@ allow_tables_to_appear_in_same_query!(
|
||||||
activity,
|
activity,
|
||||||
comment,
|
comment,
|
||||||
comment_aggregates,
|
comment_aggregates,
|
||||||
|
community_block,
|
||||||
comment_like,
|
comment_like,
|
||||||
comment_report,
|
comment_report,
|
||||||
comment_saved,
|
comment_saved,
|
||||||
|
@ -617,6 +641,7 @@ allow_tables_to_appear_in_same_query!(
|
||||||
person,
|
person,
|
||||||
person_aggregates,
|
person_aggregates,
|
||||||
person_ban,
|
person_ban,
|
||||||
|
person_block,
|
||||||
person_mention,
|
person_mention,
|
||||||
post,
|
post,
|
||||||
post_aggregates,
|
post_aggregates,
|
||||||
|
|
25
crates/db_schema/src/source/community_block.rs
Normal file
25
crates/db_schema/src/source/community_block.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
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,
|
||||||
|
}
|
|
@ -2,10 +2,12 @@ pub mod activity;
|
||||||
pub mod comment;
|
pub mod comment;
|
||||||
pub mod comment_report;
|
pub mod comment_report;
|
||||||
pub mod community;
|
pub mod community;
|
||||||
|
pub mod community_block;
|
||||||
pub mod local_user;
|
pub mod local_user;
|
||||||
pub mod moderator;
|
pub mod moderator;
|
||||||
pub mod password_reset_request;
|
pub mod password_reset_request;
|
||||||
pub mod person;
|
pub mod person;
|
||||||
|
pub mod person_block;
|
||||||
pub mod person_mention;
|
pub mod person_mention;
|
||||||
pub mod post;
|
pub mod post;
|
||||||
pub mod post_report;
|
pub mod post_report;
|
||||||
|
|
18
crates/db_schema/src/source/person_block.rs
Normal file
18
crates/db_schema/src/source/person_block.rs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
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,
|
||||||
|
}
|
|
@ -18,16 +18,19 @@ use lemmy_db_schema::{
|
||||||
comment_like,
|
comment_like,
|
||||||
comment_saved,
|
comment_saved,
|
||||||
community,
|
community,
|
||||||
|
community_block,
|
||||||
community_follower,
|
community_follower,
|
||||||
community_person_ban,
|
community_person_ban,
|
||||||
person,
|
person,
|
||||||
person_alias_1,
|
person_alias_1,
|
||||||
|
person_block,
|
||||||
post,
|
post,
|
||||||
},
|
},
|
||||||
source::{
|
source::{
|
||||||
comment::{Comment, CommentAlias1, CommentSaved},
|
comment::{Comment, CommentAlias1, CommentSaved},
|
||||||
community::{Community, CommunityFollower, CommunityPersonBan, CommunitySafe},
|
community::{Community, CommunityFollower, CommunityPersonBan, CommunitySafe},
|
||||||
person::{Person, PersonAlias1, PersonSafe, PersonSafeAlias1},
|
person::{Person, PersonAlias1, PersonSafe, PersonSafeAlias1},
|
||||||
|
person_block::PersonBlock,
|
||||||
post::Post,
|
post::Post,
|
||||||
},
|
},
|
||||||
CommentId,
|
CommentId,
|
||||||
|
@ -49,6 +52,7 @@ pub struct CommentView {
|
||||||
pub creator_banned_from_community: bool, // Left Join to CommunityPersonBan
|
pub creator_banned_from_community: bool, // Left Join to CommunityPersonBan
|
||||||
pub subscribed: bool, // Left join to CommunityFollower
|
pub subscribed: bool, // Left join to CommunityFollower
|
||||||
pub saved: bool, // Left join to CommentSaved
|
pub saved: bool, // Left join to CommentSaved
|
||||||
|
pub creator_blocked: bool, // Left join to PersonBlock
|
||||||
pub my_vote: Option<i16>, // Left join to CommentLike
|
pub my_vote: Option<i16>, // Left join to CommentLike
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,6 +67,7 @@ type CommentViewTuple = (
|
||||||
Option<CommunityPersonBan>,
|
Option<CommunityPersonBan>,
|
||||||
Option<CommunityFollower>,
|
Option<CommunityFollower>,
|
||||||
Option<CommentSaved>,
|
Option<CommentSaved>,
|
||||||
|
Option<PersonBlock>,
|
||||||
Option<i16>,
|
Option<i16>,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -86,6 +91,7 @@ impl CommentView {
|
||||||
creator_banned_from_community,
|
creator_banned_from_community,
|
||||||
subscribed,
|
subscribed,
|
||||||
saved,
|
saved,
|
||||||
|
creator_blocked,
|
||||||
comment_like,
|
comment_like,
|
||||||
) = comment::table
|
) = comment::table
|
||||||
.find(comment_id)
|
.find(comment_id)
|
||||||
|
@ -117,6 +123,13 @@ impl CommentView {
|
||||||
.and(comment_saved::person_id.eq(person_id_join)),
|
.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(
|
.left_join(
|
||||||
comment_like::table.on(
|
comment_like::table.on(
|
||||||
comment::id
|
comment::id
|
||||||
|
@ -135,6 +148,7 @@ impl CommentView {
|
||||||
community_person_ban::all_columns.nullable(),
|
community_person_ban::all_columns.nullable(),
|
||||||
community_follower::all_columns.nullable(),
|
community_follower::all_columns.nullable(),
|
||||||
comment_saved::all_columns.nullable(),
|
comment_saved::all_columns.nullable(),
|
||||||
|
person_block::all_columns.nullable(),
|
||||||
comment_like::score.nullable(),
|
comment_like::score.nullable(),
|
||||||
))
|
))
|
||||||
.first::<CommentViewTuple>(conn)?;
|
.first::<CommentViewTuple>(conn)?;
|
||||||
|
@ -157,6 +171,7 @@ impl CommentView {
|
||||||
creator_banned_from_community: creator_banned_from_community.is_some(),
|
creator_banned_from_community: creator_banned_from_community.is_some(),
|
||||||
subscribed: subscribed.is_some(),
|
subscribed: subscribed.is_some(),
|
||||||
saved: saved.is_some(),
|
saved: saved.is_some(),
|
||||||
|
creator_blocked: creator_blocked.is_some(),
|
||||||
my_vote,
|
my_vote,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -315,6 +330,20 @@ impl<'a> CommentQueryBuilder<'a> {
|
||||||
.and(comment_saved::person_id.eq(person_id_join)),
|
.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(
|
.left_join(
|
||||||
comment_like::table.on(
|
comment_like::table.on(
|
||||||
comment::id
|
comment::id
|
||||||
|
@ -333,6 +362,7 @@ impl<'a> CommentQueryBuilder<'a> {
|
||||||
community_person_ban::all_columns.nullable(),
|
community_person_ban::all_columns.nullable(),
|
||||||
community_follower::all_columns.nullable(),
|
community_follower::all_columns.nullable(),
|
||||||
comment_saved::all_columns.nullable(),
|
comment_saved::all_columns.nullable(),
|
||||||
|
person_block::all_columns.nullable(),
|
||||||
comment_like::score.nullable(),
|
comment_like::score.nullable(),
|
||||||
))
|
))
|
||||||
.into_boxed();
|
.into_boxed();
|
||||||
|
@ -413,6 +443,12 @@ impl<'a> CommentQueryBuilder<'a> {
|
||||||
.order_by(comment_aggregates::score.desc()),
|
.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);
|
let (limit, offset) = limit_and_offset(self.page, self.limit);
|
||||||
|
|
||||||
// Note: deleted and removed comments are done on the front side
|
// Note: deleted and removed comments are done on the front side
|
||||||
|
@ -440,7 +476,8 @@ impl ViewToVec for CommentView {
|
||||||
creator_banned_from_community: a.7.is_some(),
|
creator_banned_from_community: a.7.is_some(),
|
||||||
subscribed: a.8.is_some(),
|
subscribed: a.8.is_some(),
|
||||||
saved: a.9.is_some(),
|
saved: a.9.is_some(),
|
||||||
my_vote: a.10,
|
creator_blocked: a.10.is_some(),
|
||||||
|
my_vote: a.11,
|
||||||
})
|
})
|
||||||
.collect::<Vec<Self>>()
|
.collect::<Vec<Self>>()
|
||||||
}
|
}
|
||||||
|
@ -452,10 +489,17 @@ mod tests {
|
||||||
use lemmy_db_queries::{
|
use lemmy_db_queries::{
|
||||||
aggregates::comment_aggregates::CommentAggregates,
|
aggregates::comment_aggregates::CommentAggregates,
|
||||||
establish_unpooled_connection,
|
establish_unpooled_connection,
|
||||||
|
Blockable,
|
||||||
Crud,
|
Crud,
|
||||||
Likeable,
|
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;
|
use serial_test::serial;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -470,6 +514,13 @@ mod tests {
|
||||||
|
|
||||||
let inserted_person = Person::create(&conn, &new_person).unwrap();
|
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 {
|
let new_community = CommunityForm {
|
||||||
name: "test community 5".to_string(),
|
name: "test community 5".to_string(),
|
||||||
title: "nada".to_owned(),
|
title: "nada".to_owned(),
|
||||||
|
@ -496,6 +547,32 @@ mod tests {
|
||||||
|
|
||||||
let inserted_comment = Comment::create(&conn, &comment_form).unwrap();
|
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 {
|
let comment_like_form = CommentLikeForm {
|
||||||
comment_id: inserted_comment.id,
|
comment_id: inserted_comment.id,
|
||||||
post_id: inserted_post.id,
|
post_id: inserted_post.id,
|
||||||
|
@ -512,6 +589,7 @@ mod tests {
|
||||||
my_vote: None,
|
my_vote: None,
|
||||||
subscribed: false,
|
subscribed: false,
|
||||||
saved: false,
|
saved: false,
|
||||||
|
creator_blocked: false,
|
||||||
comment: Comment {
|
comment: Comment {
|
||||||
id: inserted_comment.id,
|
id: inserted_comment.id,
|
||||||
content: "A test comment 32".into(),
|
content: "A test comment 32".into(),
|
||||||
|
@ -606,20 +684,32 @@ mod tests {
|
||||||
.list()
|
.list()
|
||||||
.unwrap();
|
.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 like_removed = CommentLike::remove(&conn, inserted_person.id, inserted_comment.id).unwrap();
|
||||||
let num_deleted = Comment::delete(&conn, 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();
|
Post::delete(&conn, inserted_post.id).unwrap();
|
||||||
Community::delete(&conn, inserted_community.id).unwrap();
|
Community::delete(&conn, inserted_community.id).unwrap();
|
||||||
Person::delete(&conn, inserted_person.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!(
|
assert_eq!(
|
||||||
expected_comment_view_no_person,
|
expected_comment_view_no_person,
|
||||||
read_comment_views_no_person[0]
|
read_comment_views_no_person[1]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expected_comment_view_with_person,
|
expected_comment_view_with_person,
|
||||||
read_comment_views_with_person[0]
|
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, num_deleted);
|
||||||
assert_eq!(1, like_removed);
|
assert_eq!(1, like_removed);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,9 +13,11 @@ use lemmy_db_queries::{
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
schema::{
|
schema::{
|
||||||
community,
|
community,
|
||||||
|
community_block,
|
||||||
community_follower,
|
community_follower,
|
||||||
community_person_ban,
|
community_person_ban,
|
||||||
person,
|
person,
|
||||||
|
person_block,
|
||||||
post,
|
post,
|
||||||
post_aggregates,
|
post_aggregates,
|
||||||
post_like,
|
post_like,
|
||||||
|
@ -25,6 +27,7 @@ use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
community::{Community, CommunityFollower, CommunityPersonBan, CommunitySafe},
|
community::{Community, CommunityFollower, CommunityPersonBan, CommunitySafe},
|
||||||
person::{Person, PersonSafe},
|
person::{Person, PersonSafe},
|
||||||
|
person_block::PersonBlock,
|
||||||
post::{Post, PostRead, PostSaved},
|
post::{Post, PostRead, PostSaved},
|
||||||
},
|
},
|
||||||
CommunityId,
|
CommunityId,
|
||||||
|
@ -45,6 +48,7 @@ pub struct PostView {
|
||||||
pub subscribed: bool, // Left join to CommunityFollower
|
pub subscribed: bool, // Left join to CommunityFollower
|
||||||
pub saved: bool, // Left join to PostSaved
|
pub saved: bool, // Left join to PostSaved
|
||||||
pub read: bool, // Left join to PostRead
|
pub read: bool, // Left join to PostRead
|
||||||
|
pub creator_blocked: bool, // Left join to PersonBlock
|
||||||
pub my_vote: Option<i16>, // Left join to PostLike
|
pub my_vote: Option<i16>, // Left join to PostLike
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +61,7 @@ type PostViewTuple = (
|
||||||
Option<CommunityFollower>,
|
Option<CommunityFollower>,
|
||||||
Option<PostSaved>,
|
Option<PostSaved>,
|
||||||
Option<PostRead>,
|
Option<PostRead>,
|
||||||
|
Option<PersonBlock>,
|
||||||
Option<i16>,
|
Option<i16>,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -78,6 +83,7 @@ impl PostView {
|
||||||
follower,
|
follower,
|
||||||
saved,
|
saved,
|
||||||
read,
|
read,
|
||||||
|
creator_blocked,
|
||||||
post_like,
|
post_like,
|
||||||
) = post::table
|
) = post::table
|
||||||
.find(post_id)
|
.find(post_id)
|
||||||
|
@ -112,6 +118,13 @@ impl PostView {
|
||||||
.and(post_read::person_id.eq(person_id_join)),
|
.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(
|
.left_join(
|
||||||
post_like::table.on(
|
post_like::table.on(
|
||||||
post::id
|
post::id
|
||||||
|
@ -128,6 +141,7 @@ impl PostView {
|
||||||
community_follower::all_columns.nullable(),
|
community_follower::all_columns.nullable(),
|
||||||
post_saved::all_columns.nullable(),
|
post_saved::all_columns.nullable(),
|
||||||
post_read::all_columns.nullable(),
|
post_read::all_columns.nullable(),
|
||||||
|
person_block::all_columns.nullable(),
|
||||||
post_like::score.nullable(),
|
post_like::score.nullable(),
|
||||||
))
|
))
|
||||||
.first::<PostViewTuple>(conn)?;
|
.first::<PostViewTuple>(conn)?;
|
||||||
|
@ -149,6 +163,7 @@ impl PostView {
|
||||||
subscribed: follower.is_some(),
|
subscribed: follower.is_some(),
|
||||||
saved: saved.is_some(),
|
saved: saved.is_some(),
|
||||||
read: read.is_some(),
|
read: read.is_some(),
|
||||||
|
creator_blocked: creator_blocked.is_some(),
|
||||||
my_vote,
|
my_vote,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -301,6 +316,20 @@ impl<'a> PostQueryBuilder<'a> {
|
||||||
.and(post_read::person_id.eq(person_id_join)),
|
.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(
|
.left_join(
|
||||||
post_like::table.on(
|
post_like::table.on(
|
||||||
post::id
|
post::id
|
||||||
|
@ -317,6 +346,7 @@ impl<'a> PostQueryBuilder<'a> {
|
||||||
community_follower::all_columns.nullable(),
|
community_follower::all_columns.nullable(),
|
||||||
post_saved::all_columns.nullable(),
|
post_saved::all_columns.nullable(),
|
||||||
post_read::all_columns.nullable(),
|
post_read::all_columns.nullable(),
|
||||||
|
person_block::all_columns.nullable(),
|
||||||
post_like::score.nullable(),
|
post_like::score.nullable(),
|
||||||
))
|
))
|
||||||
.into_boxed();
|
.into_boxed();
|
||||||
|
@ -377,6 +407,12 @@ impl<'a> PostQueryBuilder<'a> {
|
||||||
query = query.filter(post_saved::id.is_not_null());
|
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) {
|
query = match self.sort.unwrap_or(SortType::Hot) {
|
||||||
SortType::Active => query
|
SortType::Active => query
|
||||||
.then_order_by(
|
.then_order_by(
|
||||||
|
@ -440,7 +476,8 @@ impl ViewToVec for PostView {
|
||||||
subscribed: a.5.is_some(),
|
subscribed: a.5.is_some(),
|
||||||
saved: a.6.is_some(),
|
saved: a.6.is_some(),
|
||||||
read: a.7.is_some(),
|
read: a.7.is_some(),
|
||||||
my_vote: a.8,
|
creator_blocked: a.8.is_some(),
|
||||||
|
my_vote: a.9,
|
||||||
})
|
})
|
||||||
.collect::<Vec<Self>>()
|
.collect::<Vec<Self>>()
|
||||||
}
|
}
|
||||||
|
@ -452,12 +489,19 @@ mod tests {
|
||||||
use lemmy_db_queries::{
|
use lemmy_db_queries::{
|
||||||
aggregates::post_aggregates::PostAggregates,
|
aggregates::post_aggregates::PostAggregates,
|
||||||
establish_unpooled_connection,
|
establish_unpooled_connection,
|
||||||
|
Blockable,
|
||||||
Crud,
|
Crud,
|
||||||
Likeable,
|
Likeable,
|
||||||
ListingType,
|
ListingType,
|
||||||
SortType,
|
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;
|
use serial_test::serial;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -493,6 +537,32 @@ mod tests {
|
||||||
|
|
||||||
let inserted_community = Community::create(&conn, &new_community).unwrap();
|
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 {
|
let new_post = PostForm {
|
||||||
name: post_name.to_owned(),
|
name: post_name.to_owned(),
|
||||||
creator_id: inserted_person.id,
|
creator_id: inserted_person.id,
|
||||||
|
@ -623,17 +693,37 @@ mod tests {
|
||||||
subscribed: false,
|
subscribed: false,
|
||||||
read: false,
|
read: false,
|
||||||
saved: 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
|
// TODO More needs to be added here
|
||||||
let mut expected_post_listing_with_user = expected_post_listing_no_person.to_owned();
|
let mut expected_post_listing_with_user = expected_post_listing_no_person.to_owned();
|
||||||
expected_post_listing_with_user.my_vote = Some(1);
|
expected_post_listing_with_user.my_vote = Some(1);
|
||||||
|
|
||||||
let like_removed = PostLike::remove(&conn, inserted_person.id, inserted_post.id).unwrap();
|
let like_removed = PostLike::remove(&conn, inserted_person.id, inserted_post.id).unwrap();
|
||||||
let num_deleted = Post::delete(&conn, 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();
|
Community::delete(&conn, inserted_community.id).unwrap();
|
||||||
Person::delete(&conn, inserted_person.id).unwrap();
|
Person::delete(&conn, inserted_person.id).unwrap();
|
||||||
Person::delete(&conn, inserted_bot.id).unwrap();
|
Person::delete(&conn, inserted_bot.id).unwrap();
|
||||||
|
Person::delete(&conn, inserted_blocked_person.id).unwrap();
|
||||||
|
|
||||||
// The with user
|
// The with user
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -645,7 +735,7 @@ mod tests {
|
||||||
read_post_listing_with_person
|
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());
|
assert_eq!(1, read_post_listings_with_person.len());
|
||||||
|
|
||||||
// Without the user
|
// Without the user
|
||||||
|
@ -655,8 +745,11 @@ mod tests {
|
||||||
);
|
);
|
||||||
assert_eq!(expected_post_listing_no_person, read_post_listing_no_person);
|
assert_eq!(expected_post_listing_no_person, read_post_listing_no_person);
|
||||||
|
|
||||||
// Should be 2 posts, with the bot post
|
// Should be 2 posts, with the bot post, and the blocked
|
||||||
assert_eq!(2, read_post_listings_no_person.len());
|
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!(expected_post_like, inserted_post_like);
|
||||||
assert_eq!(1, like_removed);
|
assert_eq!(1, like_removed);
|
||||||
|
|
49
crates/db_views_actor/src/community_block_view.rs
Normal file
49
crates/db_views_actor/src/community_block_view.rs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
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>>()
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,8 +12,11 @@ use lemmy_db_queries::{
|
||||||
ViewToVec,
|
ViewToVec,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
schema::{community, community_aggregates, community_follower},
|
schema::{community, community_aggregates, community_block, community_follower},
|
||||||
source::community::{Community, CommunityFollower, CommunitySafe},
|
source::{
|
||||||
|
community::{Community, CommunityFollower, CommunitySafe},
|
||||||
|
community_block::CommunityBlock,
|
||||||
|
},
|
||||||
CommunityId,
|
CommunityId,
|
||||||
PersonId,
|
PersonId,
|
||||||
};
|
};
|
||||||
|
@ -23,6 +26,7 @@ use serde::Serialize;
|
||||||
pub struct CommunityView {
|
pub struct CommunityView {
|
||||||
pub community: CommunitySafe,
|
pub community: CommunitySafe,
|
||||||
pub subscribed: bool,
|
pub subscribed: bool,
|
||||||
|
pub blocked: bool,
|
||||||
pub counts: CommunityAggregates,
|
pub counts: CommunityAggregates,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +34,7 @@ type CommunityViewTuple = (
|
||||||
CommunitySafe,
|
CommunitySafe,
|
||||||
CommunityAggregates,
|
CommunityAggregates,
|
||||||
Option<CommunityFollower>,
|
Option<CommunityFollower>,
|
||||||
|
Option<CommunityBlock>,
|
||||||
);
|
);
|
||||||
|
|
||||||
impl CommunityView {
|
impl CommunityView {
|
||||||
|
@ -41,7 +46,7 @@ impl CommunityView {
|
||||||
// The left join below will return None in this case
|
// The left join below will return None in this case
|
||||||
let person_id_join = my_person_id.unwrap_or(PersonId(-1));
|
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)
|
.find(community_id)
|
||||||
.inner_join(community_aggregates::table)
|
.inner_join(community_aggregates::table)
|
||||||
.left_join(
|
.left_join(
|
||||||
|
@ -51,16 +56,25 @@ impl CommunityView {
|
||||||
.and(community_follower::person_id.eq(person_id_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((
|
.select((
|
||||||
Community::safe_columns_tuple(),
|
Community::safe_columns_tuple(),
|
||||||
community_aggregates::all_columns,
|
community_aggregates::all_columns,
|
||||||
community_follower::all_columns.nullable(),
|
community_follower::all_columns.nullable(),
|
||||||
|
community_block::all_columns.nullable(),
|
||||||
))
|
))
|
||||||
.first::<CommunityViewTuple>(conn)?;
|
.first::<CommunityViewTuple>(conn)?;
|
||||||
|
|
||||||
Ok(CommunityView {
|
Ok(CommunityView {
|
||||||
community,
|
community,
|
||||||
subscribed: follower.is_some(),
|
subscribed: follower.is_some(),
|
||||||
|
blocked: blocked.is_some(),
|
||||||
counts,
|
counts,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -165,10 +179,18 @@ impl<'a> CommunityQueryBuilder<'a> {
|
||||||
.and(community_follower::person_id.eq(person_id_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((
|
.select((
|
||||||
Community::safe_columns_tuple(),
|
Community::safe_columns_tuple(),
|
||||||
community_aggregates::all_columns,
|
community_aggregates::all_columns,
|
||||||
community_follower::all_columns.nullable(),
|
community_follower::all_columns.nullable(),
|
||||||
|
community_block::all_columns.nullable(),
|
||||||
))
|
))
|
||||||
.into_boxed();
|
.into_boxed();
|
||||||
|
|
||||||
|
@ -210,6 +232,11 @@ impl<'a> CommunityQueryBuilder<'a> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 (limit, offset) = limit_and_offset(self.page, self.limit);
|
||||||
let res = query
|
let res = query
|
||||||
.limit(limit)
|
.limit(limit)
|
||||||
|
@ -231,6 +258,7 @@ impl ViewToVec for CommunityView {
|
||||||
community: a.0.to_owned(),
|
community: a.0.to_owned(),
|
||||||
counts: a.1.to_owned(),
|
counts: a.1.to_owned(),
|
||||||
subscribed: a.2.is_some(),
|
subscribed: a.2.is_some(),
|
||||||
|
blocked: a.3.is_some(),
|
||||||
})
|
})
|
||||||
.collect::<Vec<Self>>()
|
.collect::<Vec<Self>>()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
pub mod community_block_view;
|
||||||
pub mod community_follower_view;
|
pub mod community_follower_view;
|
||||||
pub mod community_moderator_view;
|
pub mod community_moderator_view;
|
||||||
pub mod community_person_ban_view;
|
pub mod community_person_ban_view;
|
||||||
pub mod community_view;
|
pub mod community_view;
|
||||||
|
pub mod person_block_view;
|
||||||
pub mod person_mention_view;
|
pub mod person_mention_view;
|
||||||
pub mod person_view;
|
pub mod person_view;
|
||||||
|
|
46
crates/db_views_actor/src/person_block_view.rs
Normal file
46
crates/db_views_actor/src/person_block_view.rs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
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>>()
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ use lemmy_db_schema::{
|
||||||
community_person_ban,
|
community_person_ban,
|
||||||
person,
|
person,
|
||||||
person_alias_1,
|
person_alias_1,
|
||||||
|
person_block,
|
||||||
person_mention,
|
person_mention,
|
||||||
post,
|
post,
|
||||||
},
|
},
|
||||||
|
@ -26,6 +27,7 @@ use lemmy_db_schema::{
|
||||||
comment::{Comment, CommentSaved},
|
comment::{Comment, CommentSaved},
|
||||||
community::{Community, CommunityFollower, CommunityPersonBan, CommunitySafe},
|
community::{Community, CommunityFollower, CommunityPersonBan, CommunitySafe},
|
||||||
person::{Person, PersonAlias1, PersonSafe, PersonSafeAlias1},
|
person::{Person, PersonAlias1, PersonSafe, PersonSafeAlias1},
|
||||||
|
person_block::PersonBlock,
|
||||||
person_mention::PersonMention,
|
person_mention::PersonMention,
|
||||||
post::Post,
|
post::Post,
|
||||||
},
|
},
|
||||||
|
@ -46,6 +48,7 @@ pub struct PersonMentionView {
|
||||||
pub creator_banned_from_community: bool, // Left Join to CommunityPersonBan
|
pub creator_banned_from_community: bool, // Left Join to CommunityPersonBan
|
||||||
pub subscribed: bool, // Left join to CommunityFollower
|
pub subscribed: bool, // Left join to CommunityFollower
|
||||||
pub saved: bool, // Left join to CommentSaved
|
pub saved: bool, // Left join to CommentSaved
|
||||||
|
pub creator_blocked: bool, // Left join to PersonBlock
|
||||||
pub my_vote: Option<i16>, // Left join to CommentLike
|
pub my_vote: Option<i16>, // Left join to CommentLike
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,6 +63,7 @@ type PersonMentionViewTuple = (
|
||||||
Option<CommunityPersonBan>,
|
Option<CommunityPersonBan>,
|
||||||
Option<CommunityFollower>,
|
Option<CommunityFollower>,
|
||||||
Option<CommentSaved>,
|
Option<CommentSaved>,
|
||||||
|
Option<PersonBlock>,
|
||||||
Option<i16>,
|
Option<i16>,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -83,6 +87,7 @@ impl PersonMentionView {
|
||||||
creator_banned_from_community,
|
creator_banned_from_community,
|
||||||
subscribed,
|
subscribed,
|
||||||
saved,
|
saved,
|
||||||
|
creator_blocked,
|
||||||
my_vote,
|
my_vote,
|
||||||
) = person_mention::table
|
) = person_mention::table
|
||||||
.find(person_mention_id)
|
.find(person_mention_id)
|
||||||
|
@ -113,6 +118,13 @@ impl PersonMentionView {
|
||||||
.and(comment_saved::person_id.eq(person_id_join)),
|
.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(
|
.left_join(
|
||||||
comment_like::table.on(
|
comment_like::table.on(
|
||||||
comment::id
|
comment::id
|
||||||
|
@ -131,6 +143,7 @@ impl PersonMentionView {
|
||||||
community_person_ban::all_columns.nullable(),
|
community_person_ban::all_columns.nullable(),
|
||||||
community_follower::all_columns.nullable(),
|
community_follower::all_columns.nullable(),
|
||||||
comment_saved::all_columns.nullable(),
|
comment_saved::all_columns.nullable(),
|
||||||
|
person_block::all_columns.nullable(),
|
||||||
comment_like::score.nullable(),
|
comment_like::score.nullable(),
|
||||||
))
|
))
|
||||||
.first::<PersonMentionViewTuple>(conn)?;
|
.first::<PersonMentionViewTuple>(conn)?;
|
||||||
|
@ -146,6 +159,7 @@ impl PersonMentionView {
|
||||||
creator_banned_from_community: creator_banned_from_community.is_some(),
|
creator_banned_from_community: creator_banned_from_community.is_some(),
|
||||||
subscribed: subscribed.is_some(),
|
subscribed: subscribed.is_some(),
|
||||||
saved: saved.is_some(),
|
saved: saved.is_some(),
|
||||||
|
creator_blocked: creator_blocked.is_some(),
|
||||||
my_vote,
|
my_vote,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -238,6 +252,13 @@ impl<'a> PersonMentionQueryBuilder<'a> {
|
||||||
.and(comment_saved::person_id.eq(person_id_join)),
|
.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(
|
.left_join(
|
||||||
comment_like::table.on(
|
comment_like::table.on(
|
||||||
comment::id
|
comment::id
|
||||||
|
@ -256,6 +277,7 @@ impl<'a> PersonMentionQueryBuilder<'a> {
|
||||||
community_person_ban::all_columns.nullable(),
|
community_person_ban::all_columns.nullable(),
|
||||||
community_follower::all_columns.nullable(),
|
community_follower::all_columns.nullable(),
|
||||||
comment_saved::all_columns.nullable(),
|
comment_saved::all_columns.nullable(),
|
||||||
|
person_block::all_columns.nullable(),
|
||||||
comment_like::score.nullable(),
|
comment_like::score.nullable(),
|
||||||
))
|
))
|
||||||
.into_boxed();
|
.into_boxed();
|
||||||
|
@ -317,7 +339,8 @@ impl ViewToVec for PersonMentionView {
|
||||||
creator_banned_from_community: a.7.is_some(),
|
creator_banned_from_community: a.7.is_some(),
|
||||||
subscribed: a.8.is_some(),
|
subscribed: a.8.is_some(),
|
||||||
saved: a.9.is_some(),
|
saved: a.9.is_some(),
|
||||||
my_vote: a.10,
|
creator_blocked: a.10.is_some(),
|
||||||
|
my_vote: a.11,
|
||||||
})
|
})
|
||||||
.collect::<Vec<Self>>()
|
.collect::<Vec<Self>>()
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,7 +101,6 @@ pub enum UserOperation {
|
||||||
ListPostReports,
|
ListPostReports,
|
||||||
GetReportCount,
|
GetReportCount,
|
||||||
FollowCommunity,
|
FollowCommunity,
|
||||||
GetFollowedCommunities,
|
|
||||||
GetReplies,
|
GetReplies,
|
||||||
GetPersonMentions,
|
GetPersonMentions,
|
||||||
MarkPersonMentionAsRead,
|
MarkPersonMentionAsRead,
|
||||||
|
@ -126,6 +125,8 @@ pub enum UserOperation {
|
||||||
ModJoin,
|
ModJoin,
|
||||||
ChangePassword,
|
ChangePassword,
|
||||||
GetSiteMetadata,
|
GetSiteMetadata,
|
||||||
|
BlockCommunity,
|
||||||
|
BlockPerson,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(EnumString, ToString, Debug, Clone)]
|
#[derive(EnumString, ToString, Debug, Clone)]
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
drop table person_block;
|
||||||
|
drop table community_block;
|
|
@ -0,0 +1,15 @@
|
||||||
|
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)
|
||||||
|
);
|
|
@ -47,6 +47,7 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimit) {
|
||||||
.route("", web::put().to(route_post_crud::<EditCommunity>))
|
.route("", web::put().to(route_post_crud::<EditCommunity>))
|
||||||
.route("/list", web::get().to(route_get_crud::<ListCommunities>))
|
.route("/list", web::get().to(route_get_crud::<ListCommunities>))
|
||||||
.route("/follow", web::post().to(route_post::<FollowCommunity>))
|
.route("/follow", web::post().to(route_post::<FollowCommunity>))
|
||||||
|
.route("/block", web::post().to(route_post::<BlockCommunity>))
|
||||||
.route(
|
.route(
|
||||||
"/delete",
|
"/delete",
|
||||||
web::post().to(route_post_crud::<DeleteCommunity>),
|
web::post().to(route_post_crud::<DeleteCommunity>),
|
||||||
|
@ -155,13 +156,10 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimit) {
|
||||||
web::post().to(route_post::<MarkPersonMentionAsRead>),
|
web::post().to(route_post::<MarkPersonMentionAsRead>),
|
||||||
)
|
)
|
||||||
.route("/replies", web::get().to(route_get::<GetReplies>))
|
.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>))
|
.route("/join", web::post().to(route_post::<UserJoin>))
|
||||||
// Admin action. I don't like that it's in /user
|
// Admin action. I don't like that it's in /user
|
||||||
.route("/ban", web::post().to(route_post::<BanPerson>))
|
.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
|
// Account actions. I don't like that they're in /user maybe /accounts
|
||||||
.route("/login", web::post().to(route_post::<Login>))
|
.route("/login", web::post().to(route_post::<Login>))
|
||||||
.route("/get_captcha", web::get().to(route_get::<GetCaptcha>))
|
.route("/get_captcha", web::get().to(route_get::<GetCaptcha>))
|
||||||
|
|
Loading…
Reference in a new issue