Create actions structs (#5482)

* migration

* update code

* tests

* triggers

* fix

* fmt

* clippy

* post aggregate migration

* changes for post aggregate code

* wip: update tests for post aggregate

* format

* fix partialeq

* trigger fix

* fix post insert trigger

* wip

* reorder

* fixes

* community aggregate migration

* update code

* triggers

* person aggregate migration

* person aggregate code

* person triggers

* test fixes

* fix scheduled task

* update api tests

* site_aggregates to local_site migration

* site_aggregates code changes

* triggers, tests

* more fixes

* Rename PersonPostAggregates to PostActions

* Merge local_user_vote_display_mode into local_user

* fix schema

* remove duplicate fields

* remove "aggregates" from index names

* uncomment indices

* if count = 0

* remove commentaggregates

* Fix triggers in remove aggregates tables pr (#5451)

* prevent all db_schema test errors

* fix the delete_comments_before_post problem in a way that doesn't affect the returned number of affected rows

* remove unnecessary recursion checks and add comment to remaining check

* clean up

* Fixing SQL format.

* Update triggers.sql

* Update triggers.sql

* Update triggers.sql

* Update triggers.sql

* remove update of deleted column

---------

Co-authored-by: Dessalines <tyhou13@gmx.com>

* rename migration

* Fix migration errors

* Move community.hidden to visibility (fixes #5458)

* Fixing person_saved_combined. (#5481)

* Remove comment and post specific action structs. #5473

* Doing reports

* fix up migration by dropping index

* also add enum variant `LocalOnlyPublic`, rename `LocalOnly` to `LocalOnlyPrivate`

fixes #5351

* fix column order in down.sql

* wip

* Moving blocks.

* Adding a few more views.

* more wip

* fixes

* migration for modlog

* fix migration

* Working views and schema.

* Fix ts_optionals.

* wip

* db_schema compiling

* make the code compile

* Merging from main.

* lint

* Fixing SQL format.

* fix down migration

* Fixing api tests.

* Adding field comments for the actions tables.

* Refactoring CommunityFollower to include follow_state

* fix test

* make hidden status federate

* ts attr

* fix

* fix api test

* Update crates/api/src/reports/post_report/resolve.rs

Co-authored-by: Nutomic <me@nutomic.com>

* Addressing PR comments

* Fix ts export.

* update api client

* review

* Extracting filter_not_hidden_or_is_subscribed (#5497)

* Extracting filter_not_hidden_or_is_subscribed

* Cleanup.

* Cleanup 2.

* Remove follower_state_helper function.

* Cleaning up some utils functions.

* rename hidden to unlisted

---------

Co-authored-by: Felix Ableitner <me@nutomic.com>
Co-authored-by: dullbananas <dull.bananas0@gmail.com>
This commit is contained in:
Dessalines 2025-03-12 11:51:34 -04:00 committed by GitHub
parent 8ffeeca52d
commit 5fa6a490d5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
109 changed files with 2420 additions and 3344 deletions

View file

@ -95,6 +95,16 @@ steps:
when: when:
- event: pull_request - 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: cargo_clippy:
image: *rust_image image: *rust_image
environment: environment:

View file

@ -29,7 +29,7 @@
"eslint": "^9.20.0", "eslint": "^9.20.0",
"eslint-plugin-prettier": "^5.2.3", "eslint-plugin-prettier": "^5.2.3",
"jest": "^29.5.0", "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", "prettier": "^3.5.0",
"ts-jest": "^29.1.0", "ts-jest": "^29.1.0",
"tsoa": "^6.6.0", "tsoa": "^6.6.0",

View file

@ -33,8 +33,8 @@ importers:
specifier: ^29.5.0 specifier: ^29.5.0
version: 29.7.0(@types/node@22.13.1) version: 29.7.0(@types/node@22.13.1)
lemmy-js-client: lemmy-js-client:
specifier: 0.20.0-move-community-hidden.3 specifier: 1.0.0-action-structs.1
version: 0.20.0-move-community-hidden.3 version: 1.0.0-action-structs.1
prettier: prettier:
specifier: ^3.5.0 specifier: ^3.5.0
version: 3.5.0 version: 3.5.0
@ -1528,8 +1528,8 @@ packages:
resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
engines: {node: '>=6'} engines: {node: '>=6'}
lemmy-js-client@0.20.0-move-community-hidden.3: lemmy-js-client@1.0.0-action-structs.1:
resolution: {integrity: sha512-X7bbSrnGGgupr//Qk2M1Z9nvFawNU4T116X+4/j912GO6KbQdWN7+10obbQJuoEYr15oCXwSaK8DLusofLxIig==} resolution: {integrity: sha512-Th/7TAjBQ7jcxJHpSMeHF50Y8GwK2YbCBVbLZ9AEPYmOyll0yK+bwTwhCEyvy/SIgbNQTd1OaIYv3F8iAvjvuw==}
leven@3.1.0: leven@3.1.0:
resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
@ -4169,7 +4169,7 @@ snapshots:
kleur@3.0.3: {} 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: {} leven@3.1.0: {}

View file

@ -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"); expect(gammaFollow.community_view.community.name).toBe("main");
await waitUntil( await waitUntil(
() => resolveBetaCommunity(alpha), () => resolveBetaCommunity(alpha),
c => c.community?.subscribed === "Subscribed", c => c.community?.community_actions?.follow_state === "Accepted",
); );
await waitUntil( await waitUntil(
() => resolveBetaCommunity(gamma), () => resolveBetaCommunity(gamma),
c => c.community?.subscribed === "Subscribed", c => c.community?.community_actions?.follow_state === "Accepted",
); );
// Create a post on beta // Create a post on beta

View file

@ -205,17 +205,17 @@ test("Admin actions in remote community are not federated to origin", async () =
gammaCommunity = ( gammaCommunity = (
await waitUntil( await waitUntil(
() => resolveCommunity(gamma, communityRes.community.ap_id), () => resolveCommunity(gamma, communityRes.community.ap_id),
g => g.community?.subscribed === "Subscribed", g => g.community?.community_actions?.follow_state == "Accepted",
) )
).community; ).community;
if (!gammaCommunity) { if (!gammaCommunity) {
throw "Missing gamma community"; 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)) let gammaPost = (await createPost(gamma, gammaCommunity.community.id))
.post_view; .post_view;
expect(gammaPost.post.id).toBeDefined(); 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 // admin of beta decides to ban gamma from community
let betaCommunity = ( 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 // ban doesn't federate to community's origin instance alpha
let alphaPost = (await resolvePost(alpha, gammaPost.post)).post; 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 // and neither to gamma
let gammaPost2 = await getPost(gamma, gammaPost.post.id); 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 () => { test("moderator view", async () => {
@ -391,7 +393,7 @@ test.skip("Community follower count is federated", async () => {
let followed = ( let followed = (
await waitUntil( await waitUntil(
() => resolveCommunity(alpha, communityActorId), () => resolveCommunity(alpha, communityActorId),
c => c.community?.subscribed === "Subscribed", c => c.community?.community_actions?.follow_state == "Accepted",
) )
).community; ).community;
@ -408,7 +410,7 @@ test.skip("Community follower count is federated", async () => {
followed = ( followed = (
await waitUntil( await waitUntil(
() => resolveCommunity(gamma, communityActorId), () => resolveCommunity(gamma, communityActorId),
c => c.community?.subscribed === "Subscribed", c => c.community?.community_actions?.follow_state == "Accepted",
) )
).community; ).community;
@ -425,7 +427,7 @@ test.skip("Community follower count is federated", async () => {
followed = ( followed = (
await waitUntil( await waitUntil(
() => resolveCommunity(delta, communityActorId), () => resolveCommunity(delta, communityActorId),
c => c.community?.subscribed === "Subscribed", c => c.community?.community_actions?.follow_state == "Accepted",
) )
).community; ).community;

View file

@ -26,7 +26,9 @@ test("Follow local community", async () => {
// Make sure the follow response went through // Make sure the follow response went through
expect(follow.community_view.community.local).toBe(true); 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( expect(follow.community_view.community.subscribers).toBe(
community.community.subscribers + 1, community.community.subscribers + 1,
); );
@ -36,7 +38,9 @@ test("Follow local community", async () => {
// Test an unfollow // Test an unfollow
let unfollow = await followCommunity(user, false, community.community.id); 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( expect(unfollow.community_view.community.subscribers).toBe(
community.community.subscribers, community.community.subscribers,
); );
@ -62,18 +66,18 @@ test("Follow federated community", async () => {
true, true,
betaCommunityInitial.community.id, betaCommunityInitial.community.id,
); );
expect(follow.community_view.subscribed).toBe("Pending"); expect(follow.community_view.community_actions?.follow_state).toBe("Pending");
const betaCommunity = ( const betaCommunity = (
await waitUntil( await waitUntil(
() => resolveBetaCommunity(alpha), () => resolveBetaCommunity(alpha),
c => c.community?.subscribed === "Subscribed", c => c.community?.community_actions?.follow_state === "Accepted",
) )
).community; ).community;
// Make sure the follow response went through // Make sure the follow response went through
expect(betaCommunity?.community.local).toBe(false); expect(betaCommunity?.community.local).toBe(false);
expect(betaCommunity?.community.name).toBe("main"); 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( expect(betaCommunity?.community.subscribers_local).toBe(
betaCommunityInitial.community.subscribers_local + 1, betaCommunityInitial.community.subscribers_local + 1,
); );
@ -99,7 +103,9 @@ test("Follow federated community", async () => {
// Test an unfollow // Test an unfollow
let unfollow = await followCommunity(alpha, false, remoteCommunityId); 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 // Make sure you are unsubbed locally
let siteUnfollowCheck = await getMyUser(alpha); let siteUnfollowCheck = await getMyUser(alpha);

View file

@ -281,7 +281,7 @@ test("Lock a post", async () => {
await followCommunity(alpha, true, betaCommunity.community.id); await followCommunity(alpha, true, betaCommunity.community.id);
await waitUntil( await waitUntil(
() => resolveBetaCommunity(alpha), () => resolveBetaCommunity(alpha),
c => c.community?.subscribed === "Subscribed", c => c.community?.community_actions?.follow_state == "Accepted",
); );
let postRes = await createPost(alpha, betaCommunity.community.id); 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(betaBanRes.post_view.post.removed).toBe(true);
expect(alphaPostAfterRemoveOnBeta.post_view.post.removed).toBe(true); expect(alphaPostAfterRemoveOnBeta.post_view.post.removed).toBe(true);
expect( expect(
alphaPostAfterRemoveOnBeta.post_view.creator_banned_from_community, alphaPostAfterRemoveOnBeta.post_view.creator_community_actions
).toBe(true); ?.received_ban,
).toBeDefined();
await unfollowRemotes(alpha); await unfollowRemotes(alpha);
}); });
@ -635,8 +636,12 @@ test("Enforce community ban for federated user", async () => {
s => s.post_view.post.removed, s => s.post_view.post.removed,
); );
expect(removePostRes.post_view.post.removed).toBe(true); expect(removePostRes.post_view.post.removed).toBe(true);
expect(removePostRes.post_view.creator_banned_from_community).toBe(true); expect(
expect(removePostRes.community_view.banned_from_community).toBe(true); 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 // Alpha tries to make post on beta, but it fails because of ban
await expect( await expect(
@ -670,7 +675,7 @@ test("Enforce community ban for federated user", async () => {
// Make sure that post makes it to beta community // Make sure that post makes it to beta community
let postRes4 = await waitForPost(beta, postRes3.post_view.post); let postRes4 = await waitForPost(beta, postRes3.post_view.post);
expect(postRes4.post).toBeDefined(); expect(postRes4.post).toBeDefined();
expect(postRes4.creator_banned_from_community).toBe(false); expect(postRes4.creator_community_actions?.received_ban).toBeUndefined();
await unfollowRemotes(alpha); await unfollowRemotes(alpha);
}); });

View file

@ -60,7 +60,9 @@ test("Follow a private community", async () => {
// Follow listed as pending // Follow listed as pending
const follow1 = await getCommunity(user, betaCommunityId); 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 // Wait for follow to federate, shown as pending
let pendingFollows1 = await waitUntil( let pendingFollows1 = await waitUntil(
@ -76,7 +78,9 @@ test("Follow a private community", async () => {
// user still sees approval required at this point // user still sees approval required at this point
const betaCommunity2 = await getCommunity(user, betaCommunityId); 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 // Approve the follow
const approve = await approveCommunityPendingFollow( const approve = await approveCommunityPendingFollow(
@ -89,7 +93,7 @@ test("Follow a private community", async () => {
// Follow is confirmed // Follow is confirmed
await waitUntil( await waitUntil(
() => getCommunity(user, betaCommunityId), () => getCommunity(user, betaCommunityId),
c => c.community_view.subscribed == "Subscribed", c => c.community_view.community_actions?.follow_state == "Accepted",
); );
const pendingFollows2 = await listCommunityPendingFollows(alpha); const pendingFollows2 = await listCommunityPendingFollows(alpha);
expect(pendingFollows2.items.length).toBe(0); 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); const post1 = await createPost(user, betaCommunity.id);
expect(post1.post_view).toBeDefined(); expect(post1.post_view).toBeDefined();
const like = await likeComment(user, 1, resolvedComment!.comment); 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 () => { test("Reject follower", async () => {
@ -188,7 +192,9 @@ test("Reject follower", async () => {
follow: true, follow: true,
}; };
const follow = await user.followCommunity(follow_form); 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( const pendingFollows1 = await waitUntil(
() => listCommunityPendingFollows(alpha), () => listCommunityPendingFollows(alpha),
@ -204,7 +210,7 @@ test("Reject follower", async () => {
await waitUntil( await waitUntil(
() => getCommunity(user, betaCommunity1.id), () => 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 // Follow is confirmed
await waitUntil( await waitUntil(
() => getCommunity(beta, betaCommunityId), () => getCommunity(beta, betaCommunityId),
c => c.community_view.subscribed == "Subscribed", c => c.community_view.community_actions?.follow_state == "Accepted",
); );
await waitUntil( await waitUntil(
() => getCommunity(gamma, gammaCommunityId), () => 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 // create a post and comment from gamma
@ -293,7 +299,7 @@ test("Fetch remote content in private community", async () => {
// Follow is confirmed // Follow is confirmed
await waitUntil( await waitUntil(
() => getCommunity(beta, betaCommunityId), () => getCommunity(beta, betaCommunityId),
c => c.community_view.subscribed == "Subscribed", c => c.community_view.community_actions?.follow_state == "Accepted",
); );
// beta creates post and comment // beta creates post and comment

View file

@ -471,8 +471,10 @@ export async function followCommunity(
const res = await api.followCommunity(form); const res = await api.followCommunity(form);
await waitUntil( await waitUntil(
() => getCommunity(api, res.community_view.community.id), () => getCommunity(api, res.community_view.community.id),
g => g => {
g.community_view.subscribed === (follow ? "Subscribed" : "NotSubscribed"), 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) // wait FOLLOW_ADDITIONS_RECHECK_DELAY (there's no API to wait for this currently)
await delay(2000); await delay(2000);

View file

@ -10,14 +10,14 @@ use lemmy_api_common::{
use lemmy_db_schema::{ use lemmy_db_schema::{
newtypes::{LocalUserId, PostOrCommentId}, newtypes::{LocalUserId, PostOrCommentId},
source::{ source::{
comment::{CommentLike, CommentLikeForm}, comment::{CommentActions, CommentLikeForm},
comment_reply::CommentReply, comment_reply::CommentReply,
local_site::LocalSite, local_site::LocalSite,
}, },
traits::Likeable, traits::Likeable,
}; };
use lemmy_db_views::structs::{CommentView, LocalUserView}; use lemmy_db_views::structs::{CommentView, LocalUserView};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; use lemmy_utils::error::LemmyResult;
use std::ops::Deref; use std::ops::Deref;
pub async fn like_comment( pub async fn like_comment(
@ -64,23 +64,18 @@ pub async fn like_comment(
} }
} }
let like_form = CommentLikeForm { let like_form = CommentLikeForm::new(local_user_view.person.id, data.comment_id, data.score);
comment_id: data.comment_id,
person_id: local_user_view.person.id,
score: data.score,
};
// Remove any likes first // Remove any likes first
let person_id = local_user_view.person.id; 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 // 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 { if do_add {
CommentLike::like(&mut context.pool(), &like_form) CommentActions::like(&mut context.pool(), &like_form).await?;
.await
.with_lemmy_type(LemmyErrorType::CouldntLikeComment)?;
} }
ActivityChannel::submit_activity( ActivityChannel::submit_activity(

View file

@ -4,27 +4,23 @@ use lemmy_api_common::{
context::LemmyContext, context::LemmyContext,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::comment::{CommentSaved, CommentSavedForm}, source::comment::{CommentActions, CommentSavedForm},
traits::Saveable, traits::Saveable,
}; };
use lemmy_db_views::structs::{CommentView, LocalUserView}; use lemmy_db_views::structs::{CommentView, LocalUserView};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; use lemmy_utils::error::LemmyResult;
pub async fn save_comment( pub async fn save_comment(
data: Json<SaveComment>, data: Json<SaveComment>,
context: Data<LemmyContext>, context: Data<LemmyContext>,
local_user_view: LocalUserView, local_user_view: LocalUserView,
) -> LemmyResult<Json<CommentResponse>> { ) -> LemmyResult<Json<CommentResponse>> {
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 { if data.save {
CommentSaved::save(&mut context.pool(), &comment_saved_form) CommentActions::save(&mut context.pool(), &comment_saved_form).await?;
.await
.with_lemmy_type(LemmyErrorType::CouldntSaveComment)?;
} else { } else {
CommentSaved::unsave(&mut context.pool(), &comment_saved_form) CommentActions::unsave(&mut context.pool(), &comment_saved_form).await?;
.await
.with_lemmy_type(LemmyErrorType::CouldntSaveComment)?;
} }
let comment_id = data.comment_id; let comment_id = data.comment_id;

View file

@ -8,14 +8,14 @@ use lemmy_api_common::{
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
community::{Community, CommunityModerator, CommunityModeratorForm}, community::{Community, CommunityActions, CommunityModeratorForm},
local_user::LocalUser, local_user::LocalUser,
mod_log::moderator::{ModAddCommunity, ModAddCommunityForm}, mod_log::moderator::{ModAddCommunity, ModAddCommunityForm},
}, },
traits::{Crud, Joinable}, traits::{Crud, Joinable},
}; };
use lemmy_db_views::structs::{CommunityModeratorView, LocalUserView}; 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( pub async fn add_mod_to_community(
data: Json<AddModToCommunity>, data: Json<AddModToCommunity>,
@ -56,18 +56,11 @@ pub async fn add_mod_to_community(
} }
// Update in local database // Update in local database
let community_moderator_form = CommunityModeratorForm { let community_moderator_form = CommunityModeratorForm::new(data.community_id, data.person_id);
community_id: data.community_id,
person_id: data.person_id,
};
if data.added { if data.added {
CommunityModerator::join(&mut context.pool(), &community_moderator_form) CommunityActions::join(&mut context.pool(), &community_moderator_form).await?;
.await
.with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists)?;
} else { } else {
CommunityModerator::leave(&mut context.pool(), &community_moderator_form) CommunityActions::leave(&mut context.pool(), &community_moderator_form).await?;
.await
.with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists)?;
} }
// Mod tables // Mod tables

View file

@ -12,23 +12,14 @@ use lemmy_api_common::{
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
community::{ community::{Community, CommunityActions, CommunityPersonBanForm},
Community,
CommunityFollower,
CommunityFollowerForm,
CommunityPersonBan,
CommunityPersonBanForm,
},
local_user::LocalUser, local_user::LocalUser,
mod_log::moderator::{ModBanFromCommunity, ModBanFromCommunityForm}, mod_log::moderator::{ModBanFromCommunity, ModBanFromCommunityForm},
}, },
traits::{Bannable, Crud, Followable}, traits::{Bannable, Crud, Followable},
}; };
use lemmy_db_views::structs::{LocalUserView, PersonView}; use lemmy_db_views::structs::{LocalUserView, PersonView};
use lemmy_utils::{ use lemmy_utils::{error::LemmyResult, utils::validation::is_valid_body_field};
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
utils::validation::is_valid_body_field,
};
pub async fn ban_from_community( pub async fn ban_from_community(
data: Json<BanFromCommunity>, data: Json<BanFromCommunity>,
@ -61,25 +52,19 @@ pub async fn ban_from_community(
} }
let community_user_ban_form = CommunityPersonBanForm { let community_user_ban_form = CommunityPersonBanForm {
community_id: data.community_id, ban_expires: Some(expires),
person_id: data.person_id, ..CommunityPersonBanForm::new(data.community_id, data.person_id)
expires: Some(expires),
}; };
if data.ban { if data.ban {
CommunityPersonBan::ban(&mut context.pool(), &community_user_ban_form) CommunityActions::ban(&mut context.pool(), &community_user_ban_form).await?;
.await
.with_lemmy_type(LemmyErrorType::CommunityUserAlreadyBanned)?;
// Also unsubscribe them from the community, if they are subscribed // Also unsubscribe them from the community, if they are subscribed
let community_follower_form = CommunityFollowerForm::new(data.community_id, banned_person_id); CommunityActions::unfollow(&mut context.pool(), banned_person_id, data.community_id)
CommunityFollower::unfollow(&mut context.pool(), &community_follower_form)
.await .await
.ok(); .ok();
} else { } else {
CommunityPersonBan::unban(&mut context.pool(), &community_user_ban_form) CommunityActions::unban(&mut context.pool(), &community_user_ban_form).await?;
.await
.with_lemmy_type(LemmyErrorType::CommunityUserAlreadyBanned)?;
} }
// Remove/Restore their data if that's desired // Remove/Restore their data if that's desired

View file

@ -6,14 +6,11 @@ use lemmy_api_common::{
send_activity::{ActivityChannel, SendActivityData}, send_activity::{ActivityChannel, SendActivityData},
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::community::{CommunityActions, CommunityBlockForm},
community::{CommunityFollower, CommunityFollowerForm},
community_block::{CommunityBlock, CommunityBlockForm},
},
traits::{Blockable, Followable}, traits::{Blockable, Followable},
}; };
use lemmy_db_views::structs::{CommunityView, LocalUserView}; 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( pub async fn user_block_community(
data: Json<BlockCommunity>, data: Json<BlockCommunity>,
@ -22,25 +19,17 @@ pub async fn user_block_community(
) -> LemmyResult<Json<BlockCommunityResponse>> { ) -> LemmyResult<Json<BlockCommunityResponse>> {
let community_id = data.community_id; let community_id = data.community_id;
let person_id = local_user_view.person.id; let person_id = local_user_view.person.id;
let community_block_form = CommunityBlockForm { let community_block_form = CommunityBlockForm::new(community_id, person_id);
person_id,
community_id,
};
if data.block { if data.block {
CommunityBlock::block(&mut context.pool(), &community_block_form) CommunityActions::block(&mut context.pool(), &community_block_form).await?;
.await
.with_lemmy_type(LemmyErrorType::CommunityBlockAlreadyExists)?;
// Also, unfollow the community, and send a federated unfollow // Also, unfollow the community, and send a federated unfollow
let community_follower_form = CommunityFollowerForm::new(data.community_id, person_id); CommunityActions::unfollow(&mut context.pool(), person_id, data.community_id)
CommunityFollower::unfollow(&mut context.pool(), &community_follower_form)
.await .await
.ok(); .ok();
} else { } else {
CommunityBlock::unblock(&mut context.pool(), &community_block_form) CommunityActions::unblock(&mut context.pool(), &community_block_form).await?;
.await
.with_lemmy_type(LemmyErrorType::CommunityBlockAlreadyExists)?;
} }
let community_view = CommunityView::read( let community_view = CommunityView::read(

View file

@ -9,13 +9,13 @@ use lemmy_api_common::{
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
actor_language::CommunityLanguage, actor_language::CommunityLanguage,
community::{Community, CommunityFollower, CommunityFollowerForm, CommunityFollowerState}, community::{Community, CommunityActions, CommunityFollowerForm, CommunityFollowerState},
}, },
traits::{Crud, Followable}, traits::{Crud, Followable},
CommunityVisibility, CommunityVisibility,
}; };
use lemmy_db_views::structs::{CommunityPersonBanView, CommunityView, LocalUserView}; 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( pub async fn follow_community(
data: Json<FollowCommunity>, data: Json<FollowCommunity>,
@ -24,7 +24,7 @@ pub async fn follow_community(
) -> LemmyResult<Json<CommunityResponse>> { ) -> LemmyResult<Json<CommunityResponse>> {
check_user_valid(&local_user_view.person)?; check_user_valid(&local_user_view.person)?;
let community = Community::read(&mut context.pool(), data.community_id).await?; 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 { if data.follow {
// Only run these checks for local community, in case of remote community the local // 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). // actions from existing followers for private community (so following would be impossible).
if community.local { if community.local {
check_community_deleted_removed(&community)?; check_community_deleted_removed(&community)?;
CommunityPersonBanView::check(&mut context.pool(), local_user_view.person.id, community.id) CommunityPersonBanView::check(&mut context.pool(), person_id, community.id).await?;
.await?;
} }
let state = if community.local { let follow_state = if community.local {
// Local follow is accepted immediately // Local follow is accepted immediately
Some(CommunityFollowerState::Accepted) CommunityFollowerState::Accepted
} else if community.visibility == CommunityVisibility::Private { } else if community.visibility == CommunityVisibility::Private {
// Private communities require manual approval // Private communities require manual approval
Some(CommunityFollowerState::ApprovalRequired) CommunityFollowerState::ApprovalRequired
} else { } else {
// remote follow needs to be federated first // remote follow needs to be federated first
Some(CommunityFollowerState::Pending) CommunityFollowerState::Pending
}; };
let form = CommunityFollowerForm { let form = CommunityFollowerForm::new(community.id, person_id, follow_state);
state,
..CommunityFollowerForm::new(community.id, local_user_view.person.id)
};
// Write to db // Write to db
CommunityFollower::follow(&mut context.pool(), &form) CommunityActions::follow(&mut context.pool(), &form).await?;
.await
.with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists)?;
} else { } else {
CommunityFollower::unfollow(&mut context.pool(), &form) CommunityActions::unfollow(&mut context.pool(), person_id, community.id).await?;
.await
.with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists)?;
} }
// Send the federated follow // Send the federated follow

View file

@ -7,10 +7,7 @@ use lemmy_api_common::{
utils::is_mod_or_admin, utils::is_mod_or_admin,
SuccessResponse, SuccessResponse,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{source::community::CommunityActions, traits::Followable};
source::community::{CommunityFollower, CommunityFollowerForm},
traits::Followable,
};
use lemmy_db_views::structs::LocalUserView; use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::LemmyResult; use lemmy_utils::error::LemmyResult;
@ -27,7 +24,7 @@ pub async fn post_pending_follows_approve(
.await?; .await?;
let activity_data = if data.approve { let activity_data = if data.approve {
CommunityFollower::approve( CommunityActions::approve_follower(
&mut context.pool(), &mut context.pool(),
data.community_id, data.community_id,
data.follower_id, data.follower_id,
@ -36,8 +33,7 @@ pub async fn post_pending_follows_approve(
.await?; .await?;
SendActivityData::AcceptFollower(data.community_id, data.follower_id) SendActivityData::AcceptFollower(data.community_id, data.follower_id)
} else { } else {
let form = CommunityFollowerForm::new(data.community_id, data.follower_id); CommunityActions::unfollow(&mut context.pool(), data.follower_id, data.community_id).await?;
CommunityFollower::unfollow(&mut context.pool(), &form).await?;
SendActivityData::RejectFollower(data.community_id, data.follower_id) SendActivityData::RejectFollower(data.community_id, data.follower_id)
}; };
ActivityChannel::submit_activity(activity_data, &context)?; ActivityChannel::submit_activity(activity_data, &context)?;

View file

@ -7,14 +7,14 @@ use lemmy_api_common::{
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
community::{Community, CommunityModerator, CommunityModeratorForm}, community::{Community, CommunityActions, CommunityModeratorForm},
mod_log::moderator::{ModTransferCommunity, ModTransferCommunityForm}, mod_log::moderator::{ModTransferCommunity, ModTransferCommunityForm},
}, },
traits::{Crud, Joinable}, traits::{Crud, Joinable},
}; };
use lemmy_db_views::structs::{CommunityModeratorView, CommunityView, LocalUserView}; use lemmy_db_views::structs::{CommunityModeratorView, CommunityView, LocalUserView};
use lemmy_utils::{ use lemmy_utils::{
error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, error::{LemmyErrorType, LemmyResult},
location_info, location_info,
}; };
@ -50,19 +50,15 @@ pub async fn transfer_community(
// Delete all the mods // Delete all the mods
let community_id = data.community_id; 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 // TODO: this should probably be a bulk operation
// Re-add the mods, in the new order // Re-add the mods, in the new order
for cmod in &community_mods { for cmod in &community_mods {
let community_moderator_form = CommunityModeratorForm { let community_moderator_form =
community_id: cmod.community.id, CommunityModeratorForm::new(cmod.community.id, cmod.moderator.id);
person_id: cmod.moderator.id,
};
CommunityModerator::join(&mut context.pool(), &community_moderator_form) CommunityActions::join(&mut context.pool(), &community_moderator_form).await?;
.await
.with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists)?;
} }
// Mod tables // Mod tables

View file

@ -9,12 +9,7 @@ use lemmy_api_common::{
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
community::{ community::{CommunityActions, CommunityPersonBanForm},
CommunityFollower,
CommunityFollowerForm,
CommunityPersonBan,
CommunityPersonBanForm,
},
mod_log::moderator::{ModBanFromCommunity, ModBanFromCommunityForm}, mod_log::moderator::{ModBanFromCommunity, ModBanFromCommunityForm},
person::Person, person::Person,
}, },
@ -162,25 +157,23 @@ pub(crate) async fn ban_nonlocal_user_from_local_communities(
// Ban / unban them from our local communities // Ban / unban them from our local communities
let community_user_ban_form = CommunityPersonBanForm { let community_user_ban_form = CommunityPersonBanForm {
community_id, ban_expires: Some(expires_dt),
person_id: target.id, ..CommunityPersonBanForm::new(community_id, target.id)
expires: Some(expires_dt),
}; };
if ban { if ban {
// Ignore all errors for these // Ignore all errors for these
CommunityPersonBan::ban(&mut context.pool(), &community_user_ban_form) CommunityActions::ban(&mut context.pool(), &community_user_ban_form)
.await .await
.ok(); .ok();
// Also unsubscribe them from the community, if they are subscribed // 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 .await
.ok(); .ok();
} else { } else {
CommunityPersonBan::unban(&mut context.pool(), &community_user_ban_form) CommunityActions::unban(&mut context.pool(), &community_user_ban_form)
.await .await
.ok(); .ok();
} }

View file

@ -4,11 +4,11 @@ use lemmy_api_common::{
person::{BlockPerson, BlockPersonResponse}, person::{BlockPerson, BlockPersonResponse},
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::person_block::{PersonBlock, PersonBlockForm}, source::person::{PersonActions, PersonBlockForm},
traits::Blockable, traits::Blockable,
}; };
use lemmy_db_views::structs::{LocalUserView, PersonView}; 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( pub async fn user_block_person(
data: Json<BlockPerson>, data: Json<BlockPerson>,
@ -23,10 +23,7 @@ pub async fn user_block_person(
Err(LemmyErrorType::CantBlockYourself)? Err(LemmyErrorType::CantBlockYourself)?
} }
let person_block_form = PersonBlockForm { let person_block_form = PersonBlockForm::new(person_id, target_id);
person_id,
target_id,
};
let target_user = LocalUserView::read_person(&mut context.pool(), target_id) let target_user = LocalUserView::read_person(&mut context.pool(), target_id)
.await .await
@ -37,13 +34,9 @@ pub async fn user_block_person(
} }
if data.block { if data.block {
PersonBlock::block(&mut context.pool(), &person_block_form) PersonActions::block(&mut context.pool(), &person_block_form).await?;
.await
.with_lemmy_type(LemmyErrorType::PersonBlockAlreadyExists)?;
} else { } else {
PersonBlock::unblock(&mut context.pool(), &person_block_form) PersonActions::unblock(&mut context.pool(), &person_block_form).await?;
.await
.with_lemmy_type(LemmyErrorType::PersonBlockAlreadyExists)?;
} }
let person_view = PersonView::read(&mut context.pool(), target_id, false).await?; let person_view = PersonView::read(&mut context.pool(), target_id, false).await?;

View file

@ -2,11 +2,11 @@ use activitypub_federation::config::Data;
use actix_web::web::Json; use actix_web::web::Json;
use lemmy_api_common::{context::LemmyContext, site::UserBlockInstanceParams, SuccessResponse}; use lemmy_api_common::{context::LemmyContext, site::UserBlockInstanceParams, SuccessResponse};
use lemmy_db_schema::{ use lemmy_db_schema::{
source::instance_block::{InstanceBlock, InstanceBlockForm}, source::instance::{InstanceActions, InstanceBlockForm},
traits::Blockable, traits::Blockable,
}; };
use lemmy_db_views::structs::LocalUserView; 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( pub async fn user_block_instance(
data: Json<UserBlockInstanceParams>, data: Json<UserBlockInstanceParams>,
@ -19,19 +19,12 @@ pub async fn user_block_instance(
return Err(LemmyErrorType::CantBlockLocalInstance)?; return Err(LemmyErrorType::CantBlockLocalInstance)?;
} }
let instance_block_form = InstanceBlockForm { let instance_block_form = InstanceBlockForm::new(person_id, instance_id);
person_id,
instance_id,
};
if data.block { if data.block {
InstanceBlock::block(&mut context.pool(), &instance_block_form) InstanceActions::block(&mut context.pool(), &instance_block_form).await?;
.await
.with_lemmy_type(LemmyErrorType::InstanceBlockAlreadyExists)?;
} else { } else {
InstanceBlock::unblock(&mut context.pool(), &instance_block_form) InstanceActions::unblock(&mut context.pool(), &instance_block_form).await?;
.await
.with_lemmy_type(LemmyErrorType::InstanceBlockAlreadyExists)?;
} }
Ok(Json(SuccessResponse::default())) Ok(Json(SuccessResponse::default()))

View file

@ -3,9 +3,12 @@ use lemmy_api_common::{
context::LemmyContext, context::LemmyContext,
post::{HidePost, PostResponse}, 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_db_views::structs::{LocalUserView, PostView};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; use lemmy_utils::error::LemmyResult;
pub async fn hide_post( pub async fn hide_post(
data: Json<HidePost>, data: Json<HidePost>,
@ -15,15 +18,13 @@ pub async fn hide_post(
let person_id = local_user_view.person.id; let person_id = local_user_view.person.id;
let post_id = data.post_id; let post_id = data.post_id;
let hide_form = PostHideForm::new(post_id, person_id);
// Mark the post as hidden / unhidden // Mark the post as hidden / unhidden
if data.hide { if data.hide {
PostHide::hide(&mut context.pool(), post_id, person_id) PostActions::hide(&mut context.pool(), &hide_form).await?;
.await
.with_lemmy_type(LemmyErrorType::CouldntHidePost)?;
} else { } else {
PostHide::unhide(&mut context.pool(), post_id, person_id) PostActions::unhide(&mut context.pool(), &hide_form).await?;
.await
.with_lemmy_type(LemmyErrorType::CouldntHidePost)?;
} }
let post_view = PostView::read( let post_view = PostView::read(

View file

@ -11,12 +11,12 @@ use lemmy_db_schema::{
newtypes::PostOrCommentId, newtypes::PostOrCommentId,
source::{ source::{
local_site::LocalSite, 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_db_views::structs::{LocalUserView, PostView};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; use lemmy_utils::error::LemmyResult;
use std::ops::Deref; use std::ops::Deref;
pub async fn like_post( pub async fn like_post(
@ -52,19 +52,18 @@ pub async fn like_post(
// Remove any likes first // Remove any likes first
let person_id = local_user_view.person.id; 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 // 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 { if do_add {
PostLike::like(&mut context.pool(), &like_form) PostActions::like(&mut context.pool(), &like_form).await?;
.await
.with_lemmy_type(LemmyErrorType::CouldntLikePost)?;
} }
// Mark Post Read // Mark Post Read
let read_form = PostReadForm::new(post_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?;
ActivityChannel::submit_activity( ActivityChannel::submit_activity(
SendActivityData::LikePostOrComment { SendActivityData::LikePostOrComment {

View file

@ -1,6 +1,6 @@
use actix_web::web::{Data, Json}; use actix_web::web::{Data, Json};
use lemmy_api_common::{context::LemmyContext, post::MarkManyPostsAsRead, SuccessResponse}; 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_db_views::structs::LocalUserView;
use lemmy_utils::error::{LemmyErrorType, LemmyResult, MAX_API_PARAM_ELEMENTS}; 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 person_id = local_user_view.person.id;
let forms = PostActions::build_many_read_forms(post_ids, person_id);
// Mark the posts as read // 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())) Ok(Json(SuccessResponse::default()))
} }

View file

@ -3,7 +3,10 @@ use lemmy_api_common::{
context::LemmyContext, context::LemmyContext,
post::{MarkPostAsRead, PostResponse}, 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_db_views::structs::{LocalUserView, PostView};
use lemmy_utils::error::LemmyResult; use lemmy_utils::error::LemmyResult;
@ -18,9 +21,9 @@ pub async fn mark_post_as_read(
// Mark the post as read / unread // Mark the post as read / unread
let form = PostReadForm::new(post_id, person_id); let form = PostReadForm::new(post_id, person_id);
if data.read { if data.read {
PostRead::mark_as_read(&mut context.pool(), &form).await?; PostActions::mark_as_read(&mut context.pool(), &form).await?;
} else { } else {
PostRead::mark_as_unread(&mut context.pool(), &form).await?; PostActions::mark_as_unread(&mut context.pool(), &form).await?;
} }
let post_view = PostView::read( let post_view = PostView::read(
&mut context.pool(), &mut context.pool(),

View file

@ -4,11 +4,11 @@ use lemmy_api_common::{
post::{PostResponse, SavePost}, post::{PostResponse, SavePost},
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::post::{PostRead, PostReadForm, PostSaved, PostSavedForm}, source::post::{PostActions, PostReadForm, PostSavedForm},
traits::Saveable, traits::{Readable, Saveable},
}; };
use lemmy_db_views::structs::{LocalUserView, PostView}; use lemmy_db_views::structs::{LocalUserView, PostView};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; use lemmy_utils::error::LemmyResult;
pub async fn save_post( pub async fn save_post(
data: Json<SavePost>, data: Json<SavePost>,
@ -18,13 +18,9 @@ pub async fn save_post(
let post_saved_form = PostSavedForm::new(data.post_id, local_user_view.person.id); let post_saved_form = PostSavedForm::new(data.post_id, local_user_view.person.id);
if data.save { if data.save {
PostSaved::save(&mut context.pool(), &post_saved_form) PostActions::save(&mut context.pool(), &post_saved_form).await?;
.await
.with_lemmy_type(LemmyErrorType::CouldntSavePost)?;
} else { } else {
PostSaved::unsave(&mut context.pool(), &post_saved_form) PostActions::unsave(&mut context.pool(), &post_saved_form).await?;
.await
.with_lemmy_type(LemmyErrorType::CouldntSavePost)?;
} }
let post_id = data.post_id; let post_id = data.post_id;
@ -38,7 +34,7 @@ pub async fn save_post(
.await?; .await?;
let read_form = PostReadForm::new(post_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?;
Ok(Json(PostResponse { post_view })) Ok(Json(PostResponse { post_view }))
} }

View file

@ -20,7 +20,7 @@ use lemmy_db_schema::{
traits::Reportable, traits::Reportable,
}; };
use lemmy_db_views::structs::{CommentReportView, CommentView, LocalUserView}; 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 /// Creates a comment report and notifies the moderators of the community
pub async fn create_comment_report( 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(), violates_instance_rules: data.violates_instance_rules.unwrap_or_default(),
}; };
let report = CommentReport::report(&mut context.pool(), &report_form) let report = CommentReport::report(&mut context.pool(), &report_form).await?;
.await
.with_lemmy_type(LemmyErrorType::CouldntCreateReport)?;
let comment_report_view = let comment_report_view =
CommentReportView::read(&mut context.pool(), report.id, person_id).await?; CommentReportView::read(&mut context.pool(), report.id, person_id).await?;

View file

@ -8,7 +8,7 @@ use lemmy_api_common::{
}; };
use lemmy_db_schema::{source::comment_report::CommentReport, traits::Reportable}; use lemmy_db_schema::{source::comment_report::CommentReport, traits::Reportable};
use lemmy_db_views::structs::{CommentReportView, LocalUserView}; 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 /// Resolves or unresolves a comment report and notifies the moderators of the community
pub async fn resolve_comment_report( pub async fn resolve_comment_report(
@ -30,13 +30,9 @@ pub async fn resolve_comment_report(
.await?; .await?;
if data.resolved { if data.resolved {
CommentReport::resolve(&mut context.pool(), report_id, person_id) CommentReport::resolve(&mut context.pool(), report_id, person_id).await?;
.await
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)?;
} else { } else {
CommentReport::unresolve(&mut context.pool(), report_id, person_id) CommentReport::unresolve(&mut context.pool(), report_id, person_id).await?;
.await
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)?;
} }
let report_id = data.report_id; let report_id = data.report_id;

View file

@ -14,7 +14,7 @@ use lemmy_db_schema::{
traits::{Crud, Reportable}, traits::{Crud, Reportable},
}; };
use lemmy_db_views::structs::{CommunityReportView, LocalUserView}; 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( pub async fn create_community_report(
data: Json<CreateCommunityReport>, data: Json<CreateCommunityReport>,
@ -41,9 +41,7 @@ pub async fn create_community_report(
reason, reason,
}; };
let report = CommunityReport::report(&mut context.pool(), &report_form) let report = CommunityReport::report(&mut context.pool(), &report_form).await?;
.await
.with_lemmy_type(LemmyErrorType::CouldntCreateReport)?;
let community_report_view = let community_report_view =
CommunityReportView::read(&mut context.pool(), report.id, person_id).await?; CommunityReportView::read(&mut context.pool(), report.id, person_id).await?;

View file

@ -6,7 +6,7 @@ use lemmy_api_common::{
}; };
use lemmy_db_schema::{source::community_report::CommunityReport, traits::Reportable}; use lemmy_db_schema::{source::community_report::CommunityReport, traits::Reportable};
use lemmy_db_views::structs::{CommunityReportView, LocalUserView}; 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( pub async fn resolve_community_report(
data: Json<ResolveCommunityReport>, data: Json<ResolveCommunityReport>,
@ -18,13 +18,9 @@ pub async fn resolve_community_report(
let report_id = data.report_id; let report_id = data.report_id;
let person_id = local_user_view.person.id; let person_id = local_user_view.person.id;
if data.resolved { if data.resolved {
CommunityReport::resolve(&mut context.pool(), report_id, person_id) CommunityReport::resolve(&mut context.pool(), report_id, person_id).await?;
.await
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)?;
} else { } else {
CommunityReport::unresolve(&mut context.pool(), report_id, person_id) CommunityReport::unresolve(&mut context.pool(), report_id, person_id).await?;
.await
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)?;
} }
let community_report_view = let community_report_view =

View file

@ -20,7 +20,7 @@ use lemmy_db_schema::{
traits::Reportable, traits::Reportable,
}; };
use lemmy_db_views::structs::{LocalUserView, PostReportView, PostView}; 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 /// Creates a post report and notifies the moderators of the community
pub async fn create_post_report( 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(), violates_instance_rules: data.violates_instance_rules.unwrap_or_default(),
}; };
let report = PostReport::report(&mut context.pool(), &report_form) let report = PostReport::report(&mut context.pool(), &report_form).await?;
.await
.with_lemmy_type(LemmyErrorType::CouldntCreateReport)?;
let post_report_view = PostReportView::read(&mut context.pool(), report.id, person_id).await?; let post_report_view = PostReportView::read(&mut context.pool(), report.id, person_id).await?;

View file

@ -8,7 +8,7 @@ use lemmy_api_common::{
}; };
use lemmy_db_schema::{source::post_report::PostReport, traits::Reportable}; use lemmy_db_schema::{source::post_report::PostReport, traits::Reportable};
use lemmy_db_views::structs::{LocalUserView, PostReportView}; 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 /// Resolves or unresolves a post report and notifies the moderators of the community
pub async fn resolve_post_report( pub async fn resolve_post_report(
@ -30,14 +30,9 @@ pub async fn resolve_post_report(
.await?; .await?;
if data.resolved { if data.resolved {
PostReport::resolve(&mut context.pool(), report_id, person_id) PostReport::resolve(&mut context.pool(), report_id, person_id).await?;
.await
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)?;
} else { } else {
// TODO: not federated PostReport::unresolve(&mut context.pool(), report_id, person_id).await?;
PostReport::unresolve(&mut context.pool(), report_id, person_id)
.await
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)?;
} }
let post_report_view = PostReportView::read(&mut context.pool(), report_id, person_id).await?; let post_report_view = PostReportView::read(&mut context.pool(), report_id, person_id).await?;

View file

@ -14,7 +14,7 @@ use lemmy_db_schema::{
traits::{Crud, Reportable}, traits::{Crud, Reportable},
}; };
use lemmy_db_views::structs::{LocalUserView, PrivateMessageReportView}; 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( pub async fn create_pm_report(
data: Json<CreatePrivateMessageReport>, data: Json<CreatePrivateMessageReport>,
@ -41,9 +41,7 @@ pub async fn create_pm_report(
reason, reason,
}; };
let report = PrivateMessageReport::report(&mut context.pool(), &report_form) let report = PrivateMessageReport::report(&mut context.pool(), &report_form).await?;
.await
.with_lemmy_type(LemmyErrorType::CouldntCreateReport)?;
let private_message_report_view = let private_message_report_view =
PrivateMessageReportView::read(&mut context.pool(), report.id).await?; PrivateMessageReportView::read(&mut context.pool(), report.id).await?;

View file

@ -6,7 +6,7 @@ use lemmy_api_common::{
}; };
use lemmy_db_schema::{source::private_message_report::PrivateMessageReport, traits::Reportable}; use lemmy_db_schema::{source::private_message_report::PrivateMessageReport, traits::Reportable};
use lemmy_db_views::structs::{LocalUserView, PrivateMessageReportView}; 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( pub async fn resolve_pm_report(
data: Json<ResolvePrivateMessageReport>, data: Json<ResolvePrivateMessageReport>,
@ -18,13 +18,9 @@ pub async fn resolve_pm_report(
let report_id = data.report_id; let report_id = data.report_id;
let person_id = local_user_view.person.id; let person_id = local_user_view.person.id;
if data.resolved { if data.resolved {
PrivateMessageReport::resolve(&mut context.pool(), report_id, person_id) PrivateMessageReport::resolve(&mut context.pool(), report_id, person_id).await?;
.await
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)?;
} else { } else {
PrivateMessageReport::unresolve(&mut context.pool(), report_id, person_id) PrivateMessageReport::unresolve(&mut context.pool(), report_id, person_id).await?;
.await
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)?;
} }
let private_message_report_view = let private_message_report_view =

View file

@ -15,13 +15,11 @@ use enum_map::{enum_map, EnumMap};
use lemmy_db_schema::{ use lemmy_db_schema::{
newtypes::{CommentId, CommunityId, DbUrl, InstanceId, PersonId, PostId, PostOrCommentId}, newtypes::{CommentId, CommunityId, DbUrl, InstanceId, PersonId, PostId, PostOrCommentId},
source::{ source::{
comment::{Comment, CommentLike, CommentUpdateForm}, comment::{Comment, CommentActions, CommentUpdateForm},
community::{Community, CommunityModerator, CommunityUpdateForm}, community::{Community, CommunityActions, CommunityUpdateForm},
community_block::CommunityBlock,
email_verification::{EmailVerification, EmailVerificationForm}, email_verification::{EmailVerification, EmailVerificationForm},
images::{ImageDetails, RemoteImage}, images::{ImageDetails, RemoteImage},
instance::Instance, instance::{Instance, InstanceActions},
instance_block::InstanceBlock,
local_site::LocalSite, local_site::LocalSite,
local_site_rate_limit::LocalSiteRateLimit, local_site_rate_limit::LocalSiteRateLimit,
local_site_url_blocklist::LocalSiteUrlBlocklist, local_site_url_blocklist::LocalSiteUrlBlocklist,
@ -34,15 +32,13 @@ use lemmy_db_schema::{
}, },
oauth_account::OAuthAccount, oauth_account::OAuthAccount,
password_reset_request::PasswordResetRequest, password_reset_request::PasswordResetRequest,
person::{Person, PersonUpdateForm}, person::{Person, PersonActions, PersonUpdateForm},
person_block::PersonBlock, post::{Post, PostActions, PostReadCommentsForm},
post::{Post, PostLike},
post_actions::{PostActions, PostActionsForm},
private_message::PrivateMessage, private_message::PrivateMessage,
registration_application::RegistrationApplication, registration_application::RegistrationApplication,
site::Site, site::Site,
}, },
traits::{Crud, Likeable}, traits::{Blockable, Crud, Likeable, ReadComments},
utils::DbPool, utils::DbPool,
FederationMode, FederationMode,
RegistrationMode, RegistrationMode,
@ -157,13 +153,8 @@ pub async fn update_read_comments(
read_comments: i64, read_comments: i64,
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
) -> LemmyResult<()> { ) -> LemmyResult<()> {
let person_post_agg_form = PostActionsForm { let person_post_agg_form = PostReadCommentsForm::new(post_id, person_id, read_comments);
person_id, PostActions::update_read_comments(pool, &person_post_agg_form).await?;
post_id,
read_comments,
};
PostActions::upsert(pool, &person_post_agg_form).await?;
Ok(()) Ok(())
} }
@ -288,9 +279,9 @@ pub async fn check_person_instance_community_block(
community_id: CommunityId, community_id: CommunityId,
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
) -> LemmyResult<()> { ) -> LemmyResult<()> {
PersonBlock::read(pool, potential_blocker_id, my_id).await?; PersonActions::read_block(pool, potential_blocker_id, my_id).await?;
InstanceBlock::read(pool, potential_blocker_id, community_instance_id).await?; InstanceActions::read_block(pool, potential_blocker_id, community_instance_id).await?;
CommunityBlock::read(pool, potential_blocker_id, community_id).await?; CommunityActions::read_block(pool, potential_blocker_id, community_id).await?;
Ok(()) Ok(())
} }
@ -312,9 +303,9 @@ pub async fn check_local_vote_mode(
// Undo previous vote for item if new vote fails // Undo previous vote for item if new vote fails
if downvote_fail || upvote_fail { if downvote_fail || upvote_fail {
match post_or_comment_id { 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) => { 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)?; .with_lemmy_type(LemmyErrorType::CouldntUpdatePost)?;
// Leave communities they mod // 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 // Delete the oauth accounts linked to the local user
if let Ok(local_user) = LocalUserView::read_person(pool, person_id).await { if let Ok(local_user) = LocalUserView::read_person(pool, person_id).await {

View file

@ -19,7 +19,7 @@ use lemmy_db_schema::{
impls::actor_language::validate_post_language, impls::actor_language::validate_post_language,
newtypes::PostOrCommentId, newtypes::PostOrCommentId,
source::{ source::{
comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm}, comment::{Comment, CommentActions, CommentInsertForm, CommentLikeForm},
comment_reply::{CommentReply, CommentReplyUpdateForm}, comment_reply::{CommentReply, CommentReplyUpdateForm},
person_comment_mention::{PersonCommentMention, PersonCommentMentionUpdateForm}, person_comment_mention::{PersonCommentMention, PersonCommentMentionUpdateForm},
}, },
@ -123,15 +123,9 @@ pub async fn create_comment(
.await?; .await?;
// You like your own comment by default // You like your own comment by default
let like_form = CommentLikeForm { let like_form = CommentLikeForm::new(local_user_view.person.id, inserted_comment.id, 1);
comment_id: inserted_comment.id,
person_id: local_user_view.person.id,
score: 1,
};
CommentLike::like(&mut context.pool(), &like_form) CommentActions::like(&mut context.pool(), &like_form).await?;
.await
.with_lemmy_type(LemmyErrorType::CouldntLikeComment)?;
ActivityChannel::submit_activity( ActivityChannel::submit_activity(
SendActivityData::CreateComment(inserted_comment.clone()), SendActivityData::CreateComment(inserted_comment.clone()),

View file

@ -20,11 +20,10 @@ use lemmy_db_schema::{
actor_language::{CommunityLanguage, LocalUserLanguage, SiteLanguage}, actor_language::{CommunityLanguage, LocalUserLanguage, SiteLanguage},
community::{ community::{
Community, Community,
CommunityFollower, CommunityActions,
CommunityFollowerForm, CommunityFollowerForm,
CommunityFollowerState, CommunityFollowerState,
CommunityInsertForm, CommunityInsertForm,
CommunityModerator,
CommunityModeratorForm, CommunityModeratorForm,
}, },
}, },
@ -115,26 +114,19 @@ pub async fn create_community(
let community_id = inserted_community.id; let community_id = inserted_community.id;
// The community creator becomes a moderator // The community creator becomes a moderator
let community_moderator_form = CommunityModeratorForm { let community_moderator_form =
community_id, CommunityModeratorForm::new(community_id, local_user_view.person.id);
person_id: local_user_view.person.id,
};
CommunityModerator::join(&mut context.pool(), &community_moderator_form) CommunityActions::join(&mut context.pool(), &community_moderator_form).await?;
.await
.with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists)?;
// Follow your own community // Follow your own community
let community_follower_form = CommunityFollowerForm { let community_follower_form = CommunityFollowerForm::new(
community_id, community_id,
person_id: local_user_view.person.id, local_user_view.person.id,
state: Some(CommunityFollowerState::Accepted), CommunityFollowerState::Accepted,
approver_id: None, );
};
CommunityFollower::follow(&mut context.pool(), &community_follower_form) CommunityActions::follow(&mut context.pool(), &community_follower_form).await?;
.await
.with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists)?;
// Update the discussion_languages if that's provided // Update the discussion_languages if that's provided
let site_languages = SiteLanguage::read_local_raw(&mut context.pool()).await?; let site_languages = SiteLanguage::read_local_raw(&mut context.pool()).await?;

View file

@ -23,9 +23,9 @@ use lemmy_db_schema::{
source::{ source::{
community::Community, community::Community,
local_site::LocalSite, 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, utils::diesel_url_create,
}; };
use lemmy_db_views::structs::{CommunityModeratorView, LocalUserView}; use lemmy_db_views::structs::{CommunityModeratorView, LocalUserView};
@ -144,9 +144,7 @@ pub async fn create_post(
let post_id = inserted_post.id; let post_id = inserted_post.id;
let like_form = PostLikeForm::new(post_id, person_id, 1); let like_form = PostLikeForm::new(post_id, person_id, 1);
PostLike::like(&mut context.pool(), &like_form) PostActions::like(&mut context.pool(), &like_form).await?;
.await
.with_lemmy_type(LemmyErrorType::CouldntLikePost)?;
// Scan the post body for user mentions, add those rows // Scan the post body for user mentions, add those rows
let mentions = scrape_text_for_mentions(&inserted_post.body.clone().unwrap_or_default()); let mentions = scrape_text_for_mentions(&inserted_post.body.clone().unwrap_or_default());
@ -161,7 +159,7 @@ pub async fn create_post(
.await?; .await?;
let read_form = PostReadForm::new(post_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?;
build_post_response(&context, community_id, local_user_view, post_id).await build_post_response(&context, community_id, local_user_view, post_id).await
} }

View file

@ -7,9 +7,9 @@ use lemmy_api_common::{
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
comment::Comment, comment::Comment,
post::{Post, PostRead, PostReadForm}, post::{Post, PostActions, PostReadForm},
}, },
traits::Crud, traits::{Crud, Readable},
}; };
use lemmy_db_views::{ use lemmy_db_views::{
post::post_view::PostQuery, post::post_view::PostQuery,
@ -64,7 +64,7 @@ pub async fn get_post(
let post_id = post_view.post.id; let post_id = post_view.post.id;
if let Some(person_id) = person_id { if let Some(person_id) = person_id {
let read_form = PostReadForm::new(post_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( update_read_comments(
person_id, person_id,

View file

@ -14,10 +14,10 @@ use lemmy_api_common::{
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
person_block::PersonBlock, person::PersonActions,
private_message::{PrivateMessage, PrivateMessageInsertForm}, private_message::{PrivateMessage, PrivateMessageInsertForm},
}, },
traits::Crud, traits::{Blockable, Crud},
}; };
use lemmy_db_views::structs::{LocalUserView, PrivateMessageView}; use lemmy_db_views::structs::{LocalUserView, PrivateMessageView};
use lemmy_utils::{ 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?; let content = process_markdown(&data.content, &slur_regex, &url_blocklist, &context).await?;
is_valid_body_field(&content, false)?; is_valid_body_field(&content, false)?;
PersonBlock::read( PersonActions::read_block(
&mut context.pool(), &mut context.pool(),
data.recipient_id, data.recipient_id,
local_user_view.person.id, local_user_view.person.id,

View file

@ -1,10 +1,13 @@
use actix_web::web::{Data, Json}; use actix_web::web::{Data, Json};
use lemmy_api_common::{context::LemmyContext, site::MyUserInfo, utils::check_user_valid}; use lemmy_api_common::{context::LemmyContext, site::MyUserInfo, utils::check_user_valid};
use lemmy_db_schema::source::{ use lemmy_db_schema::{
actor_language::LocalUserLanguage, source::{
community_block::CommunityBlock, actor_language::LocalUserLanguage,
instance_block::InstanceBlock, community::CommunityActions,
person_block::PersonBlock, instance::InstanceActions,
person::PersonActions,
},
traits::Blockable,
}; };
use lemmy_db_views::structs::{CommunityFollowerView, CommunityModeratorView, LocalUserView}; use lemmy_db_views::structs::{CommunityFollowerView, CommunityModeratorView, LocalUserView};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; 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) = let (follows, community_blocks, instance_blocks, person_blocks, moderates, discussion_languages) =
lemmy_db_schema::try_join_with_pool!(pool => ( lemmy_db_schema::try_join_with_pool!(pool => (
|pool| CommunityFollowerView::for_person(pool, person_id), |pool| CommunityFollowerView::for_person(pool, person_id),
|pool| CommunityBlock::for_person(pool, person_id), |pool| CommunityActions::read_blocks_for_person(pool, person_id),
|pool| InstanceBlock::for_person(pool, person_id), |pool| InstanceActions::read_blocks_for_person(pool, person_id),
|pool| PersonBlock::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| CommunityModeratorView::for_person(pool, person_id, Some(&local_user_view.local_user)),
|pool| LocalUserLanguage::read(pool, local_user_id) |pool| LocalUserLanguage::read(pool, local_user_id)
)) ))

View file

@ -30,7 +30,7 @@ use lemmy_api_common::{
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
activity::ActivitySendTargets, activity::ActivitySendTargets,
community::{CommunityPersonBan, CommunityPersonBanForm}, community::{CommunityActions, CommunityPersonBanForm},
mod_log::moderator::{ModBan, ModBanForm, ModBanFromCommunity, ModBanFromCommunityForm}, mod_log::moderator::{ModBan, ModBanForm, ModBanFromCommunity, ModBanFromCommunityForm},
person::{Person, PersonUpdateForm}, person::{Person, PersonUpdateForm},
}, },
@ -177,11 +177,10 @@ impl ActivityHandler for BlockUser {
} }
SiteOrCommunity::Community(community) => { SiteOrCommunity::Community(community) => {
let community_user_ban_form = CommunityPersonBanForm { let community_user_ban_form = CommunityPersonBanForm {
community_id: community.id, ban_expires: Some(expires),
person_id: blocked_person.id, ..CommunityPersonBanForm::new(community.id, blocked_person.id)
expires: Some(expires),
}; };
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. // 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 // If we unfollowed the community here, activities from the community would be rejected

View file

@ -26,7 +26,7 @@ use lemmy_api_common::{
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
activity::ActivitySendTargets, activity::ActivitySendTargets,
community::{CommunityPersonBan, CommunityPersonBanForm}, community::{CommunityActions, CommunityPersonBanForm},
mod_log::moderator::{ModBan, ModBanForm, ModBanFromCommunity, ModBanFromCommunityForm}, mod_log::moderator::{ModBan, ModBanForm, ModBanFromCommunity, ModBanFromCommunityForm},
person::{Person, PersonUpdateForm}, person::{Person, PersonUpdateForm},
}, },
@ -130,12 +130,8 @@ impl ActivityHandler for UndoBlockUser {
} }
SiteOrCommunity::Community(community) => { SiteOrCommunity::Community(community) => {
verify_visibility(&self.to, &self.cc, &community)?; verify_visibility(&self.to, &self.cc, &community)?;
let community_user_ban_form = CommunityPersonBanForm { let community_user_ban_form = CommunityPersonBanForm::new(community.id, blocked_person.id);
community_id: community.id, CommunityActions::unban(&mut context.pool(), &community_user_ban_form).await?;
person_id: blocked_person.id,
expires: None,
};
CommunityPersonBan::unban(&mut context.pool(), &community_user_ban_form).await?;
if self.restore_data.unwrap_or(false) { if self.restore_data.unwrap_or(false) {
remove_or_restore_user_data_in_community( remove_or_restore_user_data_in_community(

View file

@ -23,7 +23,7 @@ use activitypub_federation::{
traits::{ActivityHandler, Actor}, traits::{ActivityHandler, Actor},
}; };
use lemmy_api_common::context::LemmyContext; 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 lemmy_utils::error::{FederationError, LemmyError, LemmyErrorType, LemmyResult};
use serde_json::Value; use serde_json::Value;
use url::Url; use url::Url;
@ -212,7 +212,7 @@ async fn can_accept_activity_in_community(
return Err(LemmyErrorType::NotFound.into()); return Err(LemmyErrorType::NotFound.into());
} }
if !community.local { 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(()) Ok(())

View file

@ -30,7 +30,7 @@ use lemmy_db_schema::{
newtypes::{CommunityId, PersonId}, newtypes::{CommunityId, PersonId},
source::{ source::{
activity::ActivitySendTargets, activity::ActivitySendTargets,
community::{Community, CommunityModerator, CommunityModeratorForm}, community::{Community, CommunityActions, CommunityModeratorForm},
mod_log::moderator::{ModAddCommunity, ModAddCommunityForm}, mod_log::moderator::{ModAddCommunity, ModAddCommunityForm},
person::Person, person::Person,
post::{Post, PostUpdateForm}, 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. // already been added. Skip it here as it would result in a duplicate key error.
let new_mod_id = new_mod.id; let new_mod_id = new_mod.id;
let moderated_communities = 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?; .await?;
if !moderated_communities.contains(&community.id) { if !moderated_communities.contains(&community.id) {
let form = CommunityModeratorForm { let form = CommunityModeratorForm::new(community.id, new_mod.id);
community_id: community.id, CommunityActions::join(&mut context.pool(), &form).await?;
person_id: new_mod.id,
};
CommunityModerator::join(&mut context.pool(), &form).await?;
// write mod log // write mod log
let actor = self.actor.dereference(context).await?; let actor = self.actor.dereference(context).await?;

View file

@ -26,7 +26,7 @@ use lemmy_db_schema::{
impls::community::CollectionType, impls::community::CollectionType,
source::{ source::{
activity::ActivitySendTargets, activity::ActivitySendTargets,
community::{Community, CommunityModerator, CommunityModeratorForm}, community::{Community, CommunityActions, CommunityModeratorForm},
mod_log::moderator::{ModAddCommunity, ModAddCommunityForm}, mod_log::moderator::{ModAddCommunity, ModAddCommunityForm},
post::{Post, PostUpdateForm}, post::{Post, PostUpdateForm},
}, },
@ -124,11 +124,8 @@ impl ActivityHandler for CollectionRemove {
.dereference(context) .dereference(context)
.await?; .await?;
let form = CommunityModeratorForm { let form = CommunityModeratorForm::new(community.id, remove_mod.id);
community_id: community.id, CommunityActions::leave(&mut context.pool(), &form).await?;
person_id: remove_mod.id,
};
CommunityModerator::leave(&mut context.pool(), &form).await?;
// write mod log // write mod log
let actor = self.actor.dereference(context).await?; let actor = self.actor.dereference(context).await?;

View file

@ -10,7 +10,7 @@ use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
activity::ActivitySendTargets, activity::ActivitySendTargets,
person::{Person, PersonFollower}, person::{Person, PersonActions},
site::Site, site::Site,
}, },
traits::Crud, traits::Crud,
@ -59,7 +59,7 @@ pub(crate) async fn send_activity_in_community(
// send to user followers // send to user followers
if !is_mod_action { if !is_mod_action {
inboxes.add_inboxes( inboxes.add_inboxes(
PersonFollower::list_followers(&mut context.pool(), actor.id) PersonActions::list_followers(&mut context.pool(), actor.id)
.await? .await?
.into_iter() .into_iter()
.map(|p| ApubPerson(p).shared_inbox_or_inbox()), .map(|p| ApubPerson(p).shared_inbox_or_inbox()),

View file

@ -31,7 +31,7 @@ use lemmy_db_schema::{
newtypes::{PersonId, PostOrCommentId}, newtypes::{PersonId, PostOrCommentId},
source::{ source::{
activity::ActivitySendTargets, activity::ActivitySendTargets,
comment::{Comment, CommentLike, CommentLikeForm}, comment::{Comment, CommentActions, CommentLikeForm},
community::Community, community::Community,
person::Person, person::Person,
post::Post, post::Post,
@ -151,12 +151,8 @@ impl ActivityHandler for CreateOrUpdateNote {
let comment = ApubComment::from_json(self.object, context).await?; let comment = ApubComment::from_json(self.object, context).await?;
// author likes their own comment by default // author likes their own comment by default
let like_form = CommentLikeForm { let like_form = CommentLikeForm::new(comment.creator_id, comment.id, 1);
comment_id: comment.id, CommentActions::like(&mut context.pool(), &like_form).await?;
person_id: comment.creator_id,
score: 1,
};
CommentLike::like(&mut context.pool(), &like_form).await?;
// Calculate initial hot_rank // Calculate initial hot_rank
Comment::update_hot_rank(&mut context.pool(), comment.id).await?; Comment::update_hot_rank(&mut context.pool(), comment.id).await?;

View file

@ -27,7 +27,7 @@ use lemmy_db_schema::{
activity::ActivitySendTargets, activity::ActivitySendTargets,
community::Community, community::Community,
person::Person, person::Person,
post::{Post, PostLike, PostLikeForm}, post::{Post, PostActions, PostLikeForm},
}, },
traits::{Crud, Likeable}, traits::{Crud, Likeable},
}; };
@ -117,7 +117,7 @@ impl ActivityHandler for CreateOrUpdatePage {
// author likes their own post by default // author likes their own post by default
let like_form = PostLikeForm::new(post.id, post.creator_id, 1); 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 // Calculate initial hot_rank for post
Post::update_ranks(&mut context.pool(), post.id).await?; Post::update_ranks(&mut context.pool(), post.id).await?;

View file

@ -11,7 +11,7 @@ use activitypub_federation::{
}; };
use lemmy_api_common::context::LemmyContext; use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{activity::ActivitySendTargets, community::CommunityFollower}, source::{activity::ActivitySendTargets, community::CommunityActions},
traits::Followable, traits::Followable,
}; };
use lemmy_utils::error::{LemmyError, LemmyResult}; use lemmy_utils::error::{LemmyError, LemmyResult};
@ -66,7 +66,7 @@ impl ActivityHandler for AcceptFollow {
// This will throw an error if no follow was requested // This will throw an error if no follow was requested
let community_id = community.id; let community_id = community.id;
let person_id = person.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(()) Ok(())
} }

View file

@ -20,9 +20,9 @@ use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
activity::ActivitySendTargets, activity::ActivitySendTargets,
community::{CommunityFollower, CommunityFollowerForm, CommunityFollowerState}, community::{CommunityActions, CommunityFollowerForm, CommunityFollowerState},
instance::Instance, instance::Instance,
person::{PersonFollower, PersonFollowerForm}, person::{PersonActions, PersonFollowerForm},
}, },
traits::Followable, traits::Followable,
CommunityVisibility, CommunityVisibility,
@ -95,12 +95,8 @@ impl ActivityHandler for Follow {
let object = self.object.dereference(context).await?; let object = self.object.dereference(context).await?;
match object { match object {
UserOrCommunity::User(u) => { UserOrCommunity::User(u) => {
let form = PersonFollowerForm { let form = PersonFollowerForm::new(u.id, actor.id, false);
person_id: u.id, PersonActions::follow(&mut context.pool(), &form).await?;
follower_id: actor.id,
pending: false,
};
PersonFollower::follow(&mut context.pool(), &form).await?;
AcceptFollow::send(self, context).await?; AcceptFollow::send(self, context).await?;
} }
UserOrCommunity::Community(c) => { UserOrCommunity::Community(c) => {
@ -111,17 +107,14 @@ impl ActivityHandler for Follow {
return Err(FederationError::PlatformLackingPrivateCommunitySupport.into()); return Err(FederationError::PlatformLackingPrivateCommunitySupport.into());
} }
} }
let state = Some(match c.visibility { let follow_state = match c.visibility {
Public | Unlisted => CommunityFollowerState::Accepted, Public | Unlisted => CommunityFollowerState::Accepted,
Private => CommunityFollowerState::ApprovalRequired, Private => CommunityFollowerState::ApprovalRequired,
// Dont allow following local-only community via federation. // Dont allow following local-only community via federation.
LocalOnlyPrivate | LocalOnlyPublic => return Err(LemmyErrorType::NotFound.into()), 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 { if c.visibility == CommunityVisibility::Public {
AcceptFollow::send(self, context).await?; AcceptFollow::send(self, context).await?;
} }

View file

@ -11,10 +11,7 @@ use activitypub_federation::{
}; };
use lemmy_api_common::context::LemmyContext; use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{activity::ActivitySendTargets, community::CommunityActions},
activity::ActivitySendTargets,
community::{CommunityFollower, CommunityFollowerForm},
},
traits::Followable, traits::Followable,
}; };
use lemmy_utils::error::{LemmyError, LemmyResult}; use lemmy_utils::error::{LemmyError, LemmyResult};
@ -68,8 +65,7 @@ impl ActivityHandler for RejectFollow {
let person = self.object.actor.dereference(context).await?; let person = self.object.actor.dereference(context).await?;
// remove the follow // remove the follow
let form = CommunityFollowerForm::new(community.id, person.id); CommunityActions::unfollow(&mut context.pool(), person.id, community.id).await?;
CommunityFollower::unfollow(&mut context.pool(), &form).await?;
Ok(()) Ok(())
} }

View file

@ -13,11 +13,7 @@ use activitypub_federation::{
}; };
use lemmy_api_common::context::LemmyContext; use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{activity::ActivitySendTargets, community::CommunityActions, person::PersonActions},
activity::ActivitySendTargets,
community::{CommunityFollower, CommunityFollowerForm},
person::{PersonFollower, PersonFollowerForm},
},
traits::Followable, traits::Followable,
}; };
use lemmy_utils::error::{LemmyError, LemmyResult}; use lemmy_utils::error::{LemmyError, LemmyResult};
@ -79,16 +75,10 @@ impl ActivityHandler for UndoFollow {
match object { match object {
UserOrCommunity::User(u) => { UserOrCommunity::User(u) => {
let form = PersonFollowerForm { PersonActions::unfollow(&mut context.pool(), person.id, u.id).await?;
person_id: u.id,
follower_id: person.id,
pending: false,
};
PersonFollower::unfollow(&mut context.pool(), &form).await?;
} }
UserOrCommunity::Community(c) => { UserOrCommunity::Community(c) => {
let form = CommunityFollowerForm::new(c.id, person.id); CommunityActions::unfollow(&mut context.pool(), person.id, c.id).await?;
CommunityFollower::unfollow(&mut context.pool(), &form).await?;
} }
} }

View file

@ -14,10 +14,10 @@ use lemmy_db_schema::{
newtypes::DbUrl, newtypes::DbUrl,
source::{ source::{
activity::ActivitySendTargets, activity::ActivitySendTargets,
comment::{CommentLike, CommentLikeForm}, comment::{CommentActions, CommentLikeForm},
community::Community, community::Community,
person::Person, person::Person,
post::{PostLike, PostLikeForm}, post::{PostActions, PostLikeForm},
}, },
traits::Likeable, traits::Likeable,
}; };
@ -59,14 +59,10 @@ async fn vote_comment(
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
) -> LemmyResult<()> { ) -> LemmyResult<()> {
let comment_id = comment.id; let comment_id = comment.id;
let like_form = CommentLikeForm { let like_form = CommentLikeForm::new(actor.id, comment_id, vote_type.into());
comment_id,
person_id: actor.id,
score: vote_type.into(),
};
let person_id = actor.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?;
CommentLike::like(&mut context.pool(), &like_form).await?; CommentActions::like(&mut context.pool(), &like_form).await?;
Ok(()) Ok(())
} }
@ -79,8 +75,8 @@ async fn vote_post(
let post_id = post.id; let post_id = post.id;
let like_form = PostLikeForm::new(post.id, actor.id, vote_type.into()); let like_form = PostLikeForm::new(post.id, actor.id, vote_type.into());
let person_id = actor.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?;
PostLike::like(&mut context.pool(), &like_form).await?; PostActions::like(&mut context.pool(), &like_form).await?;
Ok(()) Ok(())
} }
@ -91,7 +87,7 @@ async fn undo_vote_comment(
) -> LemmyResult<()> { ) -> LemmyResult<()> {
let comment_id = comment.id; let comment_id = comment.id;
let person_id = actor.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(()) Ok(())
} }
@ -102,6 +98,6 @@ async fn undo_vote_post(
) -> LemmyResult<()> { ) -> LemmyResult<()> {
let post_id = post.id; let post_id = post.id;
let person_id = actor.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(()) Ok(())
} }

View file

@ -16,7 +16,8 @@ use lemmy_api_common::{
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
newtypes::PostId, newtypes::PostId,
source::{community::Community, post::PostRead}, source::{community::Community, post::PostActions},
traits::Readable,
}; };
use lemmy_db_views::{ use lemmy_db_views::{
post::post_view::PostQuery, post::post_view::PostQuery,
@ -111,7 +112,8 @@ pub async fn list_posts(
.unwrap_or(local_user.auto_mark_fetched_posts_as_read) .unwrap_or(local_user.auto_mark_fetched_posts_as_read)
{ {
let post_ids = posts.iter().map(|p| p.post.id).collect::<Vec<PostId>>(); let post_ids = posts.iter().map(|p| p.post.id).collect::<Vec<PostId>>();
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?;
} }
} }

View file

@ -12,15 +12,17 @@ use lemmy_api_common::{context::LemmyContext, SuccessResponse};
use lemmy_db_schema::{ use lemmy_db_schema::{
newtypes::DbUrl, newtypes::DbUrl,
source::{ source::{
comment::{CommentSaved, CommentSavedForm}, comment::{CommentActions, CommentSavedForm},
community::{CommunityFollower, CommunityFollowerForm, CommunityFollowerState}, community::{
community_block::{CommunityBlock, CommunityBlockForm}, CommunityActions,
instance::Instance, CommunityBlockForm,
instance_block::{InstanceBlock, InstanceBlockForm}, CommunityFollowerForm,
CommunityFollowerState,
},
instance::{Instance, InstanceActions, InstanceBlockForm},
local_user::{LocalUser, LocalUserUpdateForm}, local_user::{LocalUser, LocalUserUpdateForm},
person::{Person, PersonUpdateForm}, person::{Person, PersonActions, PersonBlockForm, PersonUpdateForm},
person_block::{PersonBlock, PersonBlockForm}, post::{PostActions, PostSavedForm},
post::{PostSaved, PostSavedForm},
}, },
traits::{Blockable, Crud, Followable, Saveable}, traits::{Blockable, Crud, Followable, Saveable},
}; };
@ -163,11 +165,9 @@ pub async fn import_settings(
&context, &context,
|(followed, context)| async move { |(followed, context)| async move {
let community = followed.dereference(&context).await?; let community = followed.dereference(&context).await?;
let form = CommunityFollowerForm { let form =
state: Some(CommunityFollowerState::Pending), CommunityFollowerForm::new(community.id, person_id, CommunityFollowerState::Pending);
..CommunityFollowerForm::new(community.id, person_id) CommunityActions::follow(&mut context.pool(), &form).await?;
};
CommunityFollower::follow(&mut context.pool(), &form).await?;
LemmyResult::Ok(()) LemmyResult::Ok(())
}, },
) )
@ -179,7 +179,7 @@ pub async fn import_settings(
|(saved, context)| async move { |(saved, context)| async move {
let post = saved.dereference(&context).await?; let post = saved.dereference(&context).await?;
let form = PostSavedForm::new(post.id, person_id); let form = PostSavedForm::new(post.id, person_id);
PostSaved::save(&mut context.pool(), &form).await?; PostActions::save(&mut context.pool(), &form).await?;
LemmyResult::Ok(()) LemmyResult::Ok(())
}, },
) )
@ -190,8 +190,8 @@ pub async fn import_settings(
&context, &context,
|(saved, context)| async move { |(saved, context)| async move {
let comment = saved.dereference(&context).await?; let comment = saved.dereference(&context).await?;
let form = CommentSavedForm::new(comment.id, person_id); let form = CommentSavedForm::new(person_id, comment.id);
CommentSaved::save(&mut context.pool(), &form).await?; CommentActions::save(&mut context.pool(), &form).await?;
LemmyResult::Ok(()) LemmyResult::Ok(())
}, },
) )
@ -202,11 +202,8 @@ pub async fn import_settings(
&context, &context,
|(blocked, context)| async move { |(blocked, context)| async move {
let community = blocked.dereference(&context).await?; let community = blocked.dereference(&context).await?;
let form = CommunityBlockForm { let form = CommunityBlockForm::new(community.id, person_id);
person_id, CommunityActions::block(&mut context.pool(), &form).await?;
community_id: community.id,
};
CommunityBlock::block(&mut context.pool(), &form).await?;
LemmyResult::Ok(()) LemmyResult::Ok(())
}, },
) )
@ -218,11 +215,8 @@ pub async fn import_settings(
|(blocked, context)| async move { |(blocked, context)| async move {
let context = context.reset_request_count(); let context = context.reset_request_count();
let target = blocked.dereference(&context).await?; let target = blocked.dereference(&context).await?;
let form = PersonBlockForm { let form = PersonBlockForm::new(person_id, target.id);
person_id, PersonActions::block(&mut context.pool(), &form).await?;
target_id: target.id,
};
PersonBlock::block(&mut context.pool(), &form).await?;
LemmyResult::Ok(()) LemmyResult::Ok(())
}, },
) )
@ -230,11 +224,8 @@ pub async fn import_settings(
try_join_all(data.blocked_instances.iter().map(|domain| async { try_join_all(data.blocked_instances.iter().map(|domain| async {
let instance = Instance::read_or_create(&mut context.pool(), domain.clone()).await?; let instance = Instance::read_or_create(&mut context.pool(), domain.clone()).await?;
let form = InstanceBlockForm { let form = InstanceBlockForm::new(person_id, instance.id);
person_id, InstanceActions::block(&mut context.pool(), &form).await?;
instance_id: instance.id,
};
InstanceBlock::block(&mut context.pool(), &form).await?;
LemmyResult::Ok(()) LemmyResult::Ok(())
})) }))
.await?; .await?;
@ -292,7 +283,7 @@ pub(crate) mod tests {
source::{ source::{
community::{ community::{
Community, Community,
CommunityFollower, CommunityActions,
CommunityFollowerForm, CommunityFollowerForm,
CommunityFollowerState, CommunityFollowerState,
CommunityInsertForm, CommunityInsertForm,
@ -322,11 +313,12 @@ pub(crate) mod tests {
"pubkey".to_string(), "pubkey".to_string(),
); );
let community = Community::create(pool, &community_form).await?; let community = Community::create(pool, &community_form).await?;
let follower_form = CommunityFollowerForm { let follower_form = CommunityFollowerForm::new(
state: Some(CommunityFollowerState::Accepted), community.id,
..CommunityFollowerForm::new(community.id, export_user.person.id) export_user.person.id,
}; CommunityFollowerState::Accepted,
CommunityFollower::follow(pool, &follower_form).await?; );
CommunityActions::follow(pool, &follower_form).await?;
let backup = export_settings(export_user.clone(), context.reset_request_count()).await?; let backup = export_settings(export_user.clone(), context.reset_request_count()).await?;

View file

@ -11,7 +11,7 @@ use activitypub_federation::{
}; };
use lemmy_api_common::{context::LemmyContext, utils::generate_moderators_url}; use lemmy_api_common::{context::LemmyContext, utils::generate_moderators_url};
use lemmy_db_schema::{ use lemmy_db_schema::{
source::community::{CommunityModerator, CommunityModeratorForm}, source::community::{CommunityActions, CommunityModeratorForm},
traits::Joinable, traits::Joinable,
}; };
use lemmy_db_views::structs::CommunityModeratorView; use lemmy_db_views::structs::CommunityModeratorView;
@ -62,11 +62,9 @@ impl Collection for ApubCommunityModerators {
for mod_user in &current_moderators { for mod_user in &current_moderators {
let mod_id = ObjectId::from(mod_user.moderator.ap_id.clone()); let mod_id = ObjectId::from(mod_user.moderator.ap_id.clone());
if !apub.ordered_items.contains(&mod_id) { if !apub.ordered_items.contains(&mod_id) {
let community_moderator_form = CommunityModeratorForm { let community_moderator_form =
community_id: mod_user.community.id, CommunityModeratorForm::new(mod_user.community.id, mod_user.moderator.id);
person_id: mod_user.moderator.id, CommunityActions::leave(&mut data.pool(), &community_moderator_form).await?;
};
CommunityModerator::leave(&mut data.pool(), &community_moderator_form).await?;
} }
} }
@ -80,11 +78,8 @@ impl Collection for ApubCommunityModerators {
.map(|c| c.moderator.ap_id.clone()) .map(|c| c.moderator.ap_id.clone())
.any(|x| x == mod_user.ap_id) .any(|x| x == mod_user.ap_id)
{ {
let community_moderator_form = CommunityModeratorForm { let community_moderator_form = CommunityModeratorForm::new(owner.id, mod_user.id);
community_id: owner.id, CommunityActions::join(&mut data.pool(), &community_moderator_form).await?;
person_id: mod_user.id,
};
CommunityModerator::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 = PersonInsertForm::test_form(inserted_instance.id, "holly");
let old_mod = Person::create(&mut context.pool(), &old_mod).await?; let old_mod = Person::create(&mut context.pool(), &old_mod).await?;
let community_moderator_form = CommunityModeratorForm { let community_moderator_form = CommunityModeratorForm::new(community.id, old_mod.id);
community_id: community.id,
person_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/"); assert_eq!(site.ap_id.to_string(), "https://enterprise.lemmy.ml/");

View file

@ -23,11 +23,10 @@ use lemmy_api_common::{
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
instance::Instance, instance::Instance,
person::Person, person::{Person, PersonActions},
person_block::PersonBlock,
private_message::{PrivateMessage as DbPrivateMessage, PrivateMessageInsertForm}, private_message::{PrivateMessage as DbPrivateMessage, PrivateMessageInsertForm},
}, },
traits::Crud, traits::{Blockable, Crud},
}; };
use lemmy_db_views::structs::LocalUserView; use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::{ use lemmy_utils::{
@ -138,7 +137,7 @@ impl Object for ApubPrivateMessage {
) -> LemmyResult<ApubPrivateMessage> { ) -> LemmyResult<ApubPrivateMessage> {
let creator = note.attributed_to.dereference(context).await?; let creator = note.attributed_to.dereference(context).await?;
let recipient = note.to[0].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 // Check that they can receive private messages
if let Ok(recipient_local_user) = if let Ok(recipient_local_user) =

View file

@ -1,14 +1,12 @@
use crate::{ use crate::{
diesel::{DecoratableTarget, OptionalExtension}, diesel::{DecoratableTarget, OptionalExtension},
impls::local_user::local_user_can_mod,
newtypes::{CommentId, DbUrl, PersonId}, newtypes::{CommentId, DbUrl, PersonId},
schema::{comment, comment_actions}, schema::{comment, comment_actions},
source::comment::{ source::comment::{
Comment, Comment,
CommentActions,
CommentInsertForm, CommentInsertForm,
CommentLike,
CommentLikeForm, CommentLikeForm,
CommentSaved,
CommentSavedForm, CommentSavedForm,
CommentUpdateForm, CommentUpdateForm,
}, },
@ -16,7 +14,6 @@ use crate::{
utils::{ utils::{
functions::{coalesce, hot_rank}, functions::{coalesce, hot_rank},
get_conn, get_conn,
now,
uplete, uplete,
DbPool, DbPool,
DELETED_REPLACEMENT_TEXT, DELETED_REPLACEMENT_TEXT,
@ -24,17 +21,18 @@ use crate::{
}; };
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use diesel::{ use diesel::{
dsl::{case_when, insert_into, not}, dsl::insert_into,
expression::SelectableHelper, expression::SelectableHelper,
result::Error, result::Error,
BoolExpressionMethods,
ExpressionMethods, ExpressionMethods,
NullableExpressionMethods,
QueryDsl, QueryDsl,
}; };
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
use diesel_ltree::Ltree; 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; use url::Url;
impl Comment { 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 { impl Crud for Comment {
type InsertForm = CommentInsertForm; type InsertForm = CommentInsertForm;
type UpdateForm = CommentUpdateForm; type UpdateForm = CommentUpdateForm;
@ -202,65 +165,58 @@ impl Crud for Comment {
} }
} }
impl Likeable for CommentLike { impl Likeable for CommentActions {
type Form = CommentLikeForm; type Form = CommentLikeForm;
type IdType = CommentId; type IdType = CommentId;
async fn like(pool: &mut DbPool<'_>, comment_like_form: &CommentLikeForm) -> Result<Self, Error> {
async fn like(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<Self> {
let conn = &mut get_conn(pool).await?; 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) insert_into(comment_actions::table)
.values(comment_like_form) .values(form)
.on_conflict((comment_actions::comment_id, comment_actions::person_id)) .on_conflict((comment_actions::comment_id, comment_actions::person_id))
.do_update() .do_update()
.set(comment_like_form) .set(form)
.returning(Self::as_select()) .returning(Self::as_select())
.get_result::<Self>(conn) .get_result::<Self>(conn)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntLikeComment)
} }
async fn remove( async fn remove_like(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
person_id: PersonId, person_id: PersonId,
comment_id: CommentId, comment_id: Self::IdType,
) -> Result<uplete::Count, Error> { ) -> LemmyResult<uplete::Count> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
uplete::new(comment_actions::table.find((person_id, comment_id))) uplete::new(comment_actions::table.find((person_id, comment_id)))
.set_null(comment_actions::like_score) .set_null(comment_actions::like_score)
.set_null(comment_actions::liked) .set_null(comment_actions::liked)
.get_result(conn) .get_result(conn)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntLikeComment)
} }
} }
impl Saveable for CommentSaved { impl Saveable for CommentActions {
type Form = CommentSavedForm; type Form = CommentSavedForm;
async fn save( async fn save(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<Self> {
pool: &mut DbPool<'_>,
comment_saved_form: &CommentSavedForm,
) -> Result<Self, Error> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
insert_into(comment_actions::table) insert_into(comment_actions::table)
.values(comment_saved_form) .values(form)
.on_conflict((comment_actions::comment_id, comment_actions::person_id)) .on_conflict((comment_actions::comment_id, comment_actions::person_id))
.do_update() .do_update()
.set(comment_saved_form) .set(form)
.returning(Self::as_select()) .returning(Self::as_select())
.get_result::<Self>(conn) .get_result::<Self>(conn)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntSaveComment)
} }
async fn unsave( async fn unsave(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<uplete::Count> {
pool: &mut DbPool<'_>,
comment_saved_form: &CommentSavedForm,
) -> Result<uplete::Count, Error> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
uplete::new( uplete::new(comment_actions::table.find((form.person_id, form.comment_id)))
comment_actions::table.find((comment_saved_form.person_id, comment_saved_form.comment_id)), .set_null(comment_actions::saved)
) .get_result(conn)
.set_null(comment_actions::saved) .await
.get_result(conn) .with_lemmy_type(LemmyErrorType::CouldntSaveComment)
.await
} }
} }
@ -356,30 +312,15 @@ mod tests {
Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?; Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?;
// Comment Like // Comment Like
let comment_like_form = CommentLikeForm { let comment_like_form = CommentLikeForm::new(inserted_person.id, inserted_comment.id, 1);
comment_id: inserted_comment.id,
person_id: inserted_person.id,
score: 1,
};
let inserted_comment_like = CommentLike::like(pool, &comment_like_form).await?; let inserted_comment_like = CommentActions::like(pool, &comment_like_form).await?;
assert_eq!(Some(1), inserted_comment_like.like_score);
let expected_comment_like = CommentLike {
comment_id: inserted_comment.id,
person_id: inserted_person.id,
published: inserted_comment_like.published,
score: 1,
};
// Comment Saved // Comment Saved
let comment_saved_form = CommentSavedForm::new(inserted_comment.id, inserted_person.id); let comment_saved_form = CommentSavedForm::new(inserted_person.id, inserted_comment.id);
let inserted_comment_saved = CommentSaved::save(pool, &comment_saved_form).await?; let inserted_comment_saved = CommentActions::save(pool, &comment_saved_form).await?;
assert!(inserted_comment_saved.saved.is_some());
let expected_comment_saved = CommentSaved {
comment_id: inserted_comment.id,
person_id: inserted_person.id,
published: inserted_comment_saved.published,
};
let comment_update_form = CommentUpdateForm { let comment_update_form = CommentUpdateForm {
content: Some("A test comment".into()), 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 updated_comment = Comment::update(pool, inserted_comment.id, &comment_update_form).await?;
let read_comment = Comment::read(pool, inserted_comment.id).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 like_removed =
let saved_removed = CommentSaved::unsave(pool, &comment_saved_form).await?; 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?; let num_deleted = Comment::delete(pool, inserted_comment.id).await?;
Comment::delete(pool, inserted_child_comment.id).await?; Comment::delete(pool, inserted_child_comment.id).await?;
Post::delete(pool, inserted_post.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, read_comment);
assert_eq!(expected_comment, updated_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!( assert_eq!(
format!("0.{}.{}", expected_comment.id, inserted_child_comment.id), format!("0.{}.{}", expected_comment.id, inserted_child_comment.id),
inserted_child_comment.path.0, inserted_child_comment.path.0,
@ -415,7 +355,7 @@ mod tests {
#[tokio::test] #[tokio::test]
#[serial] #[serial]
async fn test_aggregates() -> Result<(), Error> { async fn test_aggregates() -> LemmyResult<()> {
let pool = &build_db_pool_for_tests(); let pool = &build_db_pool_for_tests();
let pool = &mut pool.into(); let pool = &mut pool.into();
@ -459,13 +399,9 @@ mod tests {
let _inserted_child_comment = let _inserted_child_comment =
Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?; Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?;
let comment_like = CommentLikeForm { let comment_like = CommentLikeForm::new(inserted_person.id, inserted_comment.id, 1);
comment_id: inserted_comment.id,
person_id: inserted_person.id,
score: 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?; 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); assert_eq!(0, comment_aggs_before_delete.downvotes);
// Add a post dislike from the other person // Add a post dislike from the other person
let comment_dislike = CommentLikeForm { let comment_dislike = CommentLikeForm::new(another_inserted_person.id, inserted_comment.id, -1);
comment_id: inserted_comment.id,
person_id: another_inserted_person.id,
score: -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?; 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); assert_eq!(1, comment_aggs_after_dislike.downvotes);
// Remove the first comment like // 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?; let after_like_remove = Comment::read(pool, inserted_comment.id).await?;
assert_eq!(-1, after_like_remove.score); assert_eq!(-1, after_like_remove.score);
assert_eq!(0, after_like_remove.upvotes); assert_eq!(0, after_like_remove.upvotes);

View file

@ -8,13 +8,12 @@ use crate::{
use chrono::Utc; use chrono::Utc;
use diesel::{ use diesel::{
dsl::{insert_into, update}, dsl::{insert_into, update},
result::Error,
BoolExpressionMethods, BoolExpressionMethods,
ExpressionMethods, ExpressionMethods,
QueryDsl, QueryDsl,
}; };
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
use lemmy_utils::error::LemmyResult; use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
impl Reportable for CommentReport { impl Reportable for CommentReport {
type Form = CommentReportForm; type Form = CommentReportForm;
@ -24,15 +23,13 @@ impl Reportable for CommentReport {
/// ///
/// * `conn` - the postgres connection /// * `conn` - the postgres connection
/// * `comment_report_form` - the filled CommentReportForm to insert /// * `comment_report_form` - the filled CommentReportForm to insert
async fn report( async fn report(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<Self> {
pool: &mut DbPool<'_>,
comment_report_form: &CommentReportForm,
) -> Result<Self, Error> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
insert_into(comment_report::table) insert_into(comment_report::table)
.values(comment_report_form) .values(form)
.get_result::<Self>(conn) .get_result::<Self>(conn)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntCreateReport)
} }
/// resolve a comment report /// resolve a comment report
@ -44,7 +41,7 @@ impl Reportable for CommentReport {
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
report_id_: Self::IdType, report_id_: Self::IdType,
by_resolver_id: PersonId, by_resolver_id: PersonId,
) -> Result<usize, Error> { ) -> LemmyResult<usize> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
update(comment_report::table.find(report_id_)) update(comment_report::table.find(report_id_))
.set(( .set((
@ -54,6 +51,7 @@ impl Reportable for CommentReport {
)) ))
.execute(conn) .execute(conn)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)
} }
async fn resolve_apub( async fn resolve_apub(
@ -63,29 +61,28 @@ impl Reportable for CommentReport {
resolver_id: PersonId, resolver_id: PersonId,
) -> LemmyResult<usize> { ) -> LemmyResult<usize> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
Ok( update(
update( comment_report::table.filter(
comment_report::table.filter( comment_report::comment_id
comment_report::comment_id .eq(object_id)
.eq(object_id) .and(comment_report::creator_id.eq(report_creator_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?,
) )
.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( async fn resolve_all_for_object(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
comment_id_: CommentId, comment_id_: CommentId,
by_resolver_id: PersonId, by_resolver_id: PersonId,
) -> Result<usize, Error> { ) -> LemmyResult<usize> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
update(comment_report::table.filter(comment_report::comment_id.eq(comment_id_))) update(comment_report::table.filter(comment_report::comment_id.eq(comment_id_)))
.set(( .set((
@ -95,6 +92,7 @@ impl Reportable for CommentReport {
)) ))
.execute(conn) .execute(conn)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)
} }
/// unresolve a comment report /// unresolve a comment report
@ -106,7 +104,7 @@ impl Reportable for CommentReport {
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
report_id_: Self::IdType, report_id_: Self::IdType,
by_resolver_id: PersonId, by_resolver_id: PersonId,
) -> Result<usize, Error> { ) -> LemmyResult<usize> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
update(comment_report::table.find(report_id_)) update(comment_report::table.find(report_id_))
.set(( .set((
@ -116,5 +114,6 @@ impl Reportable for CommentReport {
)) ))
.execute(conn) .execute(conn)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)
} }
} }

View file

@ -6,49 +6,42 @@ use crate::{
actor_language::CommunityLanguage, actor_language::CommunityLanguage,
community::{ community::{
Community, Community,
CommunityFollower, CommunityActions,
CommunityBlockForm,
CommunityFollowerForm, CommunityFollowerForm,
CommunityFollowerState, CommunityFollowerState,
CommunityInsertForm, CommunityInsertForm,
CommunityModerator,
CommunityModeratorForm, CommunityModeratorForm,
CommunityPersonBan,
CommunityPersonBanForm, CommunityPersonBanForm,
CommunityUpdateForm, CommunityUpdateForm,
}, },
post::Post, post::Post,
}, },
traits::{ApubActor, Bannable, Crud, Followable, Joinable}, traits::{ApubActor, Bannable, Blockable, Crud, Followable, Joinable},
utils::{ utils::{
functions::{coalesce, coalesce_2_nullable, lower, random_smallint}, functions::{coalesce, coalesce_2_nullable, lower, random_smallint},
get_conn, get_conn,
now,
uplete, uplete,
DbPool, DbPool,
}, },
CommunityVisibility, CommunityVisibility,
ListingType, ListingType,
SubscribedType,
}; };
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use diesel::{ use diesel::{
deserialize,
dsl::{exists, insert_into, not}, dsl::{exists, insert_into, not},
expression::SelectableHelper, expression::SelectableHelper,
pg::Pg,
result::Error, result::Error,
select, select,
sql_types,
update, update,
BoolExpressionMethods, BoolExpressionMethods,
ExpressionMethods, ExpressionMethods,
NullableExpressionMethods, NullableExpressionMethods,
QueryDsl, QueryDsl,
Queryable,
}; };
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
use lemmy_utils::{ use lemmy_utils::{
error::{LemmyErrorType, LemmyResult}, error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
settings::structs::Settings, settings::structs::Settings,
}; };
use url::Url; use url::Url;
@ -85,42 +78,31 @@ impl Crud for Community {
} }
} }
impl Joinable for CommunityModerator { impl Joinable for CommunityActions {
type Form = CommunityModeratorForm; type Form = CommunityModeratorForm;
async fn join( async fn join(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<Self> {
pool: &mut DbPool<'_>,
community_moderator_form: &CommunityModeratorForm,
) -> Result<Self, Error> {
let conn = &mut get_conn(pool).await?; 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) insert_into(community_actions::table)
.values(community_moderator_form) .values(form)
.on_conflict(( .on_conflict((
community_actions::person_id, community_actions::person_id,
community_actions::community_id, community_actions::community_id,
)) ))
.do_update() .do_update()
.set(community_moderator_form) .set(form)
.returning(Self::as_select()) .returning(Self::as_select())
.get_result::<Self>(conn) .get_result::<Self>(conn)
.await .await
.with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists)
} }
async fn leave( async fn leave(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<uplete::Count> {
pool: &mut DbPool<'_>,
community_moderator_form: &CommunityModeratorForm,
) -> Result<uplete::Count, Error> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
uplete::new(community_actions::table.find(( uplete::new(community_actions::table.find((form.person_id, form.community_id)))
community_moderator_form.person_id, .set_null(community_actions::became_moderator)
community_moderator_form.community_id, .get_result(conn)
))) .await
.set_null(community_actions::became_moderator) .with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists)
.get_result(conn)
.await
} }
} }
@ -295,8 +277,8 @@ impl Community {
} }
} }
impl CommunityModerator { impl CommunityActions {
pub async fn delete_for_community( pub async fn delete_mods_for_community(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
for_community_id: CommunityId, for_community_id: CommunityId,
) -> Result<uplete::Count, Error> { ) -> Result<uplete::Count, Error> {
@ -310,7 +292,7 @@ impl CommunityModerator {
.await .await
} }
pub async fn leave_all_communities( pub async fn leave_mod_team_for_all_communities(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
for_person_id: PersonId, for_person_id: PersonId,
) -> Result<uplete::Count, Error> { ) -> Result<uplete::Count, Error> {
@ -365,49 +347,7 @@ impl CommunityModerator {
Err(LemmyErrorType::NotHigherMod)? Err(LemmyErrorType::NotHigherMod)?
} }
} }
}
impl Bannable for CommunityPersonBan {
type Form = CommunityPersonBanForm;
async fn ban(
pool: &mut DbPool<'_>,
community_person_ban_form: &CommunityPersonBanForm,
) -> Result<Self, Error> {
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::<Self>(conn)
.await
}
async fn unban(
pool: &mut DbPool<'_>,
community_person_ban_form: &CommunityPersonBanForm,
) -> Result<uplete::Count, Error> {
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 /// 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. /// if any follow relation is stored. Dont use this for local community.
pub async fn check_has_local_followers( pub async fn check_has_local_followers(
@ -425,7 +365,7 @@ impl CommunityFollower {
.ok_or(LemmyErrorType::CommunityHasNoFollowers.into()) .ok_or(LemmyErrorType::CommunityHasNoFollowers.into())
} }
pub async fn approve( pub async fn approve_follower(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
community_id: CommunityId, community_id: CommunityId,
follower_id: PersonId, follower_id: PersonId,
@ -446,33 +386,10 @@ impl CommunityFollower {
} }
} }
// TODO impl Bannable for CommunityActions {
// I'd really like to have these on the impl, but unfortunately they have to be top level, type Form = CommunityPersonBanForm;
// according to https://diesel.rs/guides/composing-applications.html async fn ban(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<Self> {
#[diesel::dsl::auto_type]
pub fn community_follower_select_subscribed_type() -> _ {
community_actions::follow_state.nullable()
}
impl Queryable<sql_types::Nullable<crate::schema::sql_types::CommunityFollowerState>, Pg>
for SubscribedType
{
type Row = Option<CommunityFollowerState>;
fn build(row: Self::Row) -> deserialize::Result<Self> {
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<Self, Error> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
let form = (form, community_actions::followed.eq(now().nullable()));
insert_into(community_actions::table) insert_into(community_actions::table)
.values(form) .values(form)
.on_conflict(( .on_conflict((
@ -484,12 +401,44 @@ impl Followable for CommunityFollower {
.returning(Self::as_select()) .returning(Self::as_select())
.get_result::<Self>(conn) .get_result::<Self>(conn)
.await .await
.with_lemmy_type(LemmyErrorType::CommunityUserAlreadyBanned)
}
async fn unban(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<uplete::Count> {
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<Self> {
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::<Self>(conn)
.await
.with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists)
} }
async fn follow_accepted( async fn follow_accepted(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
community_id: CommunityId, community_id: CommunityId,
person_id: PersonId, person_id: PersonId,
) -> Result<Self, Error> { ) -> LemmyResult<Self> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
let find_action = community_actions::table let find_action = community_actions::table
.find((person_id, community_id)) .find((person_id, community_id))
@ -499,18 +448,92 @@ impl Followable for CommunityFollower {
.returning(Self::as_select()) .returning(Self::as_select())
.get_result::<Self>(conn) .get_result::<Self>(conn)
.await .await
.with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists)
} }
async fn unfollow( async fn unfollow(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
form: &CommunityFollowerForm, person_id: PersonId,
) -> Result<uplete::Count, Error> { community_id: Self::IdType,
) -> LemmyResult<uplete::Count> {
let conn = &mut get_conn(pool).await?; 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::followed)
.set_null(community_actions::follow_state) .set_null(community_actions::follow_state)
.set_null(community_actions::follow_approver_id) .set_null(community_actions::follow_approver_id)
.get_result(conn) .get_result(conn)
.await .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<Self> {
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::<Self>(conn)
.await
.with_lemmy_type(LemmyErrorType::CommunityBlockAlreadyExists)
}
async fn unblock(
pool: &mut DbPool<'_>,
community_block_form: &Self::Form,
) -> LemmyResult<uplete::Count> {
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::<bool>(conn)
.await?
.then_some(())
.ok_or(LemmyErrorType::CommunityIsBlocked.into())
}
async fn read_blocks_for_person(
pool: &mut DbPool<'_>,
person_id: PersonId,
) -> Result<Vec<Self::ObjectType>, 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::<Community>(conn)
.await
} }
} }
@ -567,13 +590,11 @@ mod tests {
comment::{Comment, CommentInsertForm}, comment::{Comment, CommentInsertForm},
community::{ community::{
Community, Community,
CommunityFollower, CommunityActions,
CommunityFollowerForm, CommunityFollowerForm,
CommunityFollowerState, CommunityFollowerState,
CommunityInsertForm, CommunityInsertForm,
CommunityModerator,
CommunityModeratorForm, CommunityModeratorForm,
CommunityPersonBan,
CommunityPersonBanForm, CommunityPersonBanForm,
CommunityUpdateForm, CommunityUpdateForm,
}, },
@ -652,49 +673,35 @@ mod tests {
interactions_month: 0, interactions_month: 0,
}; };
let community_follower_form = CommunityFollowerForm { let community_follower_form = CommunityFollowerForm::new(
community_id: inserted_community.id, inserted_community.id,
person_id: inserted_bobby.id, inserted_bobby.id,
state: Some(CommunityFollowerState::Accepted), CommunityFollowerState::Accepted,
approver_id: None, );
};
let inserted_community_follower = let inserted_community_follower =
CommunityFollower::follow(pool, &community_follower_form).await?; CommunityActions::follow(pool, &community_follower_form).await?;
let expected_community_follower = CommunityFollower { assert_eq!(
community_id: inserted_community.id, Some(CommunityFollowerState::Accepted),
person_id: inserted_bobby.id, inserted_community_follower.follow_state
state: CommunityFollowerState::Accepted, );
published: inserted_community_follower.published,
approver_id: None,
};
let bobby_moderator_form = CommunityModeratorForm { let bobby_moderator_form =
community_id: inserted_community.id, CommunityModeratorForm::new(inserted_community.id, inserted_bobby.id);
person_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 { let artemis_moderator_form =
community_id: inserted_community.id, CommunityModeratorForm::new(inserted_community.id, inserted_artemis.id);
person_id: inserted_artemis.id,
};
let _inserted_artemis_moderator = let _inserted_artemis_moderator = CommunityActions::join(pool, &artemis_moderator_form).await?;
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 moderator_person_ids = vec![inserted_bobby.id, inserted_artemis.id]; 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 // 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, pool,
inserted_community.id, inserted_community.id,
inserted_bobby.id, inserted_bobby.id,
@ -714,7 +721,7 @@ mod tests {
assert!(bobby_higher_check_2.is_ok()); assert!(bobby_higher_check_2.is_ok());
// This should throw an error, since artemis was added later // 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, pool,
inserted_community.id, inserted_community.id,
inserted_artemis.id, inserted_artemis.id,
@ -723,22 +730,14 @@ mod tests {
.await; .await;
assert!(artemis_higher_check.is_err()); assert!(artemis_higher_check.is_err());
let community_person_ban_form = CommunityPersonBanForm { let community_person_ban_form =
community_id: inserted_community.id, CommunityPersonBanForm::new(inserted_community.id, inserted_bobby.id);
person_id: inserted_bobby.id,
expires: None,
};
let inserted_community_person_ban = let inserted_community_person_ban =
CommunityPersonBan::ban(pool, &community_person_ban_form).await?; CommunityActions::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,
};
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 read_community = Community::read(pool, inserted_community.id).await?;
let update_community_form = CommunityUpdateForm { let update_community_form = CommunityUpdateForm {
@ -748,9 +747,14 @@ mod tests {
let updated_community = let updated_community =
Community::update(pool, inserted_community.id, &update_community_form).await?; Community::update(pool, inserted_community.id, &update_community_form).await?;
let ignored_community = CommunityFollower::unfollow(pool, &community_follower_form).await?; let ignored_community = CommunityActions::unfollow(
let left_community = CommunityModerator::leave(pool, &bobby_moderator_form).await?; pool,
let unban = CommunityPersonBan::unban(pool, &community_person_ban_form).await?; 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?; let num_deleted = Community::delete(pool, inserted_community.id).await?;
Person::delete(pool, inserted_bobby.id).await?; Person::delete(pool, inserted_bobby.id).await?;
Person::delete(pool, inserted_artemis.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, read_community);
assert_eq!(expected_community, updated_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), ignored_community);
assert_eq!(uplete::Count::only_updated(1), left_community); assert_eq!(uplete::Count::only_updated(1), left_community);
assert_eq!(uplete::Count::only_deleted(1), unban); 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 another_inserted_community = Community::create(pool, &another_community).await?;
let first_person_follow = CommunityFollowerForm { let first_person_follow = CommunityFollowerForm::new(
community_id: inserted_community.id, inserted_community.id,
person_id: inserted_person.id, inserted_person.id,
state: Some(CommunityFollowerState::Accepted), CommunityFollowerState::Accepted,
approver_id: None, );
};
CommunityFollower::follow(pool, &first_person_follow).await?; CommunityActions::follow(pool, &first_person_follow).await?;
let second_person_follow = CommunityFollowerForm { let second_person_follow = CommunityFollowerForm::new(
community_id: inserted_community.id, inserted_community.id,
person_id: another_inserted_person.id, another_inserted_person.id,
state: Some(CommunityFollowerState::Accepted), CommunityFollowerState::Accepted,
approver_id: None, );
};
CommunityFollower::follow(pool, &second_person_follow).await?; CommunityActions::follow(pool, &second_person_follow).await?;
let another_community_follow = CommunityFollowerForm { let another_community_follow = CommunityFollowerForm::new(
community_id: another_inserted_community.id, another_inserted_community.id,
person_id: inserted_person.id, inserted_person.id,
state: Some(CommunityFollowerState::Accepted), CommunityFollowerState::Accepted,
approver_id: None, );
};
CommunityFollower::follow(pool, &another_community_follow).await?; CommunityActions::follow(pool, &another_community_follow).await?;
let new_post = PostInsertForm::new( let new_post = PostInsertForm::new(
"A test post".into(), "A test post".into(),
@ -866,13 +864,18 @@ mod tests {
assert_eq!(0, another_community_aggs.comments); assert_eq!(0, another_community_aggs.comments);
// Unfollow test // 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?; let after_unfollow = Community::read(pool, inserted_community.id).await?;
assert_eq!(1, after_unfollow.subscribers); assert_eq!(1, after_unfollow.subscribers);
assert_eq!(1, after_unfollow.subscribers_local); assert_eq!(1, after_unfollow.subscribers_local);
// Follow again just for the later tests // 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?; 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);
assert_eq!(2, after_follow_again.subscribers_local); assert_eq!(2, after_follow_again.subscribers_local);

View file

@ -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::<bool>(conn)
.await?
.then_some(())
.ok_or(LemmyErrorType::CommunityIsBlocked.into())
}
pub async fn for_person(
pool: &mut DbPool<'_>,
person_id: PersonId,
) -> Result<Vec<Community>, 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::<Community>(conn)
.await
}
}
impl Blockable for CommunityBlock {
type Form = CommunityBlockForm;
async fn block(pool: &mut DbPool<'_>, community_block_form: &Self::Form) -> Result<Self, Error> {
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::<Self>(conn)
.await
}
async fn unblock(
pool: &mut DbPool<'_>,
community_block_form: &Self::Form,
) -> Result<uplete::Count, Error> {
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
}
}

View file

@ -8,13 +8,12 @@ use crate::{
use chrono::Utc; use chrono::Utc;
use diesel::{ use diesel::{
dsl::{insert_into, update}, dsl::{insert_into, update},
result::Error,
BoolExpressionMethods, BoolExpressionMethods,
ExpressionMethods, ExpressionMethods,
QueryDsl, QueryDsl,
}; };
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
use lemmy_utils::error::LemmyResult; use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
impl Reportable for CommunityReport { impl Reportable for CommunityReport {
type Form = CommunityReportForm; type Form = CommunityReportForm;
@ -24,15 +23,13 @@ impl Reportable for CommunityReport {
/// ///
/// * `conn` - the postgres connection /// * `conn` - the postgres connection
/// * `community_report_form` - the filled CommunityReportForm to insert /// * `community_report_form` - the filled CommunityReportForm to insert
async fn report( async fn report(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<Self> {
pool: &mut DbPool<'_>,
community_report_form: &CommunityReportForm,
) -> Result<Self, Error> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
insert_into(community_report::table) insert_into(community_report::table)
.values(community_report_form) .values(form)
.get_result::<Self>(conn) .get_result::<Self>(conn)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntCreateReport)
} }
/// resolve a community report /// resolve a community report
@ -44,7 +41,7 @@ impl Reportable for CommunityReport {
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
report_id_: Self::IdType, report_id_: Self::IdType,
by_resolver_id: PersonId, by_resolver_id: PersonId,
) -> Result<usize, Error> { ) -> LemmyResult<usize> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
update(community_report::table.find(report_id_)) update(community_report::table.find(report_id_))
.set(( .set((
@ -54,6 +51,7 @@ impl Reportable for CommunityReport {
)) ))
.execute(conn) .execute(conn)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)
} }
async fn resolve_apub( async fn resolve_apub(
@ -63,29 +61,28 @@ impl Reportable for CommunityReport {
resolver_id: PersonId, resolver_id: PersonId,
) -> LemmyResult<usize> { ) -> LemmyResult<usize> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
Ok( update(
update( community_report::table.filter(
community_report::table.filter( community_report::community_id
community_report::community_id .eq(object_id)
.eq(object_id) .and(community_report::creator_id.eq(report_creator_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?,
) )
.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( async fn resolve_all_for_object(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
community_id_: CommunityId, community_id_: Self::ObjectIdType,
by_resolver_id: PersonId, by_resolver_id: PersonId,
) -> Result<usize, Error> { ) -> LemmyResult<usize> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
update(community_report::table.filter(community_report::community_id.eq(community_id_))) update(community_report::table.filter(community_report::community_id.eq(community_id_)))
.set(( .set((
@ -95,6 +92,7 @@ impl Reportable for CommunityReport {
)) ))
.execute(conn) .execute(conn)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)
} }
/// unresolve a community report /// unresolve a community report
@ -106,7 +104,7 @@ impl Reportable for CommunityReport {
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
report_id_: Self::IdType, report_id_: Self::IdType,
by_resolver_id: PersonId, by_resolver_id: PersonId,
) -> Result<usize, Error> { ) -> LemmyResult<usize> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
update(community_report::table.find(report_id_)) update(community_report::table.find(report_id_))
.set(( .set((
@ -116,5 +114,6 @@ impl Reportable for CommunityReport {
)) ))
.execute(conn) .execute(conn)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)
} }
} }

View file

@ -1,28 +1,31 @@
use crate::{ use crate::{
diesel::dsl::IntervalDsl, diesel::dsl::IntervalDsl,
newtypes::InstanceId, newtypes::{InstanceId, PersonId},
schema::{ schema::{
federation_allowlist, federation_allowlist,
federation_blocklist, federation_blocklist,
federation_queue_state, federation_queue_state,
instance, instance,
instance_actions,
local_site, local_site,
site, site,
}, },
source::{ source::{
federation_queue_state::FederationQueueState, federation_queue_state::FederationQueueState,
instance::{Instance, InstanceForm}, instance::{Instance, InstanceActions, InstanceBlockForm, InstanceForm},
}, },
traits::Blockable,
utils::{ utils::{
functions::{coalesce, lower}, functions::{coalesce, lower},
get_conn, get_conn,
now, now,
uplete,
DbPool, DbPool,
}, },
}; };
use chrono::Utc; use chrono::Utc;
use diesel::{ use diesel::{
dsl::{count_star, insert_into}, dsl::{count_star, exists, insert_into, not, select},
result::Error, result::Error,
ExpressionMethods, ExpressionMethods,
NullableExpressionMethods, NullableExpressionMethods,
@ -31,6 +34,7 @@ use diesel::{
SelectableHelper, SelectableHelper,
}; };
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
impl Instance { impl Instance {
/// Attempt to read Instance column for the given domain. If it doesn't exist, insert a new one. /// 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 .await
} }
} }
impl Blockable for InstanceActions {
type Form = InstanceBlockForm;
type ObjectIdType = InstanceId;
type ObjectType = Instance;
async fn block(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<Self> {
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::<Self>(conn)
.await
.with_lemmy_type(LemmyErrorType::InstanceBlockAlreadyExists)
}
async fn unblock(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<uplete::Count> {
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::<bool>(conn)
.await?
.then_some(())
.ok_or(LemmyErrorType::InstanceIsBlocked.into())
}
async fn read_blocks_for_person(
pool: &mut DbPool<'_>,
person_id: PersonId,
) -> Result<Vec<Self::ObjectType>, 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::<Instance>(conn)
.await
}
}

View file

@ -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::<bool>(conn)
.await?
.then_some(())
.ok_or(LemmyErrorType::InstanceIsBlocked.into())
}
pub async fn for_person(
pool: &mut DbPool<'_>,
person_id: PersonId,
) -> Result<Vec<Instance>, 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::<Instance>(conn)
.await
}
}
impl Blockable for InstanceBlock {
type Form = InstanceBlockForm;
async fn block(pool: &mut DbPool<'_>, instance_block_form: &Self::Form) -> Result<Self, Error> {
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::<Self>(conn)
.await
}
async fn unblock(
pool: &mut DbPool<'_>,
instance_block_form: &Self::Form,
) -> Result<uplete::Count, Error> {
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
}
}

View file

@ -1,5 +1,4 @@
use crate::{ use crate::{
aliases::creator_community_actions,
newtypes::{CommunityId, DbUrl, LanguageId, LocalUserId, PersonId}, newtypes::{CommunityId, DbUrl, LanguageId, LocalUserId, PersonId},
schema::{community, community_actions, local_user, person, registration_application}, schema::{community, community_actions, local_user, person, registration_application},
source::{ source::{
@ -19,12 +18,9 @@ use bcrypt::{hash, DEFAULT_COST};
use diesel::{ use diesel::{
dsl::{insert_into, not, IntervalDsl}, dsl::{insert_into, not, IntervalDsl},
result::Error, result::Error,
BoolExpressionMethods,
CombineDsl, CombineDsl,
ExpressionMethods, ExpressionMethods,
JoinOnDsl, JoinOnDsl,
NullableExpressionMethods,
PgExpressionMethods,
QueryDsl, QueryDsl,
}; };
use diesel_async::RunQueryDsl; 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 /// Adds some helper functions for an optional LocalUser
pub trait LocalUserOptionHelper { pub trait LocalUserOptionHelper {
fn person_id(&self) -> Option<PersonId>; fn person_id(&self) -> Option<PersonId>;

View file

@ -5,7 +5,6 @@ pub mod comment;
pub mod comment_reply; pub mod comment_reply;
pub mod comment_report; pub mod comment_report;
pub mod community; pub mod community;
pub mod community_block;
pub mod community_report; pub mod community_report;
pub mod custom_emoji; pub mod custom_emoji;
pub mod email_verification; pub mod email_verification;
@ -14,7 +13,6 @@ pub mod federation_blocklist;
pub mod federation_queue_state; pub mod federation_queue_state;
pub mod images; pub mod images;
pub mod instance; pub mod instance;
pub mod instance_block;
pub mod language; pub mod language;
pub mod local_site; pub mod local_site;
pub mod local_site_rate_limit; pub mod local_site_rate_limit;
@ -26,11 +24,9 @@ pub mod oauth_account;
pub mod oauth_provider; pub mod oauth_provider;
pub mod password_reset_request; pub mod password_reset_request;
pub mod person; pub mod person;
pub mod person_block;
pub mod person_comment_mention; pub mod person_comment_mention;
pub mod person_post_mention; pub mod person_post_mention;
pub mod post; pub mod post;
pub mod post_actions;
pub mod post_report; pub mod post_report;
pub mod private_message; pub mod private_message;
pub mod private_message_report; pub mod private_message_report;

View file

@ -4,28 +4,28 @@ use crate::{
schema::{comment, community, instance, local_user, person, person_actions, post}, schema::{comment, community, instance, local_user, person, person_actions, post},
source::person::{ source::person::{
Person, Person,
PersonFollower, PersonActions,
PersonBlockForm,
PersonFollowerForm, PersonFollowerForm,
PersonInsertForm, PersonInsertForm,
PersonUpdateForm, PersonUpdateForm,
}, },
traits::{ApubActor, Crud, Followable}, traits::{ApubActor, Blockable, Crud, Followable},
utils::{functions::lower, get_conn, now, uplete, DbPool}, utils::{functions::lower, get_conn, uplete, DbPool},
}; };
use chrono::Utc; use chrono::Utc;
use diesel::{ use diesel::{
dsl::{insert_into, not}, dsl::{exists, insert_into, not, select},
expression::SelectableHelper, expression::SelectableHelper,
result::Error, result::Error,
CombineDsl, CombineDsl,
ExpressionMethods, ExpressionMethods,
JoinOnDsl, JoinOnDsl,
NullableExpressionMethods,
QueryDsl, QueryDsl,
}; };
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
use lemmy_utils::{ use lemmy_utils::{
error::{LemmyErrorType, LemmyResult}, error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
settings::structs::Settings, settings::structs::Settings,
}; };
use url::Url; use url::Url;
@ -202,11 +202,12 @@ impl ApubActor for Person {
} }
} }
impl Followable for PersonFollower { impl Followable for PersonActions {
type Form = PersonFollowerForm; type Form = PersonFollowerForm;
async fn follow(pool: &mut DbPool<'_>, form: &PersonFollowerForm) -> Result<Self, Error> { type IdType = PersonId;
async fn follow(pool: &mut DbPool<'_>, form: &PersonFollowerForm) -> LemmyResult<Self> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
let form = (form, person_actions::followed.eq(now().nullable()));
insert_into(person_actions::table) insert_into(person_actions::table)
.values(form) .values(form)
.on_conflict((person_actions::person_id, person_actions::target_id)) .on_conflict((person_actions::person_id, person_actions::target_id))
@ -215,27 +216,96 @@ impl Followable for PersonFollower {
.returning(Self::as_select()) .returning(Self::as_select())
.get_result::<Self>(conn) .get_result::<Self>(conn)
.await .await
.with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists)
} }
/// Currently no user following /// Currently no user following
async fn follow_accepted(_: &mut DbPool<'_>, _: CommunityId, _: PersonId) -> Result<Self, Error> { async fn follow_accepted(_: &mut DbPool<'_>, _: CommunityId, _: PersonId) -> LemmyResult<Self> {
Err(Error::NotFound) Err(LemmyErrorType::NotFound.into())
} }
async fn unfollow( async fn unfollow(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
form: &PersonFollowerForm, person_id: PersonId,
) -> Result<uplete::Count, Error> { target_id: Self::IdType,
) -> LemmyResult<uplete::Count> {
let conn = &mut get_conn(pool).await?; 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::followed)
.set_null(person_actions::follow_pending) .set_null(person_actions::follow_pending)
.get_result(conn) .get_result(conn)
.await .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<Self> {
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::<Self>(conn)
.await
.with_lemmy_type(LemmyErrorType::PersonBlockAlreadyExists)
}
async fn unblock(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<uplete::Count> {
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::<bool>(conn)
.await?
.then_some(())
.ok_or(LemmyErrorType::PersonIsBlocked.into())
}
async fn read_blocks_for_person(
pool: &mut DbPool<'_>,
person_id: PersonId,
) -> Result<Vec<Self::ObjectType>, 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::<Person>(conn)
.await
}
}
impl PersonActions {
pub async fn list_followers( pub async fn list_followers(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
for_person_id: PersonId, for_person_id: PersonId,
@ -256,16 +326,15 @@ mod tests {
use crate::{ use crate::{
source::{ source::{
comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm, CommentUpdateForm}, comment::{Comment, CommentActions, CommentInsertForm, CommentLikeForm, CommentUpdateForm},
community::{Community, CommunityInsertForm}, community::{Community, CommunityInsertForm},
instance::Instance, instance::Instance,
person::{Person, PersonFollower, PersonFollowerForm, PersonInsertForm, PersonUpdateForm}, person::{Person, PersonActions, PersonFollowerForm, PersonInsertForm, PersonUpdateForm},
post::{Post, PostInsertForm, PostLike, PostLikeForm}, post::{Post, PostActions, PostInsertForm, PostLikeForm},
}, },
traits::{Crud, Followable, Likeable}, traits::{Crud, Followable, Likeable},
utils::{build_db_pool_for_tests, uplete}, utils::{build_db_pool_for_tests, uplete},
}; };
use diesel::result::Error;
use lemmy_utils::error::LemmyResult; use lemmy_utils::error::LemmyResult;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use serial_test::serial; use serial_test::serial;
@ -340,20 +409,17 @@ mod tests {
let person_form_2 = PersonInsertForm::test_form(inserted_instance.id, "michele"); let person_form_2 = PersonInsertForm::test_form(inserted_instance.id, "michele");
let person_2 = Person::create(pool, &person_form_2).await?; let person_2 = Person::create(pool, &person_form_2).await?;
let follow_form = PersonFollowerForm { let follow_form = PersonFollowerForm::new(person_1.id, person_2.id, false);
person_id: person_1.id, let person_follower = PersonActions::follow(pool, &follow_form).await?;
follower_id: person_2.id, assert_eq!(person_1.id, person_follower.target_id);
pending: false, assert_eq!(person_2.id, person_follower.person_id);
}; assert!(person_follower.follow_pending.is_some_and(|x| !x));
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 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); 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); assert_eq!(uplete::Count::only_deleted(1), unfollow);
Ok(()) Ok(())
@ -361,7 +427,7 @@ mod tests {
#[tokio::test] #[tokio::test]
#[serial] #[serial]
async fn test_aggregates() -> Result<(), Error> { async fn test_aggregates() -> LemmyResult<()> {
let pool = &build_db_pool_for_tests(); let pool = &build_db_pool_for_tests();
let pool = &mut pool.into(); let pool = &mut pool.into();
@ -392,7 +458,7 @@ mod tests {
let inserted_post = Post::create(pool, &new_post).await?; let inserted_post = Post::create(pool, &new_post).await?;
let post_like = PostLikeForm::new(inserted_post.id, inserted_person.id, 1); 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( let comment_form = CommentInsertForm::new(
inserted_person.id, inserted_person.id,
@ -401,13 +467,9 @@ mod tests {
); );
let inserted_comment = Comment::create(pool, &comment_form, None).await?; let inserted_comment = Comment::create(pool, &comment_form, None).await?;
let mut comment_like = CommentLikeForm { let mut comment_like = CommentLikeForm::new(inserted_person.id, inserted_comment.id, 1);
comment_id: inserted_comment.id,
person_id: inserted_person.id,
score: 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( let child_comment_form = CommentInsertForm::new(
inserted_person.id, inserted_person.id,
@ -417,13 +479,10 @@ mod tests {
let inserted_child_comment = let inserted_child_comment =
Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?; Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?;
let child_comment_like = CommentLikeForm { let child_comment_like =
comment_id: inserted_child_comment.id, CommentLikeForm::new(another_inserted_person.id, inserted_child_comment.id, 1);
person_id: another_inserted_person.id,
score: 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?; 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); assert_eq!(2, person_aggregates_before_delete.comment_score);
// Remove a post like // 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?; let after_post_like_remove = Person::read(pool, inserted_person.id).await?;
assert_eq!(0, after_post_like_remove.post_score); assert_eq!(0, after_post_like_remove.post_score);
@ -474,7 +533,7 @@ mod tests {
let _new_child_comment = let _new_child_comment =
Comment::create(pool, &child_comment_form, Some(&new_parent_comment.path)).await?; Comment::create(pool, &child_comment_form, Some(&new_parent_comment.path)).await?;
comment_like.comment_id = new_parent_comment.id; 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?; let after_comment_add = Person::read(pool, inserted_person.id).await?;
assert_eq!(2, after_comment_add.comment_count); assert_eq!(2, after_comment_add.comment_count);
// TODO: fix person aggregate comment score calculation // TODO: fix person aggregate comment score calculation

View file

@ -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::<bool>(conn)
.await?
.then_some(())
.ok_or(LemmyErrorType::PersonIsBlocked.into())
}
pub async fn for_person(
pool: &mut DbPool<'_>,
person_id: PersonId,
) -> Result<Vec<Person>, 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::<Person>(conn)
.await
}
}
impl Blockable for PersonBlock {
type Form = PersonBlockForm;
async fn block(
pool: &mut DbPool<'_>,
person_block_form: &PersonBlockForm,
) -> Result<Self, Error> {
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::<Self>(conn)
.await
}
async fn unblock(
pool: &mut DbPool<'_>,
person_block_form: &Self::Form,
) -> Result<uplete::Count, Error> {
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
}
}

View file

@ -3,19 +3,17 @@ use crate::{
schema::{community, person, post, post_actions}, schema::{community, person, post, post_actions},
source::post::{ source::post::{
Post, Post,
PostActions,
PostActionsCursor, PostActionsCursor,
PostHide,
PostHideForm, PostHideForm,
PostInsertForm, PostInsertForm,
PostLike,
PostLikeForm, PostLikeForm,
PostRead, PostReadCommentsForm,
PostReadForm, PostReadForm,
PostSaved,
PostSavedForm, PostSavedForm,
PostUpdateForm, PostUpdateForm,
}, },
traits::{Crud, Likeable, Saveable}, traits::{Crud, Hideable, Likeable, ReadComments, Readable, Saveable},
utils::{ utils::{
functions::{coalesce, hot_rank, scaled_rank}, functions::{coalesce, hot_rank, scaled_rank},
get_conn, get_conn,
@ -323,95 +321,85 @@ impl Post {
} }
} }
impl Likeable for PostLike { impl Likeable for PostActions {
type Form = PostLikeForm; type Form = PostLikeForm;
type IdType = PostId; type IdType = PostId;
async fn like(pool: &mut DbPool<'_>, post_like_form: &PostLikeForm) -> Result<Self, Error> {
async fn like(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<Self> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
insert_into(post_actions::table) insert_into(post_actions::table)
.values(post_like_form) .values(form)
.on_conflict((post_actions::post_id, post_actions::person_id)) .on_conflict((post_actions::post_id, post_actions::person_id))
.do_update() .do_update()
.set(post_like_form) .set(form)
.returning(Self::as_select()) .returning(Self::as_select())
.get_result::<Self>(conn) .get_result::<Self>(conn)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntLikePost)
} }
async fn remove( async fn remove_like(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
person_id: PersonId, person_id: PersonId,
post_id: PostId, post_id: Self::IdType,
) -> Result<uplete::Count, Error> { ) -> LemmyResult<uplete::Count> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
uplete::new(post_actions::table.find((person_id, post_id))) uplete::new(post_actions::table.find((person_id, post_id)))
.set_null(post_actions::like_score) .set_null(post_actions::like_score)
.set_null(post_actions::liked) .set_null(post_actions::liked)
.get_result(conn) .get_result(conn)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntLikePost)
} }
} }
impl Saveable for PostSaved { impl Saveable for PostActions {
type Form = PostSavedForm; type Form = PostSavedForm;
async fn save(pool: &mut DbPool<'_>, post_saved_form: &PostSavedForm) -> Result<Self, Error> { async fn save(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<Self> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
insert_into(post_actions::table) insert_into(post_actions::table)
.values(post_saved_form) .values(form)
.on_conflict((post_actions::post_id, post_actions::person_id)) .on_conflict((post_actions::post_id, post_actions::person_id))
.do_update() .do_update()
.set(post_saved_form) .set(form)
.returning(Self::as_select()) .returning(Self::as_select())
.get_result::<Self>(conn) .get_result::<Self>(conn)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntSavePost)
} }
async fn unsave( async fn unsave(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<uplete::Count> {
pool: &mut DbPool<'_>,
post_saved_form: &PostSavedForm,
) -> Result<uplete::Count, Error> {
let conn = &mut get_conn(pool).await?; 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) .set_null(post_actions::saved)
.get_result(conn) .get_result(conn)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntSavePost)
} }
} }
impl PostRead { impl Readable for PostActions {
pub async fn mark_as_read( type Form = PostReadForm;
pool: &mut DbPool<'_>,
post_read_form: &PostReadForm, async fn mark_as_read(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<usize> {
) -> LemmyResult<usize> { Self::mark_many_as_read(pool, &[form.clone()]).await
Self::mark_many_as_read(pool, &[post_read_form.post_id], post_read_form.person_id).await
} }
pub async fn mark_as_unread( async fn mark_as_unread(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<uplete::Count> {
pool: &mut DbPool<'_>,
post_read_form: &PostReadForm,
) -> Result<uplete::Count, Error> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
uplete::new( uplete::new(
post_actions::table post_actions::table
.filter(post_actions::post_id.eq(post_read_form.post_id)) .filter(post_actions::post_id.eq(form.post_id))
.filter(post_actions::person_id.eq(post_read_form.person_id)), .filter(post_actions::person_id.eq(form.person_id)),
) )
.set_null(post_actions::read) .set_null(post_actions::read)
.get_result(conn) .get_result(conn)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntMarkPostAsRead)
} }
pub async fn mark_many_as_read( async fn mark_many_as_read(pool: &mut DbPool<'_>, forms: &[Self::Form]) -> LemmyResult<usize> {
pool: &mut DbPool<'_>,
post_ids: &[PostId],
person_id: PersonId,
) -> LemmyResult<usize> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
let forms = post_ids
.iter()
.map(|post_id| (PostReadForm::new(*post_id, person_id)))
.collect::<Vec<_>>();
insert_into(post_actions::table) insert_into(post_actions::table)
.values(forms) .values(forms)
.on_conflict((post_actions::person_id, post_actions::post_id)) .on_conflict((post_actions::person_id, post_actions::post_id))
@ -423,39 +411,79 @@ impl PostRead {
} }
} }
impl PostHide { impl Hideable for PostActions {
pub async fn hide( type Form = PostHideForm;
pool: &mut DbPool<'_>, async fn hide(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<Self> {
post_id: PostId,
person_id: PersonId,
) -> Result<usize, Error> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
let form = &PostHideForm::new(post_id, person_id);
insert_into(post_actions::table) insert_into(post_actions::table)
.values(form) .values(form)
.on_conflict((post_actions::person_id, post_actions::post_id)) .on_conflict((post_actions::person_id, post_actions::post_id))
.do_update() .do_update()
.set(form) .set(form)
.execute(conn) .get_result::<Self>(conn)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntHidePost)
} }
pub async fn unhide( async fn unhide(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<uplete::Count> {
pool: &mut DbPool<'_>,
post_id_: PostId,
person_id_: PersonId,
) -> Result<uplete::Count, Error> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
uplete::new( uplete::new(
post_actions::table post_actions::table
.filter(post_actions::post_id.eq(post_id_)) .filter(post_actions::post_id.eq(form.post_id))
.filter(post_actions::person_id.eq(person_id_)), .filter(post_actions::person_id.eq(form.person_id)),
) )
.set_null(post_actions::hidden) .set_null(post_actions::hidden)
.get_result(conn) .get_result(conn)
.await .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<Self> {
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::<Self>(conn)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdateReadComments)
}
async fn remove_read_comments(
pool: &mut DbPool<'_>,
person_id: PersonId,
post_id: Self::IdType,
) -> LemmyResult<uplete::Count> {
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<PostReadForm> {
post_ids
.iter()
.map(|post_id| (PostReadForm::new(*post_id, person_id)))
.collect::<Vec<_>>()
} }
} }
@ -499,17 +527,15 @@ mod tests {
person::{Person, PersonInsertForm}, person::{Person, PersonInsertForm},
post::{ post::{
Post, Post,
PostActions,
PostInsertForm, PostInsertForm,
PostLike,
PostLikeForm, PostLikeForm,
PostRead,
PostReadForm, PostReadForm,
PostSaved,
PostSavedForm, PostSavedForm,
PostUpdateForm, PostUpdateForm,
}, },
}, },
traits::{Crud, Likeable, Saveable}, traits::{Crud, Likeable, Readable, Saveable},
utils::{build_db_pool_for_tests, uplete, RANK_DEFAULT}, utils::{build_db_pool_for_tests, uplete, RANK_DEFAULT},
}; };
use chrono::DateTime; use chrono::DateTime;
@ -603,31 +629,20 @@ mod tests {
// Post Like // Post Like
let post_like_form = PostLikeForm::new(inserted_post.id, inserted_person.id, 1); 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 inserted_post_like = PostActions::like(pool, &post_like_form).await?;
assert_eq!(Some(1), inserted_post_like.like_score);
let expected_post_like = PostLike {
post_id: inserted_post.id,
person_id: inserted_person.id,
published: inserted_post_like.published,
score: 1,
};
// Post Save // Post Save
let post_saved_form = PostSavedForm::new(inserted_post.id, inserted_person.id); let post_saved_form = PostSavedForm::new(inserted_post.id, inserted_person.id);
let inserted_post_saved = PostSaved::save(pool, &post_saved_form).await?; let inserted_post_saved = PostActions::save(pool, &post_saved_form).await?;
assert!(inserted_post_saved.saved.is_some());
let expected_post_saved = PostSaved {
post_id: inserted_post.id,
person_id: inserted_person.id,
published: inserted_post_saved.published,
};
// Mark 2 posts as read // Mark 2 posts as read
let post_read_form_1 = PostReadForm::new(inserted_post.id, inserted_person.id); 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); 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?; 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?; let scheduled_post_count = Post::user_scheduled_post_count(inserted_person.id, pool).await?;
assert_eq!(1, scheduled_post_count); 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); 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); assert_eq!(uplete::Count::only_updated(1), saved_removed);
let read_remove_form_1 = PostReadForm::new(inserted_post.id, inserted_person.id); 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); 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_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); assert_eq!(uplete::Count::only_deleted(1), read_removed_2);
let num_deleted = Post::delete(pool, inserted_post.id).await? 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, read_post);
assert_eq!(expected_post, updated_post); assert_eq!(expected_post, updated_post);
assert_eq!(expected_post_like, inserted_post_like);
assert_eq!(expected_post_saved, inserted_post_saved);
Ok(()) Ok(())
} }
#[tokio::test] #[tokio::test]
#[serial] #[serial]
async fn test_aggregates() -> Result<(), Error> { async fn test_aggregates() -> LemmyResult<()> {
let pool = &build_db_pool_for_tests(); let pool = &build_db_pool_for_tests();
let pool = &mut pool.into(); let pool = &mut pool.into();
@ -719,7 +732,7 @@ mod tests {
let post_like = PostLikeForm::new(inserted_post.id, inserted_person.id, 1); 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?; 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 // Add a post dislike from the other person
let post_dislike = PostLikeForm::new(inserted_post.id, another_inserted_person.id, -1); 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?; 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); assert_eq!(1, after_comment_delete.downvotes);
// Remove the first post like // 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?; let after_like_remove = Post::read(pool, inserted_post.id).await?;
assert_eq!(0, after_like_remove.comments); assert_eq!(0, after_like_remove.comments);
assert_eq!(-1, after_like_remove.score); assert_eq!(-1, after_like_remove.score);

View file

@ -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<Self, Error> {
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::<Self>(conn)
.await
}
pub async fn read(
pool: &mut DbPool<'_>,
person_id_: PersonId,
post_id_: PostId,
) -> Result<Option<Self>, 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()
}
}

View file

@ -1,5 +1,4 @@
use crate::{ use crate::{
diesel::BoolExpressionMethods,
newtypes::{PersonId, PostId, PostReportId}, newtypes::{PersonId, PostId, PostReportId},
schema::post_report, schema::post_report,
source::post_report::{PostReport, PostReportForm}, source::post_report::{PostReport, PostReportForm},
@ -9,31 +8,32 @@ use crate::{
use chrono::Utc; use chrono::Utc;
use diesel::{ use diesel::{
dsl::{insert_into, update}, dsl::{insert_into, update},
result::Error, BoolExpressionMethods,
ExpressionMethods, ExpressionMethods,
QueryDsl, QueryDsl,
}; };
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
use lemmy_utils::error::LemmyResult; use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
impl Reportable for PostReport { impl Reportable for PostReport {
type Form = PostReportForm; type Form = PostReportForm;
type IdType = PostReportId; type IdType = PostReportId;
type ObjectIdType = PostId; type ObjectIdType = PostId;
async fn report(pool: &mut DbPool<'_>, post_report_form: &PostReportForm) -> Result<Self, Error> { async fn report(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<Self> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
insert_into(post_report::table) insert_into(post_report::table)
.values(post_report_form) .values(form)
.get_result::<Self>(conn) .get_result::<Self>(conn)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntCreateReport)
} }
async fn resolve( async fn resolve(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
report_id: Self::IdType, report_id: Self::IdType,
by_resolver_id: PersonId, by_resolver_id: PersonId,
) -> Result<usize, Error> { ) -> LemmyResult<usize> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
update(post_report::table.find(report_id)) update(post_report::table.find(report_id))
.set(( .set((
@ -43,6 +43,7 @@ impl Reportable for PostReport {
)) ))
.execute(conn) .execute(conn)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)
} }
async fn resolve_apub( async fn resolve_apub(
@ -52,29 +53,28 @@ impl Reportable for PostReport {
resolver_id: PersonId, resolver_id: PersonId,
) -> LemmyResult<usize> { ) -> LemmyResult<usize> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
Ok( update(
update( post_report::table.filter(
post_report::table.filter( post_report::post_id
post_report::post_id .eq(object_id)
.eq(object_id) .and(post_report::creator_id.eq(report_creator_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?,
) )
.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( async fn resolve_all_for_object(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
post_id_: PostId, post_id_: PostId,
by_resolver_id: PersonId, by_resolver_id: PersonId,
) -> Result<usize, Error> { ) -> LemmyResult<usize> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
update(post_report::table.filter(post_report::post_id.eq(post_id_))) update(post_report::table.filter(post_report::post_id.eq(post_id_)))
.set(( .set((
@ -84,13 +84,14 @@ impl Reportable for PostReport {
)) ))
.execute(conn) .execute(conn)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)
} }
async fn unresolve( async fn unresolve(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
report_id: Self::IdType, report_id: Self::IdType,
by_resolver_id: PersonId, by_resolver_id: PersonId,
) -> Result<usize, Error> { ) -> LemmyResult<usize> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
update(post_report::table.find(report_id)) update(post_report::table.find(report_id))
.set(( .set((
@ -100,6 +101,7 @@ impl Reportable for PostReport {
)) ))
.execute(conn) .execute(conn)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)
} }
} }
@ -117,10 +119,9 @@ mod tests {
traits::Crud, traits::Crud,
utils::build_db_pool_for_tests, utils::build_db_pool_for_tests,
}; };
use diesel::result::Error;
use serial_test::serial; 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 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_form = PersonInsertForm::test_form(inserted_instance.id, "jim");
let person = Person::create(pool, &person_form).await?; let person = Person::create(pool, &person_form).await?;
@ -149,7 +150,7 @@ mod tests {
#[tokio::test] #[tokio::test]
#[serial] #[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 = &build_db_pool_for_tests();
let pool = &mut pool.into(); let pool = &mut pool.into();
@ -169,7 +170,7 @@ mod tests {
#[tokio::test] #[tokio::test]
#[serial] #[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 = &build_db_pool_for_tests();
let pool = &mut pool.into(); let pool = &mut pool.into();

View file

@ -8,34 +8,31 @@ use crate::{
use chrono::Utc; use chrono::Utc;
use diesel::{ use diesel::{
dsl::{insert_into, update}, dsl::{insert_into, update},
result::Error,
ExpressionMethods, ExpressionMethods,
QueryDsl, QueryDsl,
}; };
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
use lemmy_utils::error::{FederationError, LemmyResult}; use lemmy_utils::error::{FederationError, LemmyErrorExt, LemmyErrorType, LemmyResult};
impl Reportable for PrivateMessageReport { impl Reportable for PrivateMessageReport {
type Form = PrivateMessageReportForm; type Form = PrivateMessageReportForm;
type IdType = PrivateMessageReportId; type IdType = PrivateMessageReportId;
type ObjectIdType = PrivateMessageId; type ObjectIdType = PrivateMessageId;
async fn report( async fn report(pool: &mut DbPool<'_>, form: &Self::Form) -> LemmyResult<Self> {
pool: &mut DbPool<'_>,
pm_report_form: &PrivateMessageReportForm,
) -> Result<Self, Error> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
insert_into(private_message_report) insert_into(private_message_report)
.values(pm_report_form) .values(form)
.get_result::<Self>(conn) .get_result::<Self>(conn)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntCreateReport)
} }
async fn resolve( async fn resolve(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
report_id: Self::IdType, report_id: Self::IdType,
by_resolver_id: PersonId, by_resolver_id: PersonId,
) -> Result<usize, Error> { ) -> LemmyResult<usize> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
update(private_message_report.find(report_id)) update(private_message_report.find(report_id))
.set(( .set((
@ -45,6 +42,7 @@ impl Reportable for PrivateMessageReport {
)) ))
.execute(conn) .execute(conn)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)
} }
async fn resolve_apub( async fn resolve_apub(
_pool: &mut DbPool<'_>, _pool: &mut DbPool<'_>,
@ -60,15 +58,15 @@ impl Reportable for PrivateMessageReport {
_pool: &mut DbPool<'_>, _pool: &mut DbPool<'_>,
_pm_id_: PrivateMessageId, _pm_id_: PrivateMessageId,
_by_resolver_id: PersonId, _by_resolver_id: PersonId,
) -> Result<usize, Error> { ) -> LemmyResult<usize> {
Err(Error::NotFound) Err(LemmyErrorType::NotFound.into())
} }
async fn unresolve( async fn unresolve(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
report_id: Self::IdType, report_id: Self::IdType,
by_resolver_id: PersonId, by_resolver_id: PersonId,
) -> Result<usize, Error> { ) -> LemmyResult<usize> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
update(private_message_report.find(report_id)) update(private_message_report.find(report_id))
.set(( .set((
@ -78,5 +76,6 @@ impl Reportable for PrivateMessageReport {
)) ))
.execute(conn) .execute(conn)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)
} }
} }

View file

@ -38,7 +38,11 @@ pub mod schema_setup;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use strum::{Display, EnumString}; use strum::{Display, EnumString};
#[cfg(feature = "full")] #[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( #[derive(
EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default, Hash, EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default, Hash,
@ -178,17 +182,6 @@ pub enum SearchType {
Users, 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)] #[derive(EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "full", derive(TS))] #[cfg_attr(feature = "full", derive(TS))]
#[cfg_attr(feature = "full", ts(export))] #[cfg_attr(feature = "full", ts(export))]
@ -277,7 +270,7 @@ pub enum CommunityVisibility {
/// Public community, any local or federated user can interact. /// Public community, any local or federated user can interact.
#[default] #[default]
Public, 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. /// are not shown in Local and All feeds, except for subscribed users.
Unlisted, Unlisted,
/// Unfederated community, only local users can interact (with or without login). /// Unfederated community, only local users can interact (with or without login).
@ -330,7 +323,7 @@ macro_rules! assert_length {
} }
#[cfg(feature = "full")] #[cfg(feature = "full")]
/// A helper tuple for person alias columns /// A helper tuple for person 1 alias columns
pub type Person1AliasAllColumnsTuple = ( pub type Person1AliasAllColumnsTuple = (
AliasedField<aliases::Person1, person::id>, AliasedField<aliases::Person1, person::id>,
AliasedField<aliases::Person1, person::name>, AliasedField<aliases::Person1, person::name>,
@ -357,3 +350,46 @@ pub type Person1AliasAllColumnsTuple = (
AliasedField<aliases::Person1, person::comment_count>, AliasedField<aliases::Person1, person::comment_count>,
AliasedField<aliases::Person1, person::comment_score>, AliasedField<aliases::Person1, person::comment_score>,
); );
#[cfg(feature = "full")]
/// A helper tuple for person 2 alias columns
pub type Person2AliasAllColumnsTuple = (
AliasedField<aliases::Person2, person::id>,
AliasedField<aliases::Person2, person::name>,
AliasedField<aliases::Person2, person::display_name>,
AliasedField<aliases::Person2, person::avatar>,
AliasedField<aliases::Person2, person::banned>,
AliasedField<aliases::Person2, person::published>,
AliasedField<aliases::Person2, person::updated>,
AliasedField<aliases::Person2, person::ap_id>,
AliasedField<aliases::Person2, person::bio>,
AliasedField<aliases::Person2, person::local>,
AliasedField<aliases::Person2, person::private_key>,
AliasedField<aliases::Person2, person::public_key>,
AliasedField<aliases::Person2, person::last_refreshed_at>,
AliasedField<aliases::Person2, person::banner>,
AliasedField<aliases::Person2, person::deleted>,
AliasedField<aliases::Person2, person::inbox_url>,
AliasedField<aliases::Person2, person::matrix_user_id>,
AliasedField<aliases::Person2, person::bot_account>,
AliasedField<aliases::Person2, person::ban_expires>,
AliasedField<aliases::Person2, person::instance_id>,
AliasedField<aliases::Person2, person::post_count>,
AliasedField<aliases::Person2, person::post_score>,
AliasedField<aliases::Person2, person::comment_count>,
AliasedField<aliases::Person2, person::comment_score>,
);
#[cfg(feature = "full")]
/// A helper tuple for creator community actions
pub type CreatorCommunityActionsAllColumnsTuple = (
AliasedField<aliases::CreatorCommunityActions, community_actions::community_id>,
AliasedField<aliases::CreatorCommunityActions, community_actions::person_id>,
AliasedField<aliases::CreatorCommunityActions, community_actions::followed>,
AliasedField<aliases::CreatorCommunityActions, community_actions::follow_state>,
AliasedField<aliases::CreatorCommunityActions, community_actions::follow_approver_id>,
AliasedField<aliases::CreatorCommunityActions, community_actions::blocked>,
AliasedField<aliases::CreatorCommunityActions, community_actions::became_moderator>,
AliasedField<aliases::CreatorCommunityActions, community_actions::received_ban>,
AliasedField<aliases::CreatorCommunityActions, community_actions::ban_expires>,
);

View file

@ -198,17 +198,17 @@ pub struct LtreeDef(pub String);
#[cfg_attr(feature = "full", ts(export))] #[cfg_attr(feature = "full", ts(export))]
pub struct DbUrl(pub(crate) Box<Url>); pub struct DbUrl(pub(crate) Box<Url>);
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Default)] #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
#[cfg_attr(feature = "full", derive(DieselNewType))] #[cfg_attr(feature = "full", derive(DieselNewType))]
/// The report combined id /// The report combined id
pub struct ReportCombinedId(i32); 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))] #[cfg_attr(feature = "full", derive(DieselNewType))]
/// The person content combined id /// The person content combined id
pub struct PersonContentCombinedId(i32); 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))] #[cfg_attr(feature = "full", derive(DieselNewType))]
/// The person saved combined id /// The person saved combined id
pub struct PersonSavedCombinedId(i32); pub struct PersonSavedCombinedId(i32);

View file

@ -4,8 +4,11 @@ use crate::schema::person_content_combined;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
#[cfg(feature = "full")] #[cfg(feature = "full")]
use i_love_jesus::CursorKeysModule; 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( #[cfg_attr(
feature = "full", feature = "full",
derive(Identifiable, Queryable, Selectable, CursorKeysModule) derive(Identifiable, Queryable, Selectable, CursorKeysModule)

View file

@ -4,8 +4,11 @@ use crate::schema::person_saved_combined;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
#[cfg(feature = "full")] #[cfg(feature = "full")]
use i_love_jesus::CursorKeysModule; 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( #[cfg_attr(
feature = "full", feature = "full",
derive(Identifiable, Queryable, Selectable, CursorKeysModule) derive(Identifiable, Queryable, Selectable, CursorKeysModule)

View file

@ -10,8 +10,11 @@ use crate::schema::report_combined;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
#[cfg(feature = "full")] #[cfg(feature = "full")]
use i_love_jesus::CursorKeysModule; 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( #[cfg_attr(
feature = "full", feature = "full",
derive(Identifiable, Queryable, Selectable, CursorKeysModule) derive(Identifiable, Queryable, Selectable, CursorKeysModule)

View file

@ -5,8 +5,6 @@ use crate::newtypes::{CommentId, DbUrl, LanguageId, PersonId, PostId};
use crate::schema::{comment, comment_actions}; use crate::schema::{comment, comment_actions};
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
#[cfg(feature = "full")] #[cfg(feature = "full")]
use diesel::{dsl, expression_methods::NullableExpressionMethods};
#[cfg(feature = "full")]
use diesel_ltree::Ltree; use diesel_ltree::Ltree;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none; use serde_with::skip_serializing_none;
@ -104,59 +102,48 @@ pub struct CommentUpdateForm {
pub language_id: Option<LanguageId>, pub language_id: Option<LanguageId>,
} }
#[derive(PartialEq, Eq, Debug, Clone)] #[skip_serializing_none]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[cfg_attr( #[cfg_attr(
feature = "full", 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(belongs_to(crate::source::comment::Comment)))]
#[cfg_attr(feature = "full", diesel(table_name = comment_actions))] #[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(primary_key(person_id, comment_id)))]
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[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 person_id: PersonId,
pub comment_id: CommentId, pub comment_id: CommentId,
#[cfg_attr(feature = "full", diesel(select_expression = comment_actions::like_score.assume_not_null()))] #[cfg_attr(feature = "full", ts(optional))]
#[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull<comment_actions::like_score>))] /// The like / score for the comment.
pub score: i16, pub like_score: Option<i16>,
#[cfg_attr(feature = "full", diesel(select_expression = comment_actions::liked.assume_not_null()))] #[cfg_attr(feature = "full", ts(optional))]
#[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull<comment_actions::liked>))] /// When the comment was liked.
pub published: DateTime<Utc>, pub liked: Option<DateTime<Utc>>,
#[cfg_attr(feature = "full", ts(optional))]
/// When the comment was saved.
pub saved: Option<DateTime<Utc>>,
} }
#[derive(Clone)] #[derive(Clone, derive_new::new)]
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = comment_actions))] #[cfg_attr(feature = "full", diesel(table_name = comment_actions))]
pub struct CommentLikeForm { pub struct CommentLikeForm {
pub person_id: PersonId, pub person_id: PersonId,
pub comment_id: CommentId, pub comment_id: CommentId,
#[cfg_attr(feature = "full", diesel(column_name = like_score))] pub like_score: i16,
pub score: i16, #[new(value = "Utc::now()")]
} pub liked: DateTime<Utc>,
#[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<comment_actions::saved>))]
pub published: DateTime<Utc>,
} }
#[derive(derive_new::new)]
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = comment_actions))] #[cfg_attr(feature = "full", diesel(table_name = comment_actions))]
#[derive(derive_new::new)]
pub struct CommentSavedForm { pub struct CommentSavedForm {
pub comment_id: CommentId,
pub person_id: PersonId, pub person_id: PersonId,
pub comment_id: CommentId,
#[new(value = "Utc::now()")] #[new(value = "Utc::now()")]
pub saved: DateTime<Utc>, pub saved: DateTime<Utc>,
} }

View file

@ -7,8 +7,6 @@ use crate::{
CommunityVisibility, CommunityVisibility,
}; };
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
#[cfg(feature = "full")]
use diesel::{dsl, expression_methods::NullableExpressionMethods};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none; use serde_with::skip_serializing_none;
use strum::{Display, EnumString}; use strum::{Display, EnumString};
@ -172,10 +170,11 @@ pub struct CommunityUpdateForm {
pub description: Option<Option<String>>, pub description: Option<Option<String>>,
} }
#[derive(PartialEq, Eq, Debug)] #[skip_serializing_none]
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
#[cfg_attr( #[cfg_attr(
feature = "full", feature = "full",
derive(Identifiable, Queryable, Selectable, Associations) derive(Identifiable, Queryable, Selectable, Associations, TS)
)] )]
#[cfg_attr( #[cfg_attr(
feature = "full", feature = "full",
@ -184,52 +183,53 @@ pub struct CommunityUpdateForm {
#[cfg_attr(feature = "full", diesel(table_name = community_actions))] #[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(primary_key(person_id, community_id)))]
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[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 community_id: CommunityId,
pub person_id: PersonId, pub person_id: PersonId,
#[cfg_attr(feature = "full", diesel(select_expression = community_actions::became_moderator.assume_not_null()))] #[cfg_attr(feature = "full", ts(optional))]
#[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull<community_actions::became_moderator>))] /// When the community was followed.
pub published: DateTime<Utc>, pub followed: Option<DateTime<Utc>>,
#[cfg_attr(feature = "full", ts(optional))]
/// The state of the community follow.
pub follow_state: Option<CommunityFollowerState>,
#[cfg_attr(feature = "full", ts(optional))]
/// The approver of the community follow.
pub follow_approver_id: Option<PersonId>,
#[cfg_attr(feature = "full", ts(optional))]
/// When the community was blocked.
pub blocked: Option<DateTime<Utc>>,
#[cfg_attr(feature = "full", ts(optional))]
/// When this user became a moderator.
pub became_moderator: Option<DateTime<Utc>>,
#[cfg_attr(feature = "full", ts(optional))]
/// When this user received a ban.
pub received_ban: Option<DateTime<Utc>>,
#[cfg_attr(feature = "full", ts(optional))]
/// When their ban expires.
pub ban_expires: Option<DateTime<Utc>>,
} }
#[derive(Clone)] #[derive(Clone, derive_new::new)]
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = community_actions))] #[cfg_attr(feature = "full", diesel(table_name = community_actions))]
pub struct CommunityModeratorForm { pub struct CommunityModeratorForm {
pub community_id: CommunityId, pub community_id: CommunityId,
pub person_id: PersonId, pub person_id: PersonId,
#[new(value = "Utc::now()")]
pub became_moderator: DateTime<Utc>,
} }
#[derive(PartialEq, Eq, Debug)] #[derive(Clone, derive_new::new)]
#[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<community_actions::received_ban>))]
pub published: DateTime<Utc>,
#[cfg_attr(feature = "full", diesel(column_name = ban_expires))]
pub expires: Option<DateTime<Utc>>,
}
#[derive(Clone)]
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = community_actions))] #[cfg_attr(feature = "full", diesel(table_name = community_actions))]
pub struct CommunityPersonBanForm { pub struct CommunityPersonBanForm {
pub community_id: CommunityId, pub community_id: CommunityId,
pub person_id: PersonId, pub person_id: PersonId,
#[cfg_attr(feature = "full", diesel(column_name = ban_expires))] #[new(default)]
pub expires: Option<Option<DateTime<Utc>>>, pub ban_expires: Option<Option<DateTime<Utc>>>,
#[new(value = "Utc::now()")]
pub received_ban: DateTime<Utc>,
} }
#[derive(EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)] #[derive(EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)]
@ -246,41 +246,25 @@ pub enum CommunityFollowerState {
ApprovalRequired, 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<community_actions::followed>))]
pub published: DateTime<Utc>,
#[cfg_attr(feature = "full", diesel(select_expression = community_actions::follow_state.assume_not_null()))]
#[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull<community_actions::follow_state>))]
pub state: CommunityFollowerState,
#[cfg_attr(feature = "full", diesel(column_name = follow_approver_id))]
pub approver_id: Option<PersonId>,
}
#[derive(Clone, derive_new::new)] #[derive(Clone, derive_new::new)]
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = community_actions))] #[cfg_attr(feature = "full", diesel(table_name = community_actions))]
pub struct CommunityFollowerForm { pub struct CommunityFollowerForm {
pub community_id: CommunityId, pub community_id: CommunityId,
pub person_id: PersonId, pub person_id: PersonId,
pub follow_state: CommunityFollowerState,
#[new(default)] #[new(default)]
#[cfg_attr(feature = "full", diesel(column_name = follow_state))] pub follow_approver_id: Option<PersonId>,
pub state: Option<CommunityFollowerState>, #[new(value = "Utc::now()")]
#[new(default)] pub followed: DateTime<Utc>,
#[cfg_attr(feature = "full", diesel(column_name = follow_approver_id))] }
pub approver_id: Option<PersonId>,
#[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<Utc>,
} }

View file

@ -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<community_actions::blocked>))]
pub published: DateTime<Utc>,
}
#[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,
}

View file

@ -1,6 +1,6 @@
use crate::newtypes::InstanceId; use crate::newtypes::{InstanceId, PersonId};
#[cfg(feature = "full")] #[cfg(feature = "full")]
use crate::schema::instance; use crate::schema::{instance, instance_actions};
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none; use serde_with::skip_serializing_none;
@ -20,10 +20,13 @@ pub struct Instance {
pub domain: String, pub domain: String,
pub published: DateTime<Utc>, pub published: DateTime<Utc>,
#[cfg_attr(feature = "full", ts(optional))] #[cfg_attr(feature = "full", ts(optional))]
/// When the instance was updated.
pub updated: Option<DateTime<Utc>>, pub updated: Option<DateTime<Utc>>,
#[cfg_attr(feature = "full", ts(optional))] #[cfg_attr(feature = "full", ts(optional))]
/// The software of the instance.
pub software: Option<String>, pub software: Option<String>,
#[cfg_attr(feature = "full", ts(optional))] #[cfg_attr(feature = "full", ts(optional))]
/// The version of the instance's software.
pub version: Option<String>, pub version: Option<String>,
} }
@ -39,3 +42,34 @@ pub struct InstanceForm {
#[new(default)] #[new(default)]
pub updated: Option<DateTime<Utc>>, pub updated: Option<DateTime<Utc>>,
} }
#[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<DateTime<Utc>>,
}
#[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<Utc>,
}

View file

@ -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<instance_actions::blocked>))]
pub published: DateTime<Utc>,
}
#[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,
}

View file

@ -10,7 +10,6 @@ pub mod comment;
pub mod comment_reply; pub mod comment_reply;
pub mod comment_report; pub mod comment_report;
pub mod community; pub mod community;
pub mod community_block;
pub mod community_report; pub mod community_report;
pub mod custom_emoji; pub mod custom_emoji;
pub mod custom_emoji_keyword; pub mod custom_emoji_keyword;
@ -20,7 +19,6 @@ pub mod federation_blocklist;
pub mod federation_queue_state; pub mod federation_queue_state;
pub mod images; pub mod images;
pub mod instance; pub mod instance;
pub mod instance_block;
pub mod language; pub mod language;
pub mod local_site; pub mod local_site;
pub mod local_site_rate_limit; pub mod local_site_rate_limit;
@ -32,11 +30,9 @@ pub mod oauth_account;
pub mod oauth_provider; pub mod oauth_provider;
pub mod password_reset_request; pub mod password_reset_request;
pub mod person; pub mod person;
pub mod person_block;
pub mod person_comment_mention; pub mod person_comment_mention;
pub mod person_post_mention; pub mod person_post_mention;
pub mod post; pub mod post;
pub mod post_actions;
pub mod post_report; pub mod post_report;
pub mod private_message; pub mod private_message;
pub mod private_message_report; pub mod private_message_report;

View file

@ -7,8 +7,6 @@ use crate::{
}; };
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
#[cfg(feature = "full")] #[cfg(feature = "full")]
use diesel::{dsl, expression_methods::NullableExpressionMethods};
#[cfg(feature = "full")]
use i_love_jesus::CursorKeysModule; use i_love_jesus::CursorKeysModule;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none; use serde_with::skip_serializing_none;
@ -141,36 +139,47 @@ pub struct PersonUpdateForm {
pub ban_expires: Option<Option<DateTime<Utc>>>, pub ban_expires: Option<Option<DateTime<Utc>>>,
} }
#[derive(PartialEq, Eq, Debug)] #[skip_serializing_none]
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
#[cfg_attr( #[cfg_attr(
feature = "full", 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(belongs_to(crate::source::person::Person)))]
#[cfg_attr(feature = "full", diesel(table_name = person_actions))] #[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(primary_key(person_id, target_id)))]
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
pub struct PersonFollower { #[cfg_attr(feature = "full", ts(export))]
#[cfg_attr(feature = "full", diesel(column_name = target_id))] pub struct PersonActions {
pub target_id: PersonId,
pub person_id: PersonId, pub person_id: PersonId,
#[cfg_attr(feature = "full", diesel(column_name = person_id))] #[serde(skip)]
pub follower_id: PersonId, pub followed: Option<DateTime<Utc>>,
#[cfg_attr(feature = "full", diesel(select_expression = person_actions::followed.assume_not_null()))] #[serde(skip)]
#[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull<person_actions::followed>))] pub follow_pending: Option<bool>,
pub published: DateTime<Utc>, #[cfg_attr(feature = "full", ts(optional))]
#[cfg_attr(feature = "full", diesel(select_expression = person_actions::follow_pending.assume_not_null()))] /// When the person was blocked.
#[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull<person_actions::follow_pending>))] pub blocked: Option<DateTime<Utc>>,
pub pending: bool,
} }
#[derive(Clone)] #[derive(Clone, derive_new::new)]
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = person_actions))] #[cfg_attr(feature = "full", diesel(table_name = person_actions))]
pub struct PersonFollowerForm { pub struct PersonFollowerForm {
#[cfg_attr(feature = "full", diesel(column_name = target_id))] pub target_id: PersonId,
pub person_id: PersonId, pub person_id: PersonId,
#[cfg_attr(feature = "full", diesel(column_name = person_id))] pub follow_pending: bool,
pub follower_id: PersonId, #[new(value = "Utc::now()")]
#[cfg_attr(feature = "full", diesel(column_name = follow_pending))] pub followed: DateTime<Utc>,
pub pending: bool, }
#[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<Utc>,
} }

View file

@ -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<person_actions::blocked>))]
pub published: DateTime<Utc>,
}
#[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,
}

View file

@ -5,7 +5,6 @@ use serde_with::skip_serializing_none;
#[cfg(feature = "full")] #[cfg(feature = "full")]
use { use {
crate::schema::{post, post_actions}, crate::schema::{post, post_actions},
diesel::{dsl, expression_methods::NullableExpressionMethods},
i_love_jesus::CursorKeysModule, i_love_jesus::CursorKeysModule,
ts_rs::TS, ts_rs::TS,
}; };
@ -16,8 +15,8 @@ use {
feature = "full", feature = "full",
derive(Queryable, Selectable, Identifiable, TS, CursorKeysModule) derive(Queryable, Selectable, Identifiable, TS, CursorKeysModule)
)] )]
#[cfg_attr(feature = "full", diesel(table_name = post))]
#[cfg_attr(feature = "full", ts(export))] #[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", diesel(check_for_backend(diesel::pg::Pg)))]
#[cfg_attr(feature = "full", cursor_keys_module(name = post_keys))] #[cfg_attr(feature = "full", cursor_keys_module(name = post_keys))]
/// A post. /// A post.
@ -172,24 +171,42 @@ pub struct PostUpdateForm {
pub scheduled_publish_time: Option<Option<DateTime<Utc>>>, pub scheduled_publish_time: Option<Option<DateTime<Utc>>>,
} }
#[derive(PartialEq, Eq, Debug)] #[skip_serializing_none]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[cfg_attr( #[cfg_attr(
feature = "full", 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(belongs_to(crate::source::post::Post)))]
#[cfg_attr(feature = "full", diesel(table_name = post_actions))] #[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(primary_key(person_id, post_id)))]
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[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 post_id: PostId,
pub person_id: PersonId, pub person_id: PersonId,
#[cfg_attr(feature = "full", diesel(select_expression = post_actions::like_score.assume_not_null()))] #[cfg_attr(feature = "full", ts(optional))]
#[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull<post_actions::like_score>))] /// When the post was read.
pub score: i16, pub read: Option<DateTime<Utc>>,
#[cfg_attr(feature = "full", diesel(select_expression = post_actions::liked.assume_not_null()))] #[cfg_attr(feature = "full", ts(optional))]
#[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull<post_actions::liked>))] /// When was the last time you read the comments.
pub published: DateTime<Utc>, pub read_comments: Option<DateTime<Utc>>,
#[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<i64>,
#[cfg_attr(feature = "full", ts(optional))]
/// When the post was saved.
pub saved: Option<DateTime<Utc>>,
#[cfg_attr(feature = "full", ts(optional))]
/// When the post was liked.
pub liked: Option<DateTime<Utc>>,
#[cfg_attr(feature = "full", ts(optional))]
/// The like / score of the post.
pub like_score: Option<i16>,
#[cfg_attr(feature = "full", ts(optional))]
/// When the post was hidden.
pub hidden: Option<DateTime<Utc>>,
} }
#[derive(Clone, derive_new::new)] #[derive(Clone, derive_new::new)]
@ -198,29 +215,11 @@ pub struct PostLike {
pub struct PostLikeForm { pub struct PostLikeForm {
pub post_id: PostId, pub post_id: PostId,
pub person_id: PersonId, pub person_id: PersonId,
#[cfg_attr(feature = "full", diesel(column_name = like_score))] pub like_score: i16,
pub score: i16,
#[new(value = "Utc::now()")] #[new(value = "Utc::now()")]
pub liked: DateTime<Utc>, pub liked: DateTime<Utc>,
} }
#[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<post_actions::saved>))]
pub published: DateTime<Utc>,
}
#[derive(derive_new::new)] #[derive(derive_new::new)]
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = post_actions))] #[cfg_attr(feature = "full", diesel(table_name = post_actions))]
@ -231,24 +230,7 @@ pub struct PostSavedForm {
pub saved: DateTime<Utc>, pub saved: DateTime<Utc>,
} }
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(derive_new::new, Clone)]
#[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<post_actions::read>))]
pub published: DateTime<Utc>,
}
#[derive(derive_new::new)]
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = post_actions))] #[cfg_attr(feature = "full", diesel(table_name = post_actions))]
pub struct PostReadForm { pub struct PostReadForm {
@ -258,21 +240,15 @@ pub struct PostReadForm {
pub read: DateTime<Utc>, pub read: DateTime<Utc>,
} }
#[derive(PartialEq, Eq, Debug)] #[derive(derive_new::new)]
#[cfg_attr( #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
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(table_name = post_actions))]
#[cfg_attr(feature = "full", diesel(primary_key(person_id, post_id)))] pub struct PostReadCommentsForm {
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
pub struct PostHide {
pub post_id: PostId, pub post_id: PostId,
pub person_id: PersonId, pub person_id: PersonId,
#[cfg_attr(feature = "full", diesel(select_expression = post_actions::hidden.assume_not_null()))] pub read_comments_amount: i64,
#[cfg_attr(feature = "full", diesel(select_expression_type = dsl::AssumeNotNull<post_actions::hidden>))] #[new(value = "Utc::now()")]
pub published: DateTime<Utc>, pub read_comments: DateTime<Utc>,
} }
#[derive(derive_new::new)] #[derive(derive_new::new)]

View file

@ -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<post_actions::read_comments_amount>))]
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<post_actions::read_comments>))]
pub published: DateTime<Utc>,
}
#[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,
}

View file

@ -82,23 +82,25 @@ where
pub trait Followable { pub trait Followable {
type Form; type Form;
type IdType;
fn follow( fn follow(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
form: &Self::Form, form: &Self::Form,
) -> impl Future<Output = Result<Self, Error>> + Send ) -> impl Future<Output = LemmyResult<Self>> + Send
where where
Self: Sized; Self: Sized;
fn follow_accepted( fn follow_accepted(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
community_id: CommunityId, community_id: CommunityId,
person_id: PersonId, person_id: PersonId,
) -> impl Future<Output = Result<Self, Error>> + Send ) -> impl Future<Output = LemmyResult<Self>> + Send
where where
Self: Sized; Self: Sized;
fn unfollow( fn unfollow(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
form: &Self::Form, person_id: PersonId,
) -> impl Future<Output = Result<uplete::Count, Error>> + Send item_id: Self::IdType,
) -> impl Future<Output = LemmyResult<uplete::Count>> + Send
where where
Self: Sized; Self: Sized;
} }
@ -108,13 +110,13 @@ pub trait Joinable {
fn join( fn join(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
form: &Self::Form, form: &Self::Form,
) -> impl Future<Output = Result<Self, Error>> + Send ) -> impl Future<Output = LemmyResult<Self>> + Send
where where
Self: Sized; Self: Sized;
fn leave( fn leave(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
form: &Self::Form, form: &Self::Form,
) -> impl Future<Output = Result<uplete::Count, Error>> + Send ) -> impl Future<Output = LemmyResult<uplete::Count>> + Send
where where
Self: Sized; Self: Sized;
} }
@ -125,14 +127,14 @@ pub trait Likeable {
fn like( fn like(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
form: &Self::Form, form: &Self::Form,
) -> impl Future<Output = Result<Self, Error>> + Send ) -> impl Future<Output = LemmyResult<Self>> + Send
where where
Self: Sized; Self: Sized;
fn remove( fn remove_like(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
person_id: PersonId, person_id: PersonId,
item_id: Self::IdType, item_id: Self::IdType,
) -> impl Future<Output = Result<uplete::Count, Error>> + Send ) -> impl Future<Output = LemmyResult<uplete::Count>> + Send
where where
Self: Sized; Self: Sized;
} }
@ -142,13 +144,13 @@ pub trait Bannable {
fn ban( fn ban(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
form: &Self::Form, form: &Self::Form,
) -> impl Future<Output = Result<Self, Error>> + Send ) -> impl Future<Output = LemmyResult<Self>> + Send
where where
Self: Sized; Self: Sized;
fn unban( fn unban(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
form: &Self::Form, form: &Self::Form,
) -> impl Future<Output = Result<uplete::Count, Error>> + Send ) -> impl Future<Output = LemmyResult<uplete::Count>> + Send
where where
Self: Sized; Self: Sized;
} }
@ -158,29 +160,102 @@ pub trait Saveable {
fn save( fn save(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
form: &Self::Form, form: &Self::Form,
) -> impl Future<Output = Result<Self, Error>> + Send ) -> impl Future<Output = LemmyResult<Self>> + Send
where where
Self: Sized; Self: Sized;
fn unsave( fn unsave(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
form: &Self::Form, form: &Self::Form,
) -> impl Future<Output = Result<uplete::Count, Error>> + Send ) -> impl Future<Output = LemmyResult<uplete::Count>> + Send
where
Self: Sized;
}
pub trait Readable {
type Form;
fn mark_as_read(
pool: &mut DbPool<'_>,
form: &Self::Form,
) -> impl Future<Output = LemmyResult<usize>> + Send
where
Self: Sized;
fn mark_many_as_read(
pool: &mut DbPool<'_>,
forms: &[Self::Form],
) -> impl Future<Output = LemmyResult<usize>> + Send
where
Self: Sized;
fn mark_as_unread(
pool: &mut DbPool<'_>,
form: &Self::Form,
) -> impl Future<Output = LemmyResult<uplete::Count>> + Send
where
Self: Sized;
}
pub trait ReadComments {
type Form;
type IdType;
fn update_read_comments(
pool: &mut DbPool<'_>,
form: &Self::Form,
) -> impl Future<Output = LemmyResult<Self>> + Send
where
Self: Sized;
fn remove_read_comments(
pool: &mut DbPool<'_>,
person_id: PersonId,
item_id: Self::IdType,
) -> impl Future<Output = LemmyResult<uplete::Count>> + Send
where
Self: Sized;
}
pub trait Hideable {
type Form;
fn hide(
pool: &mut DbPool<'_>,
form: &Self::Form,
) -> impl Future<Output = LemmyResult<Self>> + Send
where
Self: Sized;
fn unhide(
pool: &mut DbPool<'_>,
form: &Self::Form,
) -> impl Future<Output = LemmyResult<uplete::Count>> + Send
where where
Self: Sized; Self: Sized;
} }
pub trait Blockable { pub trait Blockable {
type Form; type Form;
type ObjectIdType;
type ObjectType;
fn block( fn block(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
form: &Self::Form, form: &Self::Form,
) -> impl Future<Output = Result<Self, Error>> + Send ) -> impl Future<Output = LemmyResult<Self>> + Send
where where
Self: Sized; Self: Sized;
fn unblock( fn unblock(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
form: &Self::Form, form: &Self::Form,
) -> impl Future<Output = Result<uplete::Count, Error>> + Send ) -> impl Future<Output = LemmyResult<uplete::Count>> + Send
where
Self: Sized;
fn read_block(
pool: &mut DbPool<'_>,
for_person_id: PersonId,
for_item_id: Self::ObjectIdType,
) -> impl Future<Output = LemmyResult<()>> + 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<Output = Result<Vec<Self::ObjectType>, diesel::result::Error>> + Send
where where
Self: Sized; Self: Sized;
} }
@ -192,14 +267,14 @@ pub trait Reportable {
fn report( fn report(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
form: &Self::Form, form: &Self::Form,
) -> impl Future<Output = Result<Self, Error>> + Send ) -> impl Future<Output = LemmyResult<Self>> + Send
where where
Self: Sized; Self: Sized;
fn resolve( fn resolve(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
report_id: Self::IdType, report_id: Self::IdType,
resolver_id: PersonId, resolver_id: PersonId,
) -> impl Future<Output = Result<usize, Error>> + Send ) -> impl Future<Output = LemmyResult<usize>> + Send
where where
Self: Sized; Self: Sized;
fn resolve_apub( fn resolve_apub(
@ -214,14 +289,14 @@ pub trait Reportable {
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
comment_id_: Self::ObjectIdType, comment_id_: Self::ObjectIdType,
by_resolver_id: PersonId, by_resolver_id: PersonId,
) -> impl Future<Output = Result<usize, Error>> + Send ) -> impl Future<Output = LemmyResult<usize>> + Send
where where
Self: Sized; Self: Sized;
fn unresolve( fn unresolve(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
report_id: Self::IdType, report_id: Self::IdType,
resolver_id: PersonId, resolver_id: PersonId,
) -> impl Future<Output = Result<usize, Error>> + Send ) -> impl Future<Output = LemmyResult<usize>> + Send
where where
Self: Sized; Self: Sized;
} }

View file

@ -20,7 +20,6 @@ use diesel_async::RunQueryDsl;
use i_love_jesus::PaginatedQueryBuilder; use i_love_jesus::PaginatedQueryBuilder;
use lemmy_db_schema::{ use lemmy_db_schema::{
aliases::{self, creator_community_actions, creator_local_user}, aliases::{self, creator_community_actions, creator_local_user},
impls::{community::community_follower_select_subscribed_type, local_user::local_user_can_mod},
newtypes::{PaginationCursor, PersonId}, newtypes::{PaginationCursor, PersonId},
schema::{ schema::{
comment, comment,
@ -38,13 +37,11 @@ use lemmy_db_schema::{
person_post_mention, person_post_mention,
post, post,
post_actions, post_actions,
post_tag,
private_message, private_message,
tag,
}, },
source::combined::inbox::{inbox_combined_keys as key, InboxCombined}, source::combined::inbox::{inbox_combined_keys as key, InboxCombined},
traits::{InternalToCombinedView, PaginationCursorBuilder}, traits::{InternalToCombinedView, PaginationCursorBuilder},
utils::{functions::coalesce, get_conn, DbPool}, utils::{get_conn, DbPool},
InboxDataType, InboxDataType,
}; };
use lemmy_utils::error::{LemmyErrorType, LemmyResult}; use lemmy_utils::error::{LemmyErrorType, LemmyResult};
@ -55,14 +52,16 @@ impl InboxCombinedViewInternal {
let item_creator = person::id; let item_creator = person::id;
let recipient_person = aliases::person1.field(person::id); let recipient_person = aliases::person1.field(person::id);
let item_creator_join = comment::creator_id let item_creator_join = person::table.on(
.eq(item_creator) comment::creator_id
.or( .eq(item_creator)
inbox_combined::person_post_mention_id .or(
.is_not_null() inbox_combined::person_post_mention_id
.and(post::creator_id.eq(item_creator)), .is_not_null()
) .and(post::creator_id.eq(item_creator)),
.or(private_message::creator_id.eq(item_creator)); )
.or(private_message::creator_id.eq(item_creator)),
);
let recipient_join = aliases::person1.on( let recipient_join = aliases::person1.on(
comment_reply::recipient_id comment_reply::recipient_id
@ -72,27 +71,33 @@ impl InboxCombinedViewInternal {
.or(private_message::recipient_id.eq(recipient_person)), .or(private_message::recipient_id.eq(recipient_person)),
); );
let comment_join = comment_reply::comment_id let comment_join = comment::table.on(
.eq(comment::id) comment_reply::comment_id
.or(person_comment_mention::comment_id.eq(comment::id)) .eq(comment::id)
// Filter out the deleted / removed .or(person_comment_mention::comment_id.eq(comment::id))
.and(not(comment::deleted)) // Filter out the deleted / removed
.and(not(comment::removed)); .and(not(comment::deleted))
.and(not(comment::removed)),
);
let post_join = person_post_mention::post_id let post_join = post::table.on(
.eq(post::id) person_post_mention::post_id
.or(comment::post_id.eq(post::id)) .eq(post::id)
// Filter out the deleted / removed .or(comment::post_id.eq(post::id))
.and(not(post::deleted)) // Filter out the deleted / removed
.and(not(post::removed)); .and(not(post::deleted))
.and(not(post::removed)),
);
// This could be a simple join, but you need to check for deleted here // This could be a simple join, but you need to check for deleted here
let private_message_join = inbox_combined::private_message_id let private_message_join = private_message::table.on(
.eq(private_message::id.nullable()) inbox_combined::private_message_id
.and(not(private_message::deleted)) .eq(private_message::id.nullable())
.and(not(private_message::removed)); .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)); 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(comment_reply::table)
.left_join(person_comment_mention::table) .left_join(person_comment_mention::table)
.left_join(person_post_mention::table) .left_join(person_post_mention::table)
.left_join(private_message::table.on(private_message_join)) .left_join(private_message_join)
.left_join(comment::table.on(comment_join)) .left_join(comment_join)
.left_join(post::table.on(post_join)) .left_join(post_join)
.left_join(community::table.on(community_join)) .left_join(community_join)
.inner_join(person::table.on(item_creator_join)) .inner_join(item_creator_join)
.inner_join(recipient_join) .inner_join(recipient_join)
.left_join(image_details_join) .left_join(image_details_join)
.left_join(creator_community_actions_join) .left_join(creator_community_actions_join)
@ -266,58 +271,8 @@ impl InboxCombinedQuery {
let item_creator = person::id; let item_creator = person::id;
let recipient_person = aliases::person1.field(person::id); let recipient_person = aliases::person1.field(person::id);
let post_tags = post_tag::table
.inner_join(tag::table)
.select(diesel::dsl::sql::<diesel::sql_types::Json>(
"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) let mut query = InboxCombinedViewInternal::joins(my_person_id)
.select(( .select(InboxCombinedViewInternal::as_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(),
))
.into_boxed(); .into_boxed();
// Filters // Filters
@ -431,14 +386,12 @@ impl InternalToCombinedView for InboxCombinedViewInternal {
post, post,
community, community,
creator: v.item_creator, creator: v.item_creator,
creator_banned_from_community: v.item_creator_banned_from_community, community_actions: v.community_actions,
creator_is_moderator: v.item_creator_is_moderator, 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_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, can_mod: v.can_mod,
})) }))
} else if let (Some(person_comment_mention), Some(comment), Some(post), Some(community)) = ( } else if let (Some(person_comment_mention), Some(comment), Some(post), Some(community)) = (
@ -455,42 +408,31 @@ impl InternalToCombinedView for InboxCombinedViewInternal {
post, post,
community, community,
creator: v.item_creator, creator: v.item_creator,
creator_banned_from_community: v.item_creator_banned_from_community, community_actions: v.community_actions,
creator_is_moderator: v.item_creator_is_moderator, 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_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, can_mod: v.can_mod,
}, },
)) ))
} else if let (Some(person_post_mention), Some(post), Some(unread_comments), Some(community)) = ( } else if let (Some(person_post_mention), Some(post), Some(community)) =
v.person_post_mention, (v.person_post_mention, v.post, v.community)
v.post, {
v.post_unread_comments,
v.community,
) {
Some(InboxCombinedView::PostMention(PersonPostMentionView { Some(InboxCombinedView::PostMention(PersonPostMentionView {
person_post_mention, person_post_mention,
post, post,
community, community,
recipient: v.item_recipient,
unread_comments,
creator: v.item_creator, creator: v.item_creator,
creator_banned_from_community: v.item_creator_banned_from_community, recipient: v.item_recipient,
creator_is_moderator: v.item_creator_is_moderator, community_actions: v.community_actions,
creator_is_admin: v.item_creator_is_admin, person_actions: v.person_actions,
creator_blocked: v.item_creator_blocked, instance_actions: v.instance_actions,
subscribed: v.subscribed, post_actions: v.post_actions,
saved: v.post_saved,
read: v.post_read,
hidden: v.post_hidden,
my_vote: v.my_post_vote,
image_details: v.image_details, image_details: v.image_details,
post_tags: v.post_tags, creator_community_actions: v.creator_community_actions,
banned_from_community: v.banned_from_community, creator_is_admin: v.item_creator_is_admin,
can_mod: v.can_mod, can_mod: v.can_mod,
})) }))
} else if let Some(private_message) = v.private_message { } else if let Some(private_message) = v.private_message {
@ -518,10 +460,8 @@ mod tests {
comment::{Comment, CommentInsertForm}, comment::{Comment, CommentInsertForm},
comment_reply::{CommentReply, CommentReplyInsertForm, CommentReplyUpdateForm}, comment_reply::{CommentReply, CommentReplyInsertForm, CommentReplyUpdateForm},
community::{Community, CommunityInsertForm}, community::{Community, CommunityInsertForm},
instance::Instance, instance::{Instance, InstanceActions, InstanceBlockForm},
instance_block::{InstanceBlock, InstanceBlockForm}, person::{Person, PersonActions, PersonBlockForm, PersonInsertForm, PersonUpdateForm},
person::{Person, PersonInsertForm, PersonUpdateForm},
person_block::{PersonBlock, PersonBlockForm},
person_comment_mention::{PersonCommentMention, PersonCommentMentionInsertForm}, person_comment_mention::{PersonCommentMention, PersonCommentMentionInsertForm},
person_post_mention::{PersonPostMention, PersonPostMentionInsertForm}, person_post_mention::{PersonPostMention, PersonPostMentionInsertForm},
post::{Post, PostInsertForm}, post::{Post, PostInsertForm},
@ -727,11 +667,8 @@ mod tests {
} }
// Sara blocks timmy, and make sure these counts are now empty // Sara blocks timmy, and make sure these counts are now empty
let sara_blocks_timmy_form = PersonBlockForm { let sara_blocks_timmy_form = PersonBlockForm::new(data.sara.id, data.timmy.id);
person_id: data.sara.id, PersonActions::block(pool, &sara_blocks_timmy_form).await?;
target_id: data.timmy.id,
};
PersonBlock::block(pool, &sara_blocks_timmy_form).await?;
let sara_unread_mentions_after_block = let sara_unread_mentions_after_block =
InboxCombinedViewInternal::get_unread_count(pool, data.sara.id, true).await?; 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 // 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 // Test the type filter
let sara_inbox_post_mentions_only = InboxCombinedQuery { let sara_inbox_post_mentions_only = InboxCombinedQuery {
@ -883,19 +820,18 @@ mod tests {
setup_private_messages(&data, pool).await?; setup_private_messages(&data, pool).await?;
// Make sure blocks are working // Make sure blocks are working
let timmy_blocks_sara_form = PersonBlockForm { let timmy_blocks_sara_form = PersonBlockForm::new(data.timmy.id, data.sara.id);
person_id: data.timmy.id,
target_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 { assert_eq!(
person_id: data.timmy.id, (data.timmy.id, data.sara.id, true),
target_id: data.sara.id, (
published: inserted_block.published, inserted_block.person_id,
}; inserted_block.target_id,
assert_eq!(expected_block, inserted_block); inserted_block.blocked.is_some()
)
);
let timmy_messages = map_to_pm( let timmy_messages = map_to_pm(
&InboxCombinedQuery { &InboxCombinedQuery {
@ -926,19 +862,18 @@ mod tests {
setup_private_messages(&data, pool).await?; setup_private_messages(&data, pool).await?;
// Make sure instance_blocks are working // Make sure instance_blocks are working
let timmy_blocks_instance_form = InstanceBlockForm { let timmy_blocks_instance_form = InstanceBlockForm::new(data.timmy.id, data.sara.instance_id);
person_id: data.timmy.id,
instance_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 { assert_eq!(
person_id: data.timmy.id, (data.timmy.id, data.sara.instance_id, true),
instance_id: data.sara.instance_id, (
published: inserted_instance_block.published, inserted_instance_block.person_id,
}; inserted_instance_block.instance_id,
assert_eq!(expected_instance_block, inserted_instance_block); inserted_instance_block.blocked.is_some()
)
);
let timmy_messages = map_to_pm( let timmy_messages = map_to_pm(
&InboxCombinedQuery { &InboxCombinedQuery {

View file

@ -20,7 +20,7 @@ use crate::{
ModlogCombinedView, ModlogCombinedView,
ModlogCombinedViewInternal, ModlogCombinedViewInternal,
}, },
utils::{filter_is_subscribed, filter_not_hidden_or_is_subscribed}, utils::{filter_is_subscribed, filter_not_unlisted_or_is_subscribed},
}; };
use diesel::{ use diesel::{
BoolExpressionMethods, BoolExpressionMethods,
@ -378,7 +378,7 @@ impl ModlogCombinedQuery<'_> {
ListingType::Subscribed => query.filter(filter_is_subscribed()), ListingType::Subscribed => query.filter(filter_is_subscribed()),
ListingType::Local => query ListingType::Local => query
.filter(community::local.eq(true)) .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()), ListingType::ModeratorView => query.filter(community_actions::became_moderator.is_not_null()),
}; };

View file

@ -16,8 +16,8 @@ use diesel::{
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
use i_love_jesus::PaginatedQueryBuilder; use i_love_jesus::PaginatedQueryBuilder;
use lemmy_db_schema::{ use lemmy_db_schema::{
self,
aliases::{creator_community_actions, creator_local_user}, aliases::{creator_community_actions, creator_local_user},
impls::{community::community_follower_select_subscribed_type, local_user::local_user_can_mod},
newtypes::{PaginationCursor, PersonId}, newtypes::{PaginationCursor, PersonId},
schema::{ schema::{
comment, comment,
@ -25,18 +25,17 @@ use lemmy_db_schema::{
community, community,
community_actions, community_actions,
image_details, image_details,
instance_actions,
local_user, local_user,
person, person,
person_actions, person_actions,
person_content_combined, person_content_combined,
post, post,
post_actions, post_actions,
post_tag,
tag,
}, },
source::combined::person_content::{person_content_combined_keys as key, PersonContentCombined}, source::combined::person_content::{person_content_combined_keys as key, PersonContentCombined},
traits::{InternalToCombinedView, PaginationCursorBuilder}, traits::{InternalToCombinedView, PaginationCursorBuilder},
utils::{functions::coalesce, get_conn, DbPool}, utils::{get_conn, DbPool},
PersonContentType, PersonContentType,
}; };
use lemmy_utils::error::{LemmyErrorType, LemmyResult}; use lemmy_utils::error::{LemmyErrorType, LemmyResult};
@ -93,6 +92,12 @@ impl PersonContentCombinedViewInternal {
.and(community_actions::person_id.nullable().eq(my_person_id)), .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( let post_actions_join = post_actions::table.on(
post_actions::post_id post_actions::post_id
.eq(post::id) .eq(post::id)
@ -123,6 +128,7 @@ impl PersonContentCombinedViewInternal {
.left_join(local_user_join) .left_join(local_user_join)
.left_join(creator_local_user_join) .left_join(creator_local_user_join)
.left_join(community_actions_join) .left_join(community_actions_join)
.left_join(instance_actions_join)
.left_join(post_actions_join) .left_join(post_actions_join)
.left_join(person_actions_join) .left_join(person_actions_join)
.left_join(comment_actions_join) .left_join(comment_actions_join)
@ -185,15 +191,6 @@ impl PersonContentCombinedQuery {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
let post_tags = post_tag::table
.inner_join(tag::table)
.select(diesel::dsl::sql::<diesel::sql_types::Json>(
"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, // Notes: since the post_id and comment_id are optional columns,
// many joins must use an OR condition. // many joins must use an OR condition.
// For example, the creator must be the person table joined to either: // 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) let mut query = PersonContentCombinedViewInternal::joins(my_person_id)
// The creator id filter // The creator id filter
.filter(item_creator.eq(self.creator_id)) .filter(item_creator.eq(self.creator_id))
.select(( .select(PersonContentCombinedViewInternal::as_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(),
))
.into_boxed(); .into_boxed();
if let Some(type_) = self.type_ { if let Some(type_) = self.type_ {
@ -292,34 +253,26 @@ impl InternalToCombinedView for PersonContentCombinedViewInternal {
post: v.post, post: v.post,
community: v.community, community: v.community,
creator: v.item_creator, creator: v.item_creator,
creator_banned_from_community: v.item_creator_banned_from_community, community_actions: v.community_actions,
creator_is_moderator: v.item_creator_is_moderator, 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_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, can_mod: v.can_mod,
})) }))
} else { } else {
Some(PersonContentCombinedView::Post(PostView { Some(PersonContentCombinedView::Post(PostView {
post: v.post, post: v.post,
community: v.community, community: v.community,
unread_comments: v.post_unread_comments,
creator: v.item_creator, 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, image_details: v.image_details,
banned_from_community: v.banned_from_community, community_actions: v.community_actions,
tags: v.post_tags, 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, can_mod: v.can_mod,
})) }))
} }

View file

@ -17,7 +17,6 @@ use diesel_async::RunQueryDsl;
use i_love_jesus::PaginatedQueryBuilder; use i_love_jesus::PaginatedQueryBuilder;
use lemmy_db_schema::{ use lemmy_db_schema::{
aliases::{creator_community_actions, creator_local_user}, aliases::{creator_community_actions, creator_local_user},
impls::{community::community_follower_select_subscribed_type, local_user::local_user_can_mod},
newtypes::{PaginationCursor, PersonId}, newtypes::{PaginationCursor, PersonId},
schema::{ schema::{
comment, comment,
@ -25,18 +24,17 @@ use lemmy_db_schema::{
community, community,
community_actions, community_actions,
image_details, image_details,
instance_actions,
local_user, local_user,
person, person,
person_actions, person_actions,
person_saved_combined, person_saved_combined,
post, post,
post_actions, post_actions,
post_tag,
tag,
}, },
source::combined::person_saved::{person_saved_combined_keys as key, PersonSavedCombined}, source::combined::person_saved::{person_saved_combined_keys as key, PersonSavedCombined},
traits::{InternalToCombinedView, PaginationCursorBuilder}, traits::{InternalToCombinedView, PaginationCursorBuilder},
utils::{functions::coalesce, get_conn, DbPool}, utils::{get_conn, DbPool},
PersonContentType, PersonContentType,
}; };
use lemmy_utils::error::{LemmyErrorType, LemmyResult}; use lemmy_utils::error::{LemmyErrorType, LemmyResult};
@ -134,6 +132,12 @@ impl PersonSavedCombinedViewInternal {
.and(community_actions::person_id.eq(my_person_id)), .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( let post_actions_join = post_actions::table.on(
post_actions::post_id post_actions::post_id
.eq(post::id) .eq(post::id)
@ -164,6 +168,7 @@ impl PersonSavedCombinedViewInternal {
.left_join(local_user_join) .left_join(local_user_join)
.left_join(creator_local_user_join) .left_join(creator_local_user_join)
.left_join(community_actions_join) .left_join(community_actions_join)
.left_join(instance_actions_join)
.left_join(post_actions_join) .left_join(post_actions_join)
.left_join(person_actions_join) .left_join(person_actions_join)
.left_join(comment_actions_join) .left_join(comment_actions_join)
@ -181,54 +186,9 @@ impl PersonSavedCombinedQuery {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
let post_tags = post_tag::table
.inner_join(tag::table)
.select(diesel::dsl::sql::<diesel::sql_types::Json>(
"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) let mut query = PersonSavedCombinedViewInternal::joins(my_person_id)
.filter(person_saved_combined::person_id.eq(my_person_id)) .filter(person_saved_combined::person_id.eq(my_person_id))
.select(( .select(PersonSavedCombinedViewInternal::as_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(),
))
.into_boxed(); .into_boxed();
if let Some(type_) = self.type_ { if let Some(type_) = self.type_ {
@ -280,34 +240,26 @@ impl InternalToCombinedView for PersonSavedCombinedViewInternal {
post: v.post, post: v.post,
community: v.community, community: v.community,
creator: v.item_creator, creator: v.item_creator,
creator_banned_from_community: v.item_creator_banned_from_community, community_actions: v.community_actions,
creator_is_moderator: v.item_creator_is_moderator, 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_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, can_mod: v.can_mod,
})) }))
} else { } else {
Some(PersonSavedCombinedView::Post(PostView { Some(PersonSavedCombinedView::Post(PostView {
post: v.post, post: v.post,
community: v.community, community: v.community,
unread_comments: v.post_unread_comments,
creator: v.item_creator, 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, image_details: v.image_details,
banned_from_community: v.banned_from_community, community_actions: v.community_actions,
tags: v.post_tags, 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, can_mod: v.can_mod,
})) }))
} }
@ -324,12 +276,12 @@ mod tests {
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
comment::{Comment, CommentInsertForm, CommentSaved, CommentSavedForm}, comment::{Comment, CommentActions, CommentInsertForm, CommentSavedForm},
community::{Community, CommunityInsertForm}, community::{Community, CommunityInsertForm},
instance::Instance, instance::Instance,
local_user::{LocalUser, LocalUserInsertForm}, local_user::{LocalUser, LocalUserInsertForm},
person::{Person, PersonInsertForm}, person::{Person, PersonInsertForm},
post::{Post, PostInsertForm, PostSaved, PostSavedForm}, post::{Post, PostActions, PostInsertForm, PostSavedForm},
}, },
traits::{Crud, Saveable}, traits::{Crud, Saveable},
utils::{build_db_pool_for_tests, DbPool}, utils::{build_db_pool_for_tests, DbPool},
@ -424,14 +376,14 @@ mod tests {
// Save a few things // Save a few things
let save_sara_comment_2 = let save_sara_comment_2 =
CommentSavedForm::new(data.sara_comment_2.id, data.timmy_view.person.id); CommentSavedForm::new(data.timmy_view.person.id, data.sara_comment_2.id);
CommentSaved::save(pool, &save_sara_comment_2).await?; CommentActions::save(pool, &save_sara_comment_2).await?;
let save_sara_comment = CommentSavedForm::new(data.sara_comment.id, data.timmy_view.person.id); let save_sara_comment = CommentSavedForm::new(data.timmy_view.person.id, data.sara_comment.id);
CommentSaved::save(pool, &save_sara_comment).await?; CommentActions::save(pool, &save_sara_comment).await?;
let post_save_form = PostSavedForm::new(data.timmy_post.id, data.timmy.id); 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() let timmy_saved = PersonSavedCombinedQuery::default()
.list(pool, &data.timmy_view) .list(pool, &data.timmy_view)
@ -459,8 +411,8 @@ mod tests {
} }
// Try unsaving 2 things // Try unsaving 2 things
CommentSaved::unsave(pool, &save_sara_comment).await?; CommentActions::unsave(pool, &save_sara_comment).await?;
PostSaved::unsave(pool, &post_save_form).await?; PostActions::unsave(pool, &post_save_form).await?;
let timmy_saved = PersonSavedCombinedQuery::default() let timmy_saved = PersonSavedCombinedQuery::default()
.list(pool, &data.timmy_view) .list(pool, &data.timmy_view)

View file

@ -22,7 +22,6 @@ use diesel_async::RunQueryDsl;
use i_love_jesus::PaginatedQueryBuilder; use i_love_jesus::PaginatedQueryBuilder;
use lemmy_db_schema::{ use lemmy_db_schema::{
aliases::{self, creator_community_actions}, aliases::{self, creator_community_actions},
impls::community::community_follower_select_subscribed_type,
newtypes::{CommunityId, PaginationCursor, PersonId, PostId}, newtypes::{CommunityId, PaginationCursor, PersonId, PostId},
schema::{ schema::{
comment, comment,
@ -43,7 +42,7 @@ use lemmy_db_schema::{
}, },
source::combined::report::{report_combined_keys as key, ReportCombined}, source::combined::report::{report_combined_keys as key, ReportCombined},
traits::{InternalToCombinedView, PaginationCursorBuilder}, traits::{InternalToCombinedView, PaginationCursorBuilder},
utils::{functions::coalesce, get_conn, DbPool, ReverseTimestampKey}, utils::{get_conn, DbPool, ReverseTimestampKey},
ReportType, ReportType,
}; };
use lemmy_utils::error::{LemmyErrorType, LemmyResult}; use lemmy_utils::error::{LemmyErrorType, LemmyResult};
@ -255,46 +254,7 @@ impl ReportCombinedQuery {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
let mut query = ReportCombinedViewInternal::joins(my_person_id) let mut query = ReportCombinedViewInternal::joins(my_person_id)
.select(( .select(ReportCombinedViewInternal::as_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(),
))
.into_boxed(); .into_boxed();
if let Some(community_id) = self.community_id { if let Some(community_id) = self.community_id {
@ -414,36 +374,24 @@ impl InternalToCombinedView for ReportCombinedViewInternal {
// Use for a short alias // Use for a short alias
let v = self; let v = self;
if let ( if let (Some(post_report), Some(post), Some(community), Some(post_creator)) = (
Some(post_report),
Some(post),
Some(community),
Some(unread_comments),
Some(post_creator),
) = (
v.post_report, v.post_report,
v.post.clone(), v.post.clone(),
v.community.clone(), v.community.clone(),
v.post_unread_comments,
v.item_creator.clone(), v.item_creator.clone(),
) { ) {
Some(ReportCombinedView::Post(PostReportView { Some(ReportCombinedView::Post(PostReportView {
post_report, post_report,
post, post,
community, community,
unread_comments,
creator: v.report_creator,
post_creator, post_creator,
creator_banned_from_community: v.item_creator_banned_from_community, creator: v.report_creator,
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,
resolver: v.resolver, 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 ( } else if let (
Some(comment_report), Some(comment_report),
@ -465,14 +413,12 @@ impl InternalToCombinedView for ReportCombinedViewInternal {
community, community,
creator: v.report_creator, creator: v.report_creator,
comment_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, 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 ( } else if let (
Some(private_message_report), Some(private_message_report),
@ -494,7 +440,6 @@ impl InternalToCombinedView for ReportCombinedViewInternal {
community_report, community_report,
community, community,
creator: v.report_creator, creator: v.report_creator,
subscribed: v.subscribed,
resolver: v.resolver, resolver: v.resolver,
})) }))
} else { } else {
@ -527,7 +472,7 @@ mod tests {
source::{ source::{
comment::{Comment, CommentInsertForm}, comment::{Comment, CommentInsertForm},
comment_report::{CommentReport, CommentReportForm}, comment_report::{CommentReport, CommentReportForm},
community::{Community, CommunityInsertForm, CommunityModerator, CommunityModeratorForm}, community::{Community, CommunityActions, CommunityInsertForm, CommunityModeratorForm},
community_report::{CommunityReport, CommunityReportForm}, community_report::{CommunityReport, CommunityReportForm},
instance::Instance, instance::Instance,
local_user::{LocalUser, LocalUserInsertForm}, local_user::{LocalUser, LocalUserInsertForm},
@ -595,11 +540,9 @@ mod tests {
let inserted_community = Community::create(pool, &community_form).await?; let inserted_community = Community::create(pool, &community_form).await?;
// Make timmy a mod // Make timmy a mod
let timmy_moderator_form = CommunityModeratorForm { let timmy_moderator_form =
community_id: inserted_community.id, CommunityModeratorForm::new(inserted_community.id, inserted_timmy.id);
person_id: inserted_timmy.id, CommunityActions::join(pool, &timmy_moderator_form).await?;
};
CommunityModerator::join(pool, &timmy_moderator_form).await?;
let post_form = PostInsertForm::new( let post_form = PostInsertForm::new(
"A test post crv".into(), "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.community.id, data.community.id);
assert_eq!(read_jessica_report_view.creator.id, data.jessica.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.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!(read_jessica_report_view.resolver, None);
assert_eq!(agg_1.report_count, 1); assert_eq!(agg_1.report_count, 1);
assert_eq!(agg_1.unresolved_report_count, 1); assert_eq!(agg_1.unresolved_report_count, 1);

View file

@ -8,7 +8,7 @@ use crate::{
SearchCombinedView, SearchCombinedView,
SearchCombinedViewInternal, SearchCombinedViewInternal,
}, },
utils::{filter_is_subscribed, filter_not_hidden_or_is_subscribed}, utils::{filter_is_subscribed, filter_not_unlisted_or_is_subscribed},
}; };
use diesel::{ use diesel::{
dsl::not, dsl::not,
@ -24,7 +24,6 @@ use diesel_async::RunQueryDsl;
use i_love_jesus::PaginatedQueryBuilder; use i_love_jesus::PaginatedQueryBuilder;
use lemmy_db_schema::{ use lemmy_db_schema::{
aliases::{creator_community_actions, creator_local_user}, aliases::{creator_community_actions, creator_local_user},
impls::{community::community_follower_select_subscribed_type, local_user::local_user_can_mod},
newtypes::{CommunityId, PaginationCursor, PersonId}, newtypes::{CommunityId, PaginationCursor, PersonId},
schema::{ schema::{
comment, comment,
@ -32,26 +31,17 @@ use lemmy_db_schema::{
community, community,
community_actions, community_actions,
image_details, image_details,
instance_actions,
local_user, local_user,
person, person,
person_actions, person_actions,
post, post,
post_actions, post_actions,
post_tag,
search_combined, search_combined,
tag,
}, },
source::combined::search::{search_combined_keys as key, SearchCombined}, source::combined::search::{search_combined_keys as key, SearchCombined},
traits::{InternalToCombinedView, PaginationCursorBuilder}, traits::{InternalToCombinedView, PaginationCursorBuilder},
utils::{ utils::{fuzzy_search, get_conn, now, seconds_to_pg_interval, DbPool, ReverseTimestampKey},
functions::coalesce,
fuzzy_search,
get_conn,
now,
seconds_to_pg_interval,
DbPool,
ReverseTimestampKey,
},
ListingType, ListingType,
SearchSortType, SearchSortType,
SearchType, SearchType,
@ -128,6 +118,12 @@ impl SearchCombinedViewInternal {
.and(community_actions::person_id.nullable().eq(my_person_id)), .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( let post_actions_join = post_actions::table.on(
post_actions::post_id post_actions::post_id
.eq(post::id) .eq(post::id)
@ -158,6 +154,7 @@ impl SearchCombinedViewInternal {
.left_join(local_user_join) .left_join(local_user_join)
.left_join(creator_local_user_join) .left_join(creator_local_user_join)
.left_join(community_actions_join) .left_join(community_actions_join)
.left_join(instance_actions_join)
.left_join(post_actions_join) .left_join(post_actions_join)
.left_join(person_actions_join) .left_join(person_actions_join)
.left_join(comment_actions_join) .left_join(comment_actions_join)
@ -230,53 +227,8 @@ impl SearchCombinedQuery {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
let post_tags = post_tag::table
.inner_join(tag::table)
.select(diesel::dsl::sql::<diesel::sql_types::Json>(
"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) let mut query = SearchCombinedViewInternal::joins(my_person_id)
.select(( .select(SearchCombinedViewInternal::as_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(),
))
.into_boxed(); .into_boxed();
// The filters // The filters
@ -359,13 +311,14 @@ impl SearchCombinedQuery {
query = query.filter( query = query.filter(
community::local community::local
.eq(true) .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)), .or(search_combined::person_id.is_not_null().and(person::local)),
); );
} }
ListingType::All => { ListingType::All => {
query = query query = query.filter(
.filter(filter_not_hidden_or_is_subscribed().or(search_combined::person_id.is_not_null())) filter_not_unlisted_or_is_subscribed().or(search_combined::person_id.is_not_null()),
)
} }
ListingType::ModeratorView => { ListingType::ModeratorView => {
query = query.filter(community_actions::became_moderator.is_not_null()); query = query.filter(community_actions::became_moderator.is_not_null());
@ -425,47 +378,35 @@ impl InternalToCombinedView for SearchCombinedViewInternal {
post, post,
community, community,
creator, creator,
creator_banned_from_community: v.item_creator_banned_from_community, community_actions: v.community_actions,
creator_is_moderator: v.item_creator_is_moderator, 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_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, can_mod: v.can_mod,
})) }))
} else if let (Some(post), Some(creator), Some(community), Some(unread_comments)) = ( } else if let (Some(post), Some(creator), Some(community)) =
v.post, (v.post, v.item_creator.clone(), v.community.clone())
v.item_creator.clone(), {
v.community.clone(),
v.post_unread_comments,
) {
Some(SearchCombinedView::Post(PostView { Some(SearchCombinedView::Post(PostView {
post, post,
community, community,
unread_comments,
creator, 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_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, image_details: v.image_details,
banned_from_community: v.banned_from_community, community_actions: v.community_actions,
tags: v.post_tags, 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, can_mod: v.can_mod,
})) }))
} else if let Some(community) = v.community { } else if let Some(community) = v.community {
Some(SearchCombinedView::Community(CommunityView { Some(SearchCombinedView::Community(CommunityView {
community, community,
subscribed: v.subscribed, community_actions: v.community_actions,
blocked: v.community_blocked, instance_actions: v.instance_actions,
banned_from_community: v.banned_from_community,
can_mod: v.can_mod, can_mod: v.can_mod,
})) }))
} else if let Some(person) = v.item_creator { } else if let Some(person) = v.item_creator {
@ -490,12 +431,12 @@ mod tests {
use lemmy_db_schema::{ use lemmy_db_schema::{
assert_length, assert_length,
source::{ source::{
comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm, CommentUpdateForm}, comment::{Comment, CommentActions, CommentInsertForm, CommentLikeForm, CommentUpdateForm},
community::{Community, CommunityInsertForm}, community::{Community, CommunityInsertForm},
instance::Instance, instance::Instance,
local_user::{LocalUser, LocalUserInsertForm}, local_user::{LocalUser, LocalUserInsertForm},
person::{Person, PersonInsertForm}, person::{Person, PersonInsertForm},
post::{Post, PostInsertForm, PostLike, PostLikeForm, PostUpdateForm}, post::{Post, PostActions, PostInsertForm, PostLikeForm, PostUpdateForm},
}, },
traits::{Crud, Likeable}, traits::{Crud, Likeable},
utils::{build_db_pool_for_tests, DbPool}, utils::{build_db_pool_for_tests, DbPool},
@ -583,34 +524,22 @@ mod tests {
// Timmy likes and dislikes a few things // Timmy likes and dislikes a few things
let timmy_like_post_form = PostLikeForm::new(timmy_post.id, timmy.id, 1); 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); 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); 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 { let timmy_like_comment_form = CommentLikeForm::new(timmy.id, timmy_comment.id, 1);
person_id: timmy.id, CommentActions::like(pool, &timmy_like_comment_form).await?;
comment_id: timmy_comment.id,
score: 1,
};
CommentLike::like(pool, &timmy_like_comment_form).await?;
let timmy_like_sara_comment_form = CommentLikeForm { let timmy_like_sara_comment_form = CommentLikeForm::new(timmy.id, sara_comment.id, 1);
person_id: timmy.id, CommentActions::like(pool, &timmy_like_sara_comment_form).await?;
comment_id: sara_comment.id,
score: 1,
};
CommentLike::like(pool, &timmy_like_sara_comment_form).await?;
let timmy_dislike_sara_comment_form = CommentLikeForm { let timmy_dislike_sara_comment_form = CommentLikeForm::new(timmy.id, sara_comment_2.id, -1);
person_id: timmy.id, CommentActions::like(pool, &timmy_dislike_sara_comment_form).await?;
comment_id: sara_comment_2.id,
score: -1,
};
CommentLike::like(pool, &timmy_dislike_sara_comment_form).await?;
Ok(Data { Ok(Data {
instance, instance,

View file

@ -117,29 +117,18 @@ impl CommentView {
); );
} }
let mut res = query.first::<Self>(conn).await?; query.first::<Self>(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)
} }
pub fn map_to_slim(self) -> CommentSlimView { pub fn map_to_slim(self) -> CommentSlimView {
CommentSlimView { CommentSlimView {
comment: self.comment, comment: self.comment,
creator: self.creator, creator: self.creator,
creator_banned_from_community: self.creator_banned_from_community, comment_actions: self.comment_actions,
banned_from_community: self.banned_from_community, creator_community_actions: self.creator_community_actions,
creator_is_moderator: self.creator_is_moderator, person_actions: self.person_actions,
instance_actions: self.instance_actions,
creator_is_admin: self.creator_is_admin, 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, can_mod: self.can_mod,
} }
} }
@ -328,34 +317,30 @@ mod tests {
use lemmy_db_schema::{ use lemmy_db_schema::{
assert_length, assert_length,
impls::actor_language::UNDETERMINED_ID, impls::actor_language::UNDETERMINED_ID,
newtypes::{CommentId, LanguageId}, newtypes::CommentId,
source::{ source::{
actor_language::LocalUserLanguage, actor_language::LocalUserLanguage,
comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm, CommentUpdateForm}, comment::{Comment, CommentActions, CommentInsertForm, CommentLikeForm, CommentUpdateForm},
community::{ community::{
Community, Community,
CommunityFollower, CommunityActions,
CommunityFollowerForm, CommunityFollowerForm,
CommunityFollowerState, CommunityFollowerState,
CommunityInsertForm, CommunityInsertForm,
CommunityModerator,
CommunityModeratorForm, CommunityModeratorForm,
CommunityPersonBan,
CommunityPersonBanForm, CommunityPersonBanForm,
CommunityUpdateForm, CommunityUpdateForm,
}, },
instance::Instance, instance::Instance,
language::Language, language::Language,
local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm}, local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm},
person::{Person, PersonInsertForm}, person::{Person, PersonActions, PersonBlockForm, PersonInsertForm},
person_block::{PersonBlock, PersonBlockForm},
post::{Post, PostInsertForm, PostUpdateForm}, post::{Post, PostInsertForm, PostUpdateForm},
site::{Site, SiteInsertForm}, site::{Site, SiteInsertForm},
}, },
traits::{Bannable, Blockable, Crud, Followable, Joinable, Likeable}, traits::{Bannable, Blockable, Crud, Followable, Joinable, Likeable},
utils::{build_db_pool_for_tests, RANK_DEFAULT}, utils::build_db_pool_for_tests,
CommunityVisibility, CommunityVisibility,
SubscribedType,
}; };
use lemmy_utils::error::LemmyResult; use lemmy_utils::error::LemmyResult;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
@ -366,7 +351,7 @@ mod tests {
inserted_comment_0: Comment, inserted_comment_0: Comment,
inserted_comment_1: Comment, inserted_comment_1: Comment,
inserted_comment_2: Comment, inserted_comment_2: Comment,
inserted_comment_5: Comment, _inserted_comment_5: Comment,
inserted_post: Post, inserted_post: Post,
timmy_local_user_view: LocalUserView, timmy_local_user_view: LocalUserView,
inserted_sara_person: Person, inserted_sara_person: Person,
@ -474,30 +459,26 @@ mod tests {
inserted_post.id, inserted_post.id,
"Comment 5".into(), "Comment 5".into(),
); );
let inserted_comment_5 = let _inserted_comment_5 =
Comment::create(pool, &comment_form_5, Some(&inserted_comment_4.path)).await?; Comment::create(pool, &comment_form_5, Some(&inserted_comment_4.path)).await?;
let timmy_blocks_sara_form = PersonBlockForm { let timmy_blocks_sara_form =
person_id: inserted_timmy_person.id, PersonBlockForm::new(inserted_timmy_person.id, inserted_sara_person.id);
target_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 { let comment_like_form =
person_id: inserted_timmy_person.id, CommentLikeForm::new(inserted_timmy_person.id, inserted_comment_0.id, 1);
target_id: inserted_sara_person.id,
published: inserted_block.published,
};
assert_eq!(expected_block, inserted_block);
let comment_like_form = CommentLikeForm { CommentActions::like(pool, &comment_like_form).await?;
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?;
let timmy_local_user_view = LocalUserView { let timmy_local_user_view = LocalUserView {
local_user: inserted_timmy_local_user.clone(), local_user: inserted_timmy_local_user.clone(),
@ -510,7 +491,7 @@ mod tests {
inserted_comment_0, inserted_comment_0,
inserted_comment_1, inserted_comment_1,
inserted_comment_2, inserted_comment_2,
inserted_comment_5, _inserted_comment_5,
inserted_post, inserted_post,
timmy_local_user_view, timmy_local_user_view,
inserted_sara_person, inserted_sara_person,
@ -526,12 +507,6 @@ mod tests {
let pool = &mut pool.into(); let pool = &mut pool.into();
let data = init_data(pool).await?; 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 { let read_comment_views_no_person = CommentQuery {
sort: (Some(CommentSortType::Old)), sort: (Some(CommentSortType::Old)),
post_id: (Some(data.inserted_post.id)), post_id: (Some(data.inserted_post.id)),
@ -540,10 +515,8 @@ mod tests {
.list(&data.site, pool) .list(&data.site, pool)
.await?; .await?;
assert_eq!( assert!(read_comment_views_no_person[0].comment_actions.is_none());
Some(&expected_comment_view_no_person), assert!(!read_comment_views_no_person[0].can_mod);
read_comment_views_no_person.first()
);
let read_comment_views_with_person = CommentQuery { let read_comment_views_with_person = CommentQuery {
sort: (Some(CommentSortType::Old)), sort: (Some(CommentSortType::Old)),
@ -554,10 +527,11 @@ mod tests {
.list(&data.site, pool) .list(&data.site, pool)
.await?; .await?;
assert_eq!( assert!(read_comment_views_with_person[0]
expected_comment_view_with_person, .comment_actions
read_comment_views_with_person[0] .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 // Make sure its 1, not showing the blocked comment
assert_length!(5, read_comment_views_with_person); assert_length!(5, read_comment_views_with_person);
@ -570,7 +544,9 @@ mod tests {
.await?; .await?;
// Make sure block set the creator blocked // 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 cleanup(data, pool).await
} }
@ -583,19 +559,19 @@ mod tests {
let data = init_data(pool).await?; let data = init_data(pool).await?;
// Unblock sara first // Unblock sara first
let timmy_unblocks_sara_form = PersonBlockForm { let timmy_unblocks_sara_form = PersonBlockForm::new(
person_id: data.timmy_local_user_view.person.id, data.timmy_local_user_view.person.id,
target_id: data.inserted_sara_person.id, data.inserted_sara_person.id,
}; );
PersonBlock::unblock(pool, &timmy_unblocks_sara_form).await?; PersonActions::unblock(pool, &timmy_unblocks_sara_form).await?;
// Like a new comment // Like a new comment
let comment_like_form = CommentLikeForm { let comment_like_form = CommentLikeForm::new(
comment_id: data.inserted_comment_1.id, data.timmy_local_user_view.person.id,
person_id: data.timmy_local_user_view.person.id, data.inserted_comment_1.id,
score: 1, 1,
}; );
CommentLike::like(pool, &comment_like_form).await?; CommentActions::like(pool, &comment_like_form).await?;
let read_liked_comment_views = CommentQuery { let read_liked_comment_views = CommentQuery {
local_user: Some(&data.timmy_local_user_view.local_user), local_user: Some(&data.timmy_local_user_view.local_user),
@ -672,10 +648,6 @@ mod tests {
.await?; .await?;
// Make sure a depth limited one only has the top comment // 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); assert_length!(1, read_comment_views_top_max_depth);
let child_path = data.inserted_comment_1.path.clone(); let child_path = data.inserted_comment_1.path.clone();
@ -793,11 +765,8 @@ mod tests {
// Make one of the inserted persons a moderator // Make one of the inserted persons a moderator
let person_id = data.inserted_sara_person.id; let person_id = data.inserted_sara_person.id;
let community_id = data.inserted_community.id; let community_id = data.inserted_community.id;
let form = CommunityModeratorForm { let form = CommunityModeratorForm::new(community_id, person_id);
community_id, CommunityActions::join(pool, &form).await?;
person_id,
};
CommunityModerator::join(pool, &form).await?;
// Make sure that they come back as a mod in the list // Make sure that they come back as a mod in the list
let comments = CommentQuery { let comments = CommentQuery {
@ -808,8 +777,12 @@ mod tests {
.await?; .await?;
assert_eq!(comments[1].creator.name, "sara"); assert_eq!(comments[1].creator.name, "sara");
assert!(comments[1].creator_is_moderator); assert!(comments[1]
assert!(!comments[0].creator_is_moderator); .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 cleanup(data, pool).await
} }
@ -840,7 +813,7 @@ mod tests {
} }
async fn cleanup(data: Data, pool: &mut DbPool<'_>) -> LemmyResult<()> { async fn cleanup(data: Data, pool: &mut DbPool<'_>) -> LemmyResult<()> {
CommentLike::remove( CommentActions::remove_like(
pool, pool,
data.timmy_local_user_view.person.id, data.timmy_local_user_view.person.id,
data.inserted_comment_0.id, data.inserted_comment_0.id,
@ -859,147 +832,6 @@ mod tests {
Ok(()) 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] #[tokio::test]
#[serial] #[serial]
async fn local_only_instance() -> LemmyResult<()> { async fn local_only_instance() -> LemmyResult<()> {
@ -1065,13 +897,12 @@ mod tests {
) )
.await?; .await?;
CommunityPersonBan::ban( CommunityActions::ban(
pool, pool,
&CommunityPersonBanForm { &CommunityPersonBanForm::new(
community_id: data.inserted_community.id, data.inserted_community.id,
person_id: inserted_banned_from_comm_person.id, inserted_banned_from_comm_person.id,
expires: None, ),
},
) )
.await?; .await?;
@ -1082,7 +913,9 @@ mod tests {
) )
.await?; .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?; Person::delete(pool, inserted_banned_from_comm_person.id).await?;
cleanup(data, pool).await cleanup(data, pool).await
@ -1102,7 +935,7 @@ mod tests {
) )
.await?; .await?;
assert!(!comment_view.banned_from_community); assert!(comment_view.community_actions.is_none());
cleanup(data, pool).await cleanup(data, pool).await
} }
@ -1198,15 +1031,13 @@ mod tests {
data.timmy_local_user_view.local_user.admin = false; data.timmy_local_user_view.local_user.admin = false;
// User can view after following // User can view after following
CommunityFollower::follow( CommunityActions::follow(
pool, pool,
&CommunityFollowerForm { &CommunityFollowerForm::new(
state: Some(CommunityFollowerState::Accepted), data.inserted_community.id,
..CommunityFollowerForm::new( data.timmy_local_user_view.person.id,
data.inserted_community.id, CommunityFollowerState::Accepted,
data.timmy_local_user_view.person.id, ),
)
},
) )
.await?; .await?;
let read_comment_listing = CommentQuery { let read_comment_listing = CommentQuery {

View file

@ -7,12 +7,12 @@ use diesel::{
BoolExpressionMethods, BoolExpressionMethods,
ExpressionMethods, ExpressionMethods,
JoinOnDsl, JoinOnDsl,
NullableExpressionMethods,
QueryDsl, QueryDsl,
SelectableHelper, SelectableHelper,
}; };
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
use lemmy_db_schema::{ use lemmy_db_schema::{
impls::community::community_follower_select_subscribed_type,
newtypes::{CommunityId, DbUrl, InstanceId, PersonId}, newtypes::{CommunityId, DbUrl, InstanceId, PersonId},
schema::{community, community_actions, person}, schema::{community, community_actions, person},
source::{ source::{
@ -21,7 +21,6 @@ use lemmy_db_schema::{
}, },
utils::{get_conn, limit_and_offset, DbPool}, utils::{get_conn, limit_and_offset, DbPool},
CommunityVisibility, CommunityVisibility,
SubscribedType,
}; };
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
@ -151,7 +150,7 @@ impl CommunityFollowerView {
person::all_columns, person::all_columns,
community::all_columns, community::all_columns,
is_new_instance, is_new_instance,
community_follower_select_subscribed_type(), community_actions::follow_state.nullable(),
)) ))
.into_boxed(); .into_boxed();
if all_communities { if all_communities {
@ -168,17 +167,17 @@ impl CommunityFollowerView {
.order_by(community_actions::followed.asc()) .order_by(community_actions::followed.asc())
.limit(limit) .limit(limit)
.offset(offset) .offset(offset)
.load::<(Person, Community, bool, SubscribedType)>(conn) .load::<(Person, Community, bool, Option<CommunityFollowerState>)>(conn)
.await?; .await?;
Ok( Ok(
res res
.into_iter() .into_iter()
.map( .map(
|(person, community, is_new_instance, subscribed)| PendingFollow { |(person, community, is_new_instance, follow_state)| PendingFollow {
person, person,
community, community,
is_new_instance, is_new_instance,
subscribed, follow_state,
}, },
) )
.collect(), .collect(),
@ -259,7 +258,7 @@ mod tests {
use super::*; use super::*;
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
community::{CommunityFollower, CommunityFollowerForm, CommunityInsertForm}, community::{CommunityActions, CommunityFollowerForm, CommunityInsertForm},
instance::Instance, instance::Instance,
person::PersonInsertForm, person::PersonInsertForm,
}, },
@ -300,11 +299,12 @@ mod tests {
assert!(has_followers.is_err()); assert!(has_followers.is_err());
// insert unapproved follower // insert unapproved follower
let mut follower_form = CommunityFollowerForm { let mut follower_form = CommunityFollowerForm::new(
state: Some(CommunityFollowerState::ApprovalRequired), community.id,
..CommunityFollowerForm::new(community.id, person.id) person.id,
}; CommunityFollowerState::ApprovalRequired,
CommunityFollower::follow(pool, &follower_form).await?; );
CommunityActions::follow(pool, &follower_form).await?;
// still returns error // still returns error
let has_followers = CommunityFollowerView::check_has_followers_from_instance( let has_followers = CommunityFollowerView::check_has_followers_from_instance(
@ -316,8 +316,8 @@ mod tests {
assert!(has_followers.is_err()); assert!(has_followers.is_err());
// mark follower as accepted // mark follower as accepted
follower_form.state = Some(CommunityFollowerState::Accepted); follower_form.follow_state = CommunityFollowerState::Accepted;
CommunityFollower::follow(pool, &follower_form).await?; CommunityActions::follow(pool, &follower_form).await?;
// now returns ok // now returns ok
let has_followers = CommunityFollowerView::check_has_followers_from_instance( let has_followers = CommunityFollowerView::check_has_followers_from_instance(

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
structs::{CommunityModeratorView, CommunitySortType, CommunityView, PersonView}, 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::{ use diesel::{
result::Error, result::Error,
@ -131,16 +131,16 @@ impl CommunityQuery<'_> {
if !o.is_mod_or_admin { if !o.is_mod_or_admin {
query = query query = query
.filter(Community::hide_removed_and_deleted()) .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 { if let Some(listing_type) = o.listing_type {
query = match 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::Subscribed => query.filter(filter_is_subscribed()),
ListingType::Local => query ListingType::Local => query
.filter(community::local.eq(true)) .filter(community::local.eq(true))
.filter(filter_not_hidden_or_is_subscribed()), .filter(filter_not_unlisted_or_is_subscribed()),
ListingType::ModeratorView => { ListingType::ModeratorView => {
query.filter(community_actions::became_moderator.is_not_null()) query.filter(community_actions::became_moderator.is_not_null())
} }
@ -199,11 +199,10 @@ mod tests {
source::{ source::{
community::{ community::{
Community, Community,
CommunityFollower, CommunityActions,
CommunityFollowerForm, CommunityFollowerForm,
CommunityFollowerState, CommunityFollowerState,
CommunityInsertForm, CommunityInsertForm,
CommunityModerator,
CommunityModeratorForm, CommunityModeratorForm,
CommunityUpdateForm, CommunityUpdateForm,
}, },
@ -215,7 +214,6 @@ mod tests {
traits::{Crud, Followable, Joinable}, traits::{Crud, Followable, Joinable},
utils::{build_db_pool_for_tests, DbPool}, utils::{build_db_pool_for_tests, DbPool},
CommunityVisibility, CommunityVisibility,
SubscribedType,
}; };
use lemmy_utils::error::{LemmyErrorType, LemmyResult}; use lemmy_utils::error::{LemmyErrorType, LemmyResult};
use serial_test::serial; use serial_test::serial;
@ -312,28 +310,31 @@ mod tests {
#[tokio::test] #[tokio::test]
#[serial] #[serial]
async fn subscribe_state() -> LemmyResult<()> { async fn follow_state() -> LemmyResult<()> {
let pool = &build_db_pool_for_tests(); let pool = &build_db_pool_for_tests();
let pool = &mut pool.into(); let pool = &mut pool.into();
let data = init_data(pool).await?; let data = init_data(pool).await?;
let community = &data.communities[0]; let community = &data.communities[0];
let unauthenticated = CommunityView::read(pool, community.id, None, false).await?; let unauthenticated = CommunityView::read(pool, community.id, None, false).await?;
assert_eq!(SubscribedType::NotSubscribed, unauthenticated.subscribed); assert!(unauthenticated.community_actions.is_none());
let authenticated = let authenticated =
CommunityView::read(pool, community.id, Some(&data.local_user), false).await?; 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 { let form = CommunityFollowerForm::new(
state: Some(CommunityFollowerState::Pending), community.id,
..CommunityFollowerForm::new(community.id, data.local_user.person_id) data.local_user.person_id,
}; CommunityFollowerState::Pending,
CommunityFollower::follow(pool, &form).await?; );
CommunityActions::follow(pool, &form).await?;
let with_pending_follow = let with_pending_follow =
CommunityView::read(pool, community.id, Some(&data.local_user), false).await?; 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 // mark community private and set follow as approval required
Community::update( Community::update(
@ -345,27 +346,30 @@ mod tests {
}, },
) )
.await?; .await?;
let form = CommunityFollowerForm { let form = CommunityFollowerForm::new(
state: Some(CommunityFollowerState::ApprovalRequired), community.id,
..CommunityFollowerForm::new(community.id, data.local_user.person_id) data.local_user.person_id,
}; CommunityFollowerState::ApprovalRequired,
CommunityFollower::follow(pool, &form).await?; );
CommunityActions::follow(pool, &form).await?;
let with_approval_required_follow = let with_approval_required_follow =
CommunityView::read(pool, community.id, Some(&data.local_user), false).await?; CommunityView::read(pool, community.id, Some(&data.local_user), false).await?;
assert_eq!( assert!(with_approval_required_follow
SubscribedType::ApprovalRequired, .community_actions
with_approval_required_follow.subscribed .is_some_and(|x| x.follow_state == Some(CommunityFollowerState::ApprovalRequired)));
);
let form = CommunityFollowerForm { let form = CommunityFollowerForm::new(
state: Some(CommunityFollowerState::Accepted), community.id,
..CommunityFollowerForm::new(community.id, data.local_user.person_id) data.local_user.person_id,
}; CommunityFollowerState::Accepted,
CommunityFollower::follow(pool, &form).await?; );
CommunityActions::follow(pool, &form).await?;
let with_accepted_follow = let with_accepted_follow =
CommunityView::read(pool, community.id, Some(&data.local_user), false).await?; 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 cleanup(data, pool).await
} }
@ -463,17 +467,11 @@ mod tests {
let person_id = data.local_user.person_id; let person_id = data.local_user.person_id;
// Now join the mod team of test community 1 and 2 // Now join the mod team of test community 1 and 2
let mod_form_1 = CommunityModeratorForm { let mod_form_1 = CommunityModeratorForm::new(data.communities[0].id, person_id);
community_id: data.communities[0].id, CommunityActions::join(pool, &mod_form_1).await?;
person_id,
};
CommunityModerator::join(pool, &mod_form_1).await?;
let mod_form_2 = CommunityModeratorForm { let mod_form_2 = CommunityModeratorForm::new(data.communities[1].id, person_id);
community_id: data.communities[1].id, CommunityActions::join(pool, &mod_form_2).await?;
person_id,
};
CommunityModerator::join(pool, &mod_form_2).await?;
let mod_query = CommunityQuery { let mod_query = CommunityQuery {
local_user: Some(&data.local_user), local_user: Some(&data.local_user),

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
structs::{PostPaginationCursor, PostView}, 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::{ use diesel::{
debug_query, debug_query,
@ -15,15 +15,13 @@ use diesel::{
OptionalExtension, OptionalExtension,
PgTextExpressionMethods, PgTextExpressionMethods,
QueryDsl, QueryDsl,
SelectableHelper,
TextExpressionMethods, TextExpressionMethods,
}; };
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
use lemmy_db_schema::{ use lemmy_db_schema::{
aliases::{creator_community_actions, creator_local_user}, aliases::creator_community_actions,
impls::{ impls::local_user::LocalUserOptionHelper,
community::community_follower_select_subscribed_type,
local_user::{local_user_can_mod, LocalUserOptionHelper},
},
newtypes::{CommunityId, PersonId, PostId}, newtypes::{CommunityId, PersonId, PostId},
schema::{ schema::{
community, community,
@ -36,8 +34,6 @@ use lemmy_db_schema::{
person_actions, person_actions,
post, post,
post_actions, post_actions,
post_tag,
tag,
}, },
source::{ source::{
community::CommunityFollowerState, community::CommunityFollowerState,
@ -47,7 +43,6 @@ use lemmy_db_schema::{
}, },
traits::Crud, traits::Crud,
utils::{ utils::{
functions::coalesce,
fuzzy_search, fuzzy_search,
get_conn, get_conn,
limit_and_offset, limit_and_offset,
@ -124,17 +119,6 @@ impl PostView {
.left_join(local_user_join) .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( pub async fn read(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
post_id: PostId, post_id: PostId,
@ -144,45 +128,9 @@ impl PostView {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
let my_person_id = my_local_user.person_id(); let my_person_id = my_local_user.person_id();
let post_tags = post_tag::table
.inner_join(tag::table)
.select(diesel::dsl::sql::<diesel::sql_types::Json>(
"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) let mut query = Self::joins(my_person_id)
.filter(post::id.eq(post_id)) .filter(post::id.eq(post_id))
.select(( .select(Self::as_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(),
))
.into_boxed(); .into_boxed();
// Hide deleted and removed for non-admins or mods // 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_person_id = o.local_user.person_id();
let my_local_user_id = o.local_user.local_user_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::<diesel::sql_types::Json>(
"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) let mut query = PostView::joins(my_person_id)
.select(( .select(PostView::as_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(),
))
.into_boxed(); .into_boxed();
// hide posts from deleted communities // hide posts from deleted communities
@ -465,9 +377,9 @@ impl<'a> PostQuery<'a> {
ListingType::Local => { ListingType::Local => {
query = query query = query
.filter(community::local.eq(true)) .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 => { ListingType::ModeratorView => {
query = query.filter(community_actions::became_moderator.is_not_null()); query = query.filter(community_actions::became_moderator.is_not_null());
} }
@ -655,7 +567,7 @@ impl<'a> PostQuery<'a> {
mod tests { mod tests {
use crate::{ use crate::{
post::post_view::{PaginationCursorData, PostQuery, PostView}, post::post_view::{PaginationCursorData, PostQuery, PostView},
structs::{LocalUserView, PostTags}, structs::LocalUserView,
}; };
use chrono::Utc; use chrono::Utc;
use diesel_async::SimpleAsyncConnection; use diesel_async::SimpleAsyncConnection;
@ -667,41 +579,35 @@ mod tests {
comment::{Comment, CommentInsertForm}, comment::{Comment, CommentInsertForm},
community::{ community::{
Community, Community,
CommunityFollower, CommunityActions,
CommunityBlockForm,
CommunityFollowerForm, CommunityFollowerForm,
CommunityFollowerState, CommunityFollowerState,
CommunityInsertForm, CommunityInsertForm,
CommunityModerator,
CommunityModeratorForm, CommunityModeratorForm,
CommunityPersonBan,
CommunityPersonBanForm, CommunityPersonBanForm,
CommunityUpdateForm, CommunityUpdateForm,
}, },
community_block::{CommunityBlock, CommunityBlockForm}, instance::{Instance, InstanceActions, InstanceBlockForm},
instance::Instance,
instance_block::{InstanceBlock, InstanceBlockForm},
language::Language, language::Language,
local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm}, local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm},
person::{Person, PersonInsertForm}, person::{Person, PersonActions, PersonBlockForm, PersonInsertForm},
person_block::{PersonBlock, PersonBlockForm},
post::{ post::{
Post, Post,
PostHide, PostActions,
PostHideForm,
PostInsertForm, PostInsertForm,
PostLike,
PostLikeForm, PostLikeForm,
PostRead,
PostReadForm, PostReadForm,
PostUpdateForm, PostUpdateForm,
}, },
site::Site, site::Site,
tag::{PostTagInsertForm, Tag, TagInsertForm}, tag::{PostTagInsertForm, Tag, TagInsertForm},
}, },
traits::{Bannable, Blockable, Crud, Followable, Joinable, Likeable}, traits::{Bannable, Blockable, Crud, Followable, Hideable, Joinable, Likeable, Readable},
utils::{build_db_pool, get_conn, uplete, ActualDbPool, DbPool, RANK_DEFAULT}, utils::{build_db_pool, get_conn, uplete, ActualDbPool, DbPool},
CommunityVisibility, CommunityVisibility,
PostSortType, PostSortType,
SubscribedType,
}; };
use lemmy_utils::error::{LemmyErrorType, LemmyResult}; use lemmy_utils::error::{LemmyErrorType, LemmyResult};
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
@ -730,8 +636,8 @@ mod tests {
post: Post, post: Post,
bot_post: Post, bot_post: Post,
post_with_tags: Post, post_with_tags: Post,
tag_1: Tag, _tag_1: Tag,
tag_2: Tag, _tag_2: Tag,
site: Site, site: Site,
} }
@ -805,12 +711,8 @@ mod tests {
Post::create(pool, &post_from_blocked_person).await?; Post::create(pool, &post_from_blocked_person).await?;
// block that person // block that person
let person_block = PersonBlockForm { let person_block = PersonBlockForm::new(inserted_tegan_person.id, inserted_john_person.id);
person_id: inserted_tegan_person.id, PersonActions::block(pool, &person_block).await?;
target_id: inserted_john_person.id,
};
PersonBlock::block(pool, &person_block).await?;
// Two community post tags // Two community post tags
let tag_1 = Tag::create( let tag_1 = Tag::create(
@ -918,8 +820,8 @@ mod tests {
post, post,
bot_post, bot_post,
post_with_tags, post_with_tags,
tag_1, _tag_1: tag_1,
tag_2, _tag_2: tag_2,
site, site,
}) })
} }
@ -982,17 +884,11 @@ mod tests {
) )
.await?; .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!( assert_eq!(
vec![post_listing_single_with_person.clone()], vec![post_listing_single_with_person.clone()],
read_post_listing read_post_listing
); );
assert_eq!( assert_eq!(data.post.id, post_listing_single_with_person.post.id);
expected_post_listing_with_user,
post_listing_single_with_person
);
let local_user_form = LocalUserUpdateForm { let local_user_form = LocalUserUpdateForm {
show_bot_accounts: Some(true), show_bot_accounts: Some(true),
@ -1038,23 +934,16 @@ mod tests {
let read_post_listing_single_no_person = let read_post_listing_single_no_person =
PostView::read(pool, data.post.id, None, false).await?; 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 // Should be 2 posts, with the bot post, and the blocked
assert_eq!( assert_eq!(
vec![POST_WITH_TAGS, POST_BY_BOT, POST, POST_BY_BLOCKED_PERSON], vec![POST_WITH_TAGS, POST_BY_BOT, POST, POST_BY_BLOCKED_PERSON],
names(&read_post_listing_multiple_no_person) names(&read_post_listing_multiple_no_person)
); );
assert_eq!( assert!(read_post_listing_multiple_no_person
Some(&expected_post_listing_no_person), .get(2)
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);
assert_eq!(
expected_post_listing_no_person,
read_post_listing_single_no_person
);
Ok(()) Ok(())
} }
@ -1125,11 +1014,9 @@ mod tests {
let pool = &data.pool(); let pool = &data.pool();
let pool = &mut pool.into(); let pool = &mut pool.into();
let community_block = CommunityBlockForm { let community_block =
person_id: data.tegan_local_user_view.person.id, CommunityBlockForm::new(data.community.id, data.tegan_local_user_view.person.id);
community_id: data.community.id, CommunityActions::block(pool, &community_block).await?;
};
CommunityBlock::block(pool, &community_block).await?;
let read_post_listings_with_person_after_block = PostQuery { let read_post_listings_with_person_after_block = PostQuery {
community_id: Some(data.community.id), community_id: Some(data.community.id),
@ -1140,7 +1027,7 @@ mod tests {
// Should be 0 posts after the community block // Should be 0 posts after the community block
assert_eq!(read_post_listings_with_person_after_block, vec![]); assert_eq!(read_post_listings_with_person_after_block, vec![]);
CommunityBlock::unblock(pool, &community_block).await?; CommunityActions::unblock(pool, &community_block).await?;
Ok(()) 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 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 { assert_eq!(
post_id: data.post.id, (data.post.id, data.tegan_local_user_view.person.id, Some(1)),
person_id: data.tegan_local_user_view.person.id, (
published: inserted_post_like.published, inserted_post_like.post_id,
score: 1, inserted_post_like.person_id,
}; inserted_post_like.like_score,
assert_eq!(expected_post_like, inserted_post_like); )
);
let post_listing_single_with_person = PostView::read( let post_listing_single_with_person = PostView::read(
pool, pool,
@ -1171,12 +1059,17 @@ mod tests {
) )
.await?; .await?;
let mut expected_post_with_upvote = expected_post_view(data)?; assert_eq!(
expected_post_with_upvote.my_vote = Some(1); (true, 1, 1, 1),
expected_post_with_upvote.post.score = 1; (
expected_post_with_upvote.post.upvotes = 1; post_listing_single_with_person
expected_post_with_upvote.creator.post_score = 1; .post_actions
assert_eq!(expected_post_with_upvote, post_listing_single_with_person); .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 { let local_user_form = LocalUserUpdateForm {
show_bot_accounts: Some(false), show_bot_accounts: Some(false),
@ -1197,10 +1090,13 @@ mod tests {
.list(&data.site, pool) .list(&data.site, pool)
.await?; .await?;
read_post_listing.remove(0); 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 = 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); assert_eq!(uplete::Count::only_deleted(1), like_removed);
Ok(()) Ok(())
} }
@ -1215,11 +1111,11 @@ mod tests {
// Like both the bot post, and your own // Like both the bot post, and your own
// The liked_only should not show your own post // 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); 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 = let bot_post_like_form =
PostLikeForm::new(data.bot_post.id, data.tegan_local_user_view.person.id, 1); 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 // Read the liked only
let read_liked_post_listing = PostQuery { let read_liked_post_listing = PostQuery {
@ -1257,7 +1153,7 @@ mod tests {
// Only mark the bot post as read // Only mark the bot post as read
// The read_only should only show the bot post // 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); 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 // Only read the post marked as read
let read_read_post_listing = PostQuery { let read_read_post_listing = PostQuery {
@ -1292,8 +1188,9 @@ mod tests {
.map(|p| { .map(|p| {
( (
p.creator.name, p.creator.name,
p.creator_is_moderator, p.creator_community_actions
p.creator_is_admin, .map(|x| x.became_moderator.is_some())
.unwrap_or(false),
p.can_mod, p.can_mod,
) )
}) })
@ -1301,24 +1198,20 @@ mod tests {
// Tegan is an admin, so can_mod should be always true // Tegan is an admin, so can_mod should be always true
let expected_post_listing = vec![ let expected_post_listing = vec![
("tegan".to_owned(), false, true, true), ("tegan".to_owned(), false, true),
("mybot".to_owned(), false, false, true), ("mybot".to_owned(), false, true),
("tegan".to_owned(), false, true, true), ("tegan".to_owned(), false, true),
]; ];
assert_eq!(expected_post_listing, tegan_listings); assert_eq!(expected_post_listing, tegan_listings);
// Have john become a moderator, then the bot // Have john become a moderator, then the bot
let john_mod_form = CommunityModeratorForm { let john_mod_form =
community_id, CommunityModeratorForm::new(community_id, data.john_local_user_view.person.id);
person_id: data.john_local_user_view.person.id, CommunityActions::join(pool, &john_mod_form).await?;
};
CommunityModerator::join(pool, &john_mod_form).await?;
let bot_mod_form = CommunityModeratorForm { let bot_mod_form =
community_id, CommunityModeratorForm::new(community_id, data.bot_local_user_view.person.id);
person_id: data.bot_local_user_view.person.id, CommunityActions::join(pool, &bot_mod_form).await?;
};
CommunityModerator::join(pool, &bot_mod_form).await?;
let john_listings = PostQuery { let john_listings = PostQuery {
sort: Some(PostSortType::New), sort: Some(PostSortType::New),
@ -1331,8 +1224,9 @@ mod tests {
.map(|p| { .map(|p| {
( (
p.creator.name, p.creator.name,
p.creator_is_moderator, p.creator_community_actions
p.creator_is_admin, .map(|x| x.became_moderator.is_some())
.unwrap_or(false),
p.can_mod, 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. // John is a mod, so he can_mod the bots (and his own) posts, but not tegans.
let expected_post_listing = vec![ let expected_post_listing = vec![
("tegan".to_owned(), false, true, false), ("tegan".to_owned(), false, false),
("mybot".to_owned(), true, false, true), ("mybot".to_owned(), true, true),
("tegan".to_owned(), false, true, false), ("tegan".to_owned(), false, false),
("john".to_owned(), true, false, true), ("john".to_owned(), true, true),
]; ];
assert_eq!(expected_post_listing, john_listings); assert_eq!(expected_post_listing, john_listings);
@ -1359,23 +1253,24 @@ mod tests {
.map(|p| { .map(|p| {
( (
p.creator.name, p.creator.name,
p.creator_is_moderator, p.creator_community_actions
p.creator_is_admin, .map(|x| x.became_moderator.is_some())
.unwrap_or(false),
p.can_mod, p.can_mod,
) )
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let expected_post_listing = vec![ let expected_post_listing = vec![
("tegan".to_owned(), false, true, false), ("tegan".to_owned(), false, false),
("mybot".to_owned(), true, false, true), ("mybot".to_owned(), true, true),
("tegan".to_owned(), false, true, false), ("tegan".to_owned(), false, false),
("john".to_owned(), true, false, false), ("john".to_owned(), true, false),
]; ];
assert_eq!(expected_post_listing, bot_listings); assert_eq!(expected_post_listing, bot_listings);
// Make the bot leave the mod team, and make sure it can_mod is false. // 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 { let bot_listings = PostQuery {
sort: Some(PostSortType::New), sort: Some(PostSortType::New),
@ -1388,18 +1283,19 @@ mod tests {
.map(|p| { .map(|p| {
( (
p.creator.name, p.creator.name,
p.creator_is_moderator, p.creator_community_actions
p.creator_is_admin, .map(|x| x.became_moderator.is_some())
.unwrap_or(false),
p.can_mod, p.can_mod,
) )
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let expected_post_listing = vec![ let expected_post_listing = vec![
("tegan".to_owned(), false, true, false), ("tegan".to_owned(), false, false),
("mybot".to_owned(), false, false, false), ("mybot".to_owned(), false, false),
("tegan".to_owned(), false, true, false), ("tegan".to_owned(), false, false),
("john".to_owned(), true, false, false), ("john".to_owned(), true, false),
]; ];
assert_eq!(expected_post_listing, bot_listings); assert_eq!(expected_post_listing, bot_listings);
@ -1576,11 +1472,12 @@ mod tests {
assert!(posts.is_empty()); assert!(posts.is_empty());
// Follow the community // Follow the community
let form = CommunityFollowerForm { let form = CommunityFollowerForm::new(
state: Some(CommunityFollowerState::Accepted), data.community.id,
..CommunityFollowerForm::new(data.community.id, data.tegan_local_user_view.person.id) data.tegan_local_user_view.person.id,
}; CommunityFollowerState::Accepted,
CommunityFollower::follow(pool, &form).await?; );
CommunityActions::follow(pool, &form).await?;
let posts = data.default_post_query().list(&data.site, pool).await?; let posts = data.default_post_query().list(&data.site, pool).await?;
assert!(!posts.is_empty()); assert!(!posts.is_empty());
@ -1628,11 +1525,9 @@ mod tests {
assert_eq!(POST_LISTING_WITH_BLOCKED, *names(&post_listings_all)); assert_eq!(POST_LISTING_WITH_BLOCKED, *names(&post_listings_all));
// block the instance // block the instance
let block_form = InstanceBlockForm { let block_form =
person_id: data.tegan_local_user_view.person.id, InstanceBlockForm::new(data.tegan_local_user_view.person.id, blocked_instance.id);
instance_id: blocked_instance.id, InstanceActions::block(pool, &block_form).await?;
};
InstanceBlock::block(pool, &block_form).await?;
// now posts from communities on that instance should be hidden // now posts from communities on that instance should be hidden
let post_listings_blocked = data.default_post_query().list(&data.site, pool).await?; 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)); .all(|p| p.post.id != post_from_blocked_instance.id));
// Follow community from the blocked instance to see posts anyway // Follow community from the blocked instance to see posts anyway
let mut follow_form = let follow_form = CommunityFollowerForm::new(
CommunityFollowerForm::new(inserted_community.id, data.tegan_local_user_view.person.id); inserted_community.id,
follow_form.state = Some(CommunityFollowerState::Accepted); data.tegan_local_user_view.person.id,
CommunityFollower::follow(pool, &follow_form).await?; CommunityFollowerState::Accepted,
);
CommunityActions::follow(pool, &follow_form).await?;
let post_listings_bypass = data.default_post_query().list(&data.site, pool).await?; let post_listings_bypass = data.default_post_query().list(&data.site, pool).await?;
assert_eq!(POST_LISTING_WITH_BLOCKED, *names(&post_listings_bypass)); 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 // 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?; let post_listings_blocked = data.default_post_query().list(&data.site, pool).await?;
assert_eq!(POST_LISTING_WITH_BLOCKED, *names(&post_listings_blocked)); assert_eq!(POST_LISTING_WITH_BLOCKED, *names(&post_listings_blocked));
@ -1800,7 +1702,7 @@ mod tests {
// Mark a post as read // Mark a post as read
let read_form = PostReadForm::new(data.bot_post.id, data.tegan_local_user_view.person.id); 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 // 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?; 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(); let pool = &mut pool.into();
// Mark a post as hidden // 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 // 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?; 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. // 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(()) Ok(())
} }
@ -1914,135 +1819,6 @@ mod tests {
Ok(()) Ok(())
} }
fn expected_post_view(data: &Data) -> LemmyResult<PostView> {
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)] #[test_context(Data)]
#[tokio::test] #[tokio::test]
#[serial] #[serial]
@ -2109,13 +1885,9 @@ mod tests {
) )
.await?; .await?;
CommunityPersonBan::ban( CommunityActions::ban(
pool, pool,
&CommunityPersonBanForm { &CommunityPersonBanForm::new(data.community.id, inserted_banned_from_comm_person.id),
community_id: data.community.id,
person_id: inserted_banned_from_comm_person.id,
expires: None,
},
) )
.await?; .await?;
@ -2127,7 +1899,9 @@ mod tests {
) )
.await?; .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?; Person::delete(pool, inserted_banned_from_comm_person.id).await?;
Ok(()) Ok(())
@ -2148,7 +1922,7 @@ mod tests {
) )
.await?; .await?;
assert!(!post_view.banned_from_community); assert!(post_view.community_actions.is_none());
Ok(()) Ok(())
} }
@ -2311,14 +2085,13 @@ mod tests {
data.tegan_local_user_view.local_user.admin = false; data.tegan_local_user_view.local_user.admin = false;
// User can view after following // User can view after following
CommunityFollower::follow( let follow_form = CommunityFollowerForm::new(
pool, data.community.id,
&CommunityFollowerForm { data.tegan_local_user_view.person.id,
state: Some(CommunityFollowerState::Accepted), CommunityFollowerState::Accepted,
..CommunityFollowerForm::new(data.community.id, data.tegan_local_user_view.person.id) );
}, CommunityActions::follow(pool, &follow_form).await?;
)
.await?;
let read_post_listing = PostQuery { let read_post_listing = PostQuery {
community_id: Some(data.community.id), community_id: Some(data.community.id),
local_user: Some(&data.tegan_local_user_view.local_user), local_user: Some(&data.tegan_local_user_view.local_user),
@ -2404,30 +2177,31 @@ mod tests {
Ok(()) Ok(())
} }
#[test_context(Data)] // TODO add these back in later
#[tokio::test] // #[test_context(Data)]
#[serial] // #[tokio::test]
async fn post_tags_present(data: &mut Data) -> LemmyResult<()> { // #[serial]
let pool = &data.pool(); // async fn post_tags_present(data: &mut Data) -> LemmyResult<()> {
let pool = &mut pool.into(); // let pool = &data.pool();
// let pool = &mut pool.into();
let post_view = PostView::read( // let post_view = PostView::read(
pool, // pool,
data.post_with_tags.id, // data.post_with_tags.id,
Some(&data.tegan_local_user_view.local_user), // Some(&data.tegan_local_user_view.local_user),
false, // false,
) // )
.await?; // .await?;
assert_eq!(2, post_view.tags.tags.len()); // assert_eq!(2, post_view.tags.tags.len());
assert_eq!(data.tag_1.name, post_view.tags.tags[0].name); // 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!(data.tag_2.name, post_view.tags.tags[1].name);
let all_posts = data.default_post_query().list(&data.site, pool).await?; // 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!(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[1].tags.tags.len()); // bot post
assert_eq!(0, all_posts[2].tags.tags.len()); // normal post // assert_eq!(0, all_posts[2].tags.tags.len()); // normal post
Ok(()) // Ok(())
} // }
} }

View file

@ -20,13 +20,17 @@ use lemmy_db_schema::{
impl RegistrationApplicationView { impl RegistrationApplicationView {
#[diesel::dsl::auto_type(no_type_alias)] #[diesel::dsl::auto_type(no_type_alias)]
fn joins() -> _ { 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 registration_application::table
.inner_join(local_user::table.on(registration_application::local_user_id.eq(local_user::id))) .inner_join(local_user_join)
.inner_join(person::table.on(local_user::person_id.eq(person::id))) .inner_join(creator_join)
.left_join( .left_join(admin_join)
aliases::person1
.on(registration_application::admin_id.eq(aliases::person1.field(person::id).nullable())),
)
} }
pub async fn read(pool: &mut DbPool<'_>, id: RegistrationApplicationId) -> Result<Self, Error> { pub async fn read(pool: &mut DbPool<'_>, id: RegistrationApplicationId) -> Result<Self, Error> {

View file

@ -1,17 +1,16 @@
use crate::structs::CommentReportView; use crate::structs::CommentReportView;
use diesel::{ use diesel::{
dsl::now,
result::Error, result::Error,
BoolExpressionMethods, BoolExpressionMethods,
ExpressionMethods, ExpressionMethods,
JoinOnDsl, JoinOnDsl,
NullableExpressionMethods, NullableExpressionMethods,
QueryDsl, QueryDsl,
SelectableHelper,
}; };
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
use lemmy_db_schema::{ use lemmy_db_schema::{
aliases::{self, creator_community_actions}, aliases::{self, creator_community_actions},
impls::community::community_follower_select_subscribed_type,
newtypes::{CommentReportId, PersonId}, newtypes::{CommentReportId, PersonId},
schema::{ schema::{
comment, comment,
@ -24,7 +23,7 @@ use lemmy_db_schema::{
person_actions, person_actions,
post, post,
}, },
utils::{functions::coalesce, get_conn, DbPool}, utils::{get_conn, DbPool},
}; };
impl CommentReportView { impl CommentReportView {
@ -103,37 +102,7 @@ impl CommentReportView {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
Self::joins(my_person_id) Self::joins(my_person_id)
.filter(comment_report::id.eq(report_id)) .filter(comment_report::id.eq(report_id))
.select(( .select(Self::as_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(),
))
.first(conn) .first(conn)
.await .await
} }

Some files were not shown because too many files have changed in this diff Show more