diff --git a/.woodpecker.yml b/.woodpecker.yml index 50ed034c1..7250948f8 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -95,6 +95,16 @@ steps: when: - event: pull_request + no_empty_files: + image: alpine:3 + commands: + # Makes sure there are no files smaller than 2 bytes + # Don't use completely empty, as some editors use newlines + - EMPTY_FILES=$(find crates migrations api_tests/src config -type f -size -2c) + - if [[ "$EMPTY_FILES" ]]; then echo "Empty files present:\n$EMPTY_FILES\n"; exit 1; fi + when: + - event: pull_request + cargo_clippy: image: *rust_image environment: diff --git a/api_tests/package.json b/api_tests/package.json index 660183269..a13dec7cc 100644 --- a/api_tests/package.json +++ b/api_tests/package.json @@ -29,7 +29,7 @@ "eslint": "^9.20.0", "eslint-plugin-prettier": "^5.2.3", "jest": "^29.5.0", - "lemmy-js-client": "0.20.0-move-community-hidden.3", + "lemmy-js-client": "1.0.0-action-structs.1", "prettier": "^3.5.0", "ts-jest": "^29.1.0", "tsoa": "^6.6.0", diff --git a/api_tests/pnpm-lock.yaml b/api_tests/pnpm-lock.yaml index 4d86eba46..fc1cd9c95 100644 --- a/api_tests/pnpm-lock.yaml +++ b/api_tests/pnpm-lock.yaml @@ -33,8 +33,8 @@ importers: specifier: ^29.5.0 version: 29.7.0(@types/node@22.13.1) lemmy-js-client: - specifier: 0.20.0-move-community-hidden.3 - version: 0.20.0-move-community-hidden.3 + specifier: 1.0.0-action-structs.1 + version: 1.0.0-action-structs.1 prettier: specifier: ^3.5.0 version: 3.5.0 @@ -1528,8 +1528,8 @@ packages: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} - lemmy-js-client@0.20.0-move-community-hidden.3: - resolution: {integrity: sha512-X7bbSrnGGgupr//Qk2M1Z9nvFawNU4T116X+4/j912GO6KbQdWN7+10obbQJuoEYr15oCXwSaK8DLusofLxIig==} + lemmy-js-client@1.0.0-action-structs.1: + resolution: {integrity: sha512-Th/7TAjBQ7jcxJHpSMeHF50Y8GwK2YbCBVbLZ9AEPYmOyll0yK+bwTwhCEyvy/SIgbNQTd1OaIYv3F8iAvjvuw==} leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} @@ -4169,7 +4169,7 @@ snapshots: kleur@3.0.3: {} - lemmy-js-client@0.20.0-move-community-hidden.3: {} + lemmy-js-client@1.0.0-action-structs.1: {} leven@3.1.0: {} diff --git a/api_tests/src/comment.spec.ts b/api_tests/src/comment.spec.ts index 5a532d488..5eeeb59b6 100644 --- a/api_tests/src/comment.spec.ts +++ b/api_tests/src/comment.spec.ts @@ -653,11 +653,11 @@ test("Check that activity from another instance is sent to third instance", asyn expect(gammaFollow.community_view.community.name).toBe("main"); await waitUntil( () => resolveBetaCommunity(alpha), - c => c.community?.subscribed === "Subscribed", + c => c.community?.community_actions?.follow_state === "Accepted", ); await waitUntil( () => resolveBetaCommunity(gamma), - c => c.community?.subscribed === "Subscribed", + c => c.community?.community_actions?.follow_state === "Accepted", ); // Create a post on beta diff --git a/api_tests/src/community.spec.ts b/api_tests/src/community.spec.ts index 6ddd0727c..3d24cfd28 100644 --- a/api_tests/src/community.spec.ts +++ b/api_tests/src/community.spec.ts @@ -205,17 +205,17 @@ test("Admin actions in remote community are not federated to origin", async () = gammaCommunity = ( await waitUntil( () => resolveCommunity(gamma, communityRes.community.ap_id), - g => g.community?.subscribed === "Subscribed", + g => g.community?.community_actions?.follow_state == "Accepted", ) ).community; if (!gammaCommunity) { throw "Missing gamma community"; } - expect(gammaCommunity.subscribed).toBe("Subscribed"); + expect(gammaCommunity.community_actions?.follow_state).toBe("Accepted"); let gammaPost = (await createPost(gamma, gammaCommunity.community.id)) .post_view; expect(gammaPost.post.id).toBeDefined(); - expect(gammaPost.creator_banned_from_community).toBe(false); + expect(gammaPost.creator_community_actions?.received_ban).toBeUndefined(); // admin of beta decides to ban gamma from community let betaCommunity = ( @@ -244,11 +244,13 @@ test("Admin actions in remote community are not federated to origin", async () = // ban doesn't federate to community's origin instance alpha let alphaPost = (await resolvePost(alpha, gammaPost.post)).post; - expect(alphaPost?.creator_banned_from_community).toBe(false); + expect(alphaPost?.creator_community_actions?.received_ban).toBeUndefined(); // and neither to gamma let gammaPost2 = await getPost(gamma, gammaPost.post.id); - expect(gammaPost2.post_view.creator_banned_from_community).toBe(false); + expect( + gammaPost2.post_view.creator_community_actions?.received_ban, + ).toBeUndefined(); }); test("moderator view", async () => { @@ -391,7 +393,7 @@ test.skip("Community follower count is federated", async () => { let followed = ( await waitUntil( () => resolveCommunity(alpha, communityActorId), - c => c.community?.subscribed === "Subscribed", + c => c.community?.community_actions?.follow_state == "Accepted", ) ).community; @@ -408,7 +410,7 @@ test.skip("Community follower count is federated", async () => { followed = ( await waitUntil( () => resolveCommunity(gamma, communityActorId), - c => c.community?.subscribed === "Subscribed", + c => c.community?.community_actions?.follow_state == "Accepted", ) ).community; @@ -425,7 +427,7 @@ test.skip("Community follower count is federated", async () => { followed = ( await waitUntil( () => resolveCommunity(delta, communityActorId), - c => c.community?.subscribed === "Subscribed", + c => c.community?.community_actions?.follow_state == "Accepted", ) ).community; diff --git a/api_tests/src/follow.spec.ts b/api_tests/src/follow.spec.ts index 824c34f4a..7bc36e3c2 100644 --- a/api_tests/src/follow.spec.ts +++ b/api_tests/src/follow.spec.ts @@ -26,7 +26,9 @@ test("Follow local community", async () => { // Make sure the follow response went through expect(follow.community_view.community.local).toBe(true); - expect(follow.community_view.subscribed).toBe("Subscribed"); + expect(follow.community_view.community_actions?.follow_state).toBe( + "Accepted", + ); expect(follow.community_view.community.subscribers).toBe( community.community.subscribers + 1, ); @@ -36,7 +38,9 @@ test("Follow local community", async () => { // Test an unfollow let unfollow = await followCommunity(user, false, community.community.id); - expect(unfollow.community_view.subscribed).toBe("NotSubscribed"); + expect( + unfollow.community_view.community_actions?.follow_state, + ).toBeUndefined(); expect(unfollow.community_view.community.subscribers).toBe( community.community.subscribers, ); @@ -62,18 +66,18 @@ test("Follow federated community", async () => { true, betaCommunityInitial.community.id, ); - expect(follow.community_view.subscribed).toBe("Pending"); + expect(follow.community_view.community_actions?.follow_state).toBe("Pending"); const betaCommunity = ( await waitUntil( () => resolveBetaCommunity(alpha), - c => c.community?.subscribed === "Subscribed", + c => c.community?.community_actions?.follow_state === "Accepted", ) ).community; // Make sure the follow response went through expect(betaCommunity?.community.local).toBe(false); expect(betaCommunity?.community.name).toBe("main"); - expect(betaCommunity?.subscribed).toBe("Subscribed"); + expect(betaCommunity?.community_actions?.follow_state).toBe("Accepted"); expect(betaCommunity?.community.subscribers_local).toBe( betaCommunityInitial.community.subscribers_local + 1, ); @@ -99,7 +103,9 @@ test("Follow federated community", async () => { // Test an unfollow let unfollow = await followCommunity(alpha, false, remoteCommunityId); - expect(unfollow.community_view.subscribed).toBe("NotSubscribed"); + expect( + unfollow.community_view.community_actions?.follow_state, + ).toBeUndefined(); // Make sure you are unsubbed locally let siteUnfollowCheck = await getMyUser(alpha); diff --git a/api_tests/src/post.spec.ts b/api_tests/src/post.spec.ts index 09463238d..3ff005fbf 100644 --- a/api_tests/src/post.spec.ts +++ b/api_tests/src/post.spec.ts @@ -281,7 +281,7 @@ test("Lock a post", async () => { await followCommunity(alpha, true, betaCommunity.community.id); await waitUntil( () => resolveBetaCommunity(alpha), - c => c.community?.subscribed === "Subscribed", + c => c.community?.community_actions?.follow_state == "Accepted", ); let postRes = await createPost(alpha, betaCommunity.community.id); @@ -596,8 +596,9 @@ test("Enforce site ban federation for federated user", async () => { expect(betaBanRes.post_view.post.removed).toBe(true); expect(alphaPostAfterRemoveOnBeta.post_view.post.removed).toBe(true); expect( - alphaPostAfterRemoveOnBeta.post_view.creator_banned_from_community, - ).toBe(true); + alphaPostAfterRemoveOnBeta.post_view.creator_community_actions + ?.received_ban, + ).toBeDefined(); await unfollowRemotes(alpha); }); @@ -635,8 +636,12 @@ test("Enforce community ban for federated user", async () => { s => s.post_view.post.removed, ); expect(removePostRes.post_view.post.removed).toBe(true); - expect(removePostRes.post_view.creator_banned_from_community).toBe(true); - expect(removePostRes.community_view.banned_from_community).toBe(true); + expect( + removePostRes.post_view.creator_community_actions?.received_ban, + ).toBeDefined(); + expect( + removePostRes.community_view.community_actions?.received_ban, + ).toBeDefined(); // Alpha tries to make post on beta, but it fails because of ban await expect( @@ -670,7 +675,7 @@ test("Enforce community ban for federated user", async () => { // Make sure that post makes it to beta community let postRes4 = await waitForPost(beta, postRes3.post_view.post); expect(postRes4.post).toBeDefined(); - expect(postRes4.creator_banned_from_community).toBe(false); + expect(postRes4.creator_community_actions?.received_ban).toBeUndefined(); await unfollowRemotes(alpha); }); diff --git a/api_tests/src/private_community.spec.ts b/api_tests/src/private_community.spec.ts index abe5336ad..a1b93e6f1 100644 --- a/api_tests/src/private_community.spec.ts +++ b/api_tests/src/private_community.spec.ts @@ -60,7 +60,9 @@ test("Follow a private community", async () => { // Follow listed as pending const follow1 = await getCommunity(user, betaCommunityId); - expect(follow1.community_view.subscribed).toBe("ApprovalRequired"); + expect(follow1.community_view.community_actions?.follow_state).toBe( + "ApprovalRequired", + ); // Wait for follow to federate, shown as pending let pendingFollows1 = await waitUntil( @@ -76,7 +78,9 @@ test("Follow a private community", async () => { // user still sees approval required at this point const betaCommunity2 = await getCommunity(user, betaCommunityId); - expect(betaCommunity2.community_view.subscribed).toBe("ApprovalRequired"); + expect(betaCommunity2.community_view.community_actions?.follow_state).toBe( + "ApprovalRequired", + ); // Approve the follow const approve = await approveCommunityPendingFollow( @@ -89,7 +93,7 @@ test("Follow a private community", async () => { // Follow is confirmed await waitUntil( () => getCommunity(user, betaCommunityId), - c => c.community_view.subscribed == "Subscribed", + c => c.community_view.community_actions?.follow_state == "Accepted", ); const pendingFollows2 = await listCommunityPendingFollows(alpha); expect(pendingFollows2.items.length).toBe(0); @@ -167,7 +171,7 @@ test("Only followers can view and interact with private community content", asyn const post1 = await createPost(user, betaCommunity.id); expect(post1.post_view).toBeDefined(); const like = await likeComment(user, 1, resolvedComment!.comment); - expect(like.comment_view.my_vote).toBe(1); + expect(like.comment_view.comment_actions?.like_score).toBe(1); }); test("Reject follower", async () => { @@ -188,7 +192,9 @@ test("Reject follower", async () => { follow: true, }; const follow = await user.followCommunity(follow_form); - expect(follow.community_view.subscribed).toBe("ApprovalRequired"); + expect(follow.community_view.community_actions?.follow_state).toBe( + "ApprovalRequired", + ); const pendingFollows1 = await waitUntil( () => listCommunityPendingFollows(alpha), @@ -204,7 +210,7 @@ test("Reject follower", async () => { await waitUntil( () => getCommunity(user, betaCommunity1.id), - c => c.community_view.subscribed == "NotSubscribed", + c => c.community_view.community_actions?.follow_state === undefined, ); }); @@ -240,11 +246,11 @@ test("Follow a private community and receive activities", async () => { // Follow is confirmed await waitUntil( () => getCommunity(beta, betaCommunityId), - c => c.community_view.subscribed == "Subscribed", + c => c.community_view.community_actions?.follow_state == "Accepted", ); await waitUntil( () => getCommunity(gamma, gammaCommunityId), - c => c.community_view.subscribed == "Subscribed", + c => c.community_view.community_actions?.follow_state == "Accepted", ); // create a post and comment from gamma @@ -293,7 +299,7 @@ test("Fetch remote content in private community", async () => { // Follow is confirmed await waitUntil( () => getCommunity(beta, betaCommunityId), - c => c.community_view.subscribed == "Subscribed", + c => c.community_view.community_actions?.follow_state == "Accepted", ); // beta creates post and comment diff --git a/api_tests/src/shared.ts b/api_tests/src/shared.ts index e7d86c289..9a536da17 100644 --- a/api_tests/src/shared.ts +++ b/api_tests/src/shared.ts @@ -471,8 +471,10 @@ export async function followCommunity( const res = await api.followCommunity(form); await waitUntil( () => getCommunity(api, res.community_view.community.id), - g => - g.community_view.subscribed === (follow ? "Subscribed" : "NotSubscribed"), + g => { + let followState = g.community_view.community_actions?.follow_state; + return follow ? followState === "Accepted" : followState === undefined; + }, ); // wait FOLLOW_ADDITIONS_RECHECK_DELAY (there's no API to wait for this currently) await delay(2000); diff --git a/crates/api/src/comment/like.rs b/crates/api/src/comment/like.rs index 0b0151203..329107d8b 100644 --- a/crates/api/src/comment/like.rs +++ b/crates/api/src/comment/like.rs @@ -10,14 +10,14 @@ use lemmy_api_common::{ use lemmy_db_schema::{ newtypes::{LocalUserId, PostOrCommentId}, source::{ - comment::{CommentLike, CommentLikeForm}, + comment::{CommentActions, CommentLikeForm}, comment_reply::CommentReply, local_site::LocalSite, }, traits::Likeable, }; use lemmy_db_views::structs::{CommentView, LocalUserView}; -use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; +use lemmy_utils::error::LemmyResult; use std::ops::Deref; pub async fn like_comment( @@ -64,23 +64,18 @@ pub async fn like_comment( } } - let like_form = CommentLikeForm { - comment_id: data.comment_id, - person_id: local_user_view.person.id, - score: data.score, - }; + let like_form = CommentLikeForm::new(local_user_view.person.id, data.comment_id, data.score); // Remove any likes first let person_id = local_user_view.person.id; - CommentLike::remove(&mut context.pool(), person_id, comment_id).await?; + CommentActions::remove_like(&mut context.pool(), person_id, comment_id).await?; // Only add the like if the score isnt 0 - let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1); + let do_add = + like_form.like_score != 0 && (like_form.like_score == 1 || like_form.like_score == -1); if do_add { - CommentLike::like(&mut context.pool(), &like_form) - .await - .with_lemmy_type(LemmyErrorType::CouldntLikeComment)?; + CommentActions::like(&mut context.pool(), &like_form).await?; } ActivityChannel::submit_activity( diff --git a/crates/api/src/comment/save.rs b/crates/api/src/comment/save.rs index 46f9d8abe..413a524e3 100644 --- a/crates/api/src/comment/save.rs +++ b/crates/api/src/comment/save.rs @@ -4,27 +4,23 @@ use lemmy_api_common::{ context::LemmyContext, }; use lemmy_db_schema::{ - source::comment::{CommentSaved, CommentSavedForm}, + source::comment::{CommentActions, CommentSavedForm}, traits::Saveable, }; use lemmy_db_views::structs::{CommentView, LocalUserView}; -use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; +use lemmy_utils::error::LemmyResult; pub async fn save_comment( data: Json, context: Data, local_user_view: LocalUserView, ) -> LemmyResult> { - let comment_saved_form = CommentSavedForm::new(data.comment_id, local_user_view.person.id); + let comment_saved_form = CommentSavedForm::new(local_user_view.person.id, data.comment_id); if data.save { - CommentSaved::save(&mut context.pool(), &comment_saved_form) - .await - .with_lemmy_type(LemmyErrorType::CouldntSaveComment)?; + CommentActions::save(&mut context.pool(), &comment_saved_form).await?; } else { - CommentSaved::unsave(&mut context.pool(), &comment_saved_form) - .await - .with_lemmy_type(LemmyErrorType::CouldntSaveComment)?; + CommentActions::unsave(&mut context.pool(), &comment_saved_form).await?; } let comment_id = data.comment_id; diff --git a/crates/api/src/community/add_mod.rs b/crates/api/src/community/add_mod.rs index 6c221e39d..02e0a137a 100644 --- a/crates/api/src/community/add_mod.rs +++ b/crates/api/src/community/add_mod.rs @@ -8,14 +8,14 @@ use lemmy_api_common::{ }; use lemmy_db_schema::{ source::{ - community::{Community, CommunityModerator, CommunityModeratorForm}, + community::{Community, CommunityActions, CommunityModeratorForm}, local_user::LocalUser, mod_log::moderator::{ModAddCommunity, ModAddCommunityForm}, }, traits::{Crud, Joinable}, }; use lemmy_db_views::structs::{CommunityModeratorView, LocalUserView}; -use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; +use lemmy_utils::error::LemmyResult; pub async fn add_mod_to_community( data: Json, @@ -56,18 +56,11 @@ pub async fn add_mod_to_community( } // Update in local database - let community_moderator_form = CommunityModeratorForm { - community_id: data.community_id, - person_id: data.person_id, - }; + let community_moderator_form = CommunityModeratorForm::new(data.community_id, data.person_id); if data.added { - CommunityModerator::join(&mut context.pool(), &community_moderator_form) - .await - .with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists)?; + CommunityActions::join(&mut context.pool(), &community_moderator_form).await?; } else { - CommunityModerator::leave(&mut context.pool(), &community_moderator_form) - .await - .with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists)?; + CommunityActions::leave(&mut context.pool(), &community_moderator_form).await?; } // Mod tables diff --git a/crates/api/src/community/ban.rs b/crates/api/src/community/ban.rs index 83e2e05e7..6b2e657d0 100644 --- a/crates/api/src/community/ban.rs +++ b/crates/api/src/community/ban.rs @@ -12,23 +12,14 @@ use lemmy_api_common::{ }; use lemmy_db_schema::{ source::{ - community::{ - Community, - CommunityFollower, - CommunityFollowerForm, - CommunityPersonBan, - CommunityPersonBanForm, - }, + community::{Community, CommunityActions, CommunityPersonBanForm}, local_user::LocalUser, mod_log::moderator::{ModBanFromCommunity, ModBanFromCommunityForm}, }, traits::{Bannable, Crud, Followable}, }; use lemmy_db_views::structs::{LocalUserView, PersonView}; -use lemmy_utils::{ - error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, - utils::validation::is_valid_body_field, -}; +use lemmy_utils::{error::LemmyResult, utils::validation::is_valid_body_field}; pub async fn ban_from_community( data: Json, @@ -61,25 +52,19 @@ pub async fn ban_from_community( } let community_user_ban_form = CommunityPersonBanForm { - community_id: data.community_id, - person_id: data.person_id, - expires: Some(expires), + ban_expires: Some(expires), + ..CommunityPersonBanForm::new(data.community_id, data.person_id) }; if data.ban { - CommunityPersonBan::ban(&mut context.pool(), &community_user_ban_form) - .await - .with_lemmy_type(LemmyErrorType::CommunityUserAlreadyBanned)?; + CommunityActions::ban(&mut context.pool(), &community_user_ban_form).await?; // Also unsubscribe them from the community, if they are subscribed - let community_follower_form = CommunityFollowerForm::new(data.community_id, banned_person_id); - CommunityFollower::unfollow(&mut context.pool(), &community_follower_form) + CommunityActions::unfollow(&mut context.pool(), banned_person_id, data.community_id) .await .ok(); } else { - CommunityPersonBan::unban(&mut context.pool(), &community_user_ban_form) - .await - .with_lemmy_type(LemmyErrorType::CommunityUserAlreadyBanned)?; + CommunityActions::unban(&mut context.pool(), &community_user_ban_form).await?; } // Remove/Restore their data if that's desired diff --git a/crates/api/src/community/block.rs b/crates/api/src/community/block.rs index dbed2afc5..1c0750427 100644 --- a/crates/api/src/community/block.rs +++ b/crates/api/src/community/block.rs @@ -6,14 +6,11 @@ use lemmy_api_common::{ send_activity::{ActivityChannel, SendActivityData}, }; use lemmy_db_schema::{ - source::{ - community::{CommunityFollower, CommunityFollowerForm}, - community_block::{CommunityBlock, CommunityBlockForm}, - }, + source::community::{CommunityActions, CommunityBlockForm}, traits::{Blockable, Followable}, }; use lemmy_db_views::structs::{CommunityView, LocalUserView}; -use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; +use lemmy_utils::error::LemmyResult; pub async fn user_block_community( data: Json, @@ -22,25 +19,17 @@ pub async fn user_block_community( ) -> LemmyResult> { let community_id = data.community_id; let person_id = local_user_view.person.id; - let community_block_form = CommunityBlockForm { - person_id, - community_id, - }; + let community_block_form = CommunityBlockForm::new(community_id, person_id); if data.block { - CommunityBlock::block(&mut context.pool(), &community_block_form) - .await - .with_lemmy_type(LemmyErrorType::CommunityBlockAlreadyExists)?; + CommunityActions::block(&mut context.pool(), &community_block_form).await?; // Also, unfollow the community, and send a federated unfollow - let community_follower_form = CommunityFollowerForm::new(data.community_id, person_id); - CommunityFollower::unfollow(&mut context.pool(), &community_follower_form) + CommunityActions::unfollow(&mut context.pool(), person_id, data.community_id) .await .ok(); } else { - CommunityBlock::unblock(&mut context.pool(), &community_block_form) - .await - .with_lemmy_type(LemmyErrorType::CommunityBlockAlreadyExists)?; + CommunityActions::unblock(&mut context.pool(), &community_block_form).await?; } let community_view = CommunityView::read( diff --git a/crates/api/src/community/follow.rs b/crates/api/src/community/follow.rs index f0318ec23..6957e0c55 100644 --- a/crates/api/src/community/follow.rs +++ b/crates/api/src/community/follow.rs @@ -9,13 +9,13 @@ use lemmy_api_common::{ use lemmy_db_schema::{ source::{ actor_language::CommunityLanguage, - community::{Community, CommunityFollower, CommunityFollowerForm, CommunityFollowerState}, + community::{Community, CommunityActions, CommunityFollowerForm, CommunityFollowerState}, }, traits::{Crud, Followable}, CommunityVisibility, }; use lemmy_db_views::structs::{CommunityPersonBanView, CommunityView, LocalUserView}; -use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; +use lemmy_utils::error::LemmyResult; pub async fn follow_community( data: Json, @@ -24,7 +24,7 @@ pub async fn follow_community( ) -> LemmyResult> { check_user_valid(&local_user_view.person)?; let community = Community::read(&mut context.pool(), data.community_id).await?; - let form = CommunityFollowerForm::new(community.id, local_user_view.person.id); + let person_id = local_user_view.person.id; if data.follow { // Only run these checks for local community, in case of remote community the local @@ -32,34 +32,26 @@ pub async fn follow_community( // actions from existing followers for private community (so following would be impossible). if community.local { check_community_deleted_removed(&community)?; - CommunityPersonBanView::check(&mut context.pool(), local_user_view.person.id, community.id) - .await?; + CommunityPersonBanView::check(&mut context.pool(), person_id, community.id).await?; } - let state = if community.local { + let follow_state = if community.local { // Local follow is accepted immediately - Some(CommunityFollowerState::Accepted) + CommunityFollowerState::Accepted } else if community.visibility == CommunityVisibility::Private { // Private communities require manual approval - Some(CommunityFollowerState::ApprovalRequired) + CommunityFollowerState::ApprovalRequired } else { // remote follow needs to be federated first - Some(CommunityFollowerState::Pending) + CommunityFollowerState::Pending }; - let form = CommunityFollowerForm { - state, - ..CommunityFollowerForm::new(community.id, local_user_view.person.id) - }; + let form = CommunityFollowerForm::new(community.id, person_id, follow_state); // Write to db - CommunityFollower::follow(&mut context.pool(), &form) - .await - .with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists)?; + CommunityActions::follow(&mut context.pool(), &form).await?; } else { - CommunityFollower::unfollow(&mut context.pool(), &form) - .await - .with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists)?; + CommunityActions::unfollow(&mut context.pool(), person_id, community.id).await?; } // Send the federated follow diff --git a/crates/api/src/community/pending_follows/approve.rs b/crates/api/src/community/pending_follows/approve.rs index 468e9d9d0..c162d051f 100644 --- a/crates/api/src/community/pending_follows/approve.rs +++ b/crates/api/src/community/pending_follows/approve.rs @@ -7,10 +7,7 @@ use lemmy_api_common::{ utils::is_mod_or_admin, SuccessResponse, }; -use lemmy_db_schema::{ - source::community::{CommunityFollower, CommunityFollowerForm}, - traits::Followable, -}; +use lemmy_db_schema::{source::community::CommunityActions, traits::Followable}; use lemmy_db_views::structs::LocalUserView; use lemmy_utils::error::LemmyResult; @@ -27,7 +24,7 @@ pub async fn post_pending_follows_approve( .await?; let activity_data = if data.approve { - CommunityFollower::approve( + CommunityActions::approve_follower( &mut context.pool(), data.community_id, data.follower_id, @@ -36,8 +33,7 @@ pub async fn post_pending_follows_approve( .await?; SendActivityData::AcceptFollower(data.community_id, data.follower_id) } else { - let form = CommunityFollowerForm::new(data.community_id, data.follower_id); - CommunityFollower::unfollow(&mut context.pool(), &form).await?; + CommunityActions::unfollow(&mut context.pool(), data.follower_id, data.community_id).await?; SendActivityData::RejectFollower(data.community_id, data.follower_id) }; ActivityChannel::submit_activity(activity_data, &context)?; diff --git a/crates/api/src/community/transfer.rs b/crates/api/src/community/transfer.rs index 9453aced2..4414c0143 100644 --- a/crates/api/src/community/transfer.rs +++ b/crates/api/src/community/transfer.rs @@ -7,14 +7,14 @@ use lemmy_api_common::{ }; use lemmy_db_schema::{ source::{ - community::{Community, CommunityModerator, CommunityModeratorForm}, + community::{Community, CommunityActions, CommunityModeratorForm}, mod_log::moderator::{ModTransferCommunity, ModTransferCommunityForm}, }, traits::{Crud, Joinable}, }; use lemmy_db_views::structs::{CommunityModeratorView, CommunityView, LocalUserView}; use lemmy_utils::{ - error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, + error::{LemmyErrorType, LemmyResult}, location_info, }; @@ -50,19 +50,15 @@ pub async fn transfer_community( // Delete all the mods let community_id = data.community_id; - CommunityModerator::delete_for_community(&mut context.pool(), community_id).await?; + CommunityActions::delete_mods_for_community(&mut context.pool(), community_id).await?; // TODO: this should probably be a bulk operation // Re-add the mods, in the new order for cmod in &community_mods { - let community_moderator_form = CommunityModeratorForm { - community_id: cmod.community.id, - person_id: cmod.moderator.id, - }; + let community_moderator_form = + CommunityModeratorForm::new(cmod.community.id, cmod.moderator.id); - CommunityModerator::join(&mut context.pool(), &community_moderator_form) - .await - .with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists)?; + CommunityActions::join(&mut context.pool(), &community_moderator_form).await?; } // Mod tables diff --git a/crates/api/src/lib.rs b/crates/api/src/lib.rs index fe0c91abd..781f28e42 100644 --- a/crates/api/src/lib.rs +++ b/crates/api/src/lib.rs @@ -9,12 +9,7 @@ use lemmy_api_common::{ }; use lemmy_db_schema::{ source::{ - community::{ - CommunityFollower, - CommunityFollowerForm, - CommunityPersonBan, - CommunityPersonBanForm, - }, + community::{CommunityActions, CommunityPersonBanForm}, mod_log::moderator::{ModBanFromCommunity, ModBanFromCommunityForm}, person::Person, }, @@ -162,25 +157,23 @@ pub(crate) async fn ban_nonlocal_user_from_local_communities( // Ban / unban them from our local communities let community_user_ban_form = CommunityPersonBanForm { - community_id, - person_id: target.id, - expires: Some(expires_dt), + ban_expires: Some(expires_dt), + ..CommunityPersonBanForm::new(community_id, target.id) }; if ban { // Ignore all errors for these - CommunityPersonBan::ban(&mut context.pool(), &community_user_ban_form) + CommunityActions::ban(&mut context.pool(), &community_user_ban_form) .await .ok(); // Also unsubscribe them from the community, if they are subscribed - let community_follower_form = CommunityFollowerForm::new(community_id, target.id); - CommunityFollower::unfollow(&mut context.pool(), &community_follower_form) + CommunityActions::unfollow(&mut context.pool(), target.id, community_id) .await .ok(); } else { - CommunityPersonBan::unban(&mut context.pool(), &community_user_ban_form) + CommunityActions::unban(&mut context.pool(), &community_user_ban_form) .await .ok(); } diff --git a/crates/api/src/local_user/block.rs b/crates/api/src/local_user/block.rs index 34798ce4d..6149fbf6b 100644 --- a/crates/api/src/local_user/block.rs +++ b/crates/api/src/local_user/block.rs @@ -4,11 +4,11 @@ use lemmy_api_common::{ person::{BlockPerson, BlockPersonResponse}, }; use lemmy_db_schema::{ - source::person_block::{PersonBlock, PersonBlockForm}, + source::person::{PersonActions, PersonBlockForm}, traits::Blockable, }; use lemmy_db_views::structs::{LocalUserView, PersonView}; -use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; pub async fn user_block_person( data: Json, @@ -23,10 +23,7 @@ pub async fn user_block_person( Err(LemmyErrorType::CantBlockYourself)? } - let person_block_form = PersonBlockForm { - person_id, - target_id, - }; + let person_block_form = PersonBlockForm::new(person_id, target_id); let target_user = LocalUserView::read_person(&mut context.pool(), target_id) .await @@ -37,13 +34,9 @@ pub async fn user_block_person( } if data.block { - PersonBlock::block(&mut context.pool(), &person_block_form) - .await - .with_lemmy_type(LemmyErrorType::PersonBlockAlreadyExists)?; + PersonActions::block(&mut context.pool(), &person_block_form).await?; } else { - PersonBlock::unblock(&mut context.pool(), &person_block_form) - .await - .with_lemmy_type(LemmyErrorType::PersonBlockAlreadyExists)?; + PersonActions::unblock(&mut context.pool(), &person_block_form).await?; } let person_view = PersonView::read(&mut context.pool(), target_id, false).await?; diff --git a/crates/api/src/local_user/user_block_instance.rs b/crates/api/src/local_user/user_block_instance.rs index e65f5f5ef..27af8f96e 100644 --- a/crates/api/src/local_user/user_block_instance.rs +++ b/crates/api/src/local_user/user_block_instance.rs @@ -2,11 +2,11 @@ use activitypub_federation::config::Data; use actix_web::web::Json; use lemmy_api_common::{context::LemmyContext, site::UserBlockInstanceParams, SuccessResponse}; use lemmy_db_schema::{ - source::instance_block::{InstanceBlock, InstanceBlockForm}, + source::instance::{InstanceActions, InstanceBlockForm}, traits::Blockable, }; use lemmy_db_views::structs::LocalUserView; -use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; pub async fn user_block_instance( data: Json, @@ -19,19 +19,12 @@ pub async fn user_block_instance( return Err(LemmyErrorType::CantBlockLocalInstance)?; } - let instance_block_form = InstanceBlockForm { - person_id, - instance_id, - }; + let instance_block_form = InstanceBlockForm::new(person_id, instance_id); if data.block { - InstanceBlock::block(&mut context.pool(), &instance_block_form) - .await - .with_lemmy_type(LemmyErrorType::InstanceBlockAlreadyExists)?; + InstanceActions::block(&mut context.pool(), &instance_block_form).await?; } else { - InstanceBlock::unblock(&mut context.pool(), &instance_block_form) - .await - .with_lemmy_type(LemmyErrorType::InstanceBlockAlreadyExists)?; + InstanceActions::unblock(&mut context.pool(), &instance_block_form).await?; } Ok(Json(SuccessResponse::default())) diff --git a/crates/api/src/post/hide.rs b/crates/api/src/post/hide.rs index 969577a99..8f77d9cff 100644 --- a/crates/api/src/post/hide.rs +++ b/crates/api/src/post/hide.rs @@ -3,9 +3,12 @@ use lemmy_api_common::{ context::LemmyContext, post::{HidePost, PostResponse}, }; -use lemmy_db_schema::source::post::PostHide; +use lemmy_db_schema::{ + source::post::{PostActions, PostHideForm}, + traits::Hideable, +}; use lemmy_db_views::structs::{LocalUserView, PostView}; -use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; +use lemmy_utils::error::LemmyResult; pub async fn hide_post( data: Json, @@ -15,15 +18,13 @@ pub async fn hide_post( let person_id = local_user_view.person.id; let post_id = data.post_id; + let hide_form = PostHideForm::new(post_id, person_id); + // Mark the post as hidden / unhidden if data.hide { - PostHide::hide(&mut context.pool(), post_id, person_id) - .await - .with_lemmy_type(LemmyErrorType::CouldntHidePost)?; + PostActions::hide(&mut context.pool(), &hide_form).await?; } else { - PostHide::unhide(&mut context.pool(), post_id, person_id) - .await - .with_lemmy_type(LemmyErrorType::CouldntHidePost)?; + PostActions::unhide(&mut context.pool(), &hide_form).await?; } let post_view = PostView::read( diff --git a/crates/api/src/post/like.rs b/crates/api/src/post/like.rs index 3f552abf1..7001866af 100644 --- a/crates/api/src/post/like.rs +++ b/crates/api/src/post/like.rs @@ -11,12 +11,12 @@ use lemmy_db_schema::{ newtypes::PostOrCommentId, source::{ local_site::LocalSite, - post::{PostLike, PostLikeForm, PostRead, PostReadForm}, + post::{PostActions, PostLikeForm, PostReadForm}, }, - traits::Likeable, + traits::{Likeable, Readable}, }; use lemmy_db_views::structs::{LocalUserView, PostView}; -use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; +use lemmy_utils::error::LemmyResult; use std::ops::Deref; pub async fn like_post( @@ -52,19 +52,18 @@ pub async fn like_post( // Remove any likes first let person_id = local_user_view.person.id; - PostLike::remove(&mut context.pool(), person_id, post_id).await?; + PostActions::remove_like(&mut context.pool(), person_id, post_id).await?; // Only add the like if the score isnt 0 - let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1); + let do_add = + like_form.like_score != 0 && (like_form.like_score == 1 || like_form.like_score == -1); if do_add { - PostLike::like(&mut context.pool(), &like_form) - .await - .with_lemmy_type(LemmyErrorType::CouldntLikePost)?; + PostActions::like(&mut context.pool(), &like_form).await?; } // Mark Post Read let read_form = PostReadForm::new(post_id, person_id); - PostRead::mark_as_read(&mut context.pool(), &read_form).await?; + PostActions::mark_as_read(&mut context.pool(), &read_form).await?; ActivityChannel::submit_activity( SendActivityData::LikePostOrComment { diff --git a/crates/api/src/post/mark_many_read.rs b/crates/api/src/post/mark_many_read.rs index 9a7330d2f..5b117cf82 100644 --- a/crates/api/src/post/mark_many_read.rs +++ b/crates/api/src/post/mark_many_read.rs @@ -1,6 +1,6 @@ use actix_web::web::{Data, Json}; use lemmy_api_common::{context::LemmyContext, post::MarkManyPostsAsRead, SuccessResponse}; -use lemmy_db_schema::source::post::PostRead; +use lemmy_db_schema::{source::post::PostActions, traits::Readable}; use lemmy_db_views::structs::LocalUserView; use lemmy_utils::error::{LemmyErrorType, LemmyResult, MAX_API_PARAM_ELEMENTS}; @@ -16,8 +16,10 @@ pub async fn mark_posts_as_read( let person_id = local_user_view.person.id; + let forms = PostActions::build_many_read_forms(post_ids, person_id); + // Mark the posts as read - PostRead::mark_many_as_read(&mut context.pool(), post_ids, person_id).await?; + PostActions::mark_many_as_read(&mut context.pool(), &forms).await?; Ok(Json(SuccessResponse::default())) } diff --git a/crates/api/src/post/mark_read.rs b/crates/api/src/post/mark_read.rs index be9b30798..7cd974a1c 100644 --- a/crates/api/src/post/mark_read.rs +++ b/crates/api/src/post/mark_read.rs @@ -3,7 +3,10 @@ use lemmy_api_common::{ context::LemmyContext, post::{MarkPostAsRead, PostResponse}, }; -use lemmy_db_schema::source::post::{PostRead, PostReadForm}; +use lemmy_db_schema::{ + source::post::{PostActions, PostReadForm}, + traits::Readable, +}; use lemmy_db_views::structs::{LocalUserView, PostView}; use lemmy_utils::error::LemmyResult; @@ -18,9 +21,9 @@ pub async fn mark_post_as_read( // Mark the post as read / unread let form = PostReadForm::new(post_id, person_id); if data.read { - PostRead::mark_as_read(&mut context.pool(), &form).await?; + PostActions::mark_as_read(&mut context.pool(), &form).await?; } else { - PostRead::mark_as_unread(&mut context.pool(), &form).await?; + PostActions::mark_as_unread(&mut context.pool(), &form).await?; } let post_view = PostView::read( &mut context.pool(), diff --git a/crates/api/src/post/save.rs b/crates/api/src/post/save.rs index 54a423ae3..08a0db5fb 100644 --- a/crates/api/src/post/save.rs +++ b/crates/api/src/post/save.rs @@ -4,11 +4,11 @@ use lemmy_api_common::{ post::{PostResponse, SavePost}, }; use lemmy_db_schema::{ - source::post::{PostRead, PostReadForm, PostSaved, PostSavedForm}, - traits::Saveable, + source::post::{PostActions, PostReadForm, PostSavedForm}, + traits::{Readable, Saveable}, }; use lemmy_db_views::structs::{LocalUserView, PostView}; -use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; +use lemmy_utils::error::LemmyResult; pub async fn save_post( data: Json, @@ -18,13 +18,9 @@ pub async fn save_post( let post_saved_form = PostSavedForm::new(data.post_id, local_user_view.person.id); if data.save { - PostSaved::save(&mut context.pool(), &post_saved_form) - .await - .with_lemmy_type(LemmyErrorType::CouldntSavePost)?; + PostActions::save(&mut context.pool(), &post_saved_form).await?; } else { - PostSaved::unsave(&mut context.pool(), &post_saved_form) - .await - .with_lemmy_type(LemmyErrorType::CouldntSavePost)?; + PostActions::unsave(&mut context.pool(), &post_saved_form).await?; } let post_id = data.post_id; @@ -38,7 +34,7 @@ pub async fn save_post( .await?; let read_form = PostReadForm::new(post_id, person_id); - PostRead::mark_as_read(&mut context.pool(), &read_form).await?; + PostActions::mark_as_read(&mut context.pool(), &read_form).await?; Ok(Json(PostResponse { post_view })) } diff --git a/crates/api/src/reports/comment_report/create.rs b/crates/api/src/reports/comment_report/create.rs index 6187aac30..2914ae455 100644 --- a/crates/api/src/reports/comment_report/create.rs +++ b/crates/api/src/reports/comment_report/create.rs @@ -20,7 +20,7 @@ use lemmy_db_schema::{ traits::Reportable, }; use lemmy_db_views::structs::{CommentReportView, CommentView, LocalUserView}; -use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; +use lemmy_utils::error::LemmyResult; /// Creates a comment report and notifies the moderators of the community pub async fn create_comment_report( @@ -59,9 +59,7 @@ pub async fn create_comment_report( violates_instance_rules: data.violates_instance_rules.unwrap_or_default(), }; - let report = CommentReport::report(&mut context.pool(), &report_form) - .await - .with_lemmy_type(LemmyErrorType::CouldntCreateReport)?; + let report = CommentReport::report(&mut context.pool(), &report_form).await?; let comment_report_view = CommentReportView::read(&mut context.pool(), report.id, person_id).await?; diff --git a/crates/api/src/reports/comment_report/resolve.rs b/crates/api/src/reports/comment_report/resolve.rs index bed110483..bc4ff8167 100644 --- a/crates/api/src/reports/comment_report/resolve.rs +++ b/crates/api/src/reports/comment_report/resolve.rs @@ -8,7 +8,7 @@ use lemmy_api_common::{ }; use lemmy_db_schema::{source::comment_report::CommentReport, traits::Reportable}; use lemmy_db_views::structs::{CommentReportView, LocalUserView}; -use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; +use lemmy_utils::error::LemmyResult; /// Resolves or unresolves a comment report and notifies the moderators of the community pub async fn resolve_comment_report( @@ -30,13 +30,9 @@ pub async fn resolve_comment_report( .await?; if data.resolved { - CommentReport::resolve(&mut context.pool(), report_id, person_id) - .await - .with_lemmy_type(LemmyErrorType::CouldntResolveReport)?; + CommentReport::resolve(&mut context.pool(), report_id, person_id).await?; } else { - CommentReport::unresolve(&mut context.pool(), report_id, person_id) - .await - .with_lemmy_type(LemmyErrorType::CouldntResolveReport)?; + CommentReport::unresolve(&mut context.pool(), report_id, person_id).await?; } let report_id = data.report_id; diff --git a/crates/api/src/reports/community_report/create.rs b/crates/api/src/reports/community_report/create.rs index 71338d2d1..72e16dc92 100644 --- a/crates/api/src/reports/community_report/create.rs +++ b/crates/api/src/reports/community_report/create.rs @@ -14,7 +14,7 @@ use lemmy_db_schema::{ traits::{Crud, Reportable}, }; use lemmy_db_views::structs::{CommunityReportView, LocalUserView}; -use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; +use lemmy_utils::error::LemmyResult; pub async fn create_community_report( data: Json, @@ -41,9 +41,7 @@ pub async fn create_community_report( reason, }; - let report = CommunityReport::report(&mut context.pool(), &report_form) - .await - .with_lemmy_type(LemmyErrorType::CouldntCreateReport)?; + let report = CommunityReport::report(&mut context.pool(), &report_form).await?; let community_report_view = CommunityReportView::read(&mut context.pool(), report.id, person_id).await?; diff --git a/crates/api/src/reports/community_report/resolve.rs b/crates/api/src/reports/community_report/resolve.rs index 73fe1c80b..89d05fb25 100644 --- a/crates/api/src/reports/community_report/resolve.rs +++ b/crates/api/src/reports/community_report/resolve.rs @@ -6,7 +6,7 @@ use lemmy_api_common::{ }; use lemmy_db_schema::{source::community_report::CommunityReport, traits::Reportable}; use lemmy_db_views::structs::{CommunityReportView, LocalUserView}; -use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; +use lemmy_utils::error::LemmyResult; pub async fn resolve_community_report( data: Json, @@ -18,13 +18,9 @@ pub async fn resolve_community_report( let report_id = data.report_id; let person_id = local_user_view.person.id; if data.resolved { - CommunityReport::resolve(&mut context.pool(), report_id, person_id) - .await - .with_lemmy_type(LemmyErrorType::CouldntResolveReport)?; + CommunityReport::resolve(&mut context.pool(), report_id, person_id).await?; } else { - CommunityReport::unresolve(&mut context.pool(), report_id, person_id) - .await - .with_lemmy_type(LemmyErrorType::CouldntResolveReport)?; + CommunityReport::unresolve(&mut context.pool(), report_id, person_id).await?; } let community_report_view = diff --git a/crates/api/src/reports/post_report/create.rs b/crates/api/src/reports/post_report/create.rs index cf5c479a2..f8f5e1596 100644 --- a/crates/api/src/reports/post_report/create.rs +++ b/crates/api/src/reports/post_report/create.rs @@ -20,7 +20,7 @@ use lemmy_db_schema::{ traits::Reportable, }; use lemmy_db_views::structs::{LocalUserView, PostReportView, PostView}; -use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; +use lemmy_utils::error::LemmyResult; /// Creates a post report and notifies the moderators of the community pub async fn create_post_report( @@ -55,9 +55,7 @@ pub async fn create_post_report( violates_instance_rules: data.violates_instance_rules.unwrap_or_default(), }; - let report = PostReport::report(&mut context.pool(), &report_form) - .await - .with_lemmy_type(LemmyErrorType::CouldntCreateReport)?; + let report = PostReport::report(&mut context.pool(), &report_form).await?; let post_report_view = PostReportView::read(&mut context.pool(), report.id, person_id).await?; diff --git a/crates/api/src/reports/post_report/resolve.rs b/crates/api/src/reports/post_report/resolve.rs index fff6187b0..24cf4964d 100644 --- a/crates/api/src/reports/post_report/resolve.rs +++ b/crates/api/src/reports/post_report/resolve.rs @@ -8,7 +8,7 @@ use lemmy_api_common::{ }; use lemmy_db_schema::{source::post_report::PostReport, traits::Reportable}; use lemmy_db_views::structs::{LocalUserView, PostReportView}; -use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; +use lemmy_utils::error::LemmyResult; /// Resolves or unresolves a post report and notifies the moderators of the community pub async fn resolve_post_report( @@ -30,14 +30,9 @@ pub async fn resolve_post_report( .await?; if data.resolved { - PostReport::resolve(&mut context.pool(), report_id, person_id) - .await - .with_lemmy_type(LemmyErrorType::CouldntResolveReport)?; + PostReport::resolve(&mut context.pool(), report_id, person_id).await?; } else { - // TODO: not federated - PostReport::unresolve(&mut context.pool(), report_id, person_id) - .await - .with_lemmy_type(LemmyErrorType::CouldntResolveReport)?; + PostReport::unresolve(&mut context.pool(), report_id, person_id).await?; } let post_report_view = PostReportView::read(&mut context.pool(), report_id, person_id).await?; diff --git a/crates/api/src/reports/private_message_report/create.rs b/crates/api/src/reports/private_message_report/create.rs index 91295e821..04f02e5cb 100644 --- a/crates/api/src/reports/private_message_report/create.rs +++ b/crates/api/src/reports/private_message_report/create.rs @@ -14,7 +14,7 @@ use lemmy_db_schema::{ traits::{Crud, Reportable}, }; use lemmy_db_views::structs::{LocalUserView, PrivateMessageReportView}; -use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; +use lemmy_utils::error::{LemmyErrorType, LemmyResult}; pub async fn create_pm_report( data: Json, @@ -41,9 +41,7 @@ pub async fn create_pm_report( reason, }; - let report = PrivateMessageReport::report(&mut context.pool(), &report_form) - .await - .with_lemmy_type(LemmyErrorType::CouldntCreateReport)?; + let report = PrivateMessageReport::report(&mut context.pool(), &report_form).await?; let private_message_report_view = PrivateMessageReportView::read(&mut context.pool(), report.id).await?; diff --git a/crates/api/src/reports/private_message_report/resolve.rs b/crates/api/src/reports/private_message_report/resolve.rs index d15452cde..ac24c774e 100644 --- a/crates/api/src/reports/private_message_report/resolve.rs +++ b/crates/api/src/reports/private_message_report/resolve.rs @@ -6,7 +6,7 @@ use lemmy_api_common::{ }; use lemmy_db_schema::{source::private_message_report::PrivateMessageReport, traits::Reportable}; use lemmy_db_views::structs::{LocalUserView, PrivateMessageReportView}; -use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; +use lemmy_utils::error::LemmyResult; pub async fn resolve_pm_report( data: Json, @@ -18,13 +18,9 @@ pub async fn resolve_pm_report( let report_id = data.report_id; let person_id = local_user_view.person.id; if data.resolved { - PrivateMessageReport::resolve(&mut context.pool(), report_id, person_id) - .await - .with_lemmy_type(LemmyErrorType::CouldntResolveReport)?; + PrivateMessageReport::resolve(&mut context.pool(), report_id, person_id).await?; } else { - PrivateMessageReport::unresolve(&mut context.pool(), report_id, person_id) - .await - .with_lemmy_type(LemmyErrorType::CouldntResolveReport)?; + PrivateMessageReport::unresolve(&mut context.pool(), report_id, person_id).await?; } let private_message_report_view = diff --git a/crates/api_common/src/utils.rs b/crates/api_common/src/utils.rs index 76ea0c22d..34ab74380 100644 --- a/crates/api_common/src/utils.rs +++ b/crates/api_common/src/utils.rs @@ -15,13 +15,11 @@ use enum_map::{enum_map, EnumMap}; use lemmy_db_schema::{ newtypes::{CommentId, CommunityId, DbUrl, InstanceId, PersonId, PostId, PostOrCommentId}, source::{ - comment::{Comment, CommentLike, CommentUpdateForm}, - community::{Community, CommunityModerator, CommunityUpdateForm}, - community_block::CommunityBlock, + comment::{Comment, CommentActions, CommentUpdateForm}, + community::{Community, CommunityActions, CommunityUpdateForm}, email_verification::{EmailVerification, EmailVerificationForm}, images::{ImageDetails, RemoteImage}, - instance::Instance, - instance_block::InstanceBlock, + instance::{Instance, InstanceActions}, local_site::LocalSite, local_site_rate_limit::LocalSiteRateLimit, local_site_url_blocklist::LocalSiteUrlBlocklist, @@ -34,15 +32,13 @@ use lemmy_db_schema::{ }, oauth_account::OAuthAccount, password_reset_request::PasswordResetRequest, - person::{Person, PersonUpdateForm}, - person_block::PersonBlock, - post::{Post, PostLike}, - post_actions::{PostActions, PostActionsForm}, + person::{Person, PersonActions, PersonUpdateForm}, + post::{Post, PostActions, PostReadCommentsForm}, private_message::PrivateMessage, registration_application::RegistrationApplication, site::Site, }, - traits::{Crud, Likeable}, + traits::{Blockable, Crud, Likeable, ReadComments}, utils::DbPool, FederationMode, RegistrationMode, @@ -157,13 +153,8 @@ pub async fn update_read_comments( read_comments: i64, pool: &mut DbPool<'_>, ) -> LemmyResult<()> { - let person_post_agg_form = PostActionsForm { - person_id, - post_id, - read_comments, - }; - - PostActions::upsert(pool, &person_post_agg_form).await?; + let person_post_agg_form = PostReadCommentsForm::new(post_id, person_id, read_comments); + PostActions::update_read_comments(pool, &person_post_agg_form).await?; Ok(()) } @@ -288,9 +279,9 @@ pub async fn check_person_instance_community_block( community_id: CommunityId, pool: &mut DbPool<'_>, ) -> LemmyResult<()> { - PersonBlock::read(pool, potential_blocker_id, my_id).await?; - InstanceBlock::read(pool, potential_blocker_id, community_instance_id).await?; - CommunityBlock::read(pool, potential_blocker_id, community_id).await?; + PersonActions::read_block(pool, potential_blocker_id, my_id).await?; + InstanceActions::read_block(pool, potential_blocker_id, community_instance_id).await?; + CommunityActions::read_block(pool, potential_blocker_id, community_id).await?; Ok(()) } @@ -312,9 +303,9 @@ pub async fn check_local_vote_mode( // Undo previous vote for item if new vote fails if downvote_fail || upvote_fail { match post_or_comment_id { - PostOrCommentId::Post(post_id) => PostLike::remove(pool, person_id, post_id).await?, + PostOrCommentId::Post(post_id) => PostActions::remove_like(pool, person_id, post_id).await?, PostOrCommentId::Comment(comment_id) => { - CommentLike::remove(pool, person_id, comment_id).await? + CommentActions::remove_like(pool, person_id, comment_id).await? } }; } @@ -984,7 +975,7 @@ pub async fn purge_user_account(person_id: PersonId, context: &LemmyContext) -> .with_lemmy_type(LemmyErrorType::CouldntUpdatePost)?; // Leave communities they mod - CommunityModerator::leave_all_communities(pool, person_id).await?; + CommunityActions::leave_mod_team_for_all_communities(pool, person_id).await?; // Delete the oauth accounts linked to the local user if let Ok(local_user) = LocalUserView::read_person(pool, person_id).await { diff --git a/crates/api_crud/src/comment/create.rs b/crates/api_crud/src/comment/create.rs index f85b5658c..ca1561203 100644 --- a/crates/api_crud/src/comment/create.rs +++ b/crates/api_crud/src/comment/create.rs @@ -19,7 +19,7 @@ use lemmy_db_schema::{ impls::actor_language::validate_post_language, newtypes::PostOrCommentId, source::{ - comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm}, + comment::{Comment, CommentActions, CommentInsertForm, CommentLikeForm}, comment_reply::{CommentReply, CommentReplyUpdateForm}, person_comment_mention::{PersonCommentMention, PersonCommentMentionUpdateForm}, }, @@ -123,15 +123,9 @@ pub async fn create_comment( .await?; // You like your own comment by default - let like_form = CommentLikeForm { - comment_id: inserted_comment.id, - person_id: local_user_view.person.id, - score: 1, - }; + let like_form = CommentLikeForm::new(local_user_view.person.id, inserted_comment.id, 1); - CommentLike::like(&mut context.pool(), &like_form) - .await - .with_lemmy_type(LemmyErrorType::CouldntLikeComment)?; + CommentActions::like(&mut context.pool(), &like_form).await?; ActivityChannel::submit_activity( SendActivityData::CreateComment(inserted_comment.clone()), diff --git a/crates/api_crud/src/community/create.rs b/crates/api_crud/src/community/create.rs index 511fdf2b0..1ef2e68c5 100644 --- a/crates/api_crud/src/community/create.rs +++ b/crates/api_crud/src/community/create.rs @@ -20,11 +20,10 @@ use lemmy_db_schema::{ actor_language::{CommunityLanguage, LocalUserLanguage, SiteLanguage}, community::{ Community, - CommunityFollower, + CommunityActions, CommunityFollowerForm, CommunityFollowerState, CommunityInsertForm, - CommunityModerator, CommunityModeratorForm, }, }, @@ -115,26 +114,19 @@ pub async fn create_community( let community_id = inserted_community.id; // The community creator becomes a moderator - let community_moderator_form = CommunityModeratorForm { - community_id, - person_id: local_user_view.person.id, - }; + let community_moderator_form = + CommunityModeratorForm::new(community_id, local_user_view.person.id); - CommunityModerator::join(&mut context.pool(), &community_moderator_form) - .await - .with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists)?; + CommunityActions::join(&mut context.pool(), &community_moderator_form).await?; // Follow your own community - let community_follower_form = CommunityFollowerForm { + let community_follower_form = CommunityFollowerForm::new( community_id, - person_id: local_user_view.person.id, - state: Some(CommunityFollowerState::Accepted), - approver_id: None, - }; + local_user_view.person.id, + CommunityFollowerState::Accepted, + ); - CommunityFollower::follow(&mut context.pool(), &community_follower_form) - .await - .with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists)?; + CommunityActions::follow(&mut context.pool(), &community_follower_form).await?; // Update the discussion_languages if that's provided let site_languages = SiteLanguage::read_local_raw(&mut context.pool()).await?; diff --git a/crates/api_crud/src/post/create.rs b/crates/api_crud/src/post/create.rs index 75827b1b4..15335b50d 100644 --- a/crates/api_crud/src/post/create.rs +++ b/crates/api_crud/src/post/create.rs @@ -23,9 +23,9 @@ use lemmy_db_schema::{ source::{ community::Community, local_site::LocalSite, - post::{Post, PostInsertForm, PostLike, PostLikeForm, PostRead, PostReadForm}, + post::{Post, PostActions, PostInsertForm, PostLikeForm, PostReadForm}, }, - traits::{Crud, Likeable}, + traits::{Crud, Likeable, Readable}, utils::diesel_url_create, }; use lemmy_db_views::structs::{CommunityModeratorView, LocalUserView}; @@ -144,9 +144,7 @@ pub async fn create_post( let post_id = inserted_post.id; let like_form = PostLikeForm::new(post_id, person_id, 1); - PostLike::like(&mut context.pool(), &like_form) - .await - .with_lemmy_type(LemmyErrorType::CouldntLikePost)?; + PostActions::like(&mut context.pool(), &like_form).await?; // Scan the post body for user mentions, add those rows let mentions = scrape_text_for_mentions(&inserted_post.body.clone().unwrap_or_default()); @@ -161,7 +159,7 @@ pub async fn create_post( .await?; let read_form = PostReadForm::new(post_id, person_id); - PostRead::mark_as_read(&mut context.pool(), &read_form).await?; + PostActions::mark_as_read(&mut context.pool(), &read_form).await?; build_post_response(&context, community_id, local_user_view, post_id).await } diff --git a/crates/api_crud/src/post/read.rs b/crates/api_crud/src/post/read.rs index a2e5a7c18..a930e2ada 100644 --- a/crates/api_crud/src/post/read.rs +++ b/crates/api_crud/src/post/read.rs @@ -7,9 +7,9 @@ use lemmy_api_common::{ use lemmy_db_schema::{ source::{ comment::Comment, - post::{Post, PostRead, PostReadForm}, + post::{Post, PostActions, PostReadForm}, }, - traits::Crud, + traits::{Crud, Readable}, }; use lemmy_db_views::{ post::post_view::PostQuery, @@ -64,7 +64,7 @@ pub async fn get_post( let post_id = post_view.post.id; if let Some(person_id) = person_id { let read_form = PostReadForm::new(post_id, person_id); - PostRead::mark_as_read(&mut context.pool(), &read_form).await?; + PostActions::mark_as_read(&mut context.pool(), &read_form).await?; update_read_comments( person_id, diff --git a/crates/api_crud/src/private_message/create.rs b/crates/api_crud/src/private_message/create.rs index b8f50d49f..f02da8c27 100644 --- a/crates/api_crud/src/private_message/create.rs +++ b/crates/api_crud/src/private_message/create.rs @@ -14,10 +14,10 @@ use lemmy_api_common::{ }; use lemmy_db_schema::{ source::{ - person_block::PersonBlock, + person::PersonActions, private_message::{PrivateMessage, PrivateMessageInsertForm}, }, - traits::Crud, + traits::{Blockable, Crud}, }; use lemmy_db_views::structs::{LocalUserView, PrivateMessageView}; use lemmy_utils::{ @@ -35,7 +35,7 @@ pub async fn create_private_message( let content = process_markdown(&data.content, &slur_regex, &url_blocklist, &context).await?; is_valid_body_field(&content, false)?; - PersonBlock::read( + PersonActions::read_block( &mut context.pool(), data.recipient_id, local_user_view.person.id, diff --git a/crates/api_crud/src/user/my_user.rs b/crates/api_crud/src/user/my_user.rs index 193f54c89..9504bbd8f 100644 --- a/crates/api_crud/src/user/my_user.rs +++ b/crates/api_crud/src/user/my_user.rs @@ -1,10 +1,13 @@ use actix_web::web::{Data, Json}; use lemmy_api_common::{context::LemmyContext, site::MyUserInfo, utils::check_user_valid}; -use lemmy_db_schema::source::{ - actor_language::LocalUserLanguage, - community_block::CommunityBlock, - instance_block::InstanceBlock, - person_block::PersonBlock, +use lemmy_db_schema::{ + source::{ + actor_language::LocalUserLanguage, + community::CommunityActions, + instance::InstanceActions, + person::PersonActions, + }, + traits::Blockable, }; use lemmy_db_views::structs::{CommunityFollowerView, CommunityModeratorView, LocalUserView}; use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; @@ -23,9 +26,9 @@ pub async fn get_my_user( let (follows, community_blocks, instance_blocks, person_blocks, moderates, discussion_languages) = lemmy_db_schema::try_join_with_pool!(pool => ( |pool| CommunityFollowerView::for_person(pool, person_id), - |pool| CommunityBlock::for_person(pool, person_id), - |pool| InstanceBlock::for_person(pool, person_id), - |pool| PersonBlock::for_person(pool, person_id), + |pool| CommunityActions::read_blocks_for_person(pool, person_id), + |pool| InstanceActions::read_blocks_for_person(pool, person_id), + |pool| PersonActions::read_blocks_for_person(pool, person_id), |pool| CommunityModeratorView::for_person(pool, person_id, Some(&local_user_view.local_user)), |pool| LocalUserLanguage::read(pool, local_user_id) )) diff --git a/crates/apub/src/activities/block/block_user.rs b/crates/apub/src/activities/block/block_user.rs index 32f0efb34..7dc42d1c9 100644 --- a/crates/apub/src/activities/block/block_user.rs +++ b/crates/apub/src/activities/block/block_user.rs @@ -30,7 +30,7 @@ use lemmy_api_common::{ use lemmy_db_schema::{ source::{ activity::ActivitySendTargets, - community::{CommunityPersonBan, CommunityPersonBanForm}, + community::{CommunityActions, CommunityPersonBanForm}, mod_log::moderator::{ModBan, ModBanForm, ModBanFromCommunity, ModBanFromCommunityForm}, person::{Person, PersonUpdateForm}, }, @@ -177,11 +177,10 @@ impl ActivityHandler for BlockUser { } SiteOrCommunity::Community(community) => { let community_user_ban_form = CommunityPersonBanForm { - community_id: community.id, - person_id: blocked_person.id, - expires: Some(expires), + ban_expires: Some(expires), + ..CommunityPersonBanForm::new(community.id, blocked_person.id) }; - CommunityPersonBan::ban(&mut context.pool(), &community_user_ban_form).await?; + CommunityActions::ban(&mut context.pool(), &community_user_ban_form).await?; // Dont unsubscribe the user so that we can receive a potential unban activity. // If we unfollowed the community here, activities from the community would be rejected diff --git a/crates/apub/src/activities/block/undo_block_user.rs b/crates/apub/src/activities/block/undo_block_user.rs index b8d5ab508..77fe22d36 100644 --- a/crates/apub/src/activities/block/undo_block_user.rs +++ b/crates/apub/src/activities/block/undo_block_user.rs @@ -26,7 +26,7 @@ use lemmy_api_common::{ use lemmy_db_schema::{ source::{ activity::ActivitySendTargets, - community::{CommunityPersonBan, CommunityPersonBanForm}, + community::{CommunityActions, CommunityPersonBanForm}, mod_log::moderator::{ModBan, ModBanForm, ModBanFromCommunity, ModBanFromCommunityForm}, person::{Person, PersonUpdateForm}, }, @@ -130,12 +130,8 @@ impl ActivityHandler for UndoBlockUser { } SiteOrCommunity::Community(community) => { verify_visibility(&self.to, &self.cc, &community)?; - let community_user_ban_form = CommunityPersonBanForm { - community_id: community.id, - person_id: blocked_person.id, - expires: None, - }; - CommunityPersonBan::unban(&mut context.pool(), &community_user_ban_form).await?; + let community_user_ban_form = CommunityPersonBanForm::new(community.id, blocked_person.id); + CommunityActions::unban(&mut context.pool(), &community_user_ban_form).await?; if self.restore_data.unwrap_or(false) { remove_or_restore_user_data_in_community( diff --git a/crates/apub/src/activities/community/announce.rs b/crates/apub/src/activities/community/announce.rs index 2e2f53c6c..dd1c70581 100644 --- a/crates/apub/src/activities/community/announce.rs +++ b/crates/apub/src/activities/community/announce.rs @@ -23,7 +23,7 @@ use activitypub_federation::{ traits::{ActivityHandler, Actor}, }; use lemmy_api_common::context::LemmyContext; -use lemmy_db_schema::source::{activity::ActivitySendTargets, community::CommunityFollower}; +use lemmy_db_schema::source::{activity::ActivitySendTargets, community::CommunityActions}; use lemmy_utils::error::{FederationError, LemmyError, LemmyErrorType, LemmyResult}; use serde_json::Value; use url::Url; @@ -212,7 +212,7 @@ async fn can_accept_activity_in_community( return Err(LemmyErrorType::NotFound.into()); } if !community.local { - CommunityFollower::check_has_local_followers(&mut context.pool(), community.id).await? + CommunityActions::check_has_local_followers(&mut context.pool(), community.id).await? } } Ok(()) diff --git a/crates/apub/src/activities/community/collection_add.rs b/crates/apub/src/activities/community/collection_add.rs index 8edfb3583..98b74bca5 100644 --- a/crates/apub/src/activities/community/collection_add.rs +++ b/crates/apub/src/activities/community/collection_add.rs @@ -30,7 +30,7 @@ use lemmy_db_schema::{ newtypes::{CommunityId, PersonId}, source::{ activity::ActivitySendTargets, - community::{Community, CommunityModerator, CommunityModeratorForm}, + community::{Community, CommunityActions, CommunityModeratorForm}, mod_log::moderator::{ModAddCommunity, ModAddCommunityForm}, person::Person, post::{Post, PostUpdateForm}, @@ -133,14 +133,11 @@ impl ActivityHandler for CollectionAdd { // already been added. Skip it here as it would result in a duplicate key error. let new_mod_id = new_mod.id; let moderated_communities = - CommunityModerator::get_person_moderated_communities(&mut context.pool(), new_mod_id) + CommunityActions::get_person_moderated_communities(&mut context.pool(), new_mod_id) .await?; if !moderated_communities.contains(&community.id) { - let form = CommunityModeratorForm { - community_id: community.id, - person_id: new_mod.id, - }; - CommunityModerator::join(&mut context.pool(), &form).await?; + let form = CommunityModeratorForm::new(community.id, new_mod.id); + CommunityActions::join(&mut context.pool(), &form).await?; // write mod log let actor = self.actor.dereference(context).await?; diff --git a/crates/apub/src/activities/community/collection_remove.rs b/crates/apub/src/activities/community/collection_remove.rs index a5d5e4616..db29c2f18 100644 --- a/crates/apub/src/activities/community/collection_remove.rs +++ b/crates/apub/src/activities/community/collection_remove.rs @@ -26,7 +26,7 @@ use lemmy_db_schema::{ impls::community::CollectionType, source::{ activity::ActivitySendTargets, - community::{Community, CommunityModerator, CommunityModeratorForm}, + community::{Community, CommunityActions, CommunityModeratorForm}, mod_log::moderator::{ModAddCommunity, ModAddCommunityForm}, post::{Post, PostUpdateForm}, }, @@ -124,11 +124,8 @@ impl ActivityHandler for CollectionRemove { .dereference(context) .await?; - let form = CommunityModeratorForm { - community_id: community.id, - person_id: remove_mod.id, - }; - CommunityModerator::leave(&mut context.pool(), &form).await?; + let form = CommunityModeratorForm::new(community.id, remove_mod.id); + CommunityActions::leave(&mut context.pool(), &form).await?; // write mod log let actor = self.actor.dereference(context).await?; diff --git a/crates/apub/src/activities/community/mod.rs b/crates/apub/src/activities/community/mod.rs index 62bb207d0..623b45991 100644 --- a/crates/apub/src/activities/community/mod.rs +++ b/crates/apub/src/activities/community/mod.rs @@ -10,7 +10,7 @@ use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::{ source::{ activity::ActivitySendTargets, - person::{Person, PersonFollower}, + person::{Person, PersonActions}, site::Site, }, traits::Crud, @@ -59,7 +59,7 @@ pub(crate) async fn send_activity_in_community( // send to user followers if !is_mod_action { inboxes.add_inboxes( - PersonFollower::list_followers(&mut context.pool(), actor.id) + PersonActions::list_followers(&mut context.pool(), actor.id) .await? .into_iter() .map(|p| ApubPerson(p).shared_inbox_or_inbox()), diff --git a/crates/apub/src/activities/create_or_update/comment.rs b/crates/apub/src/activities/create_or_update/comment.rs index 4474d2476..8dbda1d76 100644 --- a/crates/apub/src/activities/create_or_update/comment.rs +++ b/crates/apub/src/activities/create_or_update/comment.rs @@ -31,7 +31,7 @@ use lemmy_db_schema::{ newtypes::{PersonId, PostOrCommentId}, source::{ activity::ActivitySendTargets, - comment::{Comment, CommentLike, CommentLikeForm}, + comment::{Comment, CommentActions, CommentLikeForm}, community::Community, person::Person, post::Post, @@ -151,12 +151,8 @@ impl ActivityHandler for CreateOrUpdateNote { let comment = ApubComment::from_json(self.object, context).await?; // author likes their own comment by default - let like_form = CommentLikeForm { - comment_id: comment.id, - person_id: comment.creator_id, - score: 1, - }; - CommentLike::like(&mut context.pool(), &like_form).await?; + let like_form = CommentLikeForm::new(comment.creator_id, comment.id, 1); + CommentActions::like(&mut context.pool(), &like_form).await?; // Calculate initial hot_rank Comment::update_hot_rank(&mut context.pool(), comment.id).await?; diff --git a/crates/apub/src/activities/create_or_update/post.rs b/crates/apub/src/activities/create_or_update/post.rs index d263c6f31..b9d5722de 100644 --- a/crates/apub/src/activities/create_or_update/post.rs +++ b/crates/apub/src/activities/create_or_update/post.rs @@ -27,7 +27,7 @@ use lemmy_db_schema::{ activity::ActivitySendTargets, community::Community, person::Person, - post::{Post, PostLike, PostLikeForm}, + post::{Post, PostActions, PostLikeForm}, }, traits::{Crud, Likeable}, }; @@ -117,7 +117,7 @@ impl ActivityHandler for CreateOrUpdatePage { // author likes their own post by default let like_form = PostLikeForm::new(post.id, post.creator_id, 1); - PostLike::like(&mut context.pool(), &like_form).await?; + PostActions::like(&mut context.pool(), &like_form).await?; // Calculate initial hot_rank for post Post::update_ranks(&mut context.pool(), post.id).await?; diff --git a/crates/apub/src/activities/following/accept.rs b/crates/apub/src/activities/following/accept.rs index eea279314..a5466e46e 100644 --- a/crates/apub/src/activities/following/accept.rs +++ b/crates/apub/src/activities/following/accept.rs @@ -11,7 +11,7 @@ use activitypub_federation::{ }; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::{ - source::{activity::ActivitySendTargets, community::CommunityFollower}, + source::{activity::ActivitySendTargets, community::CommunityActions}, traits::Followable, }; use lemmy_utils::error::{LemmyError, LemmyResult}; @@ -66,7 +66,7 @@ impl ActivityHandler for AcceptFollow { // This will throw an error if no follow was requested let community_id = community.id; let person_id = person.id; - CommunityFollower::follow_accepted(&mut context.pool(), community_id, person_id).await?; + CommunityActions::follow_accepted(&mut context.pool(), community_id, person_id).await?; Ok(()) } diff --git a/crates/apub/src/activities/following/follow.rs b/crates/apub/src/activities/following/follow.rs index 6ddf03186..d79571722 100644 --- a/crates/apub/src/activities/following/follow.rs +++ b/crates/apub/src/activities/following/follow.rs @@ -20,9 +20,9 @@ use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::{ source::{ activity::ActivitySendTargets, - community::{CommunityFollower, CommunityFollowerForm, CommunityFollowerState}, + community::{CommunityActions, CommunityFollowerForm, CommunityFollowerState}, instance::Instance, - person::{PersonFollower, PersonFollowerForm}, + person::{PersonActions, PersonFollowerForm}, }, traits::Followable, CommunityVisibility, @@ -95,12 +95,8 @@ impl ActivityHandler for Follow { let object = self.object.dereference(context).await?; match object { UserOrCommunity::User(u) => { - let form = PersonFollowerForm { - person_id: u.id, - follower_id: actor.id, - pending: false, - }; - PersonFollower::follow(&mut context.pool(), &form).await?; + let form = PersonFollowerForm::new(u.id, actor.id, false); + PersonActions::follow(&mut context.pool(), &form).await?; AcceptFollow::send(self, context).await?; } UserOrCommunity::Community(c) => { @@ -111,17 +107,14 @@ impl ActivityHandler for Follow { return Err(FederationError::PlatformLackingPrivateCommunitySupport.into()); } } - let state = Some(match c.visibility { + let follow_state = match c.visibility { Public | Unlisted => CommunityFollowerState::Accepted, Private => CommunityFollowerState::ApprovalRequired, // Dont allow following local-only community via federation. LocalOnlyPrivate | LocalOnlyPublic => return Err(LemmyErrorType::NotFound.into()), - }); - let form = CommunityFollowerForm { - state, - ..CommunityFollowerForm::new(c.id, actor.id) }; - CommunityFollower::follow(&mut context.pool(), &form).await?; + let form = CommunityFollowerForm::new(c.id, actor.id, follow_state); + CommunityActions::follow(&mut context.pool(), &form).await?; if c.visibility == CommunityVisibility::Public { AcceptFollow::send(self, context).await?; } diff --git a/crates/apub/src/activities/following/reject.rs b/crates/apub/src/activities/following/reject.rs index d9b7ed547..47b009cb7 100644 --- a/crates/apub/src/activities/following/reject.rs +++ b/crates/apub/src/activities/following/reject.rs @@ -11,10 +11,7 @@ use activitypub_federation::{ }; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::{ - source::{ - activity::ActivitySendTargets, - community::{CommunityFollower, CommunityFollowerForm}, - }, + source::{activity::ActivitySendTargets, community::CommunityActions}, traits::Followable, }; use lemmy_utils::error::{LemmyError, LemmyResult}; @@ -68,8 +65,7 @@ impl ActivityHandler for RejectFollow { let person = self.object.actor.dereference(context).await?; // remove the follow - let form = CommunityFollowerForm::new(community.id, person.id); - CommunityFollower::unfollow(&mut context.pool(), &form).await?; + CommunityActions::unfollow(&mut context.pool(), person.id, community.id).await?; Ok(()) } diff --git a/crates/apub/src/activities/following/undo_follow.rs b/crates/apub/src/activities/following/undo_follow.rs index 1688f133e..7eb28ceb7 100644 --- a/crates/apub/src/activities/following/undo_follow.rs +++ b/crates/apub/src/activities/following/undo_follow.rs @@ -13,11 +13,7 @@ use activitypub_federation::{ }; use lemmy_api_common::context::LemmyContext; use lemmy_db_schema::{ - source::{ - activity::ActivitySendTargets, - community::{CommunityFollower, CommunityFollowerForm}, - person::{PersonFollower, PersonFollowerForm}, - }, + source::{activity::ActivitySendTargets, community::CommunityActions, person::PersonActions}, traits::Followable, }; use lemmy_utils::error::{LemmyError, LemmyResult}; @@ -79,16 +75,10 @@ impl ActivityHandler for UndoFollow { match object { UserOrCommunity::User(u) => { - let form = PersonFollowerForm { - person_id: u.id, - follower_id: person.id, - pending: false, - }; - PersonFollower::unfollow(&mut context.pool(), &form).await?; + PersonActions::unfollow(&mut context.pool(), person.id, u.id).await?; } UserOrCommunity::Community(c) => { - let form = CommunityFollowerForm::new(c.id, person.id); - CommunityFollower::unfollow(&mut context.pool(), &form).await?; + CommunityActions::unfollow(&mut context.pool(), person.id, c.id).await?; } } diff --git a/crates/apub/src/activities/voting/mod.rs b/crates/apub/src/activities/voting/mod.rs index 9427b6c51..519afb93b 100644 --- a/crates/apub/src/activities/voting/mod.rs +++ b/crates/apub/src/activities/voting/mod.rs @@ -14,10 +14,10 @@ use lemmy_db_schema::{ newtypes::DbUrl, source::{ activity::ActivitySendTargets, - comment::{CommentLike, CommentLikeForm}, + comment::{CommentActions, CommentLikeForm}, community::Community, person::Person, - post::{PostLike, PostLikeForm}, + post::{PostActions, PostLikeForm}, }, traits::Likeable, }; @@ -59,14 +59,10 @@ async fn vote_comment( context: &Data, ) -> LemmyResult<()> { let comment_id = comment.id; - let like_form = CommentLikeForm { - comment_id, - person_id: actor.id, - score: vote_type.into(), - }; + let like_form = CommentLikeForm::new(actor.id, comment_id, vote_type.into()); let person_id = actor.id; - CommentLike::remove(&mut context.pool(), person_id, comment_id).await?; - CommentLike::like(&mut context.pool(), &like_form).await?; + CommentActions::remove_like(&mut context.pool(), person_id, comment_id).await?; + CommentActions::like(&mut context.pool(), &like_form).await?; Ok(()) } @@ -79,8 +75,8 @@ async fn vote_post( let post_id = post.id; let like_form = PostLikeForm::new(post.id, actor.id, vote_type.into()); let person_id = actor.id; - PostLike::remove(&mut context.pool(), person_id, post_id).await?; - PostLike::like(&mut context.pool(), &like_form).await?; + PostActions::remove_like(&mut context.pool(), person_id, post_id).await?; + PostActions::like(&mut context.pool(), &like_form).await?; Ok(()) } @@ -91,7 +87,7 @@ async fn undo_vote_comment( ) -> LemmyResult<()> { let comment_id = comment.id; let person_id = actor.id; - CommentLike::remove(&mut context.pool(), person_id, comment_id).await?; + CommentActions::remove_like(&mut context.pool(), person_id, comment_id).await?; Ok(()) } @@ -102,6 +98,6 @@ async fn undo_vote_post( ) -> LemmyResult<()> { let post_id = post.id; let person_id = actor.id; - PostLike::remove(&mut context.pool(), person_id, post_id).await?; + PostActions::remove_like(&mut context.pool(), person_id, post_id).await?; Ok(()) } diff --git a/crates/apub/src/api/list_posts.rs b/crates/apub/src/api/list_posts.rs index d9e93cc3a..b8065a7cb 100644 --- a/crates/apub/src/api/list_posts.rs +++ b/crates/apub/src/api/list_posts.rs @@ -16,7 +16,8 @@ use lemmy_api_common::{ }; use lemmy_db_schema::{ newtypes::PostId, - source::{community::Community, post::PostRead}, + source::{community::Community, post::PostActions}, + traits::Readable, }; use lemmy_db_views::{ post::post_view::PostQuery, @@ -111,7 +112,8 @@ pub async fn list_posts( .unwrap_or(local_user.auto_mark_fetched_posts_as_read) { let post_ids = posts.iter().map(|p| p.post.id).collect::>(); - PostRead::mark_many_as_read(&mut context.pool(), &post_ids, local_user.person_id).await?; + let forms = PostActions::build_many_read_forms(&post_ids, local_user.person_id); + PostActions::mark_many_as_read(&mut context.pool(), &forms).await?; } } diff --git a/crates/apub/src/api/user_settings_backup.rs b/crates/apub/src/api/user_settings_backup.rs index 1694322ca..2dcc99e13 100644 --- a/crates/apub/src/api/user_settings_backup.rs +++ b/crates/apub/src/api/user_settings_backup.rs @@ -12,15 +12,17 @@ use lemmy_api_common::{context::LemmyContext, SuccessResponse}; use lemmy_db_schema::{ newtypes::DbUrl, source::{ - comment::{CommentSaved, CommentSavedForm}, - community::{CommunityFollower, CommunityFollowerForm, CommunityFollowerState}, - community_block::{CommunityBlock, CommunityBlockForm}, - instance::Instance, - instance_block::{InstanceBlock, InstanceBlockForm}, + comment::{CommentActions, CommentSavedForm}, + community::{ + CommunityActions, + CommunityBlockForm, + CommunityFollowerForm, + CommunityFollowerState, + }, + instance::{Instance, InstanceActions, InstanceBlockForm}, local_user::{LocalUser, LocalUserUpdateForm}, - person::{Person, PersonUpdateForm}, - person_block::{PersonBlock, PersonBlockForm}, - post::{PostSaved, PostSavedForm}, + person::{Person, PersonActions, PersonBlockForm, PersonUpdateForm}, + post::{PostActions, PostSavedForm}, }, traits::{Blockable, Crud, Followable, Saveable}, }; @@ -163,11 +165,9 @@ pub async fn import_settings( &context, |(followed, context)| async move { let community = followed.dereference(&context).await?; - let form = CommunityFollowerForm { - state: Some(CommunityFollowerState::Pending), - ..CommunityFollowerForm::new(community.id, person_id) - }; - CommunityFollower::follow(&mut context.pool(), &form).await?; + let form = + CommunityFollowerForm::new(community.id, person_id, CommunityFollowerState::Pending); + CommunityActions::follow(&mut context.pool(), &form).await?; LemmyResult::Ok(()) }, ) @@ -179,7 +179,7 @@ pub async fn import_settings( |(saved, context)| async move { let post = saved.dereference(&context).await?; let form = PostSavedForm::new(post.id, person_id); - PostSaved::save(&mut context.pool(), &form).await?; + PostActions::save(&mut context.pool(), &form).await?; LemmyResult::Ok(()) }, ) @@ -190,8 +190,8 @@ pub async fn import_settings( &context, |(saved, context)| async move { let comment = saved.dereference(&context).await?; - let form = CommentSavedForm::new(comment.id, person_id); - CommentSaved::save(&mut context.pool(), &form).await?; + let form = CommentSavedForm::new(person_id, comment.id); + CommentActions::save(&mut context.pool(), &form).await?; LemmyResult::Ok(()) }, ) @@ -202,11 +202,8 @@ pub async fn import_settings( &context, |(blocked, context)| async move { let community = blocked.dereference(&context).await?; - let form = CommunityBlockForm { - person_id, - community_id: community.id, - }; - CommunityBlock::block(&mut context.pool(), &form).await?; + let form = CommunityBlockForm::new(community.id, person_id); + CommunityActions::block(&mut context.pool(), &form).await?; LemmyResult::Ok(()) }, ) @@ -218,11 +215,8 @@ pub async fn import_settings( |(blocked, context)| async move { let context = context.reset_request_count(); let target = blocked.dereference(&context).await?; - let form = PersonBlockForm { - person_id, - target_id: target.id, - }; - PersonBlock::block(&mut context.pool(), &form).await?; + let form = PersonBlockForm::new(person_id, target.id); + PersonActions::block(&mut context.pool(), &form).await?; LemmyResult::Ok(()) }, ) @@ -230,11 +224,8 @@ pub async fn import_settings( try_join_all(data.blocked_instances.iter().map(|domain| async { let instance = Instance::read_or_create(&mut context.pool(), domain.clone()).await?; - let form = InstanceBlockForm { - person_id, - instance_id: instance.id, - }; - InstanceBlock::block(&mut context.pool(), &form).await?; + let form = InstanceBlockForm::new(person_id, instance.id); + InstanceActions::block(&mut context.pool(), &form).await?; LemmyResult::Ok(()) })) .await?; @@ -292,7 +283,7 @@ pub(crate) mod tests { source::{ community::{ Community, - CommunityFollower, + CommunityActions, CommunityFollowerForm, CommunityFollowerState, CommunityInsertForm, @@ -322,11 +313,12 @@ pub(crate) mod tests { "pubkey".to_string(), ); let community = Community::create(pool, &community_form).await?; - let follower_form = CommunityFollowerForm { - state: Some(CommunityFollowerState::Accepted), - ..CommunityFollowerForm::new(community.id, export_user.person.id) - }; - CommunityFollower::follow(pool, &follower_form).await?; + let follower_form = CommunityFollowerForm::new( + community.id, + export_user.person.id, + CommunityFollowerState::Accepted, + ); + CommunityActions::follow(pool, &follower_form).await?; let backup = export_settings(export_user.clone(), context.reset_request_count()).await?; diff --git a/crates/apub/src/collections/community_moderators.rs b/crates/apub/src/collections/community_moderators.rs index f2c667bd9..45dfa0fb8 100644 --- a/crates/apub/src/collections/community_moderators.rs +++ b/crates/apub/src/collections/community_moderators.rs @@ -11,7 +11,7 @@ use activitypub_federation::{ }; use lemmy_api_common::{context::LemmyContext, utils::generate_moderators_url}; use lemmy_db_schema::{ - source::community::{CommunityModerator, CommunityModeratorForm}, + source::community::{CommunityActions, CommunityModeratorForm}, traits::Joinable, }; use lemmy_db_views::structs::CommunityModeratorView; @@ -62,11 +62,9 @@ impl Collection for ApubCommunityModerators { for mod_user in ¤t_moderators { let mod_id = ObjectId::from(mod_user.moderator.ap_id.clone()); if !apub.ordered_items.contains(&mod_id) { - let community_moderator_form = CommunityModeratorForm { - community_id: mod_user.community.id, - person_id: mod_user.moderator.id, - }; - CommunityModerator::leave(&mut data.pool(), &community_moderator_form).await?; + let community_moderator_form = + CommunityModeratorForm::new(mod_user.community.id, mod_user.moderator.id); + CommunityActions::leave(&mut data.pool(), &community_moderator_form).await?; } } @@ -80,11 +78,8 @@ impl Collection for ApubCommunityModerators { .map(|c| c.moderator.ap_id.clone()) .any(|x| x == mod_user.ap_id) { - let community_moderator_form = CommunityModeratorForm { - community_id: owner.id, - person_id: mod_user.id, - }; - CommunityModerator::join(&mut data.pool(), &community_moderator_form).await?; + let community_moderator_form = CommunityModeratorForm::new(owner.id, mod_user.id); + CommunityActions::join(&mut data.pool(), &community_moderator_form).await?; } } } @@ -129,12 +124,9 @@ mod tests { let old_mod = PersonInsertForm::test_form(inserted_instance.id, "holly"); let old_mod = Person::create(&mut context.pool(), &old_mod).await?; - let community_moderator_form = CommunityModeratorForm { - community_id: community.id, - person_id: old_mod.id, - }; + let community_moderator_form = CommunityModeratorForm::new(community.id, old_mod.id); - CommunityModerator::join(&mut context.pool(), &community_moderator_form).await?; + CommunityActions::join(&mut context.pool(), &community_moderator_form).await?; assert_eq!(site.ap_id.to_string(), "https://enterprise.lemmy.ml/"); diff --git a/crates/apub/src/objects/private_message.rs b/crates/apub/src/objects/private_message.rs index cfc60e041..e5382870f 100644 --- a/crates/apub/src/objects/private_message.rs +++ b/crates/apub/src/objects/private_message.rs @@ -23,11 +23,10 @@ use lemmy_api_common::{ use lemmy_db_schema::{ source::{ instance::Instance, - person::Person, - person_block::PersonBlock, + person::{Person, PersonActions}, private_message::{PrivateMessage as DbPrivateMessage, PrivateMessageInsertForm}, }, - traits::Crud, + traits::{Blockable, Crud}, }; use lemmy_db_views::structs::LocalUserView; use lemmy_utils::{ @@ -138,7 +137,7 @@ impl Object for ApubPrivateMessage { ) -> LemmyResult { let creator = note.attributed_to.dereference(context).await?; let recipient = note.to[0].dereference(context).await?; - PersonBlock::read(&mut context.pool(), recipient.id, creator.id).await?; + PersonActions::read_block(&mut context.pool(), recipient.id, creator.id).await?; // Check that they can receive private messages if let Ok(recipient_local_user) = diff --git a/crates/db_schema/src/impls/comment.rs b/crates/db_schema/src/impls/comment.rs index c81fc2cb3..6e0d1523a 100644 --- a/crates/db_schema/src/impls/comment.rs +++ b/crates/db_schema/src/impls/comment.rs @@ -1,14 +1,12 @@ use crate::{ diesel::{DecoratableTarget, OptionalExtension}, - impls::local_user::local_user_can_mod, newtypes::{CommentId, DbUrl, PersonId}, schema::{comment, comment_actions}, source::comment::{ Comment, + CommentActions, CommentInsertForm, - CommentLike, CommentLikeForm, - CommentSaved, CommentSavedForm, CommentUpdateForm, }, @@ -16,7 +14,6 @@ use crate::{ utils::{ functions::{coalesce, hot_rank}, get_conn, - now, uplete, DbPool, DELETED_REPLACEMENT_TEXT, @@ -24,17 +21,18 @@ use crate::{ }; use chrono::{DateTime, Utc}; use diesel::{ - dsl::{case_when, insert_into, not}, + dsl::insert_into, expression::SelectableHelper, result::Error, - BoolExpressionMethods, ExpressionMethods, - NullableExpressionMethods, QueryDsl, }; use diesel_async::RunQueryDsl; use diesel_ltree::Ltree; -use lemmy_utils::{error::LemmyResult, settings::structs::Settings}; +use lemmy_utils::{ + error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, + settings::structs::Settings, +}; use url::Url; impl Comment { @@ -143,41 +141,6 @@ impl Comment { } } -/// Selects the comment columns, but gives an empty string for content when -/// deleted or removed, and you're not a mod/admin. -#[diesel::dsl::auto_type] -pub fn comment_select_remove_deletes() -> _ { - let deleted_or_removed = comment::deleted.or(comment::removed); - - // You can only view the content if it hasn't been removed, or you can mod. - let can_view_content = not(deleted_or_removed).or(local_user_can_mod()); - let content = case_when(can_view_content, comment::content).otherwise(""); - - ( - comment::id, - comment::creator_id, - comment::post_id, - content, - comment::removed, - comment::published, - comment::updated, - comment::deleted, - comment::ap_id, - comment::local, - comment::path, - comment::distinguished, - comment::language_id, - comment::score, - comment::upvotes, - comment::downvotes, - comment::child_count, - comment::hot_rank, - comment::controversy_rank, - comment::report_count, - comment::unresolved_report_count, - ) -} - impl Crud for Comment { type InsertForm = CommentInsertForm; type UpdateForm = CommentUpdateForm; @@ -202,65 +165,58 @@ impl Crud for Comment { } } -impl Likeable for CommentLike { +impl Likeable for CommentActions { type Form = CommentLikeForm; type IdType = CommentId; - async fn like(pool: &mut DbPool<'_>, comment_like_form: &CommentLikeForm) -> Result { + + async fn like(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult { let conn = &mut get_conn(pool).await?; - let comment_like_form = ( - comment_like_form, - comment_actions::liked.eq(now().nullable()), - ); insert_into(comment_actions::table) - .values(comment_like_form) + .values(form) .on_conflict((comment_actions::comment_id, comment_actions::person_id)) .do_update() - .set(comment_like_form) + .set(form) .returning(Self::as_select()) .get_result::(conn) .await + .with_lemmy_type(LemmyErrorType::CouldntLikeComment) } - async fn remove( + async fn remove_like( pool: &mut DbPool<'_>, person_id: PersonId, - comment_id: CommentId, - ) -> Result { + comment_id: Self::IdType, + ) -> LemmyResult { let conn = &mut get_conn(pool).await?; uplete::new(comment_actions::table.find((person_id, comment_id))) .set_null(comment_actions::like_score) .set_null(comment_actions::liked) .get_result(conn) .await + .with_lemmy_type(LemmyErrorType::CouldntLikeComment) } } -impl Saveable for CommentSaved { +impl Saveable for CommentActions { type Form = CommentSavedForm; - async fn save( - pool: &mut DbPool<'_>, - comment_saved_form: &CommentSavedForm, - ) -> Result { + async fn save(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult { let conn = &mut get_conn(pool).await?; insert_into(comment_actions::table) - .values(comment_saved_form) + .values(form) .on_conflict((comment_actions::comment_id, comment_actions::person_id)) .do_update() - .set(comment_saved_form) + .set(form) .returning(Self::as_select()) .get_result::(conn) .await + .with_lemmy_type(LemmyErrorType::CouldntSaveComment) } - async fn unsave( - pool: &mut DbPool<'_>, - comment_saved_form: &CommentSavedForm, - ) -> Result { + async fn unsave(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult { let conn = &mut get_conn(pool).await?; - uplete::new( - comment_actions::table.find((comment_saved_form.person_id, comment_saved_form.comment_id)), - ) - .set_null(comment_actions::saved) - .get_result(conn) - .await + uplete::new(comment_actions::table.find((form.person_id, form.comment_id))) + .set_null(comment_actions::saved) + .get_result(conn) + .await + .with_lemmy_type(LemmyErrorType::CouldntSaveComment) } } @@ -356,30 +312,15 @@ mod tests { Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?; // Comment Like - let comment_like_form = CommentLikeForm { - comment_id: inserted_comment.id, - person_id: inserted_person.id, - score: 1, - }; + let comment_like_form = CommentLikeForm::new(inserted_person.id, inserted_comment.id, 1); - let inserted_comment_like = CommentLike::like(pool, &comment_like_form).await?; - - let expected_comment_like = CommentLike { - comment_id: inserted_comment.id, - person_id: inserted_person.id, - published: inserted_comment_like.published, - score: 1, - }; + let inserted_comment_like = CommentActions::like(pool, &comment_like_form).await?; + assert_eq!(Some(1), inserted_comment_like.like_score); // Comment Saved - let comment_saved_form = CommentSavedForm::new(inserted_comment.id, inserted_person.id); - let inserted_comment_saved = CommentSaved::save(pool, &comment_saved_form).await?; - - let expected_comment_saved = CommentSaved { - comment_id: inserted_comment.id, - person_id: inserted_person.id, - published: inserted_comment_saved.published, - }; + let comment_saved_form = CommentSavedForm::new(inserted_person.id, inserted_comment.id); + let inserted_comment_saved = CommentActions::save(pool, &comment_saved_form).await?; + assert!(inserted_comment_saved.saved.is_some()); let comment_update_form = CommentUpdateForm { content: Some("A test comment".into()), @@ -389,8 +330,9 @@ mod tests { let updated_comment = Comment::update(pool, inserted_comment.id, &comment_update_form).await?; let read_comment = Comment::read(pool, inserted_comment.id).await?; - let like_removed = CommentLike::remove(pool, inserted_person.id, inserted_comment.id).await?; - let saved_removed = CommentSaved::unsave(pool, &comment_saved_form).await?; + let like_removed = + CommentActions::remove_like(pool, inserted_person.id, inserted_comment.id).await?; + let saved_removed = CommentActions::unsave(pool, &comment_saved_form).await?; let num_deleted = Comment::delete(pool, inserted_comment.id).await?; Comment::delete(pool, inserted_child_comment.id).await?; Post::delete(pool, inserted_post.id).await?; @@ -400,8 +342,6 @@ mod tests { assert_eq!(expected_comment, read_comment); assert_eq!(expected_comment, updated_comment); - assert_eq!(expected_comment_like, inserted_comment_like); - assert_eq!(expected_comment_saved, inserted_comment_saved); assert_eq!( format!("0.{}.{}", expected_comment.id, inserted_child_comment.id), inserted_child_comment.path.0, @@ -415,7 +355,7 @@ mod tests { #[tokio::test] #[serial] - async fn test_aggregates() -> Result<(), Error> { + async fn test_aggregates() -> LemmyResult<()> { let pool = &build_db_pool_for_tests(); let pool = &mut pool.into(); @@ -459,13 +399,9 @@ mod tests { let _inserted_child_comment = Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?; - let comment_like = CommentLikeForm { - comment_id: inserted_comment.id, - person_id: inserted_person.id, - score: 1, - }; + let comment_like = CommentLikeForm::new(inserted_person.id, inserted_comment.id, 1); - CommentLike::like(pool, &comment_like).await?; + CommentActions::like(pool, &comment_like).await?; let comment_aggs_before_delete = Comment::read(pool, inserted_comment.id).await?; @@ -474,13 +410,9 @@ mod tests { assert_eq!(0, comment_aggs_before_delete.downvotes); // Add a post dislike from the other person - let comment_dislike = CommentLikeForm { - comment_id: inserted_comment.id, - person_id: another_inserted_person.id, - score: -1, - }; + let comment_dislike = CommentLikeForm::new(another_inserted_person.id, inserted_comment.id, -1); - CommentLike::like(pool, &comment_dislike).await?; + CommentActions::like(pool, &comment_dislike).await?; let comment_aggs_after_dislike = Comment::read(pool, inserted_comment.id).await?; @@ -489,7 +421,7 @@ mod tests { assert_eq!(1, comment_aggs_after_dislike.downvotes); // Remove the first comment like - CommentLike::remove(pool, inserted_person.id, inserted_comment.id).await?; + CommentActions::remove_like(pool, inserted_person.id, inserted_comment.id).await?; let after_like_remove = Comment::read(pool, inserted_comment.id).await?; assert_eq!(-1, after_like_remove.score); assert_eq!(0, after_like_remove.upvotes); diff --git a/crates/db_schema/src/impls/comment_report.rs b/crates/db_schema/src/impls/comment_report.rs index f8ff7d6c9..6d544825c 100644 --- a/crates/db_schema/src/impls/comment_report.rs +++ b/crates/db_schema/src/impls/comment_report.rs @@ -8,13 +8,12 @@ use crate::{ use chrono::Utc; use diesel::{ dsl::{insert_into, update}, - result::Error, BoolExpressionMethods, ExpressionMethods, QueryDsl, }; use diesel_async::RunQueryDsl; -use lemmy_utils::error::LemmyResult; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; impl Reportable for CommentReport { type Form = CommentReportForm; @@ -24,15 +23,13 @@ impl Reportable for CommentReport { /// /// * `conn` - the postgres connection /// * `comment_report_form` - the filled CommentReportForm to insert - async fn report( - pool: &mut DbPool<'_>, - comment_report_form: &CommentReportForm, - ) -> Result { + async fn report(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult { let conn = &mut get_conn(pool).await?; insert_into(comment_report::table) - .values(comment_report_form) + .values(form) .get_result::(conn) .await + .with_lemmy_type(LemmyErrorType::CouldntCreateReport) } /// resolve a comment report @@ -44,7 +41,7 @@ impl Reportable for CommentReport { pool: &mut DbPool<'_>, report_id_: Self::IdType, by_resolver_id: PersonId, - ) -> Result { + ) -> LemmyResult { let conn = &mut get_conn(pool).await?; update(comment_report::table.find(report_id_)) .set(( @@ -54,6 +51,7 @@ impl Reportable for CommentReport { )) .execute(conn) .await + .with_lemmy_type(LemmyErrorType::CouldntResolveReport) } async fn resolve_apub( @@ -63,29 +61,28 @@ impl Reportable for CommentReport { resolver_id: PersonId, ) -> LemmyResult { let conn = &mut get_conn(pool).await?; - Ok( - update( - comment_report::table.filter( - comment_report::comment_id - .eq(object_id) - .and(comment_report::creator_id.eq(report_creator_id)), - ), - ) - .set(( - comment_report::resolved.eq(true), - comment_report::resolver_id.eq(resolver_id), - comment_report::updated.eq(Utc::now()), - )) - .execute(conn) - .await?, + update( + comment_report::table.filter( + comment_report::comment_id + .eq(object_id) + .and(comment_report::creator_id.eq(report_creator_id)), + ), ) + .set(( + comment_report::resolved.eq(true), + comment_report::resolver_id.eq(resolver_id), + comment_report::updated.eq(Utc::now()), + )) + .execute(conn) + .await + .with_lemmy_type(LemmyErrorType::CouldntResolveReport) } async fn resolve_all_for_object( pool: &mut DbPool<'_>, comment_id_: CommentId, by_resolver_id: PersonId, - ) -> Result { + ) -> LemmyResult { let conn = &mut get_conn(pool).await?; update(comment_report::table.filter(comment_report::comment_id.eq(comment_id_))) .set(( @@ -95,6 +92,7 @@ impl Reportable for CommentReport { )) .execute(conn) .await + .with_lemmy_type(LemmyErrorType::CouldntResolveReport) } /// unresolve a comment report @@ -106,7 +104,7 @@ impl Reportable for CommentReport { pool: &mut DbPool<'_>, report_id_: Self::IdType, by_resolver_id: PersonId, - ) -> Result { + ) -> LemmyResult { let conn = &mut get_conn(pool).await?; update(comment_report::table.find(report_id_)) .set(( @@ -116,5 +114,6 @@ impl Reportable for CommentReport { )) .execute(conn) .await + .with_lemmy_type(LemmyErrorType::CouldntResolveReport) } } diff --git a/crates/db_schema/src/impls/community.rs b/crates/db_schema/src/impls/community.rs index f6098fccc..5d31690ad 100644 --- a/crates/db_schema/src/impls/community.rs +++ b/crates/db_schema/src/impls/community.rs @@ -6,49 +6,42 @@ use crate::{ actor_language::CommunityLanguage, community::{ Community, - CommunityFollower, + CommunityActions, + CommunityBlockForm, CommunityFollowerForm, CommunityFollowerState, CommunityInsertForm, - CommunityModerator, CommunityModeratorForm, - CommunityPersonBan, CommunityPersonBanForm, CommunityUpdateForm, }, post::Post, }, - traits::{ApubActor, Bannable, Crud, Followable, Joinable}, + traits::{ApubActor, Bannable, Blockable, Crud, Followable, Joinable}, utils::{ functions::{coalesce, coalesce_2_nullable, lower, random_smallint}, get_conn, - now, uplete, DbPool, }, CommunityVisibility, ListingType, - SubscribedType, }; use chrono::{DateTime, Utc}; use diesel::{ - deserialize, dsl::{exists, insert_into, not}, expression::SelectableHelper, - pg::Pg, result::Error, select, - sql_types, update, BoolExpressionMethods, ExpressionMethods, NullableExpressionMethods, QueryDsl, - Queryable, }; use diesel_async::RunQueryDsl; use lemmy_utils::{ - error::{LemmyErrorType, LemmyResult}, + error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, settings::structs::Settings, }; use url::Url; @@ -85,42 +78,31 @@ impl Crud for Community { } } -impl Joinable for CommunityModerator { +impl Joinable for CommunityActions { type Form = CommunityModeratorForm; - async fn join( - pool: &mut DbPool<'_>, - community_moderator_form: &CommunityModeratorForm, - ) -> Result { + async fn join(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult { let conn = &mut get_conn(pool).await?; - let community_moderator_form = ( - community_moderator_form, - community_actions::became_moderator.eq(now().nullable()), - ); insert_into(community_actions::table) - .values(community_moderator_form) + .values(form) .on_conflict(( community_actions::person_id, community_actions::community_id, )) .do_update() - .set(community_moderator_form) + .set(form) .returning(Self::as_select()) .get_result::(conn) .await + .with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists) } - async fn leave( - pool: &mut DbPool<'_>, - community_moderator_form: &CommunityModeratorForm, - ) -> Result { + async fn leave(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult { let conn = &mut get_conn(pool).await?; - uplete::new(community_actions::table.find(( - community_moderator_form.person_id, - community_moderator_form.community_id, - ))) - .set_null(community_actions::became_moderator) - .get_result(conn) - .await + uplete::new(community_actions::table.find((form.person_id, form.community_id))) + .set_null(community_actions::became_moderator) + .get_result(conn) + .await + .with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists) } } @@ -295,8 +277,8 @@ impl Community { } } -impl CommunityModerator { - pub async fn delete_for_community( +impl CommunityActions { + pub async fn delete_mods_for_community( pool: &mut DbPool<'_>, for_community_id: CommunityId, ) -> Result { @@ -310,7 +292,7 @@ impl CommunityModerator { .await } - pub async fn leave_all_communities( + pub async fn leave_mod_team_for_all_communities( pool: &mut DbPool<'_>, for_person_id: PersonId, ) -> Result { @@ -365,49 +347,7 @@ impl CommunityModerator { Err(LemmyErrorType::NotHigherMod)? } } -} -impl Bannable for CommunityPersonBan { - type Form = CommunityPersonBanForm; - async fn ban( - pool: &mut DbPool<'_>, - community_person_ban_form: &CommunityPersonBanForm, - ) -> Result { - let conn = &mut get_conn(pool).await?; - let community_person_ban_form = ( - community_person_ban_form, - community_actions::received_ban.eq(now().nullable()), - ); - insert_into(community_actions::table) - .values(community_person_ban_form) - .on_conflict(( - community_actions::community_id, - community_actions::person_id, - )) - .do_update() - .set(community_person_ban_form) - .returning(Self::as_select()) - .get_result::(conn) - .await - } - - async fn unban( - pool: &mut DbPool<'_>, - community_person_ban_form: &CommunityPersonBanForm, - ) -> Result { - let conn = &mut get_conn(pool).await?; - uplete::new(community_actions::table.find(( - community_person_ban_form.person_id, - community_person_ban_form.community_id, - ))) - .set_null(community_actions::received_ban) - .set_null(community_actions::ban_expires) - .get_result(conn) - .await - } -} - -impl CommunityFollower { /// Check if a remote instance has any followers on local instance. For this it is enough to check /// if any follow relation is stored. Dont use this for local community. pub async fn check_has_local_followers( @@ -425,7 +365,7 @@ impl CommunityFollower { .ok_or(LemmyErrorType::CommunityHasNoFollowers.into()) } - pub async fn approve( + pub async fn approve_follower( pool: &mut DbPool<'_>, community_id: CommunityId, follower_id: PersonId, @@ -446,33 +386,10 @@ impl CommunityFollower { } } -// TODO -// I'd really like to have these on the impl, but unfortunately they have to be top level, -// according to https://diesel.rs/guides/composing-applications.html -#[diesel::dsl::auto_type] -pub fn community_follower_select_subscribed_type() -> _ { - community_actions::follow_state.nullable() -} - -impl Queryable, Pg> - for SubscribedType -{ - type Row = Option; - fn build(row: Self::Row) -> deserialize::Result { - Ok(match row { - Some(CommunityFollowerState::Pending) => SubscribedType::Pending, - Some(CommunityFollowerState::Accepted) => SubscribedType::Subscribed, - Some(CommunityFollowerState::ApprovalRequired) => SubscribedType::ApprovalRequired, - None => SubscribedType::NotSubscribed, - }) - } -} - -impl Followable for CommunityFollower { - type Form = CommunityFollowerForm; - async fn follow(pool: &mut DbPool<'_>, form: &CommunityFollowerForm) -> Result { +impl Bannable for CommunityActions { + type Form = CommunityPersonBanForm; + async fn ban(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult { let conn = &mut get_conn(pool).await?; - let form = (form, community_actions::followed.eq(now().nullable())); insert_into(community_actions::table) .values(form) .on_conflict(( @@ -484,12 +401,44 @@ impl Followable for CommunityFollower { .returning(Self::as_select()) .get_result::(conn) .await + .with_lemmy_type(LemmyErrorType::CommunityUserAlreadyBanned) + } + + async fn unban(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult { + let conn = &mut get_conn(pool).await?; + uplete::new(community_actions::table.find((form.person_id, form.community_id))) + .set_null(community_actions::received_ban) + .set_null(community_actions::ban_expires) + .get_result(conn) + .await + .with_lemmy_type(LemmyErrorType::CommunityUserAlreadyBanned) + } +} + +impl Followable for CommunityActions { + type Form = CommunityFollowerForm; + type IdType = CommunityId; + + async fn follow(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult { + let conn = &mut get_conn(pool).await?; + insert_into(community_actions::table) + .values(form) + .on_conflict(( + community_actions::community_id, + community_actions::person_id, + )) + .do_update() + .set(form) + .returning(Self::as_select()) + .get_result::(conn) + .await + .with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists) } async fn follow_accepted( pool: &mut DbPool<'_>, community_id: CommunityId, person_id: PersonId, - ) -> Result { + ) -> LemmyResult { let conn = &mut get_conn(pool).await?; let find_action = community_actions::table .find((person_id, community_id)) @@ -499,18 +448,92 @@ impl Followable for CommunityFollower { .returning(Self::as_select()) .get_result::(conn) .await + .with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists) } + async fn unfollow( pool: &mut DbPool<'_>, - form: &CommunityFollowerForm, - ) -> Result { + person_id: PersonId, + community_id: Self::IdType, + ) -> LemmyResult { let conn = &mut get_conn(pool).await?; - uplete::new(community_actions::table.find((form.person_id, form.community_id))) + uplete::new(community_actions::table.find((person_id, community_id))) .set_null(community_actions::followed) .set_null(community_actions::follow_state) .set_null(community_actions::follow_approver_id) .get_result(conn) .await + .with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists) + } +} + +impl Blockable for CommunityActions { + type Form = CommunityBlockForm; + type ObjectIdType = CommunityId; + type ObjectType = Community; + + async fn block(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult { + let conn = &mut get_conn(pool).await?; + insert_into(community_actions::table) + .values(form) + .on_conflict(( + community_actions::person_id, + community_actions::community_id, + )) + .do_update() + .set(form) + .returning(Self::as_select()) + .get_result::(conn) + .await + .with_lemmy_type(LemmyErrorType::CommunityBlockAlreadyExists) + } + async fn unblock( + pool: &mut DbPool<'_>, + community_block_form: &Self::Form, + ) -> LemmyResult { + let conn = &mut get_conn(pool).await?; + uplete::new(community_actions::table.find(( + community_block_form.person_id, + community_block_form.community_id, + ))) + .set_null(community_actions::blocked) + .get_result(conn) + .await + .with_lemmy_type(LemmyErrorType::CommunityBlockAlreadyExists) + } + + async fn read_block( + pool: &mut DbPool<'_>, + person_id: PersonId, + community_id: Self::ObjectIdType, + ) -> LemmyResult<()> { + let conn = &mut get_conn(pool).await?; + let find_action = community_actions::table + .find((person_id, community_id)) + .filter(community_actions::blocked.is_not_null()); + + select(not(exists(find_action))) + .get_result::(conn) + .await? + .then_some(()) + .ok_or(LemmyErrorType::CommunityIsBlocked.into()) + } + + async fn read_blocks_for_person( + pool: &mut DbPool<'_>, + person_id: PersonId, + ) -> Result, Error> { + let conn = &mut get_conn(pool).await?; + community_actions::table + .filter(community_actions::blocked.is_not_null()) + .inner_join(community::table) + .select(community::all_columns) + .filter(community_actions::person_id.eq(person_id)) + .filter(community::deleted.eq(false)) + .filter(community::removed.eq(false)) + .order_by(community_actions::blocked) + .load::(conn) + .await } } @@ -567,13 +590,11 @@ mod tests { comment::{Comment, CommentInsertForm}, community::{ Community, - CommunityFollower, + CommunityActions, CommunityFollowerForm, CommunityFollowerState, CommunityInsertForm, - CommunityModerator, CommunityModeratorForm, - CommunityPersonBan, CommunityPersonBanForm, CommunityUpdateForm, }, @@ -652,49 +673,35 @@ mod tests { interactions_month: 0, }; - let community_follower_form = CommunityFollowerForm { - community_id: inserted_community.id, - person_id: inserted_bobby.id, - state: Some(CommunityFollowerState::Accepted), - approver_id: None, - }; + let community_follower_form = CommunityFollowerForm::new( + inserted_community.id, + inserted_bobby.id, + CommunityFollowerState::Accepted, + ); let inserted_community_follower = - CommunityFollower::follow(pool, &community_follower_form).await?; + CommunityActions::follow(pool, &community_follower_form).await?; - let expected_community_follower = CommunityFollower { - community_id: inserted_community.id, - person_id: inserted_bobby.id, - state: CommunityFollowerState::Accepted, - published: inserted_community_follower.published, - approver_id: None, - }; + assert_eq!( + Some(CommunityFollowerState::Accepted), + inserted_community_follower.follow_state + ); - let bobby_moderator_form = CommunityModeratorForm { - community_id: inserted_community.id, - person_id: inserted_bobby.id, - }; + let bobby_moderator_form = + CommunityModeratorForm::new(inserted_community.id, inserted_bobby.id); - let inserted_bobby_moderator = CommunityModerator::join(pool, &bobby_moderator_form).await?; + let inserted_bobby_moderator = CommunityActions::join(pool, &bobby_moderator_form).await?; + assert!(inserted_bobby_moderator.became_moderator.is_some()); - let artemis_moderator_form = CommunityModeratorForm { - community_id: inserted_community.id, - person_id: inserted_artemis.id, - }; + let artemis_moderator_form = + CommunityModeratorForm::new(inserted_community.id, inserted_artemis.id); - let _inserted_artemis_moderator = - CommunityModerator::join(pool, &artemis_moderator_form).await?; - - let expected_community_moderator = CommunityModerator { - community_id: inserted_community.id, - person_id: inserted_bobby.id, - published: inserted_bobby_moderator.published, - }; + let _inserted_artemis_moderator = CommunityActions::join(pool, &artemis_moderator_form).await?; let moderator_person_ids = vec![inserted_bobby.id, inserted_artemis.id]; // Make sure bobby is marked as a higher mod than artemis, and vice versa - let bobby_higher_check = CommunityModerator::is_higher_mod_check( + let bobby_higher_check = CommunityActions::is_higher_mod_check( pool, inserted_community.id, inserted_bobby.id, @@ -714,7 +721,7 @@ mod tests { assert!(bobby_higher_check_2.is_ok()); // This should throw an error, since artemis was added later - let artemis_higher_check = CommunityModerator::is_higher_mod_check( + let artemis_higher_check = CommunityActions::is_higher_mod_check( pool, inserted_community.id, inserted_artemis.id, @@ -723,22 +730,14 @@ mod tests { .await; assert!(artemis_higher_check.is_err()); - let community_person_ban_form = CommunityPersonBanForm { - community_id: inserted_community.id, - person_id: inserted_bobby.id, - expires: None, - }; + let community_person_ban_form = + CommunityPersonBanForm::new(inserted_community.id, inserted_bobby.id); let inserted_community_person_ban = - CommunityPersonBan::ban(pool, &community_person_ban_form).await?; - - let expected_community_person_ban = CommunityPersonBan { - community_id: inserted_community.id, - person_id: inserted_bobby.id, - published: inserted_community_person_ban.published, - expires: None, - }; + CommunityActions::ban(pool, &community_person_ban_form).await?; + assert!(inserted_community_person_ban.received_ban.is_some()); + assert!(inserted_community_person_ban.ban_expires.is_none()); let read_community = Community::read(pool, inserted_community.id).await?; let update_community_form = CommunityUpdateForm { @@ -748,9 +747,14 @@ mod tests { let updated_community = Community::update(pool, inserted_community.id, &update_community_form).await?; - let ignored_community = CommunityFollower::unfollow(pool, &community_follower_form).await?; - let left_community = CommunityModerator::leave(pool, &bobby_moderator_form).await?; - let unban = CommunityPersonBan::unban(pool, &community_person_ban_form).await?; + let ignored_community = CommunityActions::unfollow( + pool, + community_follower_form.person_id, + community_follower_form.community_id, + ) + .await?; + let left_community = CommunityActions::leave(pool, &bobby_moderator_form).await?; + let unban = CommunityActions::unban(pool, &community_person_ban_form).await?; let num_deleted = Community::delete(pool, inserted_community.id).await?; Person::delete(pool, inserted_bobby.id).await?; Person::delete(pool, inserted_artemis.id).await?; @@ -758,9 +762,6 @@ mod tests { assert_eq!(expected_community, read_community); assert_eq!(expected_community, updated_community); - assert_eq!(expected_community_follower, inserted_community_follower); - assert_eq!(expected_community_moderator, inserted_bobby_moderator); - assert_eq!(expected_community_person_ban, inserted_community_person_ban); assert_eq!(uplete::Count::only_updated(1), ignored_community); assert_eq!(uplete::Count::only_updated(1), left_community); assert_eq!(uplete::Count::only_deleted(1), unban); @@ -802,32 +803,29 @@ mod tests { ); let another_inserted_community = Community::create(pool, &another_community).await?; - let first_person_follow = CommunityFollowerForm { - community_id: inserted_community.id, - person_id: inserted_person.id, - state: Some(CommunityFollowerState::Accepted), - approver_id: None, - }; + let first_person_follow = CommunityFollowerForm::new( + inserted_community.id, + inserted_person.id, + CommunityFollowerState::Accepted, + ); - CommunityFollower::follow(pool, &first_person_follow).await?; + CommunityActions::follow(pool, &first_person_follow).await?; - let second_person_follow = CommunityFollowerForm { - community_id: inserted_community.id, - person_id: another_inserted_person.id, - state: Some(CommunityFollowerState::Accepted), - approver_id: None, - }; + let second_person_follow = CommunityFollowerForm::new( + inserted_community.id, + another_inserted_person.id, + CommunityFollowerState::Accepted, + ); - CommunityFollower::follow(pool, &second_person_follow).await?; + CommunityActions::follow(pool, &second_person_follow).await?; - let another_community_follow = CommunityFollowerForm { - community_id: another_inserted_community.id, - person_id: inserted_person.id, - state: Some(CommunityFollowerState::Accepted), - approver_id: None, - }; + let another_community_follow = CommunityFollowerForm::new( + another_inserted_community.id, + inserted_person.id, + CommunityFollowerState::Accepted, + ); - CommunityFollower::follow(pool, &another_community_follow).await?; + CommunityActions::follow(pool, &another_community_follow).await?; let new_post = PostInsertForm::new( "A test post".into(), @@ -866,13 +864,18 @@ mod tests { assert_eq!(0, another_community_aggs.comments); // Unfollow test - CommunityFollower::unfollow(pool, &second_person_follow).await?; + CommunityActions::unfollow( + pool, + second_person_follow.person_id, + second_person_follow.community_id, + ) + .await?; let after_unfollow = Community::read(pool, inserted_community.id).await?; assert_eq!(1, after_unfollow.subscribers); assert_eq!(1, after_unfollow.subscribers_local); // Follow again just for the later tests - CommunityFollower::follow(pool, &second_person_follow).await?; + CommunityActions::follow(pool, &second_person_follow).await?; let after_follow_again = Community::read(pool, inserted_community.id).await?; assert_eq!(2, after_follow_again.subscribers); assert_eq!(2, after_follow_again.subscribers_local); diff --git a/crates/db_schema/src/impls/community_block.rs b/crates/db_schema/src/impls/community_block.rs deleted file mode 100644 index 67c84c9d2..000000000 --- a/crates/db_schema/src/impls/community_block.rs +++ /dev/null @@ -1,91 +0,0 @@ -use crate::{ - newtypes::{CommunityId, PersonId}, - schema::{community, community_actions}, - source::{ - community::Community, - community_block::{CommunityBlock, CommunityBlockForm}, - }, - traits::Blockable, - utils::{get_conn, now, uplete, DbPool}, -}; -use diesel::{ - dsl::{exists, insert_into, not}, - expression::SelectableHelper, - result::Error, - select, - ExpressionMethods, - NullableExpressionMethods, - QueryDsl, -}; -use diesel_async::RunQueryDsl; -use lemmy_utils::error::{LemmyErrorType, LemmyResult}; - -impl CommunityBlock { - pub async fn read( - pool: &mut DbPool<'_>, - for_person_id: PersonId, - for_community_id: CommunityId, - ) -> LemmyResult<()> { - let conn = &mut get_conn(pool).await?; - let find_action = community_actions::table - .find((for_person_id, for_community_id)) - .filter(community_actions::blocked.is_not_null()); - select(not(exists(find_action))) - .get_result::(conn) - .await? - .then_some(()) - .ok_or(LemmyErrorType::CommunityIsBlocked.into()) - } - - pub async fn for_person( - pool: &mut DbPool<'_>, - person_id: PersonId, - ) -> Result, Error> { - let conn = &mut get_conn(pool).await?; - community_actions::table - .filter(community_actions::blocked.is_not_null()) - .inner_join(community::table) - .select(community::all_columns) - .filter(community_actions::person_id.eq(person_id)) - .filter(community::deleted.eq(false)) - .filter(community::removed.eq(false)) - .order_by(community_actions::blocked) - .load::(conn) - .await - } -} - -impl Blockable for CommunityBlock { - type Form = CommunityBlockForm; - async fn block(pool: &mut DbPool<'_>, community_block_form: &Self::Form) -> Result { - let conn = &mut get_conn(pool).await?; - let community_block_form = ( - community_block_form, - community_actions::blocked.eq(now().nullable()), - ); - insert_into(community_actions::table) - .values(community_block_form) - .on_conflict(( - community_actions::person_id, - community_actions::community_id, - )) - .do_update() - .set(community_block_form) - .returning(Self::as_select()) - .get_result::(conn) - .await - } - async fn unblock( - pool: &mut DbPool<'_>, - community_block_form: &Self::Form, - ) -> Result { - let conn = &mut get_conn(pool).await?; - uplete::new(community_actions::table.find(( - community_block_form.person_id, - community_block_form.community_id, - ))) - .set_null(community_actions::blocked) - .get_result(conn) - .await - } -} diff --git a/crates/db_schema/src/impls/community_report.rs b/crates/db_schema/src/impls/community_report.rs index 780a30cf0..afc003185 100644 --- a/crates/db_schema/src/impls/community_report.rs +++ b/crates/db_schema/src/impls/community_report.rs @@ -8,13 +8,12 @@ use crate::{ use chrono::Utc; use diesel::{ dsl::{insert_into, update}, - result::Error, BoolExpressionMethods, ExpressionMethods, QueryDsl, }; use diesel_async::RunQueryDsl; -use lemmy_utils::error::LemmyResult; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; impl Reportable for CommunityReport { type Form = CommunityReportForm; @@ -24,15 +23,13 @@ impl Reportable for CommunityReport { /// /// * `conn` - the postgres connection /// * `community_report_form` - the filled CommunityReportForm to insert - async fn report( - pool: &mut DbPool<'_>, - community_report_form: &CommunityReportForm, - ) -> Result { + async fn report(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult { let conn = &mut get_conn(pool).await?; insert_into(community_report::table) - .values(community_report_form) + .values(form) .get_result::(conn) .await + .with_lemmy_type(LemmyErrorType::CouldntCreateReport) } /// resolve a community report @@ -44,7 +41,7 @@ impl Reportable for CommunityReport { pool: &mut DbPool<'_>, report_id_: Self::IdType, by_resolver_id: PersonId, - ) -> Result { + ) -> LemmyResult { let conn = &mut get_conn(pool).await?; update(community_report::table.find(report_id_)) .set(( @@ -54,6 +51,7 @@ impl Reportable for CommunityReport { )) .execute(conn) .await + .with_lemmy_type(LemmyErrorType::CouldntResolveReport) } async fn resolve_apub( @@ -63,29 +61,28 @@ impl Reportable for CommunityReport { resolver_id: PersonId, ) -> LemmyResult { let conn = &mut get_conn(pool).await?; - Ok( - update( - community_report::table.filter( - community_report::community_id - .eq(object_id) - .and(community_report::creator_id.eq(report_creator_id)), - ), - ) - .set(( - community_report::resolved.eq(true), - community_report::resolver_id.eq(resolver_id), - community_report::updated.eq(Utc::now()), - )) - .execute(conn) - .await?, + update( + community_report::table.filter( + community_report::community_id + .eq(object_id) + .and(community_report::creator_id.eq(report_creator_id)), + ), ) + .set(( + community_report::resolved.eq(true), + community_report::resolver_id.eq(resolver_id), + community_report::updated.eq(Utc::now()), + )) + .execute(conn) + .await + .with_lemmy_type(LemmyErrorType::CouldntResolveReport) } async fn resolve_all_for_object( pool: &mut DbPool<'_>, - community_id_: CommunityId, + community_id_: Self::ObjectIdType, by_resolver_id: PersonId, - ) -> Result { + ) -> LemmyResult { let conn = &mut get_conn(pool).await?; update(community_report::table.filter(community_report::community_id.eq(community_id_))) .set(( @@ -95,6 +92,7 @@ impl Reportable for CommunityReport { )) .execute(conn) .await + .with_lemmy_type(LemmyErrorType::CouldntResolveReport) } /// unresolve a community report @@ -106,7 +104,7 @@ impl Reportable for CommunityReport { pool: &mut DbPool<'_>, report_id_: Self::IdType, by_resolver_id: PersonId, - ) -> Result { + ) -> LemmyResult { let conn = &mut get_conn(pool).await?; update(community_report::table.find(report_id_)) .set(( @@ -116,5 +114,6 @@ impl Reportable for CommunityReport { )) .execute(conn) .await + .with_lemmy_type(LemmyErrorType::CouldntResolveReport) } } diff --git a/crates/db_schema/src/impls/instance.rs b/crates/db_schema/src/impls/instance.rs index d638786fe..c6928b4b8 100644 --- a/crates/db_schema/src/impls/instance.rs +++ b/crates/db_schema/src/impls/instance.rs @@ -1,28 +1,31 @@ use crate::{ diesel::dsl::IntervalDsl, - newtypes::InstanceId, + newtypes::{InstanceId, PersonId}, schema::{ federation_allowlist, federation_blocklist, federation_queue_state, instance, + instance_actions, local_site, site, }, source::{ federation_queue_state::FederationQueueState, - instance::{Instance, InstanceForm}, + instance::{Instance, InstanceActions, InstanceBlockForm, InstanceForm}, }, + traits::Blockable, utils::{ functions::{coalesce, lower}, get_conn, now, + uplete, DbPool, }, }; use chrono::Utc; use diesel::{ - dsl::{count_star, insert_into}, + dsl::{count_star, exists, insert_into, not, select}, result::Error, ExpressionMethods, NullableExpressionMethods, @@ -31,6 +34,7 @@ use diesel::{ SelectableHelper, }; use diesel_async::RunQueryDsl; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; impl Instance { /// Attempt to read Instance column for the given domain. If it doesn't exist, insert a new one. @@ -186,3 +190,62 @@ impl Instance { .await } } + +impl Blockable for InstanceActions { + type Form = InstanceBlockForm; + type ObjectIdType = InstanceId; + type ObjectType = Instance; + + async fn block(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult { + let conn = &mut get_conn(pool).await?; + insert_into(instance_actions::table) + .values(form) + .on_conflict((instance_actions::person_id, instance_actions::instance_id)) + .do_update() + .set(form) + .returning(Self::as_select()) + .get_result::(conn) + .await + .with_lemmy_type(LemmyErrorType::InstanceBlockAlreadyExists) + } + + async fn unblock(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult { + let conn = &mut get_conn(pool).await?; + uplete::new(instance_actions::table.find((form.person_id, form.instance_id))) + .set_null(instance_actions::blocked) + .get_result(conn) + .await + .with_lemmy_type(LemmyErrorType::InstanceBlockAlreadyExists) + } + + async fn read_block( + pool: &mut DbPool<'_>, + person_id: PersonId, + instance_id: Self::ObjectIdType, + ) -> LemmyResult<()> { + let conn = &mut get_conn(pool).await?; + let find_action = instance_actions::table + .find((person_id, instance_id)) + .filter(instance_actions::blocked.is_not_null()); + select(not(exists(find_action))) + .get_result::(conn) + .await? + .then_some(()) + .ok_or(LemmyErrorType::InstanceIsBlocked.into()) + } + + async fn read_blocks_for_person( + pool: &mut DbPool<'_>, + person_id: PersonId, + ) -> Result, Error> { + let conn = &mut get_conn(pool).await?; + instance_actions::table + .filter(instance_actions::blocked.is_not_null()) + .inner_join(instance::table) + .select(instance::all_columns) + .filter(instance_actions::person_id.eq(person_id)) + .order_by(instance_actions::blocked) + .load::(conn) + .await + } +} diff --git a/crates/db_schema/src/impls/instance_block.rs b/crates/db_schema/src/impls/instance_block.rs deleted file mode 100644 index 62ad7d297..000000000 --- a/crates/db_schema/src/impls/instance_block.rs +++ /dev/null @@ -1,86 +0,0 @@ -use crate::{ - newtypes::{InstanceId, PersonId}, - schema::{instance, instance_actions}, - source::{ - instance::Instance, - instance_block::{InstanceBlock, InstanceBlockForm}, - }, - traits::Blockable, - utils::{get_conn, now, uplete, DbPool}, -}; -use diesel::{ - dsl::{exists, insert_into, not}, - expression::SelectableHelper, - result::Error, - select, - ExpressionMethods, - NullableExpressionMethods, - QueryDsl, -}; -use diesel_async::RunQueryDsl; -use lemmy_utils::error::{LemmyErrorType, LemmyResult}; - -impl InstanceBlock { - pub async fn read( - pool: &mut DbPool<'_>, - for_person_id: PersonId, - for_instance_id: InstanceId, - ) -> LemmyResult<()> { - let conn = &mut get_conn(pool).await?; - let find_action = instance_actions::table - .find((for_person_id, for_instance_id)) - .filter(instance_actions::blocked.is_not_null()); - select(not(exists(find_action))) - .get_result::(conn) - .await? - .then_some(()) - .ok_or(LemmyErrorType::InstanceIsBlocked.into()) - } - - pub async fn for_person( - pool: &mut DbPool<'_>, - person_id: PersonId, - ) -> Result, Error> { - let conn = &mut get_conn(pool).await?; - instance_actions::table - .filter(instance_actions::blocked.is_not_null()) - .inner_join(instance::table) - .select(instance::all_columns) - .filter(instance_actions::person_id.eq(person_id)) - .order_by(instance_actions::blocked) - .load::(conn) - .await - } -} - -impl Blockable for InstanceBlock { - type Form = InstanceBlockForm; - async fn block(pool: &mut DbPool<'_>, instance_block_form: &Self::Form) -> Result { - let conn = &mut get_conn(pool).await?; - let instance_block_form = ( - instance_block_form, - instance_actions::blocked.eq(now().nullable()), - ); - insert_into(instance_actions::table) - .values(instance_block_form) - .on_conflict((instance_actions::person_id, instance_actions::instance_id)) - .do_update() - .set(instance_block_form) - .returning(Self::as_select()) - .get_result::(conn) - .await - } - async fn unblock( - pool: &mut DbPool<'_>, - instance_block_form: &Self::Form, - ) -> Result { - let conn = &mut get_conn(pool).await?; - uplete::new(instance_actions::table.find(( - instance_block_form.person_id, - instance_block_form.instance_id, - ))) - .set_null(instance_actions::blocked) - .get_result(conn) - .await - } -} diff --git a/crates/db_schema/src/impls/local_user.rs b/crates/db_schema/src/impls/local_user.rs index e643427c1..864a5e202 100644 --- a/crates/db_schema/src/impls/local_user.rs +++ b/crates/db_schema/src/impls/local_user.rs @@ -1,5 +1,4 @@ use crate::{ - aliases::creator_community_actions, newtypes::{CommunityId, DbUrl, LanguageId, LocalUserId, PersonId}, schema::{community, community_actions, local_user, person, registration_application}, source::{ @@ -19,12 +18,9 @@ use bcrypt::{hash, DEFAULT_COST}; use diesel::{ dsl::{insert_into, not, IntervalDsl}, result::Error, - BoolExpressionMethods, CombineDsl, ExpressionMethods, JoinOnDsl, - NullableExpressionMethods, - PgExpressionMethods, QueryDsl, }; use diesel_async::RunQueryDsl; @@ -301,29 +297,6 @@ impl LocalUser { } } -// TODO -// I'd really like to have these on the impl, but unfortunately they have to be top level, -// according to https://diesel.rs/guides/composing-applications.html -/// Checks to see if you can mod an item. -/// -/// Caveat: Since admin status isn't federated or ordered, it can't know whether -/// item creator is a federated admin, or a higher admin. -/// The back-end will reject an action for admin that is higher via -/// LocalUser::is_higher_mod_or_admin_check -#[diesel::dsl::auto_type] -pub fn local_user_can_mod() -> _ { - let am_admin = local_user::admin.nullable(); - let creator_became_moderator = creator_community_actions - .field(community_actions::became_moderator) - .nullable(); - - let am_higher_mod = community_actions::became_moderator - .nullable() - .le(creator_became_moderator); - - am_admin.or(am_higher_mod).is_not_distinct_from(true) -} - /// Adds some helper functions for an optional LocalUser pub trait LocalUserOptionHelper { fn person_id(&self) -> Option; diff --git a/crates/db_schema/src/impls/mod.rs b/crates/db_schema/src/impls/mod.rs index d384afdd0..e44730995 100644 --- a/crates/db_schema/src/impls/mod.rs +++ b/crates/db_schema/src/impls/mod.rs @@ -5,7 +5,6 @@ pub mod comment; pub mod comment_reply; pub mod comment_report; pub mod community; -pub mod community_block; pub mod community_report; pub mod custom_emoji; pub mod email_verification; @@ -14,7 +13,6 @@ pub mod federation_blocklist; pub mod federation_queue_state; pub mod images; pub mod instance; -pub mod instance_block; pub mod language; pub mod local_site; pub mod local_site_rate_limit; @@ -26,11 +24,9 @@ pub mod oauth_account; pub mod oauth_provider; pub mod password_reset_request; pub mod person; -pub mod person_block; pub mod person_comment_mention; pub mod person_post_mention; pub mod post; -pub mod post_actions; pub mod post_report; pub mod private_message; pub mod private_message_report; diff --git a/crates/db_schema/src/impls/person.rs b/crates/db_schema/src/impls/person.rs index 9ea082a1d..7218685d1 100644 --- a/crates/db_schema/src/impls/person.rs +++ b/crates/db_schema/src/impls/person.rs @@ -4,28 +4,28 @@ use crate::{ schema::{comment, community, instance, local_user, person, person_actions, post}, source::person::{ Person, - PersonFollower, + PersonActions, + PersonBlockForm, PersonFollowerForm, PersonInsertForm, PersonUpdateForm, }, - traits::{ApubActor, Crud, Followable}, - utils::{functions::lower, get_conn, now, uplete, DbPool}, + traits::{ApubActor, Blockable, Crud, Followable}, + utils::{functions::lower, get_conn, uplete, DbPool}, }; use chrono::Utc; use diesel::{ - dsl::{insert_into, not}, + dsl::{exists, insert_into, not, select}, expression::SelectableHelper, result::Error, CombineDsl, ExpressionMethods, JoinOnDsl, - NullableExpressionMethods, QueryDsl, }; use diesel_async::RunQueryDsl; use lemmy_utils::{ - error::{LemmyErrorType, LemmyResult}, + error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, settings::structs::Settings, }; use url::Url; @@ -202,11 +202,12 @@ impl ApubActor for Person { } } -impl Followable for PersonFollower { +impl Followable for PersonActions { type Form = PersonFollowerForm; - async fn follow(pool: &mut DbPool<'_>, form: &PersonFollowerForm) -> Result { + type IdType = PersonId; + + async fn follow(pool: &mut DbPool<'_>, form: &PersonFollowerForm) -> LemmyResult { let conn = &mut get_conn(pool).await?; - let form = (form, person_actions::followed.eq(now().nullable())); insert_into(person_actions::table) .values(form) .on_conflict((person_actions::person_id, person_actions::target_id)) @@ -215,27 +216,96 @@ impl Followable for PersonFollower { .returning(Self::as_select()) .get_result::(conn) .await + .with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists) } /// Currently no user following - async fn follow_accepted(_: &mut DbPool<'_>, _: CommunityId, _: PersonId) -> Result { - Err(Error::NotFound) + async fn follow_accepted(_: &mut DbPool<'_>, _: CommunityId, _: PersonId) -> LemmyResult { + Err(LemmyErrorType::NotFound.into()) } async fn unfollow( pool: &mut DbPool<'_>, - form: &PersonFollowerForm, - ) -> Result { + person_id: PersonId, + target_id: Self::IdType, + ) -> LemmyResult { let conn = &mut get_conn(pool).await?; - uplete::new(person_actions::table.find((form.follower_id, form.person_id))) + uplete::new(person_actions::table.find((person_id, target_id))) .set_null(person_actions::followed) .set_null(person_actions::follow_pending) .get_result(conn) .await + .with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists) } } -impl PersonFollower { +impl Blockable for PersonActions { + type Form = PersonBlockForm; + type ObjectIdType = PersonId; + type ObjectType = Person; + + async fn block(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult { + let conn = &mut get_conn(pool).await?; + insert_into(person_actions::table) + .values(form) + .on_conflict((person_actions::person_id, person_actions::target_id)) + .do_update() + .set(form) + .returning(Self::as_select()) + .get_result::(conn) + .await + .with_lemmy_type(LemmyErrorType::PersonBlockAlreadyExists) + } + + async fn unblock(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult { + let conn = &mut get_conn(pool).await?; + uplete::new(person_actions::table.find((form.person_id, form.target_id))) + .set_null(person_actions::blocked) + .get_result(conn) + .await + .with_lemmy_type(LemmyErrorType::PersonBlockAlreadyExists) + } + + async fn read_block( + pool: &mut DbPool<'_>, + person_id: PersonId, + recipient_id: Self::ObjectIdType, + ) -> LemmyResult<()> { + let conn = &mut get_conn(pool).await?; + let find_action = person_actions::table + .find((person_id, recipient_id)) + .filter(person_actions::blocked.is_not_null()); + + select(not(exists(find_action))) + .get_result::(conn) + .await? + .then_some(()) + .ok_or(LemmyErrorType::PersonIsBlocked.into()) + } + + async fn read_blocks_for_person( + pool: &mut DbPool<'_>, + person_id: PersonId, + ) -> Result, Error> { + let conn = &mut get_conn(pool).await?; + let target_person_alias = diesel::alias!(person as person1); + + person_actions::table + .filter(person_actions::blocked.is_not_null()) + .inner_join(person::table.on(person_actions::person_id.eq(person::id))) + .inner_join( + target_person_alias.on(person_actions::target_id.eq(target_person_alias.field(person::id))), + ) + .select(target_person_alias.fields(person::all_columns)) + .filter(person_actions::person_id.eq(person_id)) + .filter(target_person_alias.field(person::deleted).eq(false)) + .order_by(person_actions::blocked) + .load::(conn) + .await + } +} + +impl PersonActions { pub async fn list_followers( pool: &mut DbPool<'_>, for_person_id: PersonId, @@ -256,16 +326,15 @@ mod tests { use crate::{ source::{ - comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm, CommentUpdateForm}, + comment::{Comment, CommentActions, CommentInsertForm, CommentLikeForm, CommentUpdateForm}, community::{Community, CommunityInsertForm}, instance::Instance, - person::{Person, PersonFollower, PersonFollowerForm, PersonInsertForm, PersonUpdateForm}, - post::{Post, PostInsertForm, PostLike, PostLikeForm}, + person::{Person, PersonActions, PersonFollowerForm, PersonInsertForm, PersonUpdateForm}, + post::{Post, PostActions, PostInsertForm, PostLikeForm}, }, traits::{Crud, Followable, Likeable}, utils::{build_db_pool_for_tests, uplete}, }; - use diesel::result::Error; use lemmy_utils::error::LemmyResult; use pretty_assertions::assert_eq; use serial_test::serial; @@ -340,20 +409,17 @@ mod tests { let person_form_2 = PersonInsertForm::test_form(inserted_instance.id, "michele"); let person_2 = Person::create(pool, &person_form_2).await?; - let follow_form = PersonFollowerForm { - person_id: person_1.id, - follower_id: person_2.id, - pending: false, - }; - let person_follower = PersonFollower::follow(pool, &follow_form).await?; - assert_eq!(person_1.id, person_follower.person_id); - assert_eq!(person_2.id, person_follower.follower_id); - assert!(!person_follower.pending); + let follow_form = PersonFollowerForm::new(person_1.id, person_2.id, false); + let person_follower = PersonActions::follow(pool, &follow_form).await?; + assert_eq!(person_1.id, person_follower.target_id); + assert_eq!(person_2.id, person_follower.person_id); + assert!(person_follower.follow_pending.is_some_and(|x| !x)); - let followers = PersonFollower::list_followers(pool, person_1.id).await?; + let followers = PersonActions::list_followers(pool, person_1.id).await?; assert_eq!(vec![person_2], followers); - let unfollow = PersonFollower::unfollow(pool, &follow_form).await?; + let unfollow = + PersonActions::unfollow(pool, follow_form.person_id, follow_form.target_id).await?; assert_eq!(uplete::Count::only_deleted(1), unfollow); Ok(()) @@ -361,7 +427,7 @@ mod tests { #[tokio::test] #[serial] - async fn test_aggregates() -> Result<(), Error> { + async fn test_aggregates() -> LemmyResult<()> { let pool = &build_db_pool_for_tests(); let pool = &mut pool.into(); @@ -392,7 +458,7 @@ mod tests { let inserted_post = Post::create(pool, &new_post).await?; let post_like = PostLikeForm::new(inserted_post.id, inserted_person.id, 1); - let _inserted_post_like = PostLike::like(pool, &post_like).await?; + let _inserted_post_like = PostActions::like(pool, &post_like).await?; let comment_form = CommentInsertForm::new( inserted_person.id, @@ -401,13 +467,9 @@ mod tests { ); let inserted_comment = Comment::create(pool, &comment_form, None).await?; - let mut comment_like = CommentLikeForm { - comment_id: inserted_comment.id, - person_id: inserted_person.id, - score: 1, - }; + let mut comment_like = CommentLikeForm::new(inserted_person.id, inserted_comment.id, 1); - let _inserted_comment_like = CommentLike::like(pool, &comment_like).await?; + let _inserted_comment_like = CommentActions::like(pool, &comment_like).await?; let child_comment_form = CommentInsertForm::new( inserted_person.id, @@ -417,13 +479,10 @@ mod tests { let inserted_child_comment = Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?; - let child_comment_like = CommentLikeForm { - comment_id: inserted_child_comment.id, - person_id: another_inserted_person.id, - score: 1, - }; + let child_comment_like = + CommentLikeForm::new(another_inserted_person.id, inserted_child_comment.id, 1); - let _inserted_child_comment_like = CommentLike::like(pool, &child_comment_like).await?; + let _inserted_child_comment_like = CommentActions::like(pool, &child_comment_like).await?; let person_aggregates_before_delete = Person::read(pool, inserted_person.id).await?; @@ -433,7 +492,7 @@ mod tests { assert_eq!(2, person_aggregates_before_delete.comment_score); // Remove a post like - PostLike::remove(pool, inserted_person.id, inserted_post.id).await?; + PostActions::remove_like(pool, inserted_person.id, inserted_post.id).await?; let after_post_like_remove = Person::read(pool, inserted_person.id).await?; assert_eq!(0, after_post_like_remove.post_score); @@ -474,7 +533,7 @@ mod tests { let _new_child_comment = Comment::create(pool, &child_comment_form, Some(&new_parent_comment.path)).await?; comment_like.comment_id = new_parent_comment.id; - CommentLike::like(pool, &comment_like).await?; + CommentActions::like(pool, &comment_like).await?; let after_comment_add = Person::read(pool, inserted_person.id).await?; assert_eq!(2, after_comment_add.comment_count); // TODO: fix person aggregate comment score calculation diff --git a/crates/db_schema/src/impls/person_block.rs b/crates/db_schema/src/impls/person_block.rs deleted file mode 100644 index db0b3c86f..000000000 --- a/crates/db_schema/src/impls/person_block.rs +++ /dev/null @@ -1,95 +0,0 @@ -use crate::{ - newtypes::PersonId, - schema::{person, person_actions}, - source::{ - person::Person, - person_block::{PersonBlock, PersonBlockForm}, - }, - traits::Blockable, - utils::{get_conn, now, uplete, DbPool}, -}; -use diesel::{ - dsl::{exists, insert_into, not}, - expression::SelectableHelper, - result::Error, - select, - ExpressionMethods, - JoinOnDsl, - NullableExpressionMethods, - QueryDsl, -}; -use diesel_async::RunQueryDsl; -use lemmy_utils::error::{LemmyErrorType, LemmyResult}; - -impl PersonBlock { - pub async fn read( - pool: &mut DbPool<'_>, - for_person_id: PersonId, - for_recipient_id: PersonId, - ) -> LemmyResult<()> { - let conn = &mut get_conn(pool).await?; - let find_action = person_actions::table - .find((for_person_id, for_recipient_id)) - .filter(person_actions::blocked.is_not_null()); - select(not(exists(find_action))) - .get_result::(conn) - .await? - .then_some(()) - .ok_or(LemmyErrorType::PersonIsBlocked.into()) - } - - pub async fn for_person( - pool: &mut DbPool<'_>, - person_id: PersonId, - ) -> Result, Error> { - let conn = &mut get_conn(pool).await?; - let target_person_alias = diesel::alias!(person as person1); - - person_actions::table - .filter(person_actions::blocked.is_not_null()) - .inner_join(person::table.on(person_actions::person_id.eq(person::id))) - .inner_join( - target_person_alias.on(person_actions::target_id.eq(target_person_alias.field(person::id))), - ) - .select(target_person_alias.fields(person::all_columns)) - .filter(person_actions::person_id.eq(person_id)) - .filter(target_person_alias.field(person::deleted).eq(false)) - .order_by(person_actions::blocked) - .load::(conn) - .await - } -} - -impl Blockable for PersonBlock { - type Form = PersonBlockForm; - async fn block( - pool: &mut DbPool<'_>, - person_block_form: &PersonBlockForm, - ) -> Result { - let conn = &mut get_conn(pool).await?; - let person_block_form = ( - person_block_form, - person_actions::blocked.eq(now().nullable()), - ); - insert_into(person_actions::table) - .values(person_block_form) - .on_conflict((person_actions::person_id, person_actions::target_id)) - .do_update() - .set(person_block_form) - .returning(Self::as_select()) - .get_result::(conn) - .await - } - async fn unblock( - pool: &mut DbPool<'_>, - person_block_form: &Self::Form, - ) -> Result { - let conn = &mut get_conn(pool).await?; - uplete::new( - person_actions::table.find((person_block_form.person_id, person_block_form.target_id)), - ) - .set_null(person_actions::blocked) - .get_result(conn) - .await - } -} diff --git a/crates/db_schema/src/impls/post.rs b/crates/db_schema/src/impls/post.rs index 1e4dac6de..9419b953a 100644 --- a/crates/db_schema/src/impls/post.rs +++ b/crates/db_schema/src/impls/post.rs @@ -3,19 +3,17 @@ use crate::{ schema::{community, person, post, post_actions}, source::post::{ Post, + PostActions, PostActionsCursor, - PostHide, PostHideForm, PostInsertForm, - PostLike, PostLikeForm, - PostRead, + PostReadCommentsForm, PostReadForm, - PostSaved, PostSavedForm, PostUpdateForm, }, - traits::{Crud, Likeable, Saveable}, + traits::{Crud, Hideable, Likeable, ReadComments, Readable, Saveable}, utils::{ functions::{coalesce, hot_rank, scaled_rank}, get_conn, @@ -323,95 +321,85 @@ impl Post { } } -impl Likeable for PostLike { +impl Likeable for PostActions { type Form = PostLikeForm; type IdType = PostId; - async fn like(pool: &mut DbPool<'_>, post_like_form: &PostLikeForm) -> Result { + + async fn like(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult { let conn = &mut get_conn(pool).await?; insert_into(post_actions::table) - .values(post_like_form) + .values(form) .on_conflict((post_actions::post_id, post_actions::person_id)) .do_update() - .set(post_like_form) + .set(form) .returning(Self::as_select()) .get_result::(conn) .await + .with_lemmy_type(LemmyErrorType::CouldntLikePost) } - async fn remove( + async fn remove_like( pool: &mut DbPool<'_>, person_id: PersonId, - post_id: PostId, - ) -> Result { + post_id: Self::IdType, + ) -> LemmyResult { let conn = &mut get_conn(pool).await?; uplete::new(post_actions::table.find((person_id, post_id))) .set_null(post_actions::like_score) .set_null(post_actions::liked) .get_result(conn) .await + .with_lemmy_type(LemmyErrorType::CouldntLikePost) } } -impl Saveable for PostSaved { +impl Saveable for PostActions { type Form = PostSavedForm; - async fn save(pool: &mut DbPool<'_>, post_saved_form: &PostSavedForm) -> Result { + async fn save(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult { let conn = &mut get_conn(pool).await?; insert_into(post_actions::table) - .values(post_saved_form) + .values(form) .on_conflict((post_actions::post_id, post_actions::person_id)) .do_update() - .set(post_saved_form) + .set(form) .returning(Self::as_select()) .get_result::(conn) .await + .with_lemmy_type(LemmyErrorType::CouldntSavePost) } - async fn unsave( - pool: &mut DbPool<'_>, - post_saved_form: &PostSavedForm, - ) -> Result { + async fn unsave(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult { let conn = &mut get_conn(pool).await?; - uplete::new(post_actions::table.find((post_saved_form.person_id, post_saved_form.post_id))) + uplete::new(post_actions::table.find((form.person_id, form.post_id))) .set_null(post_actions::saved) .get_result(conn) .await + .with_lemmy_type(LemmyErrorType::CouldntSavePost) } } -impl PostRead { - pub async fn mark_as_read( - pool: &mut DbPool<'_>, - post_read_form: &PostReadForm, - ) -> LemmyResult { - Self::mark_many_as_read(pool, &[post_read_form.post_id], post_read_form.person_id).await +impl Readable for PostActions { + type Form = PostReadForm; + + async fn mark_as_read(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult { + Self::mark_many_as_read(pool, &[form.clone()]).await } - pub async fn mark_as_unread( - pool: &mut DbPool<'_>, - post_read_form: &PostReadForm, - ) -> Result { + async fn mark_as_unread(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult { let conn = &mut get_conn(pool).await?; uplete::new( post_actions::table - .filter(post_actions::post_id.eq(post_read_form.post_id)) - .filter(post_actions::person_id.eq(post_read_form.person_id)), + .filter(post_actions::post_id.eq(form.post_id)) + .filter(post_actions::person_id.eq(form.person_id)), ) .set_null(post_actions::read) .get_result(conn) .await + .with_lemmy_type(LemmyErrorType::CouldntMarkPostAsRead) } - pub async fn mark_many_as_read( - pool: &mut DbPool<'_>, - post_ids: &[PostId], - person_id: PersonId, - ) -> LemmyResult { + async fn mark_many_as_read(pool: &mut DbPool<'_>, forms: &[Self::Form]) -> LemmyResult { let conn = &mut get_conn(pool).await?; - let forms = post_ids - .iter() - .map(|post_id| (PostReadForm::new(*post_id, person_id))) - .collect::>(); - insert_into(post_actions::table) .values(forms) .on_conflict((post_actions::person_id, post_actions::post_id)) @@ -423,39 +411,79 @@ impl PostRead { } } -impl PostHide { - pub async fn hide( - pool: &mut DbPool<'_>, - post_id: PostId, - person_id: PersonId, - ) -> Result { +impl Hideable for PostActions { + type Form = PostHideForm; + async fn hide(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult { let conn = &mut get_conn(pool).await?; - let form = &PostHideForm::new(post_id, person_id); insert_into(post_actions::table) .values(form) .on_conflict((post_actions::person_id, post_actions::post_id)) .do_update() .set(form) - .execute(conn) + .get_result::(conn) .await + .with_lemmy_type(LemmyErrorType::CouldntHidePost) } - pub async fn unhide( - pool: &mut DbPool<'_>, - post_id_: PostId, - person_id_: PersonId, - ) -> Result { + async fn unhide(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult { let conn = &mut get_conn(pool).await?; uplete::new( post_actions::table - .filter(post_actions::post_id.eq(post_id_)) - .filter(post_actions::person_id.eq(person_id_)), + .filter(post_actions::post_id.eq(form.post_id)) + .filter(post_actions::person_id.eq(form.person_id)), ) .set_null(post_actions::hidden) .get_result(conn) .await + .with_lemmy_type(LemmyErrorType::CouldntHidePost) + } +} + +impl ReadComments for PostActions { + type Form = PostReadCommentsForm; + type IdType = PostId; + + async fn update_read_comments(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult { + let conn = &mut get_conn(pool).await?; + + insert_into(post_actions::table) + .values(form) + .on_conflict((post_actions::person_id, post_actions::post_id)) + .do_update() + .set(form) + .get_result::(conn) + .await + .with_lemmy_type(LemmyErrorType::CouldntUpdateReadComments) + } + + async fn remove_read_comments( + pool: &mut DbPool<'_>, + person_id: PersonId, + post_id: Self::IdType, + ) -> LemmyResult { + let conn = &mut get_conn(pool).await?; + + uplete::new( + post_actions::table + .filter(post_actions::post_id.eq(post_id)) + .filter(post_actions::person_id.eq(person_id)), + ) + .set_null(post_actions::read_comments_amount) + .set_null(post_actions::read_comments) + .get_result(conn) + .await + .with_lemmy_type(LemmyErrorType::CouldntUpdateReadComments) + } +} + +impl PostActions { + pub fn build_many_read_forms(post_ids: &[PostId], person_id: PersonId) -> Vec { + post_ids + .iter() + .map(|post_id| (PostReadForm::new(*post_id, person_id))) + .collect::>() } } @@ -499,17 +527,15 @@ mod tests { person::{Person, PersonInsertForm}, post::{ Post, + PostActions, PostInsertForm, - PostLike, PostLikeForm, - PostRead, PostReadForm, - PostSaved, PostSavedForm, PostUpdateForm, }, }, - traits::{Crud, Likeable, Saveable}, + traits::{Crud, Likeable, Readable, Saveable}, utils::{build_db_pool_for_tests, uplete, RANK_DEFAULT}, }; use chrono::DateTime; @@ -603,31 +629,20 @@ mod tests { // Post Like let post_like_form = PostLikeForm::new(inserted_post.id, inserted_person.id, 1); - let inserted_post_like = PostLike::like(pool, &post_like_form).await?; - - let expected_post_like = PostLike { - post_id: inserted_post.id, - person_id: inserted_person.id, - published: inserted_post_like.published, - score: 1, - }; + let inserted_post_like = PostActions::like(pool, &post_like_form).await?; + assert_eq!(Some(1), inserted_post_like.like_score); // Post Save let post_saved_form = PostSavedForm::new(inserted_post.id, inserted_person.id); - let inserted_post_saved = PostSaved::save(pool, &post_saved_form).await?; - - let expected_post_saved = PostSaved { - post_id: inserted_post.id, - person_id: inserted_person.id, - published: inserted_post_saved.published, - }; + let inserted_post_saved = PostActions::save(pool, &post_saved_form).await?; + assert!(inserted_post_saved.saved.is_some()); // Mark 2 posts as read let post_read_form_1 = PostReadForm::new(inserted_post.id, inserted_person.id); - PostRead::mark_as_read(pool, &post_read_form_1).await?; + PostActions::mark_as_read(pool, &post_read_form_1).await?; let post_read_form_2 = PostReadForm::new(inserted_post2.id, inserted_person.id); - PostRead::mark_as_read(pool, &post_read_form_2).await?; + PostActions::mark_as_read(pool, &post_read_form_2).await?; let read_post = Post::read(pool, inserted_post.id).await?; @@ -641,17 +656,17 @@ mod tests { let scheduled_post_count = Post::user_scheduled_post_count(inserted_person.id, pool).await?; assert_eq!(1, scheduled_post_count); - let like_removed = PostLike::remove(pool, inserted_person.id, inserted_post.id).await?; + let like_removed = PostActions::remove_like(pool, inserted_person.id, inserted_post.id).await?; assert_eq!(uplete::Count::only_updated(1), like_removed); - let saved_removed = PostSaved::unsave(pool, &post_saved_form).await?; + let saved_removed = PostActions::unsave(pool, &post_saved_form).await?; assert_eq!(uplete::Count::only_updated(1), saved_removed); let read_remove_form_1 = PostReadForm::new(inserted_post.id, inserted_person.id); - let read_removed_1 = PostRead::mark_as_unread(pool, &read_remove_form_1).await?; + let read_removed_1 = PostActions::mark_as_unread(pool, &read_remove_form_1).await?; assert_eq!(uplete::Count::only_deleted(1), read_removed_1); let read_remove_form_2 = PostReadForm::new(inserted_post2.id, inserted_person.id); - let read_removed_2 = PostRead::mark_as_unread(pool, &read_remove_form_2).await?; + let read_removed_2 = PostActions::mark_as_unread(pool, &read_remove_form_2).await?; assert_eq!(uplete::Count::only_deleted(1), read_removed_2); let num_deleted = Post::delete(pool, inserted_post.id).await? @@ -665,15 +680,13 @@ mod tests { assert_eq!(expected_post, read_post); assert_eq!(expected_post, updated_post); - assert_eq!(expected_post_like, inserted_post_like); - assert_eq!(expected_post_saved, inserted_post_saved); Ok(()) } #[tokio::test] #[serial] - async fn test_aggregates() -> Result<(), Error> { + async fn test_aggregates() -> LemmyResult<()> { let pool = &build_db_pool_for_tests(); let pool = &mut pool.into(); @@ -719,7 +732,7 @@ mod tests { let post_like = PostLikeForm::new(inserted_post.id, inserted_person.id, 1); - PostLike::like(pool, &post_like).await?; + PostActions::like(pool, &post_like).await?; let post_aggs_before_delete = Post::read(pool, inserted_post.id).await?; @@ -731,7 +744,7 @@ mod tests { // Add a post dislike from the other person let post_dislike = PostLikeForm::new(inserted_post.id, another_inserted_person.id, -1); - PostLike::like(pool, &post_dislike).await?; + PostActions::like(pool, &post_dislike).await?; let post_aggs_after_dislike = Post::read(pool, inserted_post.id).await?; @@ -750,7 +763,7 @@ mod tests { assert_eq!(1, after_comment_delete.downvotes); // Remove the first post like - PostLike::remove(pool, inserted_person.id, inserted_post.id).await?; + PostActions::remove_like(pool, inserted_person.id, inserted_post.id).await?; let after_like_remove = Post::read(pool, inserted_post.id).await?; assert_eq!(0, after_like_remove.comments); assert_eq!(-1, after_like_remove.score); diff --git a/crates/db_schema/src/impls/post_actions.rs b/crates/db_schema/src/impls/post_actions.rs deleted file mode 100644 index ac3ef22e7..000000000 --- a/crates/db_schema/src/impls/post_actions.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::{ - diesel::OptionalExtension, - newtypes::{PersonId, PostId}, - schema::post_actions, - source::post_actions::{PostActions, PostActionsForm}, - utils::{get_conn, now, DbPool}, -}; -use diesel::{ - expression::SelectableHelper, - insert_into, - result::Error, - ExpressionMethods, - NullableExpressionMethods, - QueryDsl, -}; -use diesel_async::RunQueryDsl; - -impl PostActions { - pub async fn upsert(pool: &mut DbPool<'_>, form: &PostActionsForm) -> Result { - let conn = &mut get_conn(pool).await?; - let form = (form, post_actions::read_comments.eq(now().nullable())); - insert_into(post_actions::table) - .values(form) - .on_conflict((post_actions::person_id, post_actions::post_id)) - .do_update() - .set(form) - .returning(Self::as_select()) - .get_result::(conn) - .await - } - pub async fn read( - pool: &mut DbPool<'_>, - person_id_: PersonId, - post_id_: PostId, - ) -> Result, Error> { - let conn = &mut get_conn(pool).await?; - post_actions::table - .find((person_id_, post_id_)) - .filter(post_actions::read_comments.is_not_null()) - .select(Self::as_select()) - .first(conn) - .await - .optional() - } -} diff --git a/crates/db_schema/src/impls/post_report.rs b/crates/db_schema/src/impls/post_report.rs index bcc61691a..f48fa624f 100644 --- a/crates/db_schema/src/impls/post_report.rs +++ b/crates/db_schema/src/impls/post_report.rs @@ -1,5 +1,4 @@ use crate::{ - diesel::BoolExpressionMethods, newtypes::{PersonId, PostId, PostReportId}, schema::post_report, source::post_report::{PostReport, PostReportForm}, @@ -9,31 +8,32 @@ use crate::{ use chrono::Utc; use diesel::{ dsl::{insert_into, update}, - result::Error, + BoolExpressionMethods, ExpressionMethods, QueryDsl, }; use diesel_async::RunQueryDsl; -use lemmy_utils::error::LemmyResult; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; impl Reportable for PostReport { type Form = PostReportForm; type IdType = PostReportId; type ObjectIdType = PostId; - async fn report(pool: &mut DbPool<'_>, post_report_form: &PostReportForm) -> Result { + async fn report(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult { let conn = &mut get_conn(pool).await?; insert_into(post_report::table) - .values(post_report_form) + .values(form) .get_result::(conn) .await + .with_lemmy_type(LemmyErrorType::CouldntCreateReport) } async fn resolve( pool: &mut DbPool<'_>, report_id: Self::IdType, by_resolver_id: PersonId, - ) -> Result { + ) -> LemmyResult { let conn = &mut get_conn(pool).await?; update(post_report::table.find(report_id)) .set(( @@ -43,6 +43,7 @@ impl Reportable for PostReport { )) .execute(conn) .await + .with_lemmy_type(LemmyErrorType::CouldntResolveReport) } async fn resolve_apub( @@ -52,29 +53,28 @@ impl Reportable for PostReport { resolver_id: PersonId, ) -> LemmyResult { let conn = &mut get_conn(pool).await?; - Ok( - update( - post_report::table.filter( - post_report::post_id - .eq(object_id) - .and(post_report::creator_id.eq(report_creator_id)), - ), - ) - .set(( - post_report::resolved.eq(true), - post_report::resolver_id.eq(resolver_id), - post_report::updated.eq(Utc::now()), - )) - .execute(conn) - .await?, + update( + post_report::table.filter( + post_report::post_id + .eq(object_id) + .and(post_report::creator_id.eq(report_creator_id)), + ), ) + .set(( + post_report::resolved.eq(true), + post_report::resolver_id.eq(resolver_id), + post_report::updated.eq(Utc::now()), + )) + .execute(conn) + .await + .with_lemmy_type(LemmyErrorType::CouldntResolveReport) } async fn resolve_all_for_object( pool: &mut DbPool<'_>, post_id_: PostId, by_resolver_id: PersonId, - ) -> Result { + ) -> LemmyResult { let conn = &mut get_conn(pool).await?; update(post_report::table.filter(post_report::post_id.eq(post_id_))) .set(( @@ -84,13 +84,14 @@ impl Reportable for PostReport { )) .execute(conn) .await + .with_lemmy_type(LemmyErrorType::CouldntResolveReport) } async fn unresolve( pool: &mut DbPool<'_>, report_id: Self::IdType, by_resolver_id: PersonId, - ) -> Result { + ) -> LemmyResult { let conn = &mut get_conn(pool).await?; update(post_report::table.find(report_id)) .set(( @@ -100,6 +101,7 @@ impl Reportable for PostReport { )) .execute(conn) .await + .with_lemmy_type(LemmyErrorType::CouldntResolveReport) } } @@ -117,10 +119,9 @@ mod tests { traits::Crud, utils::build_db_pool_for_tests, }; - use diesel::result::Error; use serial_test::serial; - async fn init(pool: &mut DbPool<'_>) -> Result<(Person, PostReport), Error> { + async fn init(pool: &mut DbPool<'_>) -> LemmyResult<(Person, PostReport)> { let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?; let person_form = PersonInsertForm::test_form(inserted_instance.id, "jim"); let person = Person::create(pool, &person_form).await?; @@ -149,7 +150,7 @@ mod tests { #[tokio::test] #[serial] - async fn test_resolve_post_report() -> Result<(), Error> { + async fn test_resolve_post_report() -> LemmyResult<()> { let pool = &build_db_pool_for_tests(); let pool = &mut pool.into(); @@ -169,7 +170,7 @@ mod tests { #[tokio::test] #[serial] - async fn test_resolve_all_post_reports() -> Result<(), Error> { + async fn test_resolve_all_post_reports() -> LemmyResult<()> { let pool = &build_db_pool_for_tests(); let pool = &mut pool.into(); diff --git a/crates/db_schema/src/impls/private_message_report.rs b/crates/db_schema/src/impls/private_message_report.rs index 3366f2fbf..f0bfd3802 100644 --- a/crates/db_schema/src/impls/private_message_report.rs +++ b/crates/db_schema/src/impls/private_message_report.rs @@ -8,34 +8,31 @@ use crate::{ use chrono::Utc; use diesel::{ dsl::{insert_into, update}, - result::Error, ExpressionMethods, QueryDsl, }; use diesel_async::RunQueryDsl; -use lemmy_utils::error::{FederationError, LemmyResult}; +use lemmy_utils::error::{FederationError, LemmyErrorExt, LemmyErrorType, LemmyResult}; impl Reportable for PrivateMessageReport { type Form = PrivateMessageReportForm; type IdType = PrivateMessageReportId; type ObjectIdType = PrivateMessageId; - async fn report( - pool: &mut DbPool<'_>, - pm_report_form: &PrivateMessageReportForm, - ) -> Result { + async fn report(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult { let conn = &mut get_conn(pool).await?; insert_into(private_message_report) - .values(pm_report_form) + .values(form) .get_result::(conn) .await + .with_lemmy_type(LemmyErrorType::CouldntCreateReport) } async fn resolve( pool: &mut DbPool<'_>, report_id: Self::IdType, by_resolver_id: PersonId, - ) -> Result { + ) -> LemmyResult { let conn = &mut get_conn(pool).await?; update(private_message_report.find(report_id)) .set(( @@ -45,6 +42,7 @@ impl Reportable for PrivateMessageReport { )) .execute(conn) .await + .with_lemmy_type(LemmyErrorType::CouldntResolveReport) } async fn resolve_apub( _pool: &mut DbPool<'_>, @@ -60,15 +58,15 @@ impl Reportable for PrivateMessageReport { _pool: &mut DbPool<'_>, _pm_id_: PrivateMessageId, _by_resolver_id: PersonId, - ) -> Result { - Err(Error::NotFound) + ) -> LemmyResult { + Err(LemmyErrorType::NotFound.into()) } async fn unresolve( pool: &mut DbPool<'_>, report_id: Self::IdType, by_resolver_id: PersonId, - ) -> Result { + ) -> LemmyResult { let conn = &mut get_conn(pool).await?; update(private_message_report.find(report_id)) .set(( @@ -78,5 +76,6 @@ impl Reportable for PrivateMessageReport { )) .execute(conn) .await + .with_lemmy_type(LemmyErrorType::CouldntResolveReport) } } diff --git a/crates/db_schema/src/lib.rs b/crates/db_schema/src/lib.rs index e3eda379d..c7859c542 100644 --- a/crates/db_schema/src/lib.rs +++ b/crates/db_schema/src/lib.rs @@ -38,7 +38,11 @@ pub mod schema_setup; use serde::{Deserialize, Serialize}; use strum::{Display, EnumString}; #[cfg(feature = "full")] -use {diesel::query_source::AliasedField, schema::person, ts_rs::TS}; +use { + diesel::query_source::AliasedField, + schema::{community_actions, person}, + ts_rs::TS, +}; #[derive( EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default, Hash, @@ -178,17 +182,6 @@ pub enum SearchType { Users, } -#[derive(EnumString, Display, Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy, Hash)] -#[cfg_attr(feature = "full", derive(TS))] -#[cfg_attr(feature = "full", ts(export))] -/// A type / status for a community subscribe. -pub enum SubscribedType { - Subscribed, - NotSubscribed, - Pending, - ApprovalRequired, -} - #[derive(EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "full", derive(TS))] #[cfg_attr(feature = "full", ts(export))] @@ -277,7 +270,7 @@ pub enum CommunityVisibility { /// Public community, any local or federated user can interact. #[default] Public, - /// Community is hidden and doesn't appear in community list. Post from the community + /// Community is unlisted/hidden and doesn't appear in community list. Posts from the community /// are not shown in Local and All feeds, except for subscribed users. Unlisted, /// Unfederated community, only local users can interact (with or without login). @@ -330,7 +323,7 @@ macro_rules! assert_length { } #[cfg(feature = "full")] -/// A helper tuple for person alias columns +/// A helper tuple for person 1 alias columns pub type Person1AliasAllColumnsTuple = ( AliasedField, AliasedField, @@ -357,3 +350,46 @@ pub type Person1AliasAllColumnsTuple = ( AliasedField, AliasedField, ); + +#[cfg(feature = "full")] +/// A helper tuple for person 2 alias columns +pub type Person2AliasAllColumnsTuple = ( + AliasedField, + AliasedField, + AliasedField, + AliasedField, + AliasedField, + AliasedField, + AliasedField, + AliasedField, + AliasedField, + AliasedField, + AliasedField, + AliasedField, + AliasedField, + AliasedField, + AliasedField, + AliasedField, + AliasedField, + AliasedField, + AliasedField, + AliasedField, + AliasedField, + AliasedField, + AliasedField, + AliasedField, +); + +#[cfg(feature = "full")] +/// A helper tuple for creator community actions +pub type CreatorCommunityActionsAllColumnsTuple = ( + AliasedField, + AliasedField, + AliasedField, + AliasedField, + AliasedField, + AliasedField, + AliasedField, + AliasedField, + AliasedField, +); diff --git a/crates/db_schema/src/newtypes.rs b/crates/db_schema/src/newtypes.rs index 17a49c8e7..aa0c2b910 100644 --- a/crates/db_schema/src/newtypes.rs +++ b/crates/db_schema/src/newtypes.rs @@ -198,17 +198,17 @@ pub struct LtreeDef(pub String); #[cfg_attr(feature = "full", ts(export))] pub struct DbUrl(pub(crate) Box); -#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Default)] +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] #[cfg_attr(feature = "full", derive(DieselNewType))] /// The report combined id pub struct ReportCombinedId(i32); -#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Default)] +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] #[cfg_attr(feature = "full", derive(DieselNewType))] /// The person content combined id pub struct PersonContentCombinedId(i32); -#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Default)] +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] #[cfg_attr(feature = "full", derive(DieselNewType))] /// The person saved combined id pub struct PersonSavedCombinedId(i32); diff --git a/crates/db_schema/src/source/combined/person_content.rs b/crates/db_schema/src/source/combined/person_content.rs index 05f8c1a46..ed83401c0 100644 --- a/crates/db_schema/src/source/combined/person_content.rs +++ b/crates/db_schema/src/source/combined/person_content.rs @@ -4,8 +4,11 @@ use crate::schema::person_content_combined; use chrono::{DateTime, Utc}; #[cfg(feature = "full")] use i_love_jesus::CursorKeysModule; +use serde::{Deserialize, Serialize}; +use serde_with::skip_serializing_none; -#[derive(PartialEq, Eq, Debug, Clone)] +#[skip_serializing_none] +#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Clone)] #[cfg_attr( feature = "full", derive(Identifiable, Queryable, Selectable, CursorKeysModule) diff --git a/crates/db_schema/src/source/combined/person_saved.rs b/crates/db_schema/src/source/combined/person_saved.rs index bee11e8b8..267c89832 100644 --- a/crates/db_schema/src/source/combined/person_saved.rs +++ b/crates/db_schema/src/source/combined/person_saved.rs @@ -4,8 +4,11 @@ use crate::schema::person_saved_combined; use chrono::{DateTime, Utc}; #[cfg(feature = "full")] use i_love_jesus::CursorKeysModule; +use serde::{Deserialize, Serialize}; +use serde_with::skip_serializing_none; -#[derive(PartialEq, Eq, Debug, Clone)] +#[skip_serializing_none] +#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Clone)] #[cfg_attr( feature = "full", derive(Identifiable, Queryable, Selectable, CursorKeysModule) diff --git a/crates/db_schema/src/source/combined/report.rs b/crates/db_schema/src/source/combined/report.rs index d8a927285..53a1eff05 100644 --- a/crates/db_schema/src/source/combined/report.rs +++ b/crates/db_schema/src/source/combined/report.rs @@ -10,8 +10,11 @@ use crate::schema::report_combined; use chrono::{DateTime, Utc}; #[cfg(feature = "full")] use i_love_jesus::CursorKeysModule; +use serde::{Deserialize, Serialize}; +use serde_with::skip_serializing_none; -#[derive(PartialEq, Eq, Debug, Clone)] +#[skip_serializing_none] +#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Clone)] #[cfg_attr( feature = "full", derive(Identifiable, Queryable, Selectable, CursorKeysModule) diff --git a/crates/db_schema/src/source/comment.rs b/crates/db_schema/src/source/comment.rs index a66dad02c..12d03dae0 100644 --- a/crates/db_schema/src/source/comment.rs +++ b/crates/db_schema/src/source/comment.rs @@ -5,8 +5,6 @@ use crate::newtypes::{CommentId, DbUrl, LanguageId, PersonId, PostId}; use crate::schema::{comment, comment_actions}; use chrono::{DateTime, Utc}; #[cfg(feature = "full")] -use diesel::{dsl, expression_methods::NullableExpressionMethods}; -#[cfg(feature = "full")] use diesel_ltree::Ltree; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; @@ -104,59 +102,48 @@ pub struct CommentUpdateForm { pub language_id: Option, } -#[derive(PartialEq, Eq, Debug, Clone)] +#[skip_serializing_none] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[cfg_attr( feature = "full", - derive(Identifiable, Queryable, Selectable, Associations) + derive(Identifiable, Queryable, Selectable, Associations, TS) )] #[cfg_attr(feature = "full", diesel(belongs_to(crate::source::comment::Comment)))] #[cfg_attr(feature = "full", diesel(table_name = comment_actions))] #[cfg_attr(feature = "full", diesel(primary_key(person_id, comment_id)))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -pub struct CommentLike { +#[cfg_attr(feature = "full", ts(export))] +pub struct CommentActions { pub person_id: PersonId, pub comment_id: CommentId, - #[cfg_attr(feature = "full", diesel(select_expression = comment_actions::like_score.assume_not_null()))] - #[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull))] - pub score: i16, - #[cfg_attr(feature = "full", diesel(select_expression = comment_actions::liked.assume_not_null()))] - #[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull))] - pub published: DateTime, + #[cfg_attr(feature = "full", ts(optional))] + /// The like / score for the comment. + pub like_score: Option, + #[cfg_attr(feature = "full", ts(optional))] + /// When the comment was liked. + pub liked: Option>, + #[cfg_attr(feature = "full", ts(optional))] + /// When the comment was saved. + pub saved: Option>, } -#[derive(Clone)] +#[derive(Clone, derive_new::new)] #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] #[cfg_attr(feature = "full", diesel(table_name = comment_actions))] pub struct CommentLikeForm { pub person_id: PersonId, pub comment_id: CommentId, - #[cfg_attr(feature = "full", diesel(column_name = like_score))] - pub score: i16, -} - -#[derive(PartialEq, Eq, Debug)] -#[cfg_attr( - feature = "full", - derive(Identifiable, Queryable, Selectable, Associations) -)] -#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::comment::Comment)))] -#[cfg_attr(feature = "full", diesel(table_name = comment_actions))] -#[cfg_attr(feature = "full", diesel(primary_key(person_id, comment_id)))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -pub struct CommentSaved { - pub comment_id: CommentId, - pub person_id: PersonId, - #[cfg_attr(feature = "full", diesel(select_expression = comment_actions::saved.assume_not_null()))] - #[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull))] - pub published: DateTime, + pub like_score: i16, + #[new(value = "Utc::now()")] + pub liked: DateTime, } +#[derive(derive_new::new)] #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] #[cfg_attr(feature = "full", diesel(table_name = comment_actions))] -#[derive(derive_new::new)] pub struct CommentSavedForm { - pub comment_id: CommentId, pub person_id: PersonId, + pub comment_id: CommentId, #[new(value = "Utc::now()")] pub saved: DateTime, } diff --git a/crates/db_schema/src/source/community.rs b/crates/db_schema/src/source/community.rs index 184a55d26..b5427d7a9 100644 --- a/crates/db_schema/src/source/community.rs +++ b/crates/db_schema/src/source/community.rs @@ -7,8 +7,6 @@ use crate::{ CommunityVisibility, }; use chrono::{DateTime, Utc}; -#[cfg(feature = "full")] -use diesel::{dsl, expression_methods::NullableExpressionMethods}; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; use strum::{Display, EnumString}; @@ -172,10 +170,11 @@ pub struct CommunityUpdateForm { pub description: Option>, } -#[derive(PartialEq, Eq, Debug)] +#[skip_serializing_none] +#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] #[cfg_attr( feature = "full", - derive(Identifiable, Queryable, Selectable, Associations) + derive(Identifiable, Queryable, Selectable, Associations, TS) )] #[cfg_attr( feature = "full", @@ -184,52 +183,53 @@ pub struct CommunityUpdateForm { #[cfg_attr(feature = "full", diesel(table_name = community_actions))] #[cfg_attr(feature = "full", diesel(primary_key(person_id, community_id)))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -pub struct CommunityModerator { +#[cfg_attr(feature = "full", ts(export))] +pub struct CommunityActions { pub community_id: CommunityId, pub person_id: PersonId, - #[cfg_attr(feature = "full", diesel(select_expression = community_actions::became_moderator.assume_not_null()))] - #[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull))] - pub published: DateTime, + #[cfg_attr(feature = "full", ts(optional))] + /// When the community was followed. + pub followed: Option>, + #[cfg_attr(feature = "full", ts(optional))] + /// The state of the community follow. + pub follow_state: Option, + #[cfg_attr(feature = "full", ts(optional))] + /// The approver of the community follow. + pub follow_approver_id: Option, + #[cfg_attr(feature = "full", ts(optional))] + /// When the community was blocked. + pub blocked: Option>, + #[cfg_attr(feature = "full", ts(optional))] + /// When this user became a moderator. + pub became_moderator: Option>, + #[cfg_attr(feature = "full", ts(optional))] + /// When this user received a ban. + pub received_ban: Option>, + #[cfg_attr(feature = "full", ts(optional))] + /// When their ban expires. + pub ban_expires: Option>, } -#[derive(Clone)] +#[derive(Clone, derive_new::new)] #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] #[cfg_attr(feature = "full", diesel(table_name = community_actions))] pub struct CommunityModeratorForm { pub community_id: CommunityId, pub person_id: PersonId, + #[new(value = "Utc::now()")] + pub became_moderator: DateTime, } -#[derive(PartialEq, Eq, Debug)] -#[cfg_attr( - feature = "full", - derive(Identifiable, Queryable, Selectable, Associations) -)] -#[cfg_attr( - feature = "full", - diesel(belongs_to(crate::source::community::Community)) -)] -#[cfg_attr(feature = "full", diesel(table_name = community_actions))] -#[cfg_attr(feature = "full", diesel(primary_key(person_id, community_id)))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -pub struct CommunityPersonBan { - pub community_id: CommunityId, - pub person_id: PersonId, - #[cfg_attr(feature = "full", diesel(select_expression = community_actions::received_ban.assume_not_null()))] - #[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull))] - pub published: DateTime, - #[cfg_attr(feature = "full", diesel(column_name = ban_expires))] - pub expires: Option>, -} - -#[derive(Clone)] +#[derive(Clone, derive_new::new)] #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] #[cfg_attr(feature = "full", diesel(table_name = community_actions))] pub struct CommunityPersonBanForm { pub community_id: CommunityId, pub person_id: PersonId, - #[cfg_attr(feature = "full", diesel(column_name = ban_expires))] - pub expires: Option>>, + #[new(default)] + pub ban_expires: Option>>, + #[new(value = "Utc::now()")] + pub received_ban: DateTime, } #[derive(EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)] @@ -246,41 +246,25 @@ pub enum CommunityFollowerState { ApprovalRequired, } -#[derive(PartialEq, Eq, Debug)] -#[cfg_attr( - feature = "full", - derive(Identifiable, Queryable, Selectable, Associations) -)] -#[cfg_attr( - feature = "full", - diesel(belongs_to(crate::source::community::Community)) -)] -#[cfg_attr(feature = "full", diesel(table_name = community_actions))] -#[cfg_attr(feature = "full", diesel(primary_key(person_id, community_id)))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -pub struct CommunityFollower { - pub community_id: CommunityId, - pub person_id: PersonId, - #[cfg_attr(feature = "full", diesel(select_expression = community_actions::followed.assume_not_null()))] - #[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull))] - pub published: DateTime, - #[cfg_attr(feature = "full", diesel(select_expression = community_actions::follow_state.assume_not_null()))] - #[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull))] - pub state: CommunityFollowerState, - #[cfg_attr(feature = "full", diesel(column_name = follow_approver_id))] - pub approver_id: Option, -} - #[derive(Clone, derive_new::new)] #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] #[cfg_attr(feature = "full", diesel(table_name = community_actions))] pub struct CommunityFollowerForm { pub community_id: CommunityId, pub person_id: PersonId, + pub follow_state: CommunityFollowerState, #[new(default)] - #[cfg_attr(feature = "full", diesel(column_name = follow_state))] - pub state: Option, - #[new(default)] - #[cfg_attr(feature = "full", diesel(column_name = follow_approver_id))] - pub approver_id: Option, + pub follow_approver_id: Option, + #[new(value = "Utc::now()")] + pub followed: DateTime, +} + +#[derive(derive_new::new)] +#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] +#[cfg_attr(feature = "full", diesel(table_name = community_actions))] +pub struct CommunityBlockForm { + pub community_id: CommunityId, + pub person_id: PersonId, + #[new(value = "Utc::now()")] + pub blocked: DateTime, } diff --git a/crates/db_schema/src/source/community_block.rs b/crates/db_schema/src/source/community_block.rs deleted file mode 100644 index a7c23419c..000000000 --- a/crates/db_schema/src/source/community_block.rs +++ /dev/null @@ -1,34 +0,0 @@ -use crate::newtypes::{CommunityId, PersonId}; -#[cfg(feature = "full")] -use crate::schema::community_actions; -use chrono::{DateTime, Utc}; -#[cfg(feature = "full")] -use diesel::{dsl, expression_methods::NullableExpressionMethods}; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] -#[cfg_attr( - feature = "full", - derive(Queryable, Selectable, Associations, Identifiable) -)] -#[cfg_attr( - feature = "full", - diesel(belongs_to(crate::source::community::Community)) -)] -#[cfg_attr(feature = "full", diesel(table_name = community_actions))] -#[cfg_attr(feature = "full", diesel(primary_key(person_id, community_id)))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -pub struct CommunityBlock { - pub person_id: PersonId, - pub community_id: CommunityId, - #[cfg_attr(feature = "full", diesel(select_expression = community_actions::blocked.assume_not_null()))] - #[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull))] - pub published: DateTime, -} - -#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] -#[cfg_attr(feature = "full", diesel(table_name = community_actions))] -pub struct CommunityBlockForm { - pub person_id: PersonId, - pub community_id: CommunityId, -} diff --git a/crates/db_schema/src/source/instance.rs b/crates/db_schema/src/source/instance.rs index f622751cc..e76c30aad 100644 --- a/crates/db_schema/src/source/instance.rs +++ b/crates/db_schema/src/source/instance.rs @@ -1,6 +1,6 @@ -use crate::newtypes::InstanceId; +use crate::newtypes::{InstanceId, PersonId}; #[cfg(feature = "full")] -use crate::schema::instance; +use crate::schema::{instance, instance_actions}; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; @@ -20,10 +20,13 @@ pub struct Instance { pub domain: String, pub published: DateTime, #[cfg_attr(feature = "full", ts(optional))] + /// When the instance was updated. pub updated: Option>, #[cfg_attr(feature = "full", ts(optional))] + /// The software of the instance. pub software: Option, #[cfg_attr(feature = "full", ts(optional))] + /// The version of the instance's software. pub version: Option, } @@ -39,3 +42,34 @@ pub struct InstanceForm { #[new(default)] pub updated: Option>, } + +#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] +#[cfg_attr( + feature = "full", + derive(Queryable, Selectable, Associations, Identifiable, TS) +)] +#[cfg_attr( + feature = "full", + diesel(belongs_to(crate::source::instance::Instance)) +)] +#[cfg_attr(feature = "full", diesel(table_name = instance_actions))] +#[cfg_attr(feature = "full", diesel(primary_key(person_id, instance_id)))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +pub struct InstanceActions { + pub person_id: PersonId, + pub instance_id: InstanceId, + #[cfg_attr(feature = "full", ts(optional))] + /// When the instance was blocked. + pub blocked: Option>, +} + +#[derive(derive_new::new)] +#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] +#[cfg_attr(feature = "full", diesel(table_name = instance_actions))] +pub struct InstanceBlockForm { + pub person_id: PersonId, + pub instance_id: InstanceId, + #[new(value = "Utc::now()")] + pub blocked: DateTime, +} diff --git a/crates/db_schema/src/source/instance_block.rs b/crates/db_schema/src/source/instance_block.rs deleted file mode 100644 index e1963c894..000000000 --- a/crates/db_schema/src/source/instance_block.rs +++ /dev/null @@ -1,34 +0,0 @@ -use crate::newtypes::{InstanceId, PersonId}; -#[cfg(feature = "full")] -use crate::schema::instance_actions; -use chrono::{DateTime, Utc}; -#[cfg(feature = "full")] -use diesel::{dsl, expression_methods::NullableExpressionMethods}; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] -#[cfg_attr( - feature = "full", - derive(Queryable, Selectable, Associations, Identifiable) -)] -#[cfg_attr( - feature = "full", - diesel(belongs_to(crate::source::instance::Instance)) -)] -#[cfg_attr(feature = "full", diesel(table_name = instance_actions))] -#[cfg_attr(feature = "full", diesel(primary_key(person_id, instance_id)))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -pub struct InstanceBlock { - pub person_id: PersonId, - pub instance_id: InstanceId, - #[cfg_attr(feature = "full", diesel(select_expression = instance_actions::blocked.assume_not_null()))] - #[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull))] - pub published: DateTime, -} - -#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] -#[cfg_attr(feature = "full", diesel(table_name = instance_actions))] -pub struct InstanceBlockForm { - pub person_id: PersonId, - pub instance_id: InstanceId, -} diff --git a/crates/db_schema/src/source/mod.rs b/crates/db_schema/src/source/mod.rs index a9a99c433..8f04e5eac 100644 --- a/crates/db_schema/src/source/mod.rs +++ b/crates/db_schema/src/source/mod.rs @@ -10,7 +10,6 @@ pub mod comment; pub mod comment_reply; pub mod comment_report; pub mod community; -pub mod community_block; pub mod community_report; pub mod custom_emoji; pub mod custom_emoji_keyword; @@ -20,7 +19,6 @@ pub mod federation_blocklist; pub mod federation_queue_state; pub mod images; pub mod instance; -pub mod instance_block; pub mod language; pub mod local_site; pub mod local_site_rate_limit; @@ -32,11 +30,9 @@ pub mod oauth_account; pub mod oauth_provider; pub mod password_reset_request; pub mod person; -pub mod person_block; pub mod person_comment_mention; pub mod person_post_mention; pub mod post; -pub mod post_actions; pub mod post_report; pub mod private_message; pub mod private_message_report; diff --git a/crates/db_schema/src/source/person.rs b/crates/db_schema/src/source/person.rs index c915e09f2..3c8dac0bf 100644 --- a/crates/db_schema/src/source/person.rs +++ b/crates/db_schema/src/source/person.rs @@ -7,8 +7,6 @@ use crate::{ }; use chrono::{DateTime, Utc}; #[cfg(feature = "full")] -use diesel::{dsl, expression_methods::NullableExpressionMethods}; -#[cfg(feature = "full")] use i_love_jesus::CursorKeysModule; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; @@ -141,36 +139,47 @@ pub struct PersonUpdateForm { pub ban_expires: Option>>, } -#[derive(PartialEq, Eq, Debug)] +#[skip_serializing_none] +#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] #[cfg_attr( feature = "full", - derive(Identifiable, Queryable, Selectable, Associations) + derive(Identifiable, Queryable, Selectable, Associations, TS) )] #[cfg_attr(feature = "full", diesel(belongs_to(crate::source::person::Person)))] #[cfg_attr(feature = "full", diesel(table_name = person_actions))] #[cfg_attr(feature = "full", diesel(primary_key(person_id, target_id)))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -pub struct PersonFollower { - #[cfg_attr(feature = "full", diesel(column_name = target_id))] +#[cfg_attr(feature = "full", ts(export))] +pub struct PersonActions { + pub target_id: PersonId, pub person_id: PersonId, - #[cfg_attr(feature = "full", diesel(column_name = person_id))] - pub follower_id: PersonId, - #[cfg_attr(feature = "full", diesel(select_expression = person_actions::followed.assume_not_null()))] - #[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull))] - pub published: DateTime, - #[cfg_attr(feature = "full", diesel(select_expression = person_actions::follow_pending.assume_not_null()))] - #[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull))] - pub pending: bool, + #[serde(skip)] + pub followed: Option>, + #[serde(skip)] + pub follow_pending: Option, + #[cfg_attr(feature = "full", ts(optional))] + /// When the person was blocked. + pub blocked: Option>, } -#[derive(Clone)] +#[derive(Clone, derive_new::new)] #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] #[cfg_attr(feature = "full", diesel(table_name = person_actions))] pub struct PersonFollowerForm { - #[cfg_attr(feature = "full", diesel(column_name = target_id))] + pub target_id: PersonId, pub person_id: PersonId, - #[cfg_attr(feature = "full", diesel(column_name = person_id))] - pub follower_id: PersonId, - #[cfg_attr(feature = "full", diesel(column_name = follow_pending))] - pub pending: bool, + pub follow_pending: bool, + #[new(value = "Utc::now()")] + pub followed: DateTime, +} + +#[derive(derive_new::new)] +#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] +#[cfg_attr(feature = "full", diesel(table_name = person_actions))] +pub struct PersonBlockForm { + // This order is switched so blocks can work the same. + pub person_id: PersonId, + pub target_id: PersonId, + #[new(value = "Utc::now()")] + pub blocked: DateTime, } diff --git a/crates/db_schema/src/source/person_block.rs b/crates/db_schema/src/source/person_block.rs deleted file mode 100644 index ec988a60f..000000000 --- a/crates/db_schema/src/source/person_block.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::newtypes::PersonId; -#[cfg(feature = "full")] -use crate::schema::person_actions; -use chrono::{DateTime, Utc}; -#[cfg(feature = "full")] -use diesel::{dsl, expression_methods::NullableExpressionMethods}; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] -#[cfg_attr( - feature = "full", - derive(Queryable, Selectable, Associations, Identifiable) -)] -#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::person::Person)))] -#[cfg_attr(feature = "full", diesel(table_name = person_actions))] -#[cfg_attr(feature = "full", diesel(primary_key(person_id, target_id)))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -pub struct PersonBlock { - pub person_id: PersonId, - pub target_id: PersonId, - #[cfg_attr(feature = "full", diesel(select_expression = person_actions::blocked.assume_not_null()))] - #[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull))] - pub published: DateTime, -} - -#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] -#[cfg_attr(feature = "full", diesel(table_name = person_actions))] -pub struct PersonBlockForm { - pub person_id: PersonId, - pub target_id: PersonId, -} diff --git a/crates/db_schema/src/source/post.rs b/crates/db_schema/src/source/post.rs index f466378d4..643c6650c 100644 --- a/crates/db_schema/src/source/post.rs +++ b/crates/db_schema/src/source/post.rs @@ -5,7 +5,6 @@ use serde_with::skip_serializing_none; #[cfg(feature = "full")] use { crate::schema::{post, post_actions}, - diesel::{dsl, expression_methods::NullableExpressionMethods}, i_love_jesus::CursorKeysModule, ts_rs::TS, }; @@ -16,8 +15,8 @@ use { feature = "full", derive(Queryable, Selectable, Identifiable, TS, CursorKeysModule) )] -#[cfg_attr(feature = "full", diesel(table_name = post))] #[cfg_attr(feature = "full", ts(export))] +#[cfg_attr(feature = "full", diesel(table_name = post))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", cursor_keys_module(name = post_keys))] /// A post. @@ -172,24 +171,42 @@ pub struct PostUpdateForm { pub scheduled_publish_time: Option>>, } -#[derive(PartialEq, Eq, Debug)] +#[skip_serializing_none] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[cfg_attr( feature = "full", - derive(Identifiable, Queryable, Selectable, Associations) + derive(Identifiable, Queryable, Selectable, Associations, TS) )] #[cfg_attr(feature = "full", diesel(belongs_to(crate::source::post::Post)))] #[cfg_attr(feature = "full", diesel(table_name = post_actions))] #[cfg_attr(feature = "full", diesel(primary_key(person_id, post_id)))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -pub struct PostLike { +#[cfg_attr(feature = "full", ts(export))] +pub struct PostActions { pub post_id: PostId, pub person_id: PersonId, - #[cfg_attr(feature = "full", diesel(select_expression = post_actions::like_score.assume_not_null()))] - #[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull))] - pub score: i16, - #[cfg_attr(feature = "full", diesel(select_expression = post_actions::liked.assume_not_null()))] - #[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull))] - pub published: DateTime, + #[cfg_attr(feature = "full", ts(optional))] + /// When the post was read. + pub read: Option>, + #[cfg_attr(feature = "full", ts(optional))] + /// When was the last time you read the comments. + pub read_comments: Option>, + #[cfg_attr(feature = "full", ts(optional))] + /// The number of comments you read last. Subtract this from total comments to get an unread + /// count. + pub read_comments_amount: Option, + #[cfg_attr(feature = "full", ts(optional))] + /// When the post was saved. + pub saved: Option>, + #[cfg_attr(feature = "full", ts(optional))] + /// When the post was liked. + pub liked: Option>, + #[cfg_attr(feature = "full", ts(optional))] + /// The like / score of the post. + pub like_score: Option, + #[cfg_attr(feature = "full", ts(optional))] + /// When the post was hidden. + pub hidden: Option>, } #[derive(Clone, derive_new::new)] @@ -198,29 +215,11 @@ pub struct PostLike { pub struct PostLikeForm { pub post_id: PostId, pub person_id: PersonId, - #[cfg_attr(feature = "full", diesel(column_name = like_score))] - pub score: i16, + pub like_score: i16, #[new(value = "Utc::now()")] pub liked: DateTime, } -#[derive(PartialEq, Eq, Debug)] -#[cfg_attr( - feature = "full", - derive(Identifiable, Queryable, Selectable, Associations) -)] -#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::post::Post)))] -#[cfg_attr(feature = "full", diesel(table_name = post_actions))] -#[cfg_attr(feature = "full", diesel(primary_key(person_id, post_id)))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -pub struct PostSaved { - pub post_id: PostId, - pub person_id: PersonId, - #[cfg_attr(feature = "full", diesel(select_expression = post_actions::saved.assume_not_null()))] - #[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull))] - pub published: DateTime, -} - #[derive(derive_new::new)] #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] #[cfg_attr(feature = "full", diesel(table_name = post_actions))] @@ -231,24 +230,7 @@ pub struct PostSavedForm { pub saved: DateTime, } -#[derive(Clone, PartialEq, Eq, Debug)] -#[cfg_attr( - feature = "full", - derive(Identifiable, Queryable, Selectable, Associations) -)] -#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::post::Post)))] -#[cfg_attr(feature = "full", diesel(table_name = post_actions))] -#[cfg_attr(feature = "full", diesel(primary_key(person_id, post_id)))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -pub struct PostRead { - pub post_id: PostId, - pub person_id: PersonId, - #[cfg_attr(feature = "full", diesel(select_expression = post_actions::read.assume_not_null()))] - #[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull))] - pub published: DateTime, -} - -#[derive(derive_new::new)] +#[derive(derive_new::new, Clone)] #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] #[cfg_attr(feature = "full", diesel(table_name = post_actions))] pub struct PostReadForm { @@ -258,21 +240,15 @@ pub struct PostReadForm { pub read: DateTime, } -#[derive(PartialEq, Eq, Debug)] -#[cfg_attr( - feature = "full", - derive(Identifiable, Queryable, Selectable, Associations) -)] -#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::post::Post)))] +#[derive(derive_new::new)] +#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] #[cfg_attr(feature = "full", diesel(table_name = post_actions))] -#[cfg_attr(feature = "full", diesel(primary_key(person_id, post_id)))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -pub struct PostHide { +pub struct PostReadCommentsForm { pub post_id: PostId, pub person_id: PersonId, - #[cfg_attr(feature = "full", diesel(select_expression = post_actions::hidden.assume_not_null()))] - #[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull))] - pub published: DateTime, + pub read_comments_amount: i64, + #[new(value = "Utc::now()")] + pub read_comments: DateTime, } #[derive(derive_new::new)] diff --git a/crates/db_schema/src/source/post_actions.rs b/crates/db_schema/src/source/post_actions.rs deleted file mode 100644 index 3c4378951..000000000 --- a/crates/db_schema/src/source/post_actions.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::newtypes::{PersonId, PostId}; -use chrono::{DateTime, Utc}; -use serde::{Deserialize, Serialize}; -#[cfg(feature = "full")] -use { - crate::schema::post_actions, - diesel::{dsl, expression_methods::NullableExpressionMethods}, -}; - -#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)] -#[cfg_attr( - feature = "full", - derive(Queryable, Selectable, Associations, Identifiable) -)] -#[cfg_attr(feature = "full", diesel(table_name = post_actions))] -#[cfg_attr(feature = "full", diesel(primary_key(person_id, post_id)))] -#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::person::Person)))] -#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] -/// Aggregate data for a person's post. -pub struct PostActions { - pub person_id: PersonId, - pub post_id: PostId, - /// The number of comments they've read on that post. - /// - /// This is updated to the current post comment count every time they view a post. - #[cfg_attr(feature = "full", diesel(select_expression = post_actions::read_comments_amount.assume_not_null()))] - #[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull))] - pub read_comments: i64, - #[cfg_attr(feature = "full", diesel(select_expression = post_actions::read_comments.assume_not_null()))] - #[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull))] - pub published: DateTime, -} - -#[derive(Clone, Default)] -#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] -#[cfg_attr(feature = "full", diesel(table_name = post_actions))] -pub struct PostActionsForm { - pub person_id: PersonId, - pub post_id: PostId, - #[cfg_attr(feature = "full", diesel(column_name = read_comments_amount))] - pub read_comments: i64, -} diff --git a/crates/db_schema/src/traits.rs b/crates/db_schema/src/traits.rs index 7ad8318fb..2f0add08d 100644 --- a/crates/db_schema/src/traits.rs +++ b/crates/db_schema/src/traits.rs @@ -82,23 +82,25 @@ where pub trait Followable { type Form; + type IdType; fn follow( pool: &mut DbPool<'_>, form: &Self::Form, - ) -> impl Future> + Send + ) -> impl Future> + Send where Self: Sized; fn follow_accepted( pool: &mut DbPool<'_>, community_id: CommunityId, person_id: PersonId, - ) -> impl Future> + Send + ) -> impl Future> + Send where Self: Sized; fn unfollow( pool: &mut DbPool<'_>, - form: &Self::Form, - ) -> impl Future> + Send + person_id: PersonId, + item_id: Self::IdType, + ) -> impl Future> + Send where Self: Sized; } @@ -108,13 +110,13 @@ pub trait Joinable { fn join( pool: &mut DbPool<'_>, form: &Self::Form, - ) -> impl Future> + Send + ) -> impl Future> + Send where Self: Sized; fn leave( pool: &mut DbPool<'_>, form: &Self::Form, - ) -> impl Future> + Send + ) -> impl Future> + Send where Self: Sized; } @@ -125,14 +127,14 @@ pub trait Likeable { fn like( pool: &mut DbPool<'_>, form: &Self::Form, - ) -> impl Future> + Send + ) -> impl Future> + Send where Self: Sized; - fn remove( + fn remove_like( pool: &mut DbPool<'_>, person_id: PersonId, item_id: Self::IdType, - ) -> impl Future> + Send + ) -> impl Future> + Send where Self: Sized; } @@ -142,13 +144,13 @@ pub trait Bannable { fn ban( pool: &mut DbPool<'_>, form: &Self::Form, - ) -> impl Future> + Send + ) -> impl Future> + Send where Self: Sized; fn unban( pool: &mut DbPool<'_>, form: &Self::Form, - ) -> impl Future> + Send + ) -> impl Future> + Send where Self: Sized; } @@ -158,29 +160,102 @@ pub trait Saveable { fn save( pool: &mut DbPool<'_>, form: &Self::Form, - ) -> impl Future> + Send + ) -> impl Future> + Send where Self: Sized; fn unsave( pool: &mut DbPool<'_>, form: &Self::Form, - ) -> impl Future> + Send + ) -> impl Future> + Send + where + Self: Sized; +} + +pub trait Readable { + type Form; + fn mark_as_read( + pool: &mut DbPool<'_>, + form: &Self::Form, + ) -> impl Future> + Send + where + Self: Sized; + fn mark_many_as_read( + pool: &mut DbPool<'_>, + forms: &[Self::Form], + ) -> impl Future> + Send + where + Self: Sized; + fn mark_as_unread( + pool: &mut DbPool<'_>, + form: &Self::Form, + ) -> impl Future> + Send + where + Self: Sized; +} + +pub trait ReadComments { + type Form; + type IdType; + fn update_read_comments( + pool: &mut DbPool<'_>, + form: &Self::Form, + ) -> impl Future> + Send + where + Self: Sized; + fn remove_read_comments( + pool: &mut DbPool<'_>, + person_id: PersonId, + item_id: Self::IdType, + ) -> impl Future> + Send + where + Self: Sized; +} + +pub trait Hideable { + type Form; + fn hide( + pool: &mut DbPool<'_>, + form: &Self::Form, + ) -> impl Future> + Send + where + Self: Sized; + fn unhide( + pool: &mut DbPool<'_>, + form: &Self::Form, + ) -> impl Future> + Send where Self: Sized; } pub trait Blockable { type Form; + type ObjectIdType; + type ObjectType; fn block( pool: &mut DbPool<'_>, form: &Self::Form, - ) -> impl Future> + Send + ) -> impl Future> + Send where Self: Sized; fn unblock( pool: &mut DbPool<'_>, form: &Self::Form, - ) -> impl Future> + Send + ) -> impl Future> + Send + where + Self: Sized; + fn read_block( + pool: &mut DbPool<'_>, + for_person_id: PersonId, + for_item_id: Self::ObjectIdType, + ) -> impl Future> + Send + where + Self: Sized; + + fn read_blocks_for_person( + pool: &mut DbPool<'_>, + person_id: PersonId, + // Note: cant use lemmyresult because of try_pool + ) -> impl Future, diesel::result::Error>> + Send where Self: Sized; } @@ -192,14 +267,14 @@ pub trait Reportable { fn report( pool: &mut DbPool<'_>, form: &Self::Form, - ) -> impl Future> + Send + ) -> impl Future> + Send where Self: Sized; fn resolve( pool: &mut DbPool<'_>, report_id: Self::IdType, resolver_id: PersonId, - ) -> impl Future> + Send + ) -> impl Future> + Send where Self: Sized; fn resolve_apub( @@ -214,14 +289,14 @@ pub trait Reportable { pool: &mut DbPool<'_>, comment_id_: Self::ObjectIdType, by_resolver_id: PersonId, - ) -> impl Future> + Send + ) -> impl Future> + Send where Self: Sized; fn unresolve( pool: &mut DbPool<'_>, report_id: Self::IdType, resolver_id: PersonId, - ) -> impl Future> + Send + ) -> impl Future> + Send where Self: Sized; } diff --git a/crates/db_views/src/combined/inbox_combined_view.rs b/crates/db_views/src/combined/inbox_combined_view.rs index 2faa4d68c..f5d6460c5 100644 --- a/crates/db_views/src/combined/inbox_combined_view.rs +++ b/crates/db_views/src/combined/inbox_combined_view.rs @@ -20,7 +20,6 @@ use diesel_async::RunQueryDsl; use i_love_jesus::PaginatedQueryBuilder; use lemmy_db_schema::{ aliases::{self, creator_community_actions, creator_local_user}, - impls::{community::community_follower_select_subscribed_type, local_user::local_user_can_mod}, newtypes::{PaginationCursor, PersonId}, schema::{ comment, @@ -38,13 +37,11 @@ use lemmy_db_schema::{ person_post_mention, post, post_actions, - post_tag, private_message, - tag, }, source::combined::inbox::{inbox_combined_keys as key, InboxCombined}, traits::{InternalToCombinedView, PaginationCursorBuilder}, - utils::{functions::coalesce, get_conn, DbPool}, + utils::{get_conn, DbPool}, InboxDataType, }; use lemmy_utils::error::{LemmyErrorType, LemmyResult}; @@ -55,14 +52,16 @@ impl InboxCombinedViewInternal { let item_creator = person::id; let recipient_person = aliases::person1.field(person::id); - let item_creator_join = comment::creator_id - .eq(item_creator) - .or( - inbox_combined::person_post_mention_id - .is_not_null() - .and(post::creator_id.eq(item_creator)), - ) - .or(private_message::creator_id.eq(item_creator)); + let item_creator_join = person::table.on( + comment::creator_id + .eq(item_creator) + .or( + inbox_combined::person_post_mention_id + .is_not_null() + .and(post::creator_id.eq(item_creator)), + ) + .or(private_message::creator_id.eq(item_creator)), + ); let recipient_join = aliases::person1.on( comment_reply::recipient_id @@ -72,27 +71,33 @@ impl InboxCombinedViewInternal { .or(private_message::recipient_id.eq(recipient_person)), ); - let comment_join = comment_reply::comment_id - .eq(comment::id) - .or(person_comment_mention::comment_id.eq(comment::id)) - // Filter out the deleted / removed - .and(not(comment::deleted)) - .and(not(comment::removed)); + let comment_join = comment::table.on( + comment_reply::comment_id + .eq(comment::id) + .or(person_comment_mention::comment_id.eq(comment::id)) + // Filter out the deleted / removed + .and(not(comment::deleted)) + .and(not(comment::removed)), + ); - let post_join = person_post_mention::post_id - .eq(post::id) - .or(comment::post_id.eq(post::id)) - // Filter out the deleted / removed - .and(not(post::deleted)) - .and(not(post::removed)); + let post_join = post::table.on( + person_post_mention::post_id + .eq(post::id) + .or(comment::post_id.eq(post::id)) + // Filter out the deleted / removed + .and(not(post::deleted)) + .and(not(post::removed)), + ); // This could be a simple join, but you need to check for deleted here - let private_message_join = inbox_combined::private_message_id - .eq(private_message::id.nullable()) - .and(not(private_message::deleted)) - .and(not(private_message::removed)); + let private_message_join = private_message::table.on( + inbox_combined::private_message_id + .eq(private_message::id.nullable()) + .and(not(private_message::deleted)) + .and(not(private_message::removed)), + ); - let community_join = post::community_id.eq(community::id); + let community_join = community::table.on(post::community_id.eq(community::id)); let local_user_join = local_user::table.on(local_user::person_id.nullable().eq(my_person_id)); @@ -150,11 +155,11 @@ impl InboxCombinedViewInternal { .left_join(comment_reply::table) .left_join(person_comment_mention::table) .left_join(person_post_mention::table) - .left_join(private_message::table.on(private_message_join)) - .left_join(comment::table.on(comment_join)) - .left_join(post::table.on(post_join)) - .left_join(community::table.on(community_join)) - .inner_join(person::table.on(item_creator_join)) + .left_join(private_message_join) + .left_join(comment_join) + .left_join(post_join) + .left_join(community_join) + .inner_join(item_creator_join) .inner_join(recipient_join) .left_join(image_details_join) .left_join(creator_community_actions_join) @@ -266,58 +271,8 @@ impl InboxCombinedQuery { let item_creator = person::id; let recipient_person = aliases::person1.field(person::id); - let post_tags = post_tag::table - .inner_join(tag::table) - .select(diesel::dsl::sql::( - "json_agg(tag.*)", - )) - .filter(post_tag::post_id.eq(post::id)) - .filter(tag::deleted.eq(false)) - .single_value(); - let mut query = InboxCombinedViewInternal::joins(my_person_id) - .select(( - // Specific - comment_reply::all_columns.nullable(), - person_comment_mention::all_columns.nullable(), - person_post_mention::all_columns.nullable(), - coalesce( - post::comments.nullable() - post_actions::read_comments_amount.nullable(), - post::comments, - ) - .nullable(), - post_actions::saved.nullable(), - post_actions::read.nullable().is_not_null(), - post_actions::hidden.nullable().is_not_null(), - post_actions::like_score.nullable(), - image_details::all_columns.nullable(), - post_tags, - private_message::all_columns.nullable(), - // Shared - post::all_columns.nullable(), - community::all_columns.nullable(), - comment::all_columns.nullable(), - comment_actions::saved.nullable(), - comment_actions::like_score.nullable(), - community_follower_select_subscribed_type(), - person::all_columns, - aliases::person1.fields(person::all_columns), - creator_local_user - .field(local_user::admin) - .nullable() - .is_not_null(), - creator_community_actions - .field(community_actions::became_moderator) - .nullable() - .is_not_null(), - creator_community_actions - .field(community_actions::received_ban) - .nullable() - .is_not_null(), - person_actions::blocked.nullable().is_not_null(), - community_actions::received_ban.nullable().is_not_null(), - local_user_can_mod(), - )) + .select(InboxCombinedViewInternal::as_select()) .into_boxed(); // Filters @@ -431,14 +386,12 @@ impl InternalToCombinedView for InboxCombinedViewInternal { post, community, creator: v.item_creator, - creator_banned_from_community: v.item_creator_banned_from_community, - creator_is_moderator: v.item_creator_is_moderator, + community_actions: v.community_actions, + comment_actions: v.comment_actions, + person_actions: v.person_actions, + instance_actions: v.instance_actions, + creator_community_actions: v.creator_community_actions, creator_is_admin: v.item_creator_is_admin, - creator_blocked: v.item_creator_blocked, - subscribed: v.subscribed, - saved: v.comment_saved, - my_vote: v.my_comment_vote, - banned_from_community: v.banned_from_community, can_mod: v.can_mod, })) } else if let (Some(person_comment_mention), Some(comment), Some(post), Some(community)) = ( @@ -455,42 +408,31 @@ impl InternalToCombinedView for InboxCombinedViewInternal { post, community, creator: v.item_creator, - creator_banned_from_community: v.item_creator_banned_from_community, - creator_is_moderator: v.item_creator_is_moderator, + community_actions: v.community_actions, + comment_actions: v.comment_actions, + person_actions: v.person_actions, + instance_actions: v.instance_actions, + creator_community_actions: v.creator_community_actions, creator_is_admin: v.item_creator_is_admin, - creator_blocked: v.item_creator_blocked, - subscribed: v.subscribed, - saved: v.comment_saved, - my_vote: v.my_comment_vote, - banned_from_community: v.banned_from_community, can_mod: v.can_mod, }, )) - } else if let (Some(person_post_mention), Some(post), Some(unread_comments), Some(community)) = ( - v.person_post_mention, - v.post, - v.post_unread_comments, - v.community, - ) { + } else if let (Some(person_post_mention), Some(post), Some(community)) = + (v.person_post_mention, v.post, v.community) + { Some(InboxCombinedView::PostMention(PersonPostMentionView { person_post_mention, post, community, - recipient: v.item_recipient, - unread_comments, creator: v.item_creator, - creator_banned_from_community: v.item_creator_banned_from_community, - creator_is_moderator: v.item_creator_is_moderator, - creator_is_admin: v.item_creator_is_admin, - creator_blocked: v.item_creator_blocked, - subscribed: v.subscribed, - saved: v.post_saved, - read: v.post_read, - hidden: v.post_hidden, - my_vote: v.my_post_vote, + recipient: v.item_recipient, + community_actions: v.community_actions, + person_actions: v.person_actions, + instance_actions: v.instance_actions, + post_actions: v.post_actions, image_details: v.image_details, - post_tags: v.post_tags, - banned_from_community: v.banned_from_community, + creator_community_actions: v.creator_community_actions, + creator_is_admin: v.item_creator_is_admin, can_mod: v.can_mod, })) } else if let Some(private_message) = v.private_message { @@ -518,10 +460,8 @@ mod tests { comment::{Comment, CommentInsertForm}, comment_reply::{CommentReply, CommentReplyInsertForm, CommentReplyUpdateForm}, community::{Community, CommunityInsertForm}, - instance::Instance, - instance_block::{InstanceBlock, InstanceBlockForm}, - person::{Person, PersonInsertForm, PersonUpdateForm}, - person_block::{PersonBlock, PersonBlockForm}, + instance::{Instance, InstanceActions, InstanceBlockForm}, + person::{Person, PersonActions, PersonBlockForm, PersonInsertForm, PersonUpdateForm}, person_comment_mention::{PersonCommentMention, PersonCommentMentionInsertForm}, person_post_mention::{PersonPostMention, PersonPostMentionInsertForm}, post::{Post, PostInsertForm}, @@ -727,11 +667,8 @@ mod tests { } // Sara blocks timmy, and make sure these counts are now empty - let sara_blocks_timmy_form = PersonBlockForm { - person_id: data.sara.id, - target_id: data.timmy.id, - }; - PersonBlock::block(pool, &sara_blocks_timmy_form).await?; + let sara_blocks_timmy_form = PersonBlockForm::new(data.sara.id, data.timmy.id); + PersonActions::block(pool, &sara_blocks_timmy_form).await?; let sara_unread_mentions_after_block = InboxCombinedViewInternal::get_unread_count(pool, data.sara.id, true).await?; @@ -749,7 +686,7 @@ mod tests { )); // Unblock user so we can reuse the same person - PersonBlock::unblock(pool, &sara_blocks_timmy_form).await?; + PersonActions::unblock(pool, &sara_blocks_timmy_form).await?; // Test the type filter let sara_inbox_post_mentions_only = InboxCombinedQuery { @@ -883,19 +820,18 @@ mod tests { setup_private_messages(&data, pool).await?; // Make sure blocks are working - let timmy_blocks_sara_form = PersonBlockForm { - person_id: data.timmy.id, - target_id: data.sara.id, - }; + let timmy_blocks_sara_form = PersonBlockForm::new(data.timmy.id, data.sara.id); - let inserted_block = PersonBlock::block(pool, &timmy_blocks_sara_form).await?; + let inserted_block = PersonActions::block(pool, &timmy_blocks_sara_form).await?; - let expected_block = PersonBlock { - person_id: data.timmy.id, - target_id: data.sara.id, - published: inserted_block.published, - }; - assert_eq!(expected_block, inserted_block); + assert_eq!( + (data.timmy.id, data.sara.id, true), + ( + inserted_block.person_id, + inserted_block.target_id, + inserted_block.blocked.is_some() + ) + ); let timmy_messages = map_to_pm( &InboxCombinedQuery { @@ -926,19 +862,18 @@ mod tests { setup_private_messages(&data, pool).await?; // Make sure instance_blocks are working - let timmy_blocks_instance_form = InstanceBlockForm { - person_id: data.timmy.id, - instance_id: data.sara.instance_id, - }; + let timmy_blocks_instance_form = InstanceBlockForm::new(data.timmy.id, data.sara.instance_id); - let inserted_instance_block = InstanceBlock::block(pool, &timmy_blocks_instance_form).await?; + let inserted_instance_block = InstanceActions::block(pool, &timmy_blocks_instance_form).await?; - let expected_instance_block = InstanceBlock { - person_id: data.timmy.id, - instance_id: data.sara.instance_id, - published: inserted_instance_block.published, - }; - assert_eq!(expected_instance_block, inserted_instance_block); + assert_eq!( + (data.timmy.id, data.sara.instance_id, true), + ( + inserted_instance_block.person_id, + inserted_instance_block.instance_id, + inserted_instance_block.blocked.is_some() + ) + ); let timmy_messages = map_to_pm( &InboxCombinedQuery { diff --git a/crates/db_views/src/combined/modlog_combined_view.rs b/crates/db_views/src/combined/modlog_combined_view.rs index be3747450..71a80e312 100644 --- a/crates/db_views/src/combined/modlog_combined_view.rs +++ b/crates/db_views/src/combined/modlog_combined_view.rs @@ -20,7 +20,7 @@ use crate::{ ModlogCombinedView, ModlogCombinedViewInternal, }, - utils::{filter_is_subscribed, filter_not_hidden_or_is_subscribed}, + utils::{filter_is_subscribed, filter_not_unlisted_or_is_subscribed}, }; use diesel::{ BoolExpressionMethods, @@ -378,7 +378,7 @@ impl ModlogCombinedQuery<'_> { ListingType::Subscribed => query.filter(filter_is_subscribed()), ListingType::Local => query .filter(community::local.eq(true)) - .filter(filter_not_hidden_or_is_subscribed()), + .filter(filter_not_unlisted_or_is_subscribed()), ListingType::ModeratorView => query.filter(community_actions::became_moderator.is_not_null()), }; diff --git a/crates/db_views/src/combined/person_content_combined_view.rs b/crates/db_views/src/combined/person_content_combined_view.rs index eb5397b04..881ec94f6 100644 --- a/crates/db_views/src/combined/person_content_combined_view.rs +++ b/crates/db_views/src/combined/person_content_combined_view.rs @@ -16,8 +16,8 @@ use diesel::{ use diesel_async::RunQueryDsl; use i_love_jesus::PaginatedQueryBuilder; use lemmy_db_schema::{ + self, aliases::{creator_community_actions, creator_local_user}, - impls::{community::community_follower_select_subscribed_type, local_user::local_user_can_mod}, newtypes::{PaginationCursor, PersonId}, schema::{ comment, @@ -25,18 +25,17 @@ use lemmy_db_schema::{ community, community_actions, image_details, + instance_actions, local_user, person, person_actions, person_content_combined, post, post_actions, - post_tag, - tag, }, source::combined::person_content::{person_content_combined_keys as key, PersonContentCombined}, traits::{InternalToCombinedView, PaginationCursorBuilder}, - utils::{functions::coalesce, get_conn, DbPool}, + utils::{get_conn, DbPool}, PersonContentType, }; use lemmy_utils::error::{LemmyErrorType, LemmyResult}; @@ -93,6 +92,12 @@ impl PersonContentCombinedViewInternal { .and(community_actions::person_id.nullable().eq(my_person_id)), ); + let instance_actions_join = instance_actions::table.on( + instance_actions::instance_id + .eq(person::instance_id) + .and(instance_actions::person_id.nullable().eq(my_person_id)), + ); + let post_actions_join = post_actions::table.on( post_actions::post_id .eq(post::id) @@ -123,6 +128,7 @@ impl PersonContentCombinedViewInternal { .left_join(local_user_join) .left_join(creator_local_user_join) .left_join(community_actions_join) + .left_join(instance_actions_join) .left_join(post_actions_join) .left_join(person_actions_join) .left_join(comment_actions_join) @@ -185,15 +191,6 @@ impl PersonContentCombinedQuery { let conn = &mut get_conn(pool).await?; - let post_tags = post_tag::table - .inner_join(tag::table) - .select(diesel::dsl::sql::( - "json_agg(tag.*)", - )) - .filter(post_tag::post_id.eq(post::id)) - .filter(tag::deleted.eq(false)) - .single_value(); - // Notes: since the post_id and comment_id are optional columns, // many joins must use an OR condition. // For example, the creator must be the person table joined to either: @@ -202,43 +199,7 @@ impl PersonContentCombinedQuery { let mut query = PersonContentCombinedViewInternal::joins(my_person_id) // The creator id filter .filter(item_creator.eq(self.creator_id)) - .select(( - // Post-specific - coalesce( - post::comments.nullable() - post_actions::read_comments_amount.nullable(), - post::comments, - ), - post_actions::saved.nullable(), - post_actions::read.nullable().is_not_null(), - post_actions::hidden.nullable().is_not_null(), - post_actions::like_score.nullable(), - image_details::all_columns.nullable(), - post_tags, - // Comment-specific - comment::all_columns.nullable(), - comment_actions::saved.nullable(), - comment_actions::like_score.nullable(), - // Shared - post::all_columns, - community::all_columns, - person::all_columns, - community_follower_select_subscribed_type(), - creator_local_user - .field(local_user::admin) - .nullable() - .is_not_null(), - creator_community_actions - .field(community_actions::became_moderator) - .nullable() - .is_not_null(), - creator_community_actions - .field(community_actions::received_ban) - .nullable() - .is_not_null(), - person_actions::blocked.nullable().is_not_null(), - community_actions::received_ban.nullable().is_not_null(), - local_user_can_mod(), - )) + .select(PersonContentCombinedViewInternal::as_select()) .into_boxed(); if let Some(type_) = self.type_ { @@ -292,34 +253,26 @@ impl InternalToCombinedView for PersonContentCombinedViewInternal { post: v.post, community: v.community, creator: v.item_creator, - creator_banned_from_community: v.item_creator_banned_from_community, - creator_is_moderator: v.item_creator_is_moderator, + community_actions: v.community_actions, + comment_actions: v.comment_actions, + person_actions: v.person_actions, + instance_actions: v.instance_actions, + creator_community_actions: v.creator_community_actions, creator_is_admin: v.item_creator_is_admin, - creator_blocked: v.item_creator_blocked, - subscribed: v.subscribed, - saved: v.comment_saved, - my_vote: v.my_comment_vote, - banned_from_community: v.banned_from_community, can_mod: v.can_mod, })) } else { Some(PersonContentCombinedView::Post(PostView { post: v.post, community: v.community, - unread_comments: v.post_unread_comments, creator: v.item_creator, - creator_banned_from_community: v.item_creator_banned_from_community, - creator_is_moderator: v.item_creator_is_moderator, - creator_is_admin: v.item_creator_is_admin, - creator_blocked: v.item_creator_blocked, - subscribed: v.subscribed, - saved: v.post_saved, - read: v.post_read, - hidden: v.post_hidden, - my_vote: v.my_post_vote, image_details: v.image_details, - banned_from_community: v.banned_from_community, - tags: v.post_tags, + community_actions: v.community_actions, + post_actions: v.post_actions, + person_actions: v.person_actions, + instance_actions: v.instance_actions, + creator_community_actions: v.creator_community_actions, + creator_is_admin: v.item_creator_is_admin, can_mod: v.can_mod, })) } diff --git a/crates/db_views/src/combined/person_saved_combined_view.rs b/crates/db_views/src/combined/person_saved_combined_view.rs index 197d44273..abdfcbd5d 100644 --- a/crates/db_views/src/combined/person_saved_combined_view.rs +++ b/crates/db_views/src/combined/person_saved_combined_view.rs @@ -17,7 +17,6 @@ use diesel_async::RunQueryDsl; use i_love_jesus::PaginatedQueryBuilder; use lemmy_db_schema::{ aliases::{creator_community_actions, creator_local_user}, - impls::{community::community_follower_select_subscribed_type, local_user::local_user_can_mod}, newtypes::{PaginationCursor, PersonId}, schema::{ comment, @@ -25,18 +24,17 @@ use lemmy_db_schema::{ community, community_actions, image_details, + instance_actions, local_user, person, person_actions, person_saved_combined, post, post_actions, - post_tag, - tag, }, source::combined::person_saved::{person_saved_combined_keys as key, PersonSavedCombined}, traits::{InternalToCombinedView, PaginationCursorBuilder}, - utils::{functions::coalesce, get_conn, DbPool}, + utils::{get_conn, DbPool}, PersonContentType, }; use lemmy_utils::error::{LemmyErrorType, LemmyResult}; @@ -134,6 +132,12 @@ impl PersonSavedCombinedViewInternal { .and(community_actions::person_id.eq(my_person_id)), ); + let instance_actions_join = instance_actions::table.on( + instance_actions::instance_id + .eq(person::instance_id) + .and(instance_actions::person_id.eq(my_person_id)), + ); + let post_actions_join = post_actions::table.on( post_actions::post_id .eq(post::id) @@ -164,6 +168,7 @@ impl PersonSavedCombinedViewInternal { .left_join(local_user_join) .left_join(creator_local_user_join) .left_join(community_actions_join) + .left_join(instance_actions_join) .left_join(post_actions_join) .left_join(person_actions_join) .left_join(comment_actions_join) @@ -181,54 +186,9 @@ impl PersonSavedCombinedQuery { let conn = &mut get_conn(pool).await?; - let post_tags = post_tag::table - .inner_join(tag::table) - .select(diesel::dsl::sql::( - "json_agg(tag.*)", - )) - .filter(post_tag::post_id.eq(post::id)) - .filter(tag::deleted.eq(false)) - .single_value(); - let mut query = PersonSavedCombinedViewInternal::joins(my_person_id) .filter(person_saved_combined::person_id.eq(my_person_id)) - .select(( - // Post-specific - coalesce( - post::comments.nullable() - post_actions::read_comments_amount.nullable(), - post::comments, - ), - post_actions::saved.nullable(), - post_actions::read.nullable().is_not_null(), - post_actions::hidden.nullable().is_not_null(), - post_actions::like_score.nullable(), - image_details::all_columns.nullable(), - post_tags, - // Comment-specific - comment::all_columns.nullable(), - comment_actions::saved.nullable(), - comment_actions::like_score.nullable(), - // Shared - post::all_columns, - community::all_columns, - person::all_columns, - community_follower_select_subscribed_type(), - creator_local_user - .field(local_user::admin) - .nullable() - .is_not_null(), - creator_community_actions - .field(community_actions::became_moderator) - .nullable() - .is_not_null(), - creator_community_actions - .field(community_actions::received_ban) - .nullable() - .is_not_null(), - person_actions::blocked.nullable().is_not_null(), - community_actions::received_ban.nullable().is_not_null(), - local_user_can_mod(), - )) + .select(PersonSavedCombinedViewInternal::as_select()) .into_boxed(); if let Some(type_) = self.type_ { @@ -280,34 +240,26 @@ impl InternalToCombinedView for PersonSavedCombinedViewInternal { post: v.post, community: v.community, creator: v.item_creator, - creator_banned_from_community: v.item_creator_banned_from_community, - creator_is_moderator: v.item_creator_is_moderator, + community_actions: v.community_actions, + comment_actions: v.comment_actions, + person_actions: v.person_actions, + instance_actions: v.instance_actions, + creator_community_actions: v.creator_community_actions, creator_is_admin: v.item_creator_is_admin, - creator_blocked: v.item_creator_blocked, - subscribed: v.subscribed, - saved: v.comment_saved, - my_vote: v.my_comment_vote, - banned_from_community: v.banned_from_community, can_mod: v.can_mod, })) } else { Some(PersonSavedCombinedView::Post(PostView { post: v.post, community: v.community, - unread_comments: v.post_unread_comments, creator: v.item_creator, - creator_banned_from_community: v.item_creator_banned_from_community, - creator_is_moderator: v.item_creator_is_moderator, - creator_is_admin: v.item_creator_is_admin, - creator_blocked: v.item_creator_blocked, - subscribed: v.subscribed, - saved: v.post_saved, - read: v.post_read, - hidden: v.post_hidden, - my_vote: v.my_post_vote, image_details: v.image_details, - banned_from_community: v.banned_from_community, - tags: v.post_tags, + community_actions: v.community_actions, + post_actions: v.post_actions, + person_actions: v.person_actions, + instance_actions: v.instance_actions, + creator_community_actions: v.creator_community_actions, + creator_is_admin: v.item_creator_is_admin, can_mod: v.can_mod, })) } @@ -324,12 +276,12 @@ mod tests { }; use lemmy_db_schema::{ source::{ - comment::{Comment, CommentInsertForm, CommentSaved, CommentSavedForm}, + comment::{Comment, CommentActions, CommentInsertForm, CommentSavedForm}, community::{Community, CommunityInsertForm}, instance::Instance, local_user::{LocalUser, LocalUserInsertForm}, person::{Person, PersonInsertForm}, - post::{Post, PostInsertForm, PostSaved, PostSavedForm}, + post::{Post, PostActions, PostInsertForm, PostSavedForm}, }, traits::{Crud, Saveable}, utils::{build_db_pool_for_tests, DbPool}, @@ -424,14 +376,14 @@ mod tests { // Save a few things let save_sara_comment_2 = - CommentSavedForm::new(data.sara_comment_2.id, data.timmy_view.person.id); - CommentSaved::save(pool, &save_sara_comment_2).await?; + CommentSavedForm::new(data.timmy_view.person.id, data.sara_comment_2.id); + CommentActions::save(pool, &save_sara_comment_2).await?; - let save_sara_comment = CommentSavedForm::new(data.sara_comment.id, data.timmy_view.person.id); - CommentSaved::save(pool, &save_sara_comment).await?; + let save_sara_comment = CommentSavedForm::new(data.timmy_view.person.id, data.sara_comment.id); + CommentActions::save(pool, &save_sara_comment).await?; let post_save_form = PostSavedForm::new(data.timmy_post.id, data.timmy.id); - PostSaved::save(pool, &post_save_form).await?; + PostActions::save(pool, &post_save_form).await?; let timmy_saved = PersonSavedCombinedQuery::default() .list(pool, &data.timmy_view) @@ -459,8 +411,8 @@ mod tests { } // Try unsaving 2 things - CommentSaved::unsave(pool, &save_sara_comment).await?; - PostSaved::unsave(pool, &post_save_form).await?; + CommentActions::unsave(pool, &save_sara_comment).await?; + PostActions::unsave(pool, &post_save_form).await?; let timmy_saved = PersonSavedCombinedQuery::default() .list(pool, &data.timmy_view) diff --git a/crates/db_views/src/combined/report_combined_view.rs b/crates/db_views/src/combined/report_combined_view.rs index d929b66a2..a47068d66 100644 --- a/crates/db_views/src/combined/report_combined_view.rs +++ b/crates/db_views/src/combined/report_combined_view.rs @@ -22,7 +22,6 @@ use diesel_async::RunQueryDsl; use i_love_jesus::PaginatedQueryBuilder; use lemmy_db_schema::{ aliases::{self, creator_community_actions}, - impls::community::community_follower_select_subscribed_type, newtypes::{CommunityId, PaginationCursor, PersonId, PostId}, schema::{ comment, @@ -43,7 +42,7 @@ use lemmy_db_schema::{ }, source::combined::report::{report_combined_keys as key, ReportCombined}, traits::{InternalToCombinedView, PaginationCursorBuilder}, - utils::{functions::coalesce, get_conn, DbPool, ReverseTimestampKey}, + utils::{get_conn, DbPool, ReverseTimestampKey}, ReportType, }; use lemmy_utils::error::{LemmyErrorType, LemmyResult}; @@ -255,46 +254,7 @@ impl ReportCombinedQuery { let conn = &mut get_conn(pool).await?; let mut query = ReportCombinedViewInternal::joins(my_person_id) - .select(( - // Post-specific - post_report::all_columns.nullable(), - post::all_columns.nullable(), - coalesce( - post::comments.nullable() - post_actions::read_comments_amount.nullable(), - post::comments, - ) - .nullable(), - post_actions::saved.nullable(), - post_actions::read.nullable().is_not_null(), - post_actions::hidden.nullable().is_not_null(), - post_actions::like_score.nullable(), - // Comment-specific - comment_report::all_columns.nullable(), - comment::all_columns.nullable(), - comment_actions::saved.nullable(), - comment_actions::like_score.nullable(), - // Private-message-specific - private_message_report::all_columns.nullable(), - private_message::all_columns.nullable(), - // Community-specific - community_report::all_columns.nullable(), - // Shared - person::all_columns, - aliases::person1.fields(person::all_columns.nullable()), - community::all_columns.nullable(), - community_follower_select_subscribed_type(), - aliases::person2.fields(person::all_columns.nullable()), - local_user::admin.nullable().is_not_null(), - creator_community_actions - .field(community_actions::received_ban) - .nullable() - .is_not_null(), - creator_community_actions - .field(community_actions::became_moderator) - .nullable() - .is_not_null(), - person_actions::blocked.nullable().is_not_null(), - )) + .select(ReportCombinedViewInternal::as_select()) .into_boxed(); if let Some(community_id) = self.community_id { @@ -414,36 +374,24 @@ impl InternalToCombinedView for ReportCombinedViewInternal { // Use for a short alias let v = self; - if let ( - Some(post_report), - Some(post), - Some(community), - Some(unread_comments), - Some(post_creator), - ) = ( + if let (Some(post_report), Some(post), Some(community), Some(post_creator)) = ( v.post_report, v.post.clone(), v.community.clone(), - v.post_unread_comments, v.item_creator.clone(), ) { Some(ReportCombinedView::Post(PostReportView { post_report, post, community, - unread_comments, - creator: v.report_creator, post_creator, - creator_banned_from_community: v.item_creator_banned_from_community, - creator_is_moderator: v.item_creator_is_moderator, - creator_is_admin: v.item_creator_is_admin, - creator_blocked: v.item_creator_blocked, - subscribed: v.subscribed, - saved: v.post_saved, - read: v.post_read, - hidden: v.post_hidden, - my_vote: v.my_post_vote, + creator: v.report_creator, resolver: v.resolver, + creator_community_actions: v.creator_community_actions, + community_actions: v.community_actions, + post_actions: v.post_actions, + person_actions: v.person_actions, + creator_is_admin: v.item_creator_is_admin, })) } else if let ( Some(comment_report), @@ -465,14 +413,12 @@ impl InternalToCombinedView for ReportCombinedViewInternal { community, creator: v.report_creator, comment_creator, - creator_banned_from_community: v.item_creator_banned_from_community, - creator_is_moderator: v.item_creator_is_moderator, - creator_is_admin: v.item_creator_is_admin, - creator_blocked: v.item_creator_blocked, - subscribed: v.subscribed, - saved: v.comment_saved, - my_vote: v.my_comment_vote, resolver: v.resolver, + creator_community_actions: v.creator_community_actions, + community_actions: v.community_actions, + comment_actions: v.comment_actions, + person_actions: v.person_actions, + creator_is_admin: v.item_creator_is_admin, })) } else if let ( Some(private_message_report), @@ -494,7 +440,6 @@ impl InternalToCombinedView for ReportCombinedViewInternal { community_report, community, creator: v.report_creator, - subscribed: v.subscribed, resolver: v.resolver, })) } else { @@ -527,7 +472,7 @@ mod tests { source::{ comment::{Comment, CommentInsertForm}, comment_report::{CommentReport, CommentReportForm}, - community::{Community, CommunityInsertForm, CommunityModerator, CommunityModeratorForm}, + community::{Community, CommunityActions, CommunityInsertForm, CommunityModeratorForm}, community_report::{CommunityReport, CommunityReportForm}, instance::Instance, local_user::{LocalUser, LocalUserInsertForm}, @@ -595,11 +540,9 @@ mod tests { let inserted_community = Community::create(pool, &community_form).await?; // Make timmy a mod - let timmy_moderator_form = CommunityModeratorForm { - community_id: inserted_community.id, - person_id: inserted_timmy.id, - }; - CommunityModerator::join(pool, &timmy_moderator_form).await?; + let timmy_moderator_form = + CommunityModeratorForm::new(inserted_community.id, inserted_timmy.id); + CommunityActions::join(pool, &timmy_moderator_form).await?; let post_form = PostInsertForm::new( "A test post crv".into(), @@ -926,7 +869,6 @@ mod tests { assert_eq!(read_jessica_report_view.community.id, data.community.id); assert_eq!(read_jessica_report_view.creator.id, data.jessica.id); assert_eq!(read_jessica_report_view.post_creator.id, data.timmy.id); - assert_eq!(read_jessica_report_view.my_vote, None); assert_eq!(read_jessica_report_view.resolver, None); assert_eq!(agg_1.report_count, 1); assert_eq!(agg_1.unresolved_report_count, 1); diff --git a/crates/db_views/src/combined/search_combined_view.rs b/crates/db_views/src/combined/search_combined_view.rs index 21d2176d1..9c486af3a 100644 --- a/crates/db_views/src/combined/search_combined_view.rs +++ b/crates/db_views/src/combined/search_combined_view.rs @@ -8,7 +8,7 @@ use crate::{ SearchCombinedView, SearchCombinedViewInternal, }, - utils::{filter_is_subscribed, filter_not_hidden_or_is_subscribed}, + utils::{filter_is_subscribed, filter_not_unlisted_or_is_subscribed}, }; use diesel::{ dsl::not, @@ -24,7 +24,6 @@ use diesel_async::RunQueryDsl; use i_love_jesus::PaginatedQueryBuilder; use lemmy_db_schema::{ aliases::{creator_community_actions, creator_local_user}, - impls::{community::community_follower_select_subscribed_type, local_user::local_user_can_mod}, newtypes::{CommunityId, PaginationCursor, PersonId}, schema::{ comment, @@ -32,26 +31,17 @@ use lemmy_db_schema::{ community, community_actions, image_details, + instance_actions, local_user, person, person_actions, post, post_actions, - post_tag, search_combined, - tag, }, source::combined::search::{search_combined_keys as key, SearchCombined}, traits::{InternalToCombinedView, PaginationCursorBuilder}, - utils::{ - functions::coalesce, - fuzzy_search, - get_conn, - now, - seconds_to_pg_interval, - DbPool, - ReverseTimestampKey, - }, + utils::{fuzzy_search, get_conn, now, seconds_to_pg_interval, DbPool, ReverseTimestampKey}, ListingType, SearchSortType, SearchType, @@ -128,6 +118,12 @@ impl SearchCombinedViewInternal { .and(community_actions::person_id.nullable().eq(my_person_id)), ); + let instance_actions_join = instance_actions::table.on( + instance_actions::instance_id + .eq(person::instance_id) + .and(instance_actions::person_id.nullable().eq(my_person_id)), + ); + let post_actions_join = post_actions::table.on( post_actions::post_id .eq(post::id) @@ -158,6 +154,7 @@ impl SearchCombinedViewInternal { .left_join(local_user_join) .left_join(creator_local_user_join) .left_join(community_actions_join) + .left_join(instance_actions_join) .left_join(post_actions_join) .left_join(person_actions_join) .left_join(comment_actions_join) @@ -230,53 +227,8 @@ impl SearchCombinedQuery { let conn = &mut get_conn(pool).await?; - let post_tags = post_tag::table - .inner_join(tag::table) - .select(diesel::dsl::sql::( - "json_agg(tag.*)", - )) - .filter(post_tag::post_id.eq(post::id)) - .filter(tag::deleted.eq(false)) - .single_value(); - let mut query = SearchCombinedViewInternal::joins(my_person_id) - .select(( - // Post-specific - post::all_columns.nullable(), - coalesce( - post::comments.nullable() - post_actions::read_comments_amount.nullable(), - post::comments, - ) - .nullable(), - post_actions::saved.nullable(), - post_actions::read.nullable().is_not_null(), - post_actions::hidden.nullable().is_not_null(), - post_actions::like_score.nullable(), - image_details::all_columns.nullable(), - post_tags, - // Comment-specific - comment::all_columns.nullable(), - comment_actions::saved.nullable(), - comment_actions::like_score.nullable(), - // Community-specific - community::all_columns.nullable(), - community_actions::blocked.nullable().is_not_null(), - community_follower_select_subscribed_type(), - // // Shared - person::all_columns.nullable(), - local_user::admin.nullable().is_not_null(), - creator_community_actions - .field(community_actions::became_moderator) - .nullable() - .is_not_null(), - creator_community_actions - .field(community_actions::received_ban) - .nullable() - .is_not_null(), - person_actions::blocked.nullable().is_not_null(), - community_actions::received_ban.nullable().is_not_null(), - local_user_can_mod(), - )) + .select(SearchCombinedViewInternal::as_select()) .into_boxed(); // The filters @@ -359,13 +311,14 @@ impl SearchCombinedQuery { query = query.filter( community::local .eq(true) - .and(filter_not_hidden_or_is_subscribed()) + .and(filter_not_unlisted_or_is_subscribed()) .or(search_combined::person_id.is_not_null().and(person::local)), ); } ListingType::All => { - query = query - .filter(filter_not_hidden_or_is_subscribed().or(search_combined::person_id.is_not_null())) + query = query.filter( + filter_not_unlisted_or_is_subscribed().or(search_combined::person_id.is_not_null()), + ) } ListingType::ModeratorView => { query = query.filter(community_actions::became_moderator.is_not_null()); @@ -425,47 +378,35 @@ impl InternalToCombinedView for SearchCombinedViewInternal { post, community, creator, - creator_banned_from_community: v.item_creator_banned_from_community, - creator_is_moderator: v.item_creator_is_moderator, + community_actions: v.community_actions, + instance_actions: v.instance_actions, + creator_community_actions: v.creator_community_actions, + person_actions: v.person_actions, + comment_actions: v.comment_actions, creator_is_admin: v.item_creator_is_admin, - creator_blocked: v.item_creator_blocked, - subscribed: v.subscribed, - saved: v.comment_saved, - my_vote: v.my_comment_vote, - banned_from_community: v.banned_from_community, can_mod: v.can_mod, })) - } else if let (Some(post), Some(creator), Some(community), Some(unread_comments)) = ( - v.post, - v.item_creator.clone(), - v.community.clone(), - v.post_unread_comments, - ) { + } else if let (Some(post), Some(creator), Some(community)) = + (v.post, v.item_creator.clone(), v.community.clone()) + { Some(SearchCombinedView::Post(PostView { post, community, - unread_comments, creator, - creator_banned_from_community: v.item_creator_banned_from_community, - creator_is_moderator: v.item_creator_is_moderator, creator_is_admin: v.item_creator_is_admin, - creator_blocked: v.item_creator_blocked, - subscribed: v.subscribed, - saved: v.post_saved, - read: v.post_read, - hidden: v.post_hidden, - my_vote: v.my_post_vote, image_details: v.image_details, - banned_from_community: v.banned_from_community, - tags: v.post_tags, + community_actions: v.community_actions, + instance_actions: v.instance_actions, + creator_community_actions: v.creator_community_actions, + person_actions: v.person_actions, + post_actions: v.post_actions, can_mod: v.can_mod, })) } else if let Some(community) = v.community { Some(SearchCombinedView::Community(CommunityView { community, - subscribed: v.subscribed, - blocked: v.community_blocked, - banned_from_community: v.banned_from_community, + community_actions: v.community_actions, + instance_actions: v.instance_actions, can_mod: v.can_mod, })) } else if let Some(person) = v.item_creator { @@ -490,12 +431,12 @@ mod tests { use lemmy_db_schema::{ assert_length, source::{ - comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm, CommentUpdateForm}, + comment::{Comment, CommentActions, CommentInsertForm, CommentLikeForm, CommentUpdateForm}, community::{Community, CommunityInsertForm}, instance::Instance, local_user::{LocalUser, LocalUserInsertForm}, person::{Person, PersonInsertForm}, - post::{Post, PostInsertForm, PostLike, PostLikeForm, PostUpdateForm}, + post::{Post, PostActions, PostInsertForm, PostLikeForm, PostUpdateForm}, }, traits::{Crud, Likeable}, utils::{build_db_pool_for_tests, DbPool}, @@ -583,34 +524,22 @@ mod tests { // Timmy likes and dislikes a few things let timmy_like_post_form = PostLikeForm::new(timmy_post.id, timmy.id, 1); - PostLike::like(pool, &timmy_like_post_form).await?; + PostActions::like(pool, &timmy_like_post_form).await?; let timmy_like_sara_post_form = PostLikeForm::new(sara_post.id, timmy.id, 1); - PostLike::like(pool, &timmy_like_sara_post_form).await?; + PostActions::like(pool, &timmy_like_sara_post_form).await?; let timmy_dislike_post_form = PostLikeForm::new(timmy_post_2.id, timmy.id, -1); - PostLike::like(pool, &timmy_dislike_post_form).await?; + PostActions::like(pool, &timmy_dislike_post_form).await?; - let timmy_like_comment_form = CommentLikeForm { - person_id: timmy.id, - comment_id: timmy_comment.id, - score: 1, - }; - CommentLike::like(pool, &timmy_like_comment_form).await?; + let timmy_like_comment_form = CommentLikeForm::new(timmy.id, timmy_comment.id, 1); + CommentActions::like(pool, &timmy_like_comment_form).await?; - let timmy_like_sara_comment_form = CommentLikeForm { - person_id: timmy.id, - comment_id: sara_comment.id, - score: 1, - }; - CommentLike::like(pool, &timmy_like_sara_comment_form).await?; + let timmy_like_sara_comment_form = CommentLikeForm::new(timmy.id, sara_comment.id, 1); + CommentActions::like(pool, &timmy_like_sara_comment_form).await?; - let timmy_dislike_sara_comment_form = CommentLikeForm { - person_id: timmy.id, - comment_id: sara_comment_2.id, - score: -1, - }; - CommentLike::like(pool, &timmy_dislike_sara_comment_form).await?; + let timmy_dislike_sara_comment_form = CommentLikeForm::new(timmy.id, sara_comment_2.id, -1); + CommentActions::like(pool, &timmy_dislike_sara_comment_form).await?; Ok(Data { instance, diff --git a/crates/db_views/src/comment/comment_view.rs b/crates/db_views/src/comment/comment_view.rs index befd4c04a..e4e54be4e 100644 --- a/crates/db_views/src/comment/comment_view.rs +++ b/crates/db_views/src/comment/comment_view.rs @@ -117,29 +117,18 @@ impl CommentView { ); } - let mut res = query.first::(conn).await?; - - // If a person is given, then my_vote (res.9), if None, should be 0, not null - // Necessary to differentiate between other person's votes - if my_local_user.is_some() && res.my_vote.is_none() { - res.my_vote = Some(0); - } - - Ok(res) + query.first::(conn).await } pub fn map_to_slim(self) -> CommentSlimView { CommentSlimView { comment: self.comment, creator: self.creator, - creator_banned_from_community: self.creator_banned_from_community, - banned_from_community: self.banned_from_community, - creator_is_moderator: self.creator_is_moderator, + comment_actions: self.comment_actions, + creator_community_actions: self.creator_community_actions, + person_actions: self.person_actions, + instance_actions: self.instance_actions, creator_is_admin: self.creator_is_admin, - subscribed: self.subscribed, - saved: self.saved, - creator_blocked: self.creator_blocked, - my_vote: self.my_vote, can_mod: self.can_mod, } } @@ -328,34 +317,30 @@ mod tests { use lemmy_db_schema::{ assert_length, impls::actor_language::UNDETERMINED_ID, - newtypes::{CommentId, LanguageId}, + newtypes::CommentId, source::{ actor_language::LocalUserLanguage, - comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm, CommentUpdateForm}, + comment::{Comment, CommentActions, CommentInsertForm, CommentLikeForm, CommentUpdateForm}, community::{ Community, - CommunityFollower, + CommunityActions, CommunityFollowerForm, CommunityFollowerState, CommunityInsertForm, - CommunityModerator, CommunityModeratorForm, - CommunityPersonBan, CommunityPersonBanForm, CommunityUpdateForm, }, instance::Instance, language::Language, local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm}, - person::{Person, PersonInsertForm}, - person_block::{PersonBlock, PersonBlockForm}, + person::{Person, PersonActions, PersonBlockForm, PersonInsertForm}, post::{Post, PostInsertForm, PostUpdateForm}, site::{Site, SiteInsertForm}, }, traits::{Bannable, Blockable, Crud, Followable, Joinable, Likeable}, - utils::{build_db_pool_for_tests, RANK_DEFAULT}, + utils::build_db_pool_for_tests, CommunityVisibility, - SubscribedType, }; use lemmy_utils::error::LemmyResult; use pretty_assertions::assert_eq; @@ -366,7 +351,7 @@ mod tests { inserted_comment_0: Comment, inserted_comment_1: Comment, inserted_comment_2: Comment, - inserted_comment_5: Comment, + _inserted_comment_5: Comment, inserted_post: Post, timmy_local_user_view: LocalUserView, inserted_sara_person: Person, @@ -474,30 +459,26 @@ mod tests { inserted_post.id, "Comment 5".into(), ); - let inserted_comment_5 = + let _inserted_comment_5 = Comment::create(pool, &comment_form_5, Some(&inserted_comment_4.path)).await?; - let timmy_blocks_sara_form = PersonBlockForm { - person_id: inserted_timmy_person.id, - target_id: inserted_sara_person.id, - }; + let timmy_blocks_sara_form = + PersonBlockForm::new(inserted_timmy_person.id, inserted_sara_person.id); + let inserted_block = PersonActions::block(pool, &timmy_blocks_sara_form).await?; - let inserted_block = PersonBlock::block(pool, &timmy_blocks_sara_form).await?; + assert_eq!( + (inserted_timmy_person.id, inserted_sara_person.id, true), + ( + inserted_block.person_id, + inserted_block.target_id, + inserted_block.blocked.is_some() + ) + ); - let expected_block = PersonBlock { - person_id: inserted_timmy_person.id, - target_id: inserted_sara_person.id, - published: inserted_block.published, - }; - assert_eq!(expected_block, inserted_block); + let comment_like_form = + CommentLikeForm::new(inserted_timmy_person.id, inserted_comment_0.id, 1); - let comment_like_form = CommentLikeForm { - comment_id: inserted_comment_0.id, - person_id: inserted_timmy_person.id, - score: 1, - }; - - let _inserted_comment_like = CommentLike::like(pool, &comment_like_form).await?; + CommentActions::like(pool, &comment_like_form).await?; let timmy_local_user_view = LocalUserView { local_user: inserted_timmy_local_user.clone(), @@ -510,7 +491,7 @@ mod tests { inserted_comment_0, inserted_comment_1, inserted_comment_2, - inserted_comment_5, + _inserted_comment_5, inserted_post, timmy_local_user_view, inserted_sara_person, @@ -526,12 +507,6 @@ mod tests { let pool = &mut pool.into(); let data = init_data(pool).await?; - let expected_comment_view_no_person = expected_comment_view(&data); - - let mut expected_comment_view_with_person = expected_comment_view_no_person.clone(); - expected_comment_view_with_person.my_vote = Some(1); - expected_comment_view_with_person.can_mod = true; - let read_comment_views_no_person = CommentQuery { sort: (Some(CommentSortType::Old)), post_id: (Some(data.inserted_post.id)), @@ -540,10 +515,8 @@ mod tests { .list(&data.site, pool) .await?; - assert_eq!( - Some(&expected_comment_view_no_person), - read_comment_views_no_person.first() - ); + assert!(read_comment_views_no_person[0].comment_actions.is_none()); + assert!(!read_comment_views_no_person[0].can_mod); let read_comment_views_with_person = CommentQuery { sort: (Some(CommentSortType::Old)), @@ -554,10 +527,11 @@ mod tests { .list(&data.site, pool) .await?; - assert_eq!( - expected_comment_view_with_person, - read_comment_views_with_person[0] - ); + assert!(read_comment_views_with_person[0] + .comment_actions + .as_ref() + .is_some_and(|x| x.like_score == Some(1))); + assert!(read_comment_views_with_person[0].can_mod); // Make sure its 1, not showing the blocked comment assert_length!(5, read_comment_views_with_person); @@ -570,7 +544,9 @@ mod tests { .await?; // Make sure block set the creator blocked - assert!(read_comment_from_blocked_person.creator_blocked); + assert!(read_comment_from_blocked_person + .person_actions + .is_some_and(|x| x.blocked.is_some())); cleanup(data, pool).await } @@ -583,19 +559,19 @@ mod tests { let data = init_data(pool).await?; // Unblock sara first - let timmy_unblocks_sara_form = PersonBlockForm { - person_id: data.timmy_local_user_view.person.id, - target_id: data.inserted_sara_person.id, - }; - PersonBlock::unblock(pool, &timmy_unblocks_sara_form).await?; + let timmy_unblocks_sara_form = PersonBlockForm::new( + data.timmy_local_user_view.person.id, + data.inserted_sara_person.id, + ); + PersonActions::unblock(pool, &timmy_unblocks_sara_form).await?; // Like a new comment - let comment_like_form = CommentLikeForm { - comment_id: data.inserted_comment_1.id, - person_id: data.timmy_local_user_view.person.id, - score: 1, - }; - CommentLike::like(pool, &comment_like_form).await?; + let comment_like_form = CommentLikeForm::new( + data.timmy_local_user_view.person.id, + data.inserted_comment_1.id, + 1, + ); + CommentActions::like(pool, &comment_like_form).await?; let read_liked_comment_views = CommentQuery { local_user: Some(&data.timmy_local_user_view.local_user), @@ -672,10 +648,6 @@ mod tests { .await?; // Make sure a depth limited one only has the top comment - assert_eq!( - expected_comment_view(&data), - read_comment_views_top_max_depth[0] - ); assert_length!(1, read_comment_views_top_max_depth); let child_path = data.inserted_comment_1.path.clone(); @@ -793,11 +765,8 @@ mod tests { // Make one of the inserted persons a moderator let person_id = data.inserted_sara_person.id; let community_id = data.inserted_community.id; - let form = CommunityModeratorForm { - community_id, - person_id, - }; - CommunityModerator::join(pool, &form).await?; + let form = CommunityModeratorForm::new(community_id, person_id); + CommunityActions::join(pool, &form).await?; // Make sure that they come back as a mod in the list let comments = CommentQuery { @@ -808,8 +777,12 @@ mod tests { .await?; assert_eq!(comments[1].creator.name, "sara"); - assert!(comments[1].creator_is_moderator); - assert!(!comments[0].creator_is_moderator); + assert!(comments[1] + .creator_community_actions + .as_ref() + .is_some_and(|x| x.became_moderator.is_some())); + + assert!(comments[0].creator_community_actions.is_none()); cleanup(data, pool).await } @@ -840,7 +813,7 @@ mod tests { } async fn cleanup(data: Data, pool: &mut DbPool<'_>) -> LemmyResult<()> { - CommentLike::remove( + CommentActions::remove_like( pool, data.timmy_local_user_view.person.id, data.inserted_comment_0.id, @@ -859,147 +832,6 @@ mod tests { Ok(()) } - fn expected_comment_view(data: &Data) -> CommentView { - CommentView { - creator_banned_from_community: false, - banned_from_community: false, - creator_is_moderator: false, - creator_is_admin: true, - my_vote: None, - subscribed: SubscribedType::NotSubscribed, - saved: None, - creator_blocked: false, - can_mod: false, - comment: Comment { - id: data.inserted_comment_0.id, - content: "Comment 0".into(), - creator_id: data.timmy_local_user_view.person.id, - post_id: data.inserted_post.id, - removed: false, - deleted: false, - published: data.inserted_comment_0.published, - ap_id: data.inserted_comment_0.ap_id.clone(), - updated: None, - local: true, - distinguished: false, - path: data.inserted_comment_0.clone().path, - language_id: LanguageId(37), - score: 1, - upvotes: 1, - downvotes: 0, - child_count: 5, - hot_rank: RANK_DEFAULT, - controversy_rank: 0.0, - report_count: 0, - unresolved_report_count: 0, - }, - creator: Person { - id: data.timmy_local_user_view.person.id, - name: "timmy".into(), - display_name: None, - published: data.timmy_local_user_view.person.published, - avatar: None, - ap_id: data.timmy_local_user_view.person.ap_id.clone(), - local: true, - banned: false, - deleted: false, - bot_account: false, - bio: None, - banner: None, - updated: None, - inbox_url: data.timmy_local_user_view.person.inbox_url.clone(), - matrix_user_id: None, - ban_expires: None, - instance_id: data.inserted_instance.id, - private_key: data.timmy_local_user_view.person.private_key.clone(), - public_key: data.timmy_local_user_view.person.public_key.clone(), - last_refreshed_at: data.timmy_local_user_view.person.last_refreshed_at, - post_count: 1, - post_score: 0, - comment_count: 5, - comment_score: 1, - }, - post: Post { - id: data.inserted_post.id, - name: data.inserted_post.name.clone(), - creator_id: data.timmy_local_user_view.person.id, - url: None, - body: None, - alt_text: None, - published: data.inserted_post.published, - updated: None, - community_id: data.inserted_community.id, - removed: false, - deleted: false, - locked: false, - nsfw: false, - embed_title: None, - embed_description: None, - embed_video_url: None, - thumbnail_url: None, - ap_id: data.inserted_post.ap_id.clone(), - local: true, - language_id: Default::default(), - featured_community: false, - featured_local: false, - url_content_type: None, - scheduled_publish_time: None, - comments: 6, - score: 0, - upvotes: 0, - downvotes: 0, - newest_comment_time_necro: data.inserted_comment_1.published, - newest_comment_time: data.inserted_comment_5.published, - hot_rank: RANK_DEFAULT, - hot_rank_active: RANK_DEFAULT, - controversy_rank: 0.0, - scaled_rank: RANK_DEFAULT, - instance_id: data.inserted_instance.id, - report_count: 0, - unresolved_report_count: 0, - }, - community: Community { - id: data.inserted_community.id, - name: "test community 5".to_string(), - icon: None, - removed: false, - deleted: false, - nsfw: false, - ap_id: data.inserted_community.ap_id.clone(), - local: true, - title: "nada".to_owned(), - sidebar: None, - description: None, - updated: None, - banner: None, - posting_restricted_to_mods: false, - published: data.inserted_community.published, - instance_id: data.inserted_instance.id, - private_key: data.inserted_community.private_key.clone(), - public_key: data.inserted_community.public_key.clone(), - last_refreshed_at: data.inserted_community.last_refreshed_at, - followers_url: data.inserted_community.followers_url.clone(), - inbox_url: data.inserted_community.inbox_url.clone(), - moderators_url: data.inserted_community.moderators_url.clone(), - featured_url: data.inserted_community.featured_url.clone(), - visibility: CommunityVisibility::Public, - random_number: data.inserted_community.random_number, - subscribers: 0, - posts: 1, - comments: 6, - users_active_day: 0, - users_active_week: 0, - users_active_month: 0, - users_active_half_year: 0, - hot_rank: RANK_DEFAULT, - subscribers_local: 0, - report_count: 0, - unresolved_report_count: 0, - interactions_month: 0, - }, - } - } - #[tokio::test] #[serial] async fn local_only_instance() -> LemmyResult<()> { @@ -1065,13 +897,12 @@ mod tests { ) .await?; - CommunityPersonBan::ban( + CommunityActions::ban( pool, - &CommunityPersonBanForm { - community_id: data.inserted_community.id, - person_id: inserted_banned_from_comm_person.id, - expires: None, - }, + &CommunityPersonBanForm::new( + data.inserted_community.id, + inserted_banned_from_comm_person.id, + ), ) .await?; @@ -1082,7 +913,9 @@ mod tests { ) .await?; - assert!(comment_view.banned_from_community); + assert!(comment_view + .community_actions + .is_some_and(|x| x.received_ban.is_some())); Person::delete(pool, inserted_banned_from_comm_person.id).await?; cleanup(data, pool).await @@ -1102,7 +935,7 @@ mod tests { ) .await?; - assert!(!comment_view.banned_from_community); + assert!(comment_view.community_actions.is_none()); cleanup(data, pool).await } @@ -1198,15 +1031,13 @@ mod tests { data.timmy_local_user_view.local_user.admin = false; // User can view after following - CommunityFollower::follow( + CommunityActions::follow( pool, - &CommunityFollowerForm { - state: Some(CommunityFollowerState::Accepted), - ..CommunityFollowerForm::new( - data.inserted_community.id, - data.timmy_local_user_view.person.id, - ) - }, + &CommunityFollowerForm::new( + data.inserted_community.id, + data.timmy_local_user_view.person.id, + CommunityFollowerState::Accepted, + ), ) .await?; let read_comment_listing = CommentQuery { diff --git a/crates/db_views/src/community/community_follower_view.rs b/crates/db_views/src/community/community_follower_view.rs index 60ec9d960..01e11fcfc 100644 --- a/crates/db_views/src/community/community_follower_view.rs +++ b/crates/db_views/src/community/community_follower_view.rs @@ -7,12 +7,12 @@ use diesel::{ BoolExpressionMethods, ExpressionMethods, JoinOnDsl, + NullableExpressionMethods, QueryDsl, SelectableHelper, }; use diesel_async::RunQueryDsl; use lemmy_db_schema::{ - impls::community::community_follower_select_subscribed_type, newtypes::{CommunityId, DbUrl, InstanceId, PersonId}, schema::{community, community_actions, person}, source::{ @@ -21,7 +21,6 @@ use lemmy_db_schema::{ }, utils::{get_conn, limit_and_offset, DbPool}, CommunityVisibility, - SubscribedType, }; use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; @@ -151,7 +150,7 @@ impl CommunityFollowerView { person::all_columns, community::all_columns, is_new_instance, - community_follower_select_subscribed_type(), + community_actions::follow_state.nullable(), )) .into_boxed(); if all_communities { @@ -168,17 +167,17 @@ impl CommunityFollowerView { .order_by(community_actions::followed.asc()) .limit(limit) .offset(offset) - .load::<(Person, Community, bool, SubscribedType)>(conn) + .load::<(Person, Community, bool, Option)>(conn) .await?; Ok( res .into_iter() .map( - |(person, community, is_new_instance, subscribed)| PendingFollow { + |(person, community, is_new_instance, follow_state)| PendingFollow { person, community, is_new_instance, - subscribed, + follow_state, }, ) .collect(), @@ -259,7 +258,7 @@ mod tests { use super::*; use lemmy_db_schema::{ source::{ - community::{CommunityFollower, CommunityFollowerForm, CommunityInsertForm}, + community::{CommunityActions, CommunityFollowerForm, CommunityInsertForm}, instance::Instance, person::PersonInsertForm, }, @@ -300,11 +299,12 @@ mod tests { assert!(has_followers.is_err()); // insert unapproved follower - let mut follower_form = CommunityFollowerForm { - state: Some(CommunityFollowerState::ApprovalRequired), - ..CommunityFollowerForm::new(community.id, person.id) - }; - CommunityFollower::follow(pool, &follower_form).await?; + let mut follower_form = CommunityFollowerForm::new( + community.id, + person.id, + CommunityFollowerState::ApprovalRequired, + ); + CommunityActions::follow(pool, &follower_form).await?; // still returns error let has_followers = CommunityFollowerView::check_has_followers_from_instance( @@ -316,8 +316,8 @@ mod tests { assert!(has_followers.is_err()); // mark follower as accepted - follower_form.state = Some(CommunityFollowerState::Accepted); - CommunityFollower::follow(pool, &follower_form).await?; + follower_form.follow_state = CommunityFollowerState::Accepted; + CommunityActions::follow(pool, &follower_form).await?; // now returns ok let has_followers = CommunityFollowerView::check_has_followers_from_instance( diff --git a/crates/db_views/src/community/community_view.rs b/crates/db_views/src/community/community_view.rs index 4eb12da53..2d4db5f8e 100644 --- a/crates/db_views/src/community/community_view.rs +++ b/crates/db_views/src/community/community_view.rs @@ -1,6 +1,6 @@ use crate::{ structs::{CommunityModeratorView, CommunitySortType, CommunityView, PersonView}, - utils::{filter_is_subscribed, filter_not_hidden_or_is_subscribed}, + utils::{filter_is_subscribed, filter_not_unlisted_or_is_subscribed}, }; use diesel::{ result::Error, @@ -131,16 +131,16 @@ impl CommunityQuery<'_> { if !o.is_mod_or_admin { query = query .filter(Community::hide_removed_and_deleted()) - .filter(filter_not_hidden_or_is_subscribed()); + .filter(filter_not_unlisted_or_is_subscribed()); } if let Some(listing_type) = o.listing_type { query = match listing_type { - ListingType::All => query.filter(filter_not_hidden_or_is_subscribed()), + ListingType::All => query.filter(filter_not_unlisted_or_is_subscribed()), ListingType::Subscribed => query.filter(filter_is_subscribed()), ListingType::Local => query .filter(community::local.eq(true)) - .filter(filter_not_hidden_or_is_subscribed()), + .filter(filter_not_unlisted_or_is_subscribed()), ListingType::ModeratorView => { query.filter(community_actions::became_moderator.is_not_null()) } @@ -199,11 +199,10 @@ mod tests { source::{ community::{ Community, - CommunityFollower, + CommunityActions, CommunityFollowerForm, CommunityFollowerState, CommunityInsertForm, - CommunityModerator, CommunityModeratorForm, CommunityUpdateForm, }, @@ -215,7 +214,6 @@ mod tests { traits::{Crud, Followable, Joinable}, utils::{build_db_pool_for_tests, DbPool}, CommunityVisibility, - SubscribedType, }; use lemmy_utils::error::{LemmyErrorType, LemmyResult}; use serial_test::serial; @@ -312,28 +310,31 @@ mod tests { #[tokio::test] #[serial] - async fn subscribe_state() -> LemmyResult<()> { + async fn follow_state() -> LemmyResult<()> { let pool = &build_db_pool_for_tests(); let pool = &mut pool.into(); let data = init_data(pool).await?; let community = &data.communities[0]; let unauthenticated = CommunityView::read(pool, community.id, None, false).await?; - assert_eq!(SubscribedType::NotSubscribed, unauthenticated.subscribed); + assert!(unauthenticated.community_actions.is_none()); let authenticated = CommunityView::read(pool, community.id, Some(&data.local_user), false).await?; - assert_eq!(SubscribedType::NotSubscribed, authenticated.subscribed); + assert!(authenticated.community_actions.is_none()); - let form = CommunityFollowerForm { - state: Some(CommunityFollowerState::Pending), - ..CommunityFollowerForm::new(community.id, data.local_user.person_id) - }; - CommunityFollower::follow(pool, &form).await?; + let form = CommunityFollowerForm::new( + community.id, + data.local_user.person_id, + CommunityFollowerState::Pending, + ); + CommunityActions::follow(pool, &form).await?; let with_pending_follow = CommunityView::read(pool, community.id, Some(&data.local_user), false).await?; - assert_eq!(SubscribedType::Pending, with_pending_follow.subscribed); + assert!(with_pending_follow + .community_actions + .is_some_and(|x| x.follow_state == Some(CommunityFollowerState::Pending))); // mark community private and set follow as approval required Community::update( @@ -345,27 +346,30 @@ mod tests { }, ) .await?; - let form = CommunityFollowerForm { - state: Some(CommunityFollowerState::ApprovalRequired), - ..CommunityFollowerForm::new(community.id, data.local_user.person_id) - }; - CommunityFollower::follow(pool, &form).await?; + let form = CommunityFollowerForm::new( + community.id, + data.local_user.person_id, + CommunityFollowerState::ApprovalRequired, + ); + CommunityActions::follow(pool, &form).await?; let with_approval_required_follow = CommunityView::read(pool, community.id, Some(&data.local_user), false).await?; - assert_eq!( - SubscribedType::ApprovalRequired, - with_approval_required_follow.subscribed - ); + assert!(with_approval_required_follow + .community_actions + .is_some_and(|x| x.follow_state == Some(CommunityFollowerState::ApprovalRequired))); - let form = CommunityFollowerForm { - state: Some(CommunityFollowerState::Accepted), - ..CommunityFollowerForm::new(community.id, data.local_user.person_id) - }; - CommunityFollower::follow(pool, &form).await?; + let form = CommunityFollowerForm::new( + community.id, + data.local_user.person_id, + CommunityFollowerState::Accepted, + ); + CommunityActions::follow(pool, &form).await?; let with_accepted_follow = CommunityView::read(pool, community.id, Some(&data.local_user), false).await?; - assert_eq!(SubscribedType::Subscribed, with_accepted_follow.subscribed); + assert!(with_accepted_follow + .community_actions + .is_some_and(|x| x.follow_state == Some(CommunityFollowerState::Accepted))); cleanup(data, pool).await } @@ -463,17 +467,11 @@ mod tests { let person_id = data.local_user.person_id; // Now join the mod team of test community 1 and 2 - let mod_form_1 = CommunityModeratorForm { - community_id: data.communities[0].id, - person_id, - }; - CommunityModerator::join(pool, &mod_form_1).await?; + let mod_form_1 = CommunityModeratorForm::new(data.communities[0].id, person_id); + CommunityActions::join(pool, &mod_form_1).await?; - let mod_form_2 = CommunityModeratorForm { - community_id: data.communities[1].id, - person_id, - }; - CommunityModerator::join(pool, &mod_form_2).await?; + let mod_form_2 = CommunityModeratorForm::new(data.communities[1].id, person_id); + CommunityActions::join(pool, &mod_form_2).await?; let mod_query = CommunityQuery { local_user: Some(&data.local_user), diff --git a/crates/db_views/src/post/post_view.rs b/crates/db_views/src/post/post_view.rs index 8296eb864..7e8dec0f1 100644 --- a/crates/db_views/src/post/post_view.rs +++ b/crates/db_views/src/post/post_view.rs @@ -1,6 +1,6 @@ use crate::{ structs::{PostPaginationCursor, PostView}, - utils::{filter_blocked, filter_is_subscribed, filter_not_hidden_or_is_subscribed}, + utils::{filter_blocked, filter_is_subscribed, filter_not_unlisted_or_is_subscribed}, }; use diesel::{ debug_query, @@ -15,15 +15,13 @@ use diesel::{ OptionalExtension, PgTextExpressionMethods, QueryDsl, + SelectableHelper, TextExpressionMethods, }; use diesel_async::RunQueryDsl; use lemmy_db_schema::{ - aliases::{creator_community_actions, creator_local_user}, - impls::{ - community::community_follower_select_subscribed_type, - local_user::{local_user_can_mod, LocalUserOptionHelper}, - }, + aliases::creator_community_actions, + impls::local_user::LocalUserOptionHelper, newtypes::{CommunityId, PersonId, PostId}, schema::{ community, @@ -36,8 +34,6 @@ use lemmy_db_schema::{ person_actions, post, post_actions, - post_tag, - tag, }, source::{ community::CommunityFollowerState, @@ -47,7 +43,6 @@ use lemmy_db_schema::{ }, traits::Crud, utils::{ - functions::coalesce, fuzzy_search, get_conn, limit_and_offset, @@ -124,17 +119,6 @@ impl PostView { .left_join(local_user_join) } - #[diesel::dsl::auto_type(no_type_alias)] - fn creator_is_admin() -> _ { - exists( - creator_local_user.filter( - post::creator_id - .eq(creator_local_user.field(local_user::person_id)) - .and(creator_local_user.field(local_user::admin).eq(true)), - ), - ) - } - pub async fn read( pool: &mut DbPool<'_>, post_id: PostId, @@ -144,45 +128,9 @@ impl PostView { let conn = &mut get_conn(pool).await?; let my_person_id = my_local_user.person_id(); - let post_tags = post_tag::table - .inner_join(tag::table) - .select(diesel::dsl::sql::( - "json_agg(tag.*)", - )) - .filter(post_tag::post_id.eq(post::id)) - .filter(tag::deleted.eq(false)) - .single_value(); - let mut query = Self::joins(my_person_id) .filter(post::id.eq(post_id)) - .select(( - post::all_columns, - person::all_columns, - community::all_columns, - image_details::all_columns.nullable(), - creator_community_actions - .field(community_actions::received_ban) - .nullable() - .is_not_null(), - community_actions::received_ban.nullable().is_not_null(), - creator_community_actions - .field(community_actions::became_moderator) - .nullable() - .is_not_null(), - Self::creator_is_admin(), - community_follower_select_subscribed_type(), - post_actions::saved.nullable(), - post_actions::read.nullable().is_not_null(), - post_actions::hidden.nullable().is_not_null(), - person_actions::blocked.nullable().is_not_null(), - post_actions::like_score.nullable(), - coalesce( - post::comments.nullable() - post_actions::read_comments_amount.nullable(), - post::comments, - ), - post_tags, - local_user_can_mod(), - )) + .select(Self::as_select()) .into_boxed(); // Hide deleted and removed for non-admins or mods @@ -389,44 +337,8 @@ impl<'a> PostQuery<'a> { let my_person_id = o.local_user.person_id(); let my_local_user_id = o.local_user.local_user_id(); - let post_tags = post_tag::table - .inner_join(tag::table) - .select(diesel::dsl::sql::( - "json_agg(tag.*)", - )) - .filter(post_tag::post_id.eq(post::id)) - .filter(tag::deleted.eq(false)) - .single_value(); - let mut query = PostView::joins(my_person_id) - .select(( - post::all_columns, - person::all_columns, - community::all_columns, - image_details::all_columns.nullable(), - creator_community_actions - .field(community_actions::received_ban) - .nullable() - .is_not_null(), - community_actions::received_ban.nullable().is_not_null(), - creator_community_actions - .field(community_actions::became_moderator) - .nullable() - .is_not_null(), - PostView::creator_is_admin(), - community_follower_select_subscribed_type(), - post_actions::saved.nullable(), - post_actions::read.nullable().is_not_null(), - post_actions::hidden.nullable().is_not_null(), - person_actions::blocked.nullable().is_not_null(), - post_actions::like_score.nullable(), - coalesce( - post::comments.nullable() - post_actions::read_comments_amount.nullable(), - post::comments, - ), - post_tags, - local_user_can_mod(), - )) + .select(PostView::as_select()) .into_boxed(); // hide posts from deleted communities @@ -465,9 +377,9 @@ impl<'a> PostQuery<'a> { ListingType::Local => { query = query .filter(community::local.eq(true)) - .filter(filter_not_hidden_or_is_subscribed()); + .filter(filter_not_unlisted_or_is_subscribed()); } - ListingType::All => query = query.filter(filter_not_hidden_or_is_subscribed()), + ListingType::All => query = query.filter(filter_not_unlisted_or_is_subscribed()), ListingType::ModeratorView => { query = query.filter(community_actions::became_moderator.is_not_null()); } @@ -655,7 +567,7 @@ impl<'a> PostQuery<'a> { mod tests { use crate::{ post::post_view::{PaginationCursorData, PostQuery, PostView}, - structs::{LocalUserView, PostTags}, + structs::LocalUserView, }; use chrono::Utc; use diesel_async::SimpleAsyncConnection; @@ -667,41 +579,35 @@ mod tests { comment::{Comment, CommentInsertForm}, community::{ Community, - CommunityFollower, + CommunityActions, + CommunityBlockForm, CommunityFollowerForm, CommunityFollowerState, CommunityInsertForm, - CommunityModerator, CommunityModeratorForm, - CommunityPersonBan, CommunityPersonBanForm, CommunityUpdateForm, }, - community_block::{CommunityBlock, CommunityBlockForm}, - instance::Instance, - instance_block::{InstanceBlock, InstanceBlockForm}, + instance::{Instance, InstanceActions, InstanceBlockForm}, language::Language, local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm}, - person::{Person, PersonInsertForm}, - person_block::{PersonBlock, PersonBlockForm}, + person::{Person, PersonActions, PersonBlockForm, PersonInsertForm}, post::{ Post, - PostHide, + PostActions, + PostHideForm, PostInsertForm, - PostLike, PostLikeForm, - PostRead, PostReadForm, PostUpdateForm, }, site::Site, tag::{PostTagInsertForm, Tag, TagInsertForm}, }, - traits::{Bannable, Blockable, Crud, Followable, Joinable, Likeable}, - utils::{build_db_pool, get_conn, uplete, ActualDbPool, DbPool, RANK_DEFAULT}, + traits::{Bannable, Blockable, Crud, Followable, Hideable, Joinable, Likeable, Readable}, + utils::{build_db_pool, get_conn, uplete, ActualDbPool, DbPool}, CommunityVisibility, PostSortType, - SubscribedType, }; use lemmy_utils::error::{LemmyErrorType, LemmyResult}; use pretty_assertions::assert_eq; @@ -730,8 +636,8 @@ mod tests { post: Post, bot_post: Post, post_with_tags: Post, - tag_1: Tag, - tag_2: Tag, + _tag_1: Tag, + _tag_2: Tag, site: Site, } @@ -805,12 +711,8 @@ mod tests { Post::create(pool, &post_from_blocked_person).await?; // block that person - let person_block = PersonBlockForm { - person_id: inserted_tegan_person.id, - target_id: inserted_john_person.id, - }; - - PersonBlock::block(pool, &person_block).await?; + let person_block = PersonBlockForm::new(inserted_tegan_person.id, inserted_john_person.id); + PersonActions::block(pool, &person_block).await?; // Two community post tags let tag_1 = Tag::create( @@ -918,8 +820,8 @@ mod tests { post, bot_post, post_with_tags, - tag_1, - tag_2, + _tag_1: tag_1, + _tag_2: tag_2, site, }) } @@ -982,17 +884,11 @@ mod tests { ) .await?; - let expected_post_listing_with_user = expected_post_view(data)?; - - // Should be only one person, IE the bot post, and blocked should be missing assert_eq!( vec![post_listing_single_with_person.clone()], read_post_listing ); - assert_eq!( - expected_post_listing_with_user, - post_listing_single_with_person - ); + assert_eq!(data.post.id, post_listing_single_with_person.post.id); let local_user_form = LocalUserUpdateForm { show_bot_accounts: Some(true), @@ -1038,23 +934,16 @@ mod tests { let read_post_listing_single_no_person = PostView::read(pool, data.post.id, None, false).await?; - let mut expected_post_listing_no_person = expected_post_view(data)?; - expected_post_listing_no_person.can_mod = false; - // Should be 2 posts, with the bot post, and the blocked assert_eq!( vec![POST_WITH_TAGS, POST_BY_BOT, POST, POST_BY_BLOCKED_PERSON], names(&read_post_listing_multiple_no_person) ); - assert_eq!( - Some(&expected_post_listing_no_person), - read_post_listing_multiple_no_person.get(2) - ); - assert_eq!( - expected_post_listing_no_person, - read_post_listing_single_no_person - ); + assert!(read_post_listing_multiple_no_person + .get(2) + .is_some_and(|x| x.post.id == data.post.id)); + assert_eq!(false, read_post_listing_single_no_person.can_mod); Ok(()) } @@ -1125,11 +1014,9 @@ mod tests { let pool = &data.pool(); let pool = &mut pool.into(); - let community_block = CommunityBlockForm { - person_id: data.tegan_local_user_view.person.id, - community_id: data.community.id, - }; - CommunityBlock::block(pool, &community_block).await?; + let community_block = + CommunityBlockForm::new(data.community.id, data.tegan_local_user_view.person.id); + CommunityActions::block(pool, &community_block).await?; let read_post_listings_with_person_after_block = PostQuery { community_id: Some(data.community.id), @@ -1140,7 +1027,7 @@ mod tests { // Should be 0 posts after the community block assert_eq!(read_post_listings_with_person_after_block, vec![]); - CommunityBlock::unblock(pool, &community_block).await?; + CommunityActions::unblock(pool, &community_block).await?; Ok(()) } @@ -1153,15 +1040,16 @@ mod tests { let post_like_form = PostLikeForm::new(data.post.id, data.tegan_local_user_view.person.id, 1); - let inserted_post_like = PostLike::like(pool, &post_like_form).await?; + let inserted_post_like = PostActions::like(pool, &post_like_form).await?; - let expected_post_like = PostLike { - post_id: data.post.id, - person_id: data.tegan_local_user_view.person.id, - published: inserted_post_like.published, - score: 1, - }; - assert_eq!(expected_post_like, inserted_post_like); + assert_eq!( + (data.post.id, data.tegan_local_user_view.person.id, Some(1)), + ( + inserted_post_like.post_id, + inserted_post_like.person_id, + inserted_post_like.like_score, + ) + ); let post_listing_single_with_person = PostView::read( pool, @@ -1171,12 +1059,17 @@ mod tests { ) .await?; - let mut expected_post_with_upvote = expected_post_view(data)?; - expected_post_with_upvote.my_vote = Some(1); - expected_post_with_upvote.post.score = 1; - expected_post_with_upvote.post.upvotes = 1; - expected_post_with_upvote.creator.post_score = 1; - assert_eq!(expected_post_with_upvote, post_listing_single_with_person); + assert_eq!( + (true, 1, 1, 1), + ( + post_listing_single_with_person + .post_actions + .is_some_and(|t| t.like_score == Some(1)), + post_listing_single_with_person.post.score, + post_listing_single_with_person.post.upvotes, + post_listing_single_with_person.creator.post_score, + ) + ); let local_user_form = LocalUserUpdateForm { show_bot_accounts: Some(false), @@ -1197,10 +1090,13 @@ mod tests { .list(&data.site, pool) .await?; read_post_listing.remove(0); - assert_eq!(vec![expected_post_with_upvote], read_post_listing); + assert_eq!( + post_listing_single_with_person.post.id, + read_post_listing[0].post.id + ); let like_removed = - PostLike::remove(pool, data.tegan_local_user_view.person.id, data.post.id).await?; + PostActions::remove_like(pool, data.tegan_local_user_view.person.id, data.post.id).await?; assert_eq!(uplete::Count::only_deleted(1), like_removed); Ok(()) } @@ -1215,11 +1111,11 @@ mod tests { // Like both the bot post, and your own // The liked_only should not show your own post let post_like_form = PostLikeForm::new(data.post.id, data.tegan_local_user_view.person.id, 1); - PostLike::like(pool, &post_like_form).await?; + PostActions::like(pool, &post_like_form).await?; let bot_post_like_form = PostLikeForm::new(data.bot_post.id, data.tegan_local_user_view.person.id, 1); - PostLike::like(pool, &bot_post_like_form).await?; + PostActions::like(pool, &bot_post_like_form).await?; // Read the liked only let read_liked_post_listing = PostQuery { @@ -1257,7 +1153,7 @@ mod tests { // Only mark the bot post as read // The read_only should only show the bot post let post_read_form = PostReadForm::new(data.bot_post.id, data.tegan_local_user_view.person.id); - PostRead::mark_as_read(pool, &post_read_form).await?; + PostActions::mark_as_read(pool, &post_read_form).await?; // Only read the post marked as read let read_read_post_listing = PostQuery { @@ -1292,8 +1188,9 @@ mod tests { .map(|p| { ( p.creator.name, - p.creator_is_moderator, - p.creator_is_admin, + p.creator_community_actions + .map(|x| x.became_moderator.is_some()) + .unwrap_or(false), p.can_mod, ) }) @@ -1301,24 +1198,20 @@ mod tests { // Tegan is an admin, so can_mod should be always true let expected_post_listing = vec![ - ("tegan".to_owned(), false, true, true), - ("mybot".to_owned(), false, false, true), - ("tegan".to_owned(), false, true, true), + ("tegan".to_owned(), false, true), + ("mybot".to_owned(), false, true), + ("tegan".to_owned(), false, true), ]; assert_eq!(expected_post_listing, tegan_listings); // Have john become a moderator, then the bot - let john_mod_form = CommunityModeratorForm { - community_id, - person_id: data.john_local_user_view.person.id, - }; - CommunityModerator::join(pool, &john_mod_form).await?; + let john_mod_form = + CommunityModeratorForm::new(community_id, data.john_local_user_view.person.id); + CommunityActions::join(pool, &john_mod_form).await?; - let bot_mod_form = CommunityModeratorForm { - community_id, - person_id: data.bot_local_user_view.person.id, - }; - CommunityModerator::join(pool, &bot_mod_form).await?; + let bot_mod_form = + CommunityModeratorForm::new(community_id, data.bot_local_user_view.person.id); + CommunityActions::join(pool, &bot_mod_form).await?; let john_listings = PostQuery { sort: Some(PostSortType::New), @@ -1331,8 +1224,9 @@ mod tests { .map(|p| { ( p.creator.name, - p.creator_is_moderator, - p.creator_is_admin, + p.creator_community_actions + .map(|x| x.became_moderator.is_some()) + .unwrap_or(false), p.can_mod, ) }) @@ -1340,10 +1234,10 @@ mod tests { // John is a mod, so he can_mod the bots (and his own) posts, but not tegans. let expected_post_listing = vec![ - ("tegan".to_owned(), false, true, false), - ("mybot".to_owned(), true, false, true), - ("tegan".to_owned(), false, true, false), - ("john".to_owned(), true, false, true), + ("tegan".to_owned(), false, false), + ("mybot".to_owned(), true, true), + ("tegan".to_owned(), false, false), + ("john".to_owned(), true, true), ]; assert_eq!(expected_post_listing, john_listings); @@ -1359,23 +1253,24 @@ mod tests { .map(|p| { ( p.creator.name, - p.creator_is_moderator, - p.creator_is_admin, + p.creator_community_actions + .map(|x| x.became_moderator.is_some()) + .unwrap_or(false), p.can_mod, ) }) .collect::>(); let expected_post_listing = vec![ - ("tegan".to_owned(), false, true, false), - ("mybot".to_owned(), true, false, true), - ("tegan".to_owned(), false, true, false), - ("john".to_owned(), true, false, false), + ("tegan".to_owned(), false, false), + ("mybot".to_owned(), true, true), + ("tegan".to_owned(), false, false), + ("john".to_owned(), true, false), ]; assert_eq!(expected_post_listing, bot_listings); // Make the bot leave the mod team, and make sure it can_mod is false. - CommunityModerator::leave(pool, &bot_mod_form).await?; + CommunityActions::leave(pool, &bot_mod_form).await?; let bot_listings = PostQuery { sort: Some(PostSortType::New), @@ -1388,18 +1283,19 @@ mod tests { .map(|p| { ( p.creator.name, - p.creator_is_moderator, - p.creator_is_admin, + p.creator_community_actions + .map(|x| x.became_moderator.is_some()) + .unwrap_or(false), p.can_mod, ) }) .collect::>(); let expected_post_listing = vec![ - ("tegan".to_owned(), false, true, false), - ("mybot".to_owned(), false, false, false), - ("tegan".to_owned(), false, true, false), - ("john".to_owned(), true, false, false), + ("tegan".to_owned(), false, false), + ("mybot".to_owned(), false, false), + ("tegan".to_owned(), false, false), + ("john".to_owned(), true, false), ]; assert_eq!(expected_post_listing, bot_listings); @@ -1576,11 +1472,12 @@ mod tests { assert!(posts.is_empty()); // Follow the community - let form = CommunityFollowerForm { - state: Some(CommunityFollowerState::Accepted), - ..CommunityFollowerForm::new(data.community.id, data.tegan_local_user_view.person.id) - }; - CommunityFollower::follow(pool, &form).await?; + let form = CommunityFollowerForm::new( + data.community.id, + data.tegan_local_user_view.person.id, + CommunityFollowerState::Accepted, + ); + CommunityActions::follow(pool, &form).await?; let posts = data.default_post_query().list(&data.site, pool).await?; assert!(!posts.is_empty()); @@ -1628,11 +1525,9 @@ mod tests { assert_eq!(POST_LISTING_WITH_BLOCKED, *names(&post_listings_all)); // block the instance - let block_form = InstanceBlockForm { - person_id: data.tegan_local_user_view.person.id, - instance_id: blocked_instance.id, - }; - InstanceBlock::block(pool, &block_form).await?; + let block_form = + InstanceBlockForm::new(data.tegan_local_user_view.person.id, blocked_instance.id); + InstanceActions::block(pool, &block_form).await?; // now posts from communities on that instance should be hidden let post_listings_blocked = data.default_post_query().list(&data.site, pool).await?; @@ -1645,16 +1540,23 @@ mod tests { .all(|p| p.post.id != post_from_blocked_instance.id)); // Follow community from the blocked instance to see posts anyway - let mut follow_form = - CommunityFollowerForm::new(inserted_community.id, data.tegan_local_user_view.person.id); - follow_form.state = Some(CommunityFollowerState::Accepted); - CommunityFollower::follow(pool, &follow_form).await?; + let follow_form = CommunityFollowerForm::new( + inserted_community.id, + data.tegan_local_user_view.person.id, + CommunityFollowerState::Accepted, + ); + CommunityActions::follow(pool, &follow_form).await?; let post_listings_bypass = data.default_post_query().list(&data.site, pool).await?; assert_eq!(POST_LISTING_WITH_BLOCKED, *names(&post_listings_bypass)); - CommunityFollower::unfollow(pool, &follow_form).await?; + CommunityActions::unfollow( + pool, + data.tegan_local_user_view.person.id, + inserted_community.id, + ) + .await?; // after unblocking it should return all posts again - InstanceBlock::unblock(pool, &block_form).await?; + InstanceActions::unblock(pool, &block_form).await?; let post_listings_blocked = data.default_post_query().list(&data.site, pool).await?; assert_eq!(POST_LISTING_WITH_BLOCKED, *names(&post_listings_blocked)); @@ -1800,7 +1702,7 @@ mod tests { // Mark a post as read let read_form = PostReadForm::new(data.bot_post.id, data.tegan_local_user_view.person.id); - PostRead::mark_as_read(pool, &read_form).await?; + PostActions::mark_as_read(pool, &read_form).await?; // Make sure you don't see the read post in the results let post_listings_hide_read = data.default_post_query().list(&data.site, pool).await?; @@ -1840,7 +1742,8 @@ mod tests { let pool = &mut pool.into(); // Mark a post as hidden - PostHide::hide(pool, data.bot_post.id, data.tegan_local_user_view.person.id).await?; + let hide_form = PostHideForm::new(data.bot_post.id, data.tegan_local_user_view.person.id); + PostActions::hide(pool, &hide_form).await?; // Make sure you don't see the hidden post in the results let post_listings_hide_hidden = data.default_post_query().list(&data.site, pool).await?; @@ -1864,7 +1767,9 @@ mod tests { ); // Make sure that hidden field is true. - assert!(&post_listings_show_hidden.get(1).is_some_and(|p| p.hidden)); + assert!(&post_listings_show_hidden + .get(1) + .is_some_and(|p| p.post_actions.as_ref().is_some_and(|a| a.hidden.is_some()))); Ok(()) } @@ -1914,135 +1819,6 @@ mod tests { Ok(()) } - fn expected_post_view(data: &Data) -> LemmyResult { - let (inserted_person, inserted_community, inserted_post) = ( - &data.tegan_local_user_view.person, - &data.community, - &data.post, - ); - - Ok(PostView { - post: Post { - id: inserted_post.id, - name: inserted_post.name.clone(), - creator_id: inserted_person.id, - url: None, - body: None, - alt_text: None, - published: inserted_post.published, - updated: None, - community_id: inserted_community.id, - removed: false, - deleted: false, - locked: false, - nsfw: false, - embed_title: None, - embed_description: None, - embed_video_url: None, - thumbnail_url: None, - ap_id: inserted_post.ap_id.clone(), - local: true, - language_id: LanguageId(47), - featured_community: false, - featured_local: false, - url_content_type: None, - scheduled_publish_time: None, - comments: 0, - score: 0, - upvotes: 0, - downvotes: 0, - newest_comment_time_necro: inserted_post.published, - newest_comment_time: inserted_post.published, - hot_rank: RANK_DEFAULT, - hot_rank_active: RANK_DEFAULT, - controversy_rank: 0.0, - scaled_rank: RANK_DEFAULT, - instance_id: data.instance.id, - report_count: 0, - unresolved_report_count: 0, - }, - my_vote: None, - unread_comments: 0, - creator: Person { - id: inserted_person.id, - name: inserted_person.name.clone(), - display_name: None, - published: inserted_person.published, - avatar: None, - ap_id: inserted_person.ap_id.clone(), - local: true, - bot_account: false, - banned: false, - deleted: false, - bio: None, - banner: None, - updated: None, - inbox_url: inserted_person.inbox_url.clone(), - matrix_user_id: None, - ban_expires: None, - instance_id: data.instance.id, - private_key: inserted_person.private_key.clone(), - public_key: inserted_person.public_key.clone(), - last_refreshed_at: inserted_person.last_refreshed_at, - post_count: 2, - post_score: 0, - comment_count: 0, - comment_score: 0, - }, - image_details: None, - creator_banned_from_community: false, - banned_from_community: false, - creator_is_moderator: false, - creator_is_admin: true, - can_mod: true, - community: Community { - id: inserted_community.id, - name: inserted_community.name.clone(), - icon: None, - removed: false, - deleted: false, - nsfw: false, - ap_id: inserted_community.ap_id.clone(), - local: true, - title: "nada".to_owned(), - sidebar: None, - description: None, - updated: None, - banner: None, - posting_restricted_to_mods: false, - published: inserted_community.published, - instance_id: data.instance.id, - private_key: inserted_community.private_key.clone(), - public_key: inserted_community.public_key.clone(), - last_refreshed_at: inserted_community.last_refreshed_at, - followers_url: inserted_community.followers_url.clone(), - inbox_url: inserted_community.inbox_url.clone(), - moderators_url: inserted_community.moderators_url.clone(), - featured_url: inserted_community.featured_url.clone(), - visibility: CommunityVisibility::Public, - random_number: inserted_community.random_number, - subscribers: 0, - posts: 4, - comments: 0, - users_active_day: 0, - users_active_week: 0, - users_active_month: 0, - users_active_half_year: 0, - hot_rank: RANK_DEFAULT, - subscribers_local: 0, - report_count: 0, - unresolved_report_count: 0, - interactions_month: 0, - }, - subscribed: SubscribedType::NotSubscribed, - read: false, - hidden: false, - saved: None, - creator_blocked: false, - tags: PostTags::default(), - }) - } - #[test_context(Data)] #[tokio::test] #[serial] @@ -2109,13 +1885,9 @@ mod tests { ) .await?; - CommunityPersonBan::ban( + CommunityActions::ban( pool, - &CommunityPersonBanForm { - community_id: data.community.id, - person_id: inserted_banned_from_comm_person.id, - expires: None, - }, + &CommunityPersonBanForm::new(data.community.id, inserted_banned_from_comm_person.id), ) .await?; @@ -2127,7 +1899,9 @@ mod tests { ) .await?; - assert!(post_view.banned_from_community); + assert!(post_view + .community_actions + .is_some_and(|x| x.received_ban.is_some())); Person::delete(pool, inserted_banned_from_comm_person.id).await?; Ok(()) @@ -2148,7 +1922,7 @@ mod tests { ) .await?; - assert!(!post_view.banned_from_community); + assert!(post_view.community_actions.is_none()); Ok(()) } @@ -2311,14 +2085,13 @@ mod tests { data.tegan_local_user_view.local_user.admin = false; // User can view after following - CommunityFollower::follow( - pool, - &CommunityFollowerForm { - state: Some(CommunityFollowerState::Accepted), - ..CommunityFollowerForm::new(data.community.id, data.tegan_local_user_view.person.id) - }, - ) - .await?; + let follow_form = CommunityFollowerForm::new( + data.community.id, + data.tegan_local_user_view.person.id, + CommunityFollowerState::Accepted, + ); + CommunityActions::follow(pool, &follow_form).await?; + let read_post_listing = PostQuery { community_id: Some(data.community.id), local_user: Some(&data.tegan_local_user_view.local_user), @@ -2404,30 +2177,31 @@ mod tests { Ok(()) } - #[test_context(Data)] - #[tokio::test] - #[serial] - async fn post_tags_present(data: &mut Data) -> LemmyResult<()> { - let pool = &data.pool(); - let pool = &mut pool.into(); + // TODO add these back in later + // #[test_context(Data)] + // #[tokio::test] + // #[serial] + // async fn post_tags_present(data: &mut Data) -> LemmyResult<()> { + // let pool = &data.pool(); + // let pool = &mut pool.into(); - let post_view = PostView::read( - pool, - data.post_with_tags.id, - Some(&data.tegan_local_user_view.local_user), - false, - ) - .await?; + // let post_view = PostView::read( + // pool, + // data.post_with_tags.id, + // Some(&data.tegan_local_user_view.local_user), + // false, + // ) + // .await?; - assert_eq!(2, post_view.tags.tags.len()); - assert_eq!(data.tag_1.name, post_view.tags.tags[0].name); - assert_eq!(data.tag_2.name, post_view.tags.tags[1].name); + // assert_eq!(2, post_view.tags.tags.len()); + // assert_eq!(data.tag_1.name, post_view.tags.tags[0].name); + // assert_eq!(data.tag_2.name, post_view.tags.tags[1].name); - let all_posts = data.default_post_query().list(&data.site, pool).await?; - assert_eq!(2, all_posts[0].tags.tags.len()); // post with tags - assert_eq!(0, all_posts[1].tags.tags.len()); // bot post - assert_eq!(0, all_posts[2].tags.tags.len()); // normal post + // let all_posts = data.default_post_query().list(&data.site, pool).await?; + // assert_eq!(2, all_posts[0].tags.tags.len()); // post with tags + // assert_eq!(0, all_posts[1].tags.tags.len()); // bot post + // assert_eq!(0, all_posts[2].tags.tags.len()); // normal post - Ok(()) - } + // Ok(()) + // } } diff --git a/crates/db_views/src/registration_applications/registration_application_view.rs b/crates/db_views/src/registration_applications/registration_application_view.rs index f3bac8f71..5040b438c 100644 --- a/crates/db_views/src/registration_applications/registration_application_view.rs +++ b/crates/db_views/src/registration_applications/registration_application_view.rs @@ -20,13 +20,17 @@ use lemmy_db_schema::{ impl RegistrationApplicationView { #[diesel::dsl::auto_type(no_type_alias)] fn joins() -> _ { + let local_user_join = + local_user::table.on(registration_application::local_user_id.eq(local_user::id)); + + let creator_join = person::table.on(local_user::person_id.eq(person::id)); + let admin_join = aliases::person1 + .on(registration_application::admin_id.eq(aliases::person1.field(person::id).nullable())); + registration_application::table - .inner_join(local_user::table.on(registration_application::local_user_id.eq(local_user::id))) - .inner_join(person::table.on(local_user::person_id.eq(person::id))) - .left_join( - aliases::person1 - .on(registration_application::admin_id.eq(aliases::person1.field(person::id).nullable())), - ) + .inner_join(local_user_join) + .inner_join(creator_join) + .left_join(admin_join) } pub async fn read(pool: &mut DbPool<'_>, id: RegistrationApplicationId) -> Result { diff --git a/crates/db_views/src/reports/comment_report_view.rs b/crates/db_views/src/reports/comment_report_view.rs index 01f8211e6..fcefdcb65 100644 --- a/crates/db_views/src/reports/comment_report_view.rs +++ b/crates/db_views/src/reports/comment_report_view.rs @@ -1,17 +1,16 @@ use crate::structs::CommentReportView; use diesel::{ - dsl::now, result::Error, BoolExpressionMethods, ExpressionMethods, JoinOnDsl, NullableExpressionMethods, QueryDsl, + SelectableHelper, }; use diesel_async::RunQueryDsl; use lemmy_db_schema::{ aliases::{self, creator_community_actions}, - impls::community::community_follower_select_subscribed_type, newtypes::{CommentReportId, PersonId}, schema::{ comment, @@ -24,7 +23,7 @@ use lemmy_db_schema::{ person_actions, post, }, - utils::{functions::coalesce, get_conn, DbPool}, + utils::{get_conn, DbPool}, }; impl CommentReportView { @@ -103,37 +102,7 @@ impl CommentReportView { let conn = &mut get_conn(pool).await?; Self::joins(my_person_id) .filter(comment_report::id.eq(report_id)) - .select(( - comment_report::all_columns, - comment::all_columns, - post::all_columns, - community::all_columns, - person::all_columns, - aliases::person1.fields(person::all_columns), - coalesce( - creator_community_actions - .field(community_actions::received_ban) - .nullable() - .is_not_null() - .or( - creator_community_actions - .field(community_actions::ban_expires) - .nullable() - .gt(now), - ), - false, - ), - creator_community_actions - .field(community_actions::became_moderator) - .nullable() - .is_not_null(), - local_user::admin.nullable().is_not_null(), - person_actions::blocked.nullable().is_not_null(), - community_follower_select_subscribed_type(), - comment_actions::saved.nullable(), - comment_actions::like_score.nullable(), - aliases::person2.fields(person::all_columns).nullable(), - )) + .select(Self::as_select()) .first(conn) .await } diff --git a/crates/db_views/src/reports/community_report_view.rs b/crates/db_views/src/reports/community_report_view.rs index e5a938cad..aacaa1392 100644 --- a/crates/db_views/src/reports/community_report_view.rs +++ b/crates/db_views/src/reports/community_report_view.rs @@ -6,11 +6,11 @@ use diesel::{ JoinOnDsl, NullableExpressionMethods, QueryDsl, + SelectableHelper, }; use diesel_async::RunQueryDsl; use lemmy_db_schema::{ aliases, - impls::community::community_follower_select_subscribed_type, newtypes::{CommunityReportId, PersonId}, schema::{community, community_actions, community_report, person}, utils::{get_conn, DbPool}, @@ -27,6 +27,12 @@ impl CommunityReportView { ) -> Result { let conn = &mut get_conn(pool).await?; + let resolver_id = aliases::person2.field(person::id); + + let report_creator_join = person::table.on(community_report::creator_id.eq(person::id)); + let resolver_join = + aliases::person2.on(community_report::resolver_id.eq(resolver_id.nullable())); + let community_actions_join = community_actions::table.on( community_actions::community_id .eq(community_report::community_id) @@ -36,19 +42,10 @@ impl CommunityReportView { community_report::table .find(report_id) .inner_join(community::table) - .inner_join(person::table.on(community_report::creator_id.eq(person::id))) - .left_join( - aliases::person2 - .on(community_report::resolver_id.eq(aliases::person2.field(person::id).nullable())), - ) + .inner_join(report_creator_join) + .left_join(resolver_join) .left_join(community_actions_join) - .select(( - community_report::all_columns, - community::all_columns, - person::all_columns, - community_follower_select_subscribed_type(), - aliases::person2.fields(person::all_columns.nullable()), - )) + .select(Self::as_select()) .first(conn) .await } diff --git a/crates/db_views/src/reports/post_report_view.rs b/crates/db_views/src/reports/post_report_view.rs index c9fae7db3..596a95da0 100644 --- a/crates/db_views/src/reports/post_report_view.rs +++ b/crates/db_views/src/reports/post_report_view.rs @@ -6,11 +6,11 @@ use diesel::{ JoinOnDsl, NullableExpressionMethods, QueryDsl, + SelectableHelper, }; use diesel_async::RunQueryDsl; use lemmy_db_schema::{ aliases::{self, creator_community_actions}, - impls::community::community_follower_select_subscribed_type, newtypes::{PersonId, PostReportId}, schema::{ community, @@ -22,7 +22,7 @@ use lemmy_db_schema::{ post_actions, post_report, }, - utils::{functions::coalesce, get_conn, DbPool}, + utils::{get_conn, DbPool}, }; impl PostReportView { @@ -99,33 +99,7 @@ impl PostReportView { Self::joins(my_person_id) .filter(post_report::id.eq(report_id)) - .select(( - post_report::all_columns, - post::all_columns, - community::all_columns, - person::all_columns, - aliases::person1.fields(person::all_columns), - creator_community_actions - .field(community_actions::received_ban) - .nullable() - .is_not_null(), - creator_community_actions - .field(community_actions::became_moderator) - .nullable() - .is_not_null(), - local_user::admin.nullable().is_not_null(), - community_follower_select_subscribed_type(), - post_actions::saved.nullable(), - post_actions::read.nullable().is_not_null(), - post_actions::hidden.nullable().is_not_null(), - person_actions::blocked.nullable().is_not_null(), - post_actions::like_score.nullable(), - coalesce( - post::comments.nullable() - post_actions::read_comments_amount.nullable(), - post::comments, - ), - aliases::person2.fields(person::all_columns.nullable()), - )) + .select(Self::as_select()) .first(conn) .await } diff --git a/crates/db_views/src/reports/private_message_report_view.rs b/crates/db_views/src/reports/private_message_report_view.rs index 956ccf0e1..3b0bf82b9 100644 --- a/crates/db_views/src/reports/private_message_report_view.rs +++ b/crates/db_views/src/reports/private_message_report_view.rs @@ -1,5 +1,12 @@ use crate::structs::PrivateMessageReportView; -use diesel::{result::Error, ExpressionMethods, JoinOnDsl, NullableExpressionMethods, QueryDsl}; +use diesel::{ + result::Error, + ExpressionMethods, + JoinOnDsl, + NullableExpressionMethods, + QueryDsl, + SelectableHelper, +}; use diesel_async::RunQueryDsl; use lemmy_db_schema::{ aliases, @@ -17,26 +24,23 @@ impl PrivateMessageReportView { report_id: PrivateMessageReportId, ) -> Result { let conn = &mut get_conn(pool).await?; + + let recipient_id = aliases::person1.field(person::id); + let resolver_id = aliases::person2.field(person::id); + + let report_creator_join = person::table.on(private_message_report::creator_id.eq(person::id)); + let private_message_creator_join = + aliases::person1.on(private_message::creator_id.eq(recipient_id)); + let resolver_join = + aliases::person2.on(private_message_report::resolver_id.eq(resolver_id.nullable())); + private_message_report::table .find(report_id) .inner_join(private_message::table) - .inner_join(person::table.on(private_message::creator_id.eq(person::id))) - .inner_join( - aliases::person1 - .on(private_message_report::creator_id.eq(aliases::person1.field(person::id))), - ) - .left_join( - aliases::person2.on( - private_message_report::resolver_id.eq(aliases::person2.field(person::id).nullable()), - ), - ) - .select(( - private_message_report::all_columns, - private_message::all_columns, - person::all_columns, - aliases::person1.fields(person::all_columns), - aliases::person2.fields(person::all_columns).nullable(), - )) + .inner_join(report_creator_join) + .inner_join(private_message_creator_join) + .left_join(resolver_join) + .select(Self::as_select()) .first(conn) .await } diff --git a/crates/db_views/src/site/vote_view.rs b/crates/db_views/src/site/vote_view.rs index b6dd61fb5..d5becb46b 100644 --- a/crates/db_views/src/site/vote_view.rs +++ b/crates/db_views/src/site/vote_view.rs @@ -101,15 +101,14 @@ impl VoteView { #[cfg(test)] mod tests { - use crate::structs::VoteView; use lemmy_db_schema::{ source::{ - comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm}, - community::{Community, CommunityInsertForm, CommunityPersonBan, CommunityPersonBanForm}, + comment::{Comment, CommentActions, CommentInsertForm, CommentLikeForm}, + community::{Community, CommunityActions, CommunityInsertForm, CommunityPersonBanForm}, instance::Instance, person::{Person, PersonInsertForm}, - post::{Post, PostInsertForm, PostLike, PostLikeForm}, + post::{Post, PostActions, PostInsertForm, PostLikeForm}, }, traits::{Bannable, Crud, Likeable}, utils::build_db_pool_for_tests, @@ -158,11 +157,11 @@ mod tests { // Timmy upvotes his own post let timmy_post_vote_form = PostLikeForm::new(inserted_post.id, inserted_timmy.id, 1); - PostLike::like(pool, &timmy_post_vote_form).await?; + PostActions::like(pool, &timmy_post_vote_form).await?; // Sara downvotes timmy's post let sara_post_vote_form = PostLikeForm::new(inserted_post.id, inserted_sara.id, -1); - PostLike::like(pool, &sara_post_vote_form).await?; + PostActions::like(pool, &sara_post_vote_form).await?; let mut expected_post_vote_views = [ VoteView { @@ -183,20 +182,12 @@ mod tests { assert_eq!(read_post_vote_views, expected_post_vote_views); // Timothy votes down his own comment - let timmy_comment_vote_form = CommentLikeForm { - comment_id: inserted_comment.id, - person_id: inserted_timmy.id, - score: -1, - }; - CommentLike::like(pool, &timmy_comment_vote_form).await?; + let timmy_comment_vote_form = CommentLikeForm::new(inserted_timmy.id, inserted_comment.id, -1); + CommentActions::like(pool, &timmy_comment_vote_form).await?; // Sara upvotes timmy's comment - let sara_comment_vote_form = CommentLikeForm { - comment_id: inserted_comment.id, - person_id: inserted_sara.id, - score: 1, - }; - CommentLike::like(pool, &sara_comment_vote_form).await?; + let sara_comment_vote_form = CommentLikeForm::new(inserted_sara.id, inserted_comment.id, 1); + CommentActions::like(pool, &sara_comment_vote_form).await?; let mut expected_comment_vote_views = [ VoteView { @@ -218,12 +209,8 @@ mod tests { assert_eq!(read_comment_vote_views, expected_comment_vote_views); // Ban timmy from that community - let ban_timmy_form = CommunityPersonBanForm { - community_id: inserted_community.id, - person_id: inserted_timmy.id, - expires: None, - }; - CommunityPersonBan::ban(pool, &ban_timmy_form).await?; + let ban_timmy_form = CommunityPersonBanForm::new(inserted_community.id, inserted_timmy.id); + CommunityActions::ban(pool, &ban_timmy_form).await?; // Make sure creator_banned_from_community is true let read_comment_vote_views_after_ban = diff --git a/crates/db_views/src/structs.rs b/crates/db_views/src/structs.rs index 8e5d897b5..8b11932b3 100644 --- a/crates/db_views/src/structs.rs +++ b/crates/db_views/src/structs.rs @@ -1,79 +1,87 @@ -use chrono::{DateTime, Utc}; +#[cfg(feature = "full")] +use crate::utils::{ + comment_creator_is_admin, + comment_select_remove_deletes, + creator_community_actions_select, + creator_is_admin, + local_user_can_mod, + local_user_community_can_mod, + local_user_is_admin, + person1_select, + person2_select, + post_creator_is_admin, +}; #[cfg(feature = "full")] use diesel::{ deserialize::FromSqlRow, - dsl::exists, - dsl::Nullable, expression::AsExpression, sql_types, - BoolExpressionMethods, - ExpressionMethods, NullableExpressionMethods, - PgExpressionMethods, - QueryDsl, Queryable, Selectable, }; +use lemmy_db_schema::source::{ + combined::{ + inbox::InboxCombined, + person_content::PersonContentCombined, + person_saved::PersonSavedCombined, + report::ReportCombined, + search::SearchCombined, + }, + comment::{Comment, CommentActions}, + comment_reply::CommentReply, + comment_report::CommentReport, + community::{Community, CommunityActions, CommunityFollowerState}, + community_report::CommunityReport, + custom_emoji::CustomEmoji, + custom_emoji_keyword::CustomEmojiKeyword, + images::{ImageDetails, LocalImage}, + instance::{Instance, InstanceActions}, + local_site::LocalSite, + local_site_rate_limit::LocalSiteRateLimit, + local_user::LocalUser, + mod_log::{ + admin::{ + AdminAllowInstance, + AdminBlockInstance, + AdminPurgeComment, + AdminPurgeCommunity, + AdminPurgePerson, + AdminPurgePost, + }, + moderator::{ + ModAdd, + ModAddCommunity, + ModBan, + ModBanFromCommunity, + ModChangeCommunityVisibility, + ModFeaturePost, + ModLockPost, + ModRemoveComment, + ModRemoveCommunity, + ModRemovePost, + ModTransferCommunity, + }, + }, + person::{Person, PersonActions}, + person_comment_mention::PersonCommentMention, + person_post_mention::PersonPostMention, + post::{Post, PostActions}, + post_report::PostReport, + private_message::PrivateMessage, + private_message_report::PrivateMessageReport, + registration_application::RegistrationApplication, + site::Site, + tag::Tag, +}; #[cfg(feature = "full")] use lemmy_db_schema::{ - aliases::{creator_community_actions, creator_local_user, person1}, - impls::comment::comment_select_remove_deletes, - impls::community::community_follower_select_subscribed_type, - impls::local_user::local_user_can_mod, - schema::{comment, comment_actions, community_actions, local_user, person, person_actions}, + schema::local_user, utils::functions::coalesce, + CreatorCommunityActionsAllColumnsTuple, Person1AliasAllColumnsTuple, -}; -use lemmy_db_schema::{ - source::{ - comment::Comment, - comment_reply::CommentReply, - comment_report::CommentReport, - community::Community, - community_report::CommunityReport, - custom_emoji::CustomEmoji, - custom_emoji_keyword::CustomEmojiKeyword, - images::{ImageDetails, LocalImage}, - instance::Instance, - local_site::LocalSite, - local_site_rate_limit::LocalSiteRateLimit, - local_user::LocalUser, - mod_log::{ - admin::{ - AdminAllowInstance, - AdminBlockInstance, - AdminPurgeComment, - AdminPurgeCommunity, - AdminPurgePerson, - AdminPurgePost, - }, - moderator::{ - ModAdd, - ModAddCommunity, - ModBan, - ModBanFromCommunity, - ModChangeCommunityVisibility, - ModFeaturePost, - ModLockPost, - ModRemoveComment, - ModRemoveCommunity, - ModRemovePost, - ModTransferCommunity, - }, - }, - person::Person, - person_comment_mention::PersonCommentMention, - person_post_mention::PersonPostMention, - post::Post, - post_report::PostReport, - private_message::PrivateMessage, - private_message_report::PrivateMessageReport, - registration_application::RegistrationApplication, - site::Site, - tag::Tag, - }, - SubscribedType, + Person2AliasAllColumnsTuple, }; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; @@ -82,29 +90,60 @@ use ts_rs::TS; #[skip_serializing_none] #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", ts(export))] /// A comment report view. pub struct CommentReportView { + #[cfg_attr(feature = "full", diesel(embed))] pub comment_report: CommentReport, + #[cfg_attr(feature = "full", diesel(embed))] pub comment: Comment, + #[cfg_attr(feature = "full", diesel(embed))] pub post: Post, + #[cfg_attr(feature = "full", diesel(embed))] pub community: Community, + #[cfg_attr(feature = "full", diesel(embed))] pub creator: Person, + #[cfg_attr(feature = "full", + diesel( + select_expression_type = Person1AliasAllColumnsTuple, + select_expression = person1_select() + ) + )] pub comment_creator: Person, - pub creator_banned_from_community: bool, - pub creator_is_moderator: bool, - pub creator_is_admin: bool, - pub creator_blocked: bool, - pub subscribed: SubscribedType, + #[cfg_attr(feature = "full", diesel(embed))] #[cfg_attr(feature = "full", ts(optional))] - /// The time when the comment was saved. - pub saved: Option>, - #[cfg_attr(feature = "full", ts(optional))] - pub my_vote: Option, + pub comment_actions: Option, #[cfg_attr(feature = "full", ts(optional))] + #[cfg_attr(feature = "full", + diesel( + select_expression_type = Nullable, + select_expression = person2_select().nullable() + ) + )] pub resolver: Option, + #[cfg_attr(feature = "full", ts(optional))] + #[cfg_attr(feature = "full", diesel(embed))] + #[cfg_attr(feature = "full", + diesel( + select_expression_type = Nullable, + select_expression = creator_community_actions_select().nullable() + ) + )] + pub creator_community_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + #[cfg_attr(feature = "full", ts(optional))] + pub person_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + #[cfg_attr(feature = "full", ts(optional))] + pub community_actions: Option, + #[cfg_attr(feature = "full", + diesel( + select_expression = comment_creator_is_admin() + ) + )] + pub creator_is_admin: bool, } #[skip_serializing_none] @@ -126,74 +165,32 @@ pub struct CommentView { pub post: Post, #[cfg_attr(feature = "full", diesel(embed))] pub community: Community, + #[cfg_attr(feature = "full", diesel(embed))] + #[cfg_attr(feature = "full", ts(optional))] + pub community_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + #[cfg_attr(feature = "full", ts(optional))] + pub comment_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + #[cfg_attr(feature = "full", ts(optional))] + pub person_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + #[cfg_attr(feature = "full", ts(optional))] + pub instance_actions: Option, + #[cfg_attr(feature = "full", ts(optional))] #[cfg_attr(feature = "full", diesel( - select_expression = - creator_community_actions - .field(community_actions::received_ban) - .nullable() - .is_not_null() + select_expression_type = Nullable, + select_expression = creator_community_actions_select().nullable() ) )] - pub creator_banned_from_community: bool, + pub creator_community_actions: Option, #[cfg_attr(feature = "full", diesel( - select_expression = - community_actions::received_ban.nullable().is_not_null() - ) - )] - pub banned_from_community: bool, - #[cfg_attr(feature = "full", - diesel( - select_expression = - creator_community_actions - .field(community_actions::became_moderator) - .nullable() - .is_not_null() - ) - )] - pub creator_is_moderator: bool, - #[cfg_attr(feature = "full", - diesel( - select_expression = - exists(creator_local_user.filter( - comment::creator_id - .eq(creator_local_user.field(local_user::person_id)) - .and(creator_local_user.field(local_user::admin).eq(true)), - )) + select_expression = comment_creator_is_admin() ) )] pub creator_is_admin: bool, - #[cfg_attr(feature = "full", - diesel( - select_expression = community_follower_select_subscribed_type(), - ) - )] - pub subscribed: SubscribedType, - #[cfg_attr(feature = "full", ts(optional))] - #[cfg_attr(feature = "full", - diesel( - select_expression = - comment_actions::saved.nullable() - ) - )] - /// The time when the comment was saved. - pub saved: Option>, - #[cfg_attr(feature = "full", - diesel( - select_expression = - person_actions::blocked.nullable().is_not_null() - ) - )] - pub creator_blocked: bool, - #[cfg_attr(feature = "full", ts(optional))] - #[cfg_attr(feature = "full", - diesel( - select_expression = - comment_actions::like_score.nullable() - ) - )] - pub my_vote: Option, #[cfg_attr(feature = "full", diesel( select_expression = local_user_can_mod() @@ -211,32 +208,38 @@ pub struct CommentView { pub struct CommentSlimView { pub comment: Comment, pub creator: Person, - pub creator_banned_from_community: bool, - pub banned_from_community: bool, - pub creator_is_moderator: bool, + #[cfg_attr(feature = "full", ts(optional))] + pub comment_actions: Option, + #[cfg_attr(feature = "full", ts(optional))] + pub person_actions: Option, + #[cfg_attr(feature = "full", ts(optional))] + pub creator_community_actions: Option, + #[cfg_attr(feature = "full", ts(optional))] + pub instance_actions: Option, pub creator_is_admin: bool, - pub subscribed: SubscribedType, - #[cfg_attr(feature = "full", ts(optional))] - /// The time when the comment was saved. - pub saved: Option>, - pub creator_blocked: bool, - #[cfg_attr(feature = "full", ts(optional))] - pub my_vote: Option, pub can_mod: bool, } #[skip_serializing_none] #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", ts(export))] /// A community report view. pub struct CommunityReportView { + #[cfg_attr(feature = "full", diesel(embed))] pub community_report: CommunityReport, + #[cfg_attr(feature = "full", diesel(embed))] pub community: Community, + #[cfg_attr(feature = "full", diesel(embed))] pub creator: Person, - pub subscribed: SubscribedType, #[cfg_attr(feature = "full", ts(optional))] + #[cfg_attr(feature = "full", + diesel( + select_expression_type = Nullable, + select_expression = person2_select().nullable() + ) + )] pub resolver: Option, } @@ -254,31 +257,57 @@ pub struct LocalUserView { #[skip_serializing_none] #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", ts(export))] /// A post report view. pub struct PostReportView { + #[cfg_attr(feature = "full", diesel(embed))] pub post_report: PostReport, + #[cfg_attr(feature = "full", diesel(embed))] pub post: Post, + #[cfg_attr(feature = "full", diesel(embed))] pub community: Community, + #[cfg_attr(feature = "full", diesel(embed))] pub creator: Person, + #[cfg_attr(feature = "full", + diesel( + select_expression_type = Person1AliasAllColumnsTuple, + select_expression = person1_select() + ) + )] pub post_creator: Person, - pub creator_banned_from_community: bool, - pub creator_is_moderator: bool, - pub creator_is_admin: bool, - pub subscribed: SubscribedType, #[cfg_attr(feature = "full", ts(optional))] - /// The time when the post was saved. - pub saved: Option>, - pub read: bool, - pub hidden: bool, - pub creator_blocked: bool, + #[cfg_attr(feature = "full", + diesel( + select_expression_type = Nullable, + select_expression = creator_community_actions_select().nullable() + ) + )] + pub creator_community_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] #[cfg_attr(feature = "full", ts(optional))] - pub my_vote: Option, - pub unread_comments: i64, + pub community_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] #[cfg_attr(feature = "full", ts(optional))] + pub post_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + #[cfg_attr(feature = "full", ts(optional))] + pub person_actions: Option, + #[cfg_attr(feature = "full", ts(optional))] + #[cfg_attr(feature = "full", + diesel( + select_expression_type = Nullable, + select_expression = person2_select().nullable() + ) + )] pub resolver: Option, + #[cfg_attr(feature = "full", + diesel( + select_expression = post_creator_is_admin() + ) + )] + pub creator_is_admin: bool, } /// currently this is just a wrapper around post id, but should be seen as opaque from the client's @@ -293,46 +322,81 @@ pub struct PostPaginationCursor(pub String); #[skip_serializing_none] #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", ts(export))] /// A post view. pub struct PostView { + #[cfg_attr(feature = "full", diesel(embed))] pub post: Post, + #[cfg_attr(feature = "full", diesel(embed))] pub creator: Person, + #[cfg_attr(feature = "full", diesel(embed))] pub community: Community, + #[cfg_attr(feature = "full", diesel(embed))] #[cfg_attr(feature = "full", ts(optional))] pub image_details: Option, - pub creator_banned_from_community: bool, - pub banned_from_community: bool, - pub creator_is_moderator: bool, + #[cfg_attr(feature = "full", diesel(embed))] + #[cfg_attr(feature = "full", ts(optional))] + pub community_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + #[cfg_attr(feature = "full", ts(optional))] + pub person_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + #[cfg_attr(feature = "full", ts(optional))] + pub post_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + #[cfg_attr(feature = "full", ts(optional))] + pub instance_actions: Option, + #[cfg_attr(feature = "full", ts(optional))] + #[cfg_attr(feature = "full", + diesel( + select_expression_type = Nullable, + select_expression = creator_community_actions_select().nullable() + ) + )] + pub creator_community_actions: Option, + #[cfg_attr(feature = "full", + diesel( + select_expression = post_creator_is_admin() + ) + )] pub creator_is_admin: bool, - pub subscribed: SubscribedType, - #[cfg_attr(feature = "full", ts(optional))] - /// The time when the post was saved. - pub saved: Option>, - pub read: bool, - pub hidden: bool, - pub creator_blocked: bool, - #[cfg_attr(feature = "full", ts(optional))] - pub my_vote: Option, - pub unread_comments: i64, - pub tags: PostTags, + #[cfg_attr(feature = "full", + diesel( + select_expression = local_user_can_mod() + ) + )] pub can_mod: bool, } #[skip_serializing_none] #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", derive(TS, Queryable, Selectable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", ts(export))] /// A private message report view. pub struct PrivateMessageReportView { + #[cfg_attr(feature = "full", diesel(embed))] pub private_message_report: PrivateMessageReport, + #[cfg_attr(feature = "full", diesel(embed))] pub private_message: PrivateMessage, - pub private_message_creator: Person, + #[cfg_attr(feature = "full", diesel(embed))] pub creator: Person, + #[cfg_attr(feature = "full", + diesel( + select_expression_type = Person1AliasAllColumnsTuple, + select_expression = person1_select() + ) + )] + pub private_message_creator: Person, #[cfg_attr(feature = "full", ts(optional))] + #[cfg_attr(feature = "full", + diesel( + select_expression_type = Nullable, + select_expression = person2_select().nullable() + ) + )] pub resolver: Option, } @@ -353,7 +417,7 @@ pub struct RegistrationApplicationView { #[cfg_attr(feature = "full", diesel( select_expression_type = Nullable, - select_expression = person1.fields(person::all_columns).nullable() + select_expression = person1_select().nullable() ) )] pub admin: Option, @@ -409,38 +473,65 @@ pub struct LocalImageView { } #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(Queryable))] +#[cfg_attr(feature = "full", derive(Queryable, Selectable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] /// A combined report view pub struct ReportCombinedViewInternal { - // Post-specific + #[cfg_attr(feature = "full", diesel(embed))] + pub report_combined: ReportCombined, + #[cfg_attr(feature = "full", diesel(embed))] pub post_report: Option, - pub post: Option, - pub post_unread_comments: Option, - pub post_saved: Option>, - pub post_read: bool, - pub post_hidden: bool, - pub my_post_vote: Option, - // Comment-specific + #[cfg_attr(feature = "full", diesel(embed))] pub comment_report: Option, - pub comment: Option, - pub comment_saved: Option>, - pub my_comment_vote: Option, - // Private-message-specific + #[cfg_attr(feature = "full", diesel(embed))] pub private_message_report: Option, - pub private_message: Option, - // Community-specific + #[cfg_attr(feature = "full", diesel(embed))] pub community_report: Option, - // Shared + #[cfg_attr(feature = "full", diesel(embed))] pub report_creator: Person, + #[cfg_attr(feature = "full", diesel(embed))] + pub comment: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub private_message: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub post: Option, + #[cfg_attr(feature = "full", + diesel( + select_expression_type = Nullable, + select_expression = person1_select().nullable() + ) + )] pub item_creator: Option, - pub community: Option, - pub subscribed: SubscribedType, + #[cfg_attr(feature = "full", + diesel( + select_expression_type = Nullable, + select_expression = person2_select().nullable() + ) + )] pub resolver: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub community: Option, + #[cfg_attr(feature = "full", + diesel( + select_expression_type = Nullable, + select_expression = creator_community_actions_select().nullable() + ) + )] + pub creator_community_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub community_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub post_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub person_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub comment_actions: Option, + #[cfg_attr(feature = "full", + diesel( + select_expression = local_user_is_admin() + ) + )] pub item_creator_is_admin: bool, - pub item_creator_banned_from_community: bool, - pub item_creator_is_moderator: bool, - pub item_creator_blocked: bool, } #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] @@ -456,32 +547,50 @@ pub enum ReportCombinedView { } #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(Queryable))] +#[cfg_attr(feature = "full", derive(Queryable, Selectable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] /// A combined person_content view pub(crate) struct PersonContentCombinedViewInternal { - // Post-specific - pub post_unread_comments: i64, - pub post_saved: Option>, - pub post_read: bool, - pub post_hidden: bool, - pub my_post_vote: Option, - pub image_details: Option, - pub post_tags: PostTags, - // Comment-specific + #[cfg_attr(feature = "full", diesel(embed))] + pub person_content_combined: PersonContentCombined, + #[cfg_attr(feature = "full", diesel(embed))] pub comment: Option, - pub comment_saved: Option>, - pub my_comment_vote: Option, - // Shared + #[cfg_attr(feature = "full", diesel(embed))] pub post: Post, - pub community: Community, + #[cfg_attr(feature = "full", diesel(embed))] pub item_creator: Person, - pub subscribed: SubscribedType, + #[cfg_attr(feature = "full", diesel(embed))] + pub community: Community, + #[cfg_attr(feature = "full", + diesel( + select_expression_type = Nullable, + select_expression = creator_community_actions_select().nullable() + ) + )] + pub creator_community_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub community_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub instance_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub post_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub person_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub comment_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub image_details: Option, + #[cfg_attr(feature = "full", + diesel( + select_expression = creator_is_admin() + ) + )] pub item_creator_is_admin: bool, - pub item_creator_is_moderator: bool, - pub item_creator_banned_from_community: bool, - pub item_creator_blocked: bool, - pub banned_from_community: bool, + #[cfg_attr(feature = "full", + diesel( + select_expression = local_user_can_mod() + ) + )] pub can_mod: bool, } @@ -496,32 +605,50 @@ pub enum PersonContentCombinedView { } #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(Queryable))] +#[cfg_attr(feature = "full", derive(Queryable, Selectable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] /// A combined person_saved view pub(crate) struct PersonSavedCombinedViewInternal { - // Post-specific - pub post_unread_comments: i64, - pub post_saved: Option>, - pub post_read: bool, - pub post_hidden: bool, - pub my_post_vote: Option, - pub image_details: Option, - pub post_tags: PostTags, - // Comment-specific + #[cfg_attr(feature = "full", diesel(embed))] + pub person_saved_combined: PersonSavedCombined, + #[cfg_attr(feature = "full", diesel(embed))] pub comment: Option, - pub comment_saved: Option>, - pub my_comment_vote: Option, - // Shared + #[cfg_attr(feature = "full", diesel(embed))] pub post: Post, - pub community: Community, + #[cfg_attr(feature = "full", diesel(embed))] pub item_creator: Person, - pub subscribed: SubscribedType, + #[cfg_attr(feature = "full", diesel(embed))] + pub community: Community, + #[cfg_attr(feature = "full", + diesel( + select_expression_type = Nullable, + select_expression = creator_community_actions_select().nullable() + ) + )] + pub creator_community_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub community_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub instance_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub post_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub person_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub comment_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub image_details: Option, + #[cfg_attr(feature = "full", + diesel( + select_expression = creator_is_admin() + ) + )] pub item_creator_is_admin: bool, - pub item_creator_is_moderator: bool, - pub item_creator_banned_from_community: bool, - pub item_creator_blocked: bool, - pub banned_from_community: bool, + #[cfg_attr(feature = "full", + diesel( + select_expression = local_user_can_mod() + ) + )] pub can_mod: bool, } @@ -578,29 +705,15 @@ pub struct CommunityPersonBanView { pub struct CommunityView { #[cfg_attr(feature = "full", diesel(embed))] pub community: Community, + #[cfg_attr(feature = "full", diesel(embed))] + #[cfg_attr(feature = "full", ts(optional))] + pub community_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + #[cfg_attr(feature = "full", ts(optional))] + pub instance_actions: Option, #[cfg_attr(feature = "full", diesel( - select_expression = community_follower_select_subscribed_type() - ) - )] - pub subscribed: SubscribedType, - #[cfg_attr(feature = "full", - diesel( - select_expression = community_actions::blocked.nullable().is_not_null() - ) - )] - pub blocked: bool, - #[cfg_attr(feature = "full", - diesel( - select_expression = community_actions::received_ban.nullable().is_not_null() - ) - )] - pub banned_from_community: bool, - #[cfg_attr(feature = "full", - diesel( - select_expression = local_user::admin.nullable() - .or(community_actions::became_moderator.nullable().is_not_null()) - .is_not_distinct_from(true) + select_expression = local_user_community_can_mod() ) )] pub can_mod: bool, @@ -635,22 +748,22 @@ pub enum CommunitySortType { /// A person comment mention view. pub struct PersonCommentMentionView { pub person_comment_mention: PersonCommentMention, + pub recipient: Person, pub comment: Comment, pub creator: Person, pub post: Post, pub community: Community, - pub recipient: Person, - pub creator_banned_from_community: bool, - pub banned_from_community: bool, - pub creator_is_moderator: bool, + #[cfg_attr(feature = "full", ts(optional))] + pub community_actions: Option, + #[cfg_attr(feature = "full", ts(optional))] + pub comment_actions: Option, + #[cfg_attr(feature = "full", ts(optional))] + pub person_actions: Option, + #[cfg_attr(feature = "full", ts(optional))] + pub instance_actions: Option, + #[cfg_attr(feature = "full", ts(optional))] + pub creator_community_actions: Option, pub creator_is_admin: bool, - pub subscribed: SubscribedType, - #[cfg_attr(feature = "full", ts(optional))] - /// The time when the comment was saved. - pub saved: Option>, - pub creator_blocked: bool, - #[cfg_attr(feature = "full", ts(optional))] - pub my_vote: Option, pub can_mod: bool, } @@ -662,27 +775,23 @@ pub struct PersonCommentMentionView { /// A person post mention view. pub struct PersonPostMentionView { pub person_post_mention: PersonPostMention, + pub recipient: Person, pub post: Post, pub creator: Person, pub community: Community, #[cfg_attr(feature = "full", ts(optional))] pub image_details: Option, - pub recipient: Person, - pub creator_banned_from_community: bool, - pub banned_from_community: bool, - pub creator_is_moderator: bool, + #[cfg_attr(feature = "full", ts(optional))] + pub community_actions: Option, + #[cfg_attr(feature = "full", ts(optional))] + pub person_actions: Option, + #[cfg_attr(feature = "full", ts(optional))] + pub post_actions: Option, + #[cfg_attr(feature = "full", ts(optional))] + pub instance_actions: Option, + #[cfg_attr(feature = "full", ts(optional))] + pub creator_community_actions: Option, pub creator_is_admin: bool, - pub subscribed: SubscribedType, - #[cfg_attr(feature = "full", ts(optional))] - /// The time when the post was saved. - pub saved: Option>, - pub read: bool, - pub hidden: bool, - pub creator_blocked: bool, - #[cfg_attr(feature = "full", ts(optional))] - pub my_vote: Option, - pub unread_comments: i64, - pub post_tags: PostTags, pub can_mod: bool, } @@ -694,22 +803,22 @@ pub struct PersonPostMentionView { /// A comment reply view. pub struct CommentReplyView { pub comment_reply: CommentReply, + pub recipient: Person, pub comment: Comment, pub creator: Person, pub post: Post, pub community: Community, - pub recipient: Person, - pub creator_banned_from_community: bool, - pub banned_from_community: bool, - pub creator_is_moderator: bool, + #[cfg_attr(feature = "full", ts(optional))] + pub community_actions: Option, + #[cfg_attr(feature = "full", ts(optional))] + pub comment_actions: Option, + #[cfg_attr(feature = "full", ts(optional))] + pub person_actions: Option, + #[cfg_attr(feature = "full", ts(optional))] + pub instance_actions: Option, + #[cfg_attr(feature = "full", ts(optional))] + pub creator_community_actions: Option, pub creator_is_admin: bool, - pub subscribed: SubscribedType, - #[cfg_attr(feature = "full", ts(optional))] - /// The time when the comment was saved. - pub saved: Option>, - pub creator_blocked: bool, - #[cfg_attr(feature = "full", ts(optional))] - pub my_vote: Option, pub can_mod: bool, } @@ -738,7 +847,8 @@ pub struct PendingFollow { pub person: Person, pub community: Community, pub is_new_instance: bool, - pub subscribed: SubscribedType, + #[cfg_attr(feature = "full", ts(optional))] + pub follow_state: Option, } #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)] @@ -754,46 +864,72 @@ pub struct PrivateMessageView { #[cfg_attr(feature = "full", diesel( select_expression_type = Person1AliasAllColumnsTuple, - select_expression = person1.fields(person::all_columns) + select_expression = person1_select() ) )] pub recipient: Person, } #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(Queryable))] +#[cfg_attr(feature = "full", derive(Queryable, Selectable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] /// A combined inbox view pub struct InboxCombinedViewInternal { - // Comment reply + #[cfg_attr(feature = "full", diesel(embed))] + pub inbox_combined: InboxCombined, + #[cfg_attr(feature = "full", diesel(embed))] pub comment_reply: Option, - // Person comment mention + #[cfg_attr(feature = "full", diesel(embed))] pub person_comment_mention: Option, - // Person post mention + #[cfg_attr(feature = "full", diesel(embed))] pub person_post_mention: Option, - pub post_unread_comments: Option, - pub post_saved: Option>, - pub post_read: bool, - pub post_hidden: bool, - pub my_post_vote: Option, - pub image_details: Option, - pub post_tags: PostTags, - // Private message + #[cfg_attr(feature = "full", diesel(embed))] pub private_message: Option, - // Shared - pub post: Option, - pub community: Option, + #[cfg_attr(feature = "full", diesel(embed))] pub comment: Option, - pub comment_saved: Option>, - pub my_comment_vote: Option, - pub subscribed: SubscribedType, + #[cfg_attr(feature = "full", diesel(embed))] + pub post: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub community: Option, + #[cfg_attr(feature = "full", diesel(embed))] pub item_creator: Person, + #[cfg_attr(feature = "full", + diesel( + select_expression_type = Person1AliasAllColumnsTuple, + select_expression = person1_select() + ) + )] pub item_recipient: Person, + #[cfg_attr(feature = "full", diesel(embed))] + pub image_details: Option, + #[cfg_attr(feature = "full", + diesel( + select_expression_type = Nullable, + select_expression = creator_community_actions_select().nullable() + ) + )] + pub creator_community_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub community_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub instance_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub post_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub person_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub comment_actions: Option, + #[cfg_attr(feature = "full", + diesel( + select_expression = creator_is_admin() + ) + )] pub item_creator_is_admin: bool, - pub item_creator_is_moderator: bool, - pub item_creator_banned_from_community: bool, - pub item_creator_blocked: bool, - pub banned_from_community: bool, + #[cfg_attr(feature = "full", + diesel( + select_expression = local_user_can_mod() + ) + )] pub can_mod: bool, } @@ -1087,7 +1223,7 @@ pub(crate) struct ModlogCombinedViewInternal { #[cfg_attr(feature = "full", diesel( select_expression_type = Nullable, - select_expression = person1.fields(person::all_columns).nullable() + select_expression = person1_select().nullable() ) )] pub other_person: Option, @@ -1127,34 +1263,50 @@ pub enum ModlogCombinedView { } #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "full", derive(Queryable))] +#[cfg_attr(feature = "full", derive(Queryable, Selectable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] /// A combined search view pub(crate) struct SearchCombinedViewInternal { - // Post-specific - pub post: Option, - pub post_unread_comments: Option, - pub post_saved: Option>, - pub post_read: bool, - pub post_hidden: bool, - pub my_post_vote: Option, - pub image_details: Option, - pub post_tags: PostTags, - // // Comment-specific + #[cfg_attr(feature = "full", diesel(embed))] + pub search_combined: SearchCombined, + #[cfg_attr(feature = "full", diesel(embed))] pub comment: Option, - pub comment_saved: Option>, - pub my_comment_vote: Option, - // // Community-specific - pub community: Option, - pub community_blocked: bool, - pub subscribed: SubscribedType, - // Shared + #[cfg_attr(feature = "full", diesel(embed))] + pub post: Option, + #[cfg_attr(feature = "full", diesel(embed))] pub item_creator: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub community: Option, + #[cfg_attr(feature = "full", + diesel( + select_expression_type = Nullable, + select_expression = creator_community_actions_select().nullable() + ) + )] + pub creator_community_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub community_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub instance_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub post_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub person_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub comment_actions: Option, + #[cfg_attr(feature = "full", diesel(embed))] + pub image_details: Option, + #[cfg_attr(feature = "full", + diesel( + select_expression = creator_is_admin() + ) + )] pub item_creator_is_admin: bool, - pub item_creator_is_moderator: bool, - pub item_creator_banned_from_community: bool, - pub item_creator_blocked: bool, - pub banned_from_community: bool, + #[cfg_attr(feature = "full", + diesel( + select_expression = local_user_can_mod() + ) + )] pub can_mod: bool, } diff --git a/crates/db_views/src/utils.rs b/crates/db_views/src/utils.rs index 9f98a3173..1a63f6dca 100644 --- a/crates/db_views/src/utils.rs +++ b/crates/db_views/src/utils.rs @@ -1,12 +1,29 @@ use diesel::{ - helper_types::{Eq, NotEq, Or}, + dsl::{case_when, exists, not}, + helper_types::{Eq, NotEq}, BoolExpressionMethods, ExpressionMethods, + NullableExpressionMethods, + PgExpressionMethods, + QueryDsl, }; use lemmy_db_schema::{ - schema::{community, community_actions, instance_actions, person_actions}, + aliases::{creator_community_actions, creator_local_user, person1, person2}, + schema::{ + comment, + community, + community_actions, + instance_actions, + local_user, + person, + person_actions, + post, + }, source::community::CommunityFollowerState, CommunityVisibility, + CreatorCommunityActionsAllColumnsTuple, + Person1AliasAllColumnsTuple, + Person2AliasAllColumnsTuple, }; /// Hide all content from blocked communities and persons. Content from blocked instances is also @@ -20,6 +37,122 @@ pub(crate) fn filter_blocked() -> _ { .and(person_actions::blocked.is_null()) } +/// Checks that the creator_local_user is an admin. +#[diesel::dsl::auto_type] +pub(crate) fn creator_is_admin() -> _ { + creator_local_user + .field(local_user::admin) + .nullable() + .is_not_null() +} + +/// Checks that the local_user is an admin. +#[diesel::dsl::auto_type] +pub(crate) fn local_user_is_admin() -> _ { + local_user::admin.nullable().is_not_null() +} + +/// Checks to see if the comment creator is an admin. +#[diesel::dsl::auto_type] +pub(crate) fn comment_creator_is_admin() -> _ { + exists( + creator_local_user.filter( + comment::creator_id + .eq(creator_local_user.field(local_user::person_id)) + .and(creator_local_user.field(local_user::admin).eq(true)), + ), + ) +} + +#[diesel::dsl::auto_type] +pub(crate) fn post_creator_is_admin() -> _ { + exists( + creator_local_user.filter( + post::creator_id + .eq(creator_local_user.field(local_user::person_id)) + .and(creator_local_user.field(local_user::admin).eq(true)), + ), + ) +} + +/// Checks to see if you can mod an item. +/// +/// Caveat: Since admin status isn't federated or ordered, it can't know whether +/// item creator is a federated admin, or a higher admin. +/// The back-end will reject an action for admin that is higher via +/// LocalUser::is_higher_mod_or_admin_check +#[diesel::dsl::auto_type] +pub(crate) fn local_user_can_mod() -> _ { + let am_admin = local_user::admin.nullable(); + let i_became_moderator = community_actions::became_moderator.nullable(); + + let creator_became_moderator = creator_community_actions + .field(community_actions::became_moderator) + .nullable(); + + let am_higher_mod = i_became_moderator.le(creator_became_moderator); + + am_admin.or(am_higher_mod).is_not_distinct_from(true) +} + +/// A special type of can_mod for communities, which dont have creators. +#[diesel::dsl::auto_type] +pub(crate) fn local_user_community_can_mod() -> _ { + let am_admin = local_user::admin.nullable(); + let am_moderator = community_actions::became_moderator.nullable().is_not_null(); + am_admin.or(am_moderator).is_not_distinct_from(true) +} + +/// Selects the comment columns, but gives an empty string for content when +/// deleted or removed, and you're not a mod/admin. +#[diesel::dsl::auto_type] +pub(crate) fn comment_select_remove_deletes() -> _ { + let deleted_or_removed = comment::deleted.or(comment::removed); + + // You can only view the content if it hasn't been removed, or you can mod. + let can_view_content = not(deleted_or_removed).or(local_user_can_mod()); + let content = case_when(can_view_content, comment::content).otherwise(""); + + ( + comment::id, + comment::creator_id, + comment::post_id, + content, + comment::removed, + comment::published, + comment::updated, + comment::deleted, + comment::ap_id, + comment::local, + comment::path, + comment::distinguished, + comment::language_id, + comment::score, + comment::upvotes, + comment::downvotes, + comment::child_count, + comment::hot_rank, + comment::controversy_rank, + comment::report_count, + comment::unresolved_report_count, + ) +} + +/// The select for the person1 alias. +pub(crate) fn person1_select() -> Person1AliasAllColumnsTuple { + person1.fields(person::all_columns) +} + +/// The select for the person2 alias. +pub(crate) fn person2_select() -> Person2AliasAllColumnsTuple { + person2.fields(person::all_columns) +} + +/// The select for the creator community actions alias. +pub(crate) fn creator_community_actions_select() -> CreatorCommunityActionsAllColumnsTuple { + creator_community_actions.fields(community_actions::all_columns) +} + type IsSubscribedType = Eq>; @@ -27,9 +160,11 @@ pub(crate) fn filter_is_subscribed() -> IsSubscribedType { community_actions::follow_state.eq(Some(CommunityFollowerState::Accepted)) } -type IsNotHiddenType = NotEq; +type IsNotUnlistedType = NotEq; -pub(crate) fn filter_not_hidden_or_is_subscribed() -> Or { - let not_hidden = community::visibility.ne(CommunityVisibility::Unlisted); - not_hidden.or(filter_is_subscribed()) +#[diesel::dsl::auto_type] +pub(crate) fn filter_not_unlisted_or_is_subscribed() -> _ { + let not_unlisted: IsNotUnlistedType = community::visibility.ne(CommunityVisibility::Unlisted); + let is_subscribed: IsSubscribedType = filter_is_subscribed(); + not_unlisted.or(is_subscribed) } diff --git a/crates/utils/src/error.rs b/crates/utils/src/error.rs index 5cf7ae4c8..ea1272a6b 100644 --- a/crates/utils/src/error.rs +++ b/crates/utils/src/error.rs @@ -103,6 +103,7 @@ pub enum LemmyErrorType { CouldntLikePost, CouldntSavePost, CouldntMarkPostAsRead, + CouldntUpdateReadComments, CouldntHidePost, CouldntUpdateCommunity, CouldntUpdateReplies, diff --git a/migrations/.gitkeep b/migrations/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/migrations/2023-05-10-095739_force_enable_undetermined_language/down.sql b/migrations/2023-05-10-095739_force_enable_undetermined_language/down.sql index e69de29bb..deb75def2 100644 --- a/migrations/2023-05-10-095739_force_enable_undetermined_language/down.sql +++ b/migrations/2023-05-10-095739_force_enable_undetermined_language/down.sql @@ -0,0 +1,3 @@ +SELECT + 1; +