From 384e55f0e4ecfb000f66a60e9e2ddb52645b983e Mon Sep 17 00:00:00 2001 From: biosfood Date: Thu, 31 Aug 2023 13:07:45 +0200 Subject: [PATCH] update moderator view (#3820) * update api tests for new moderator view * chage moderator view to be a listing type in get posts Note: Internally, the listing type is called ListingType.ModeratorView, but it's called "Moderator View" in the api endpoint * fix formatting * add support for moderator view to list comments * add api test for moderator view when listing comments * fix api test formatting * retry tests * don't filter out blocked users and communities when using moderator view * fix cargo tests failing * fix formatting * fix previous merge * Adding ModeratorView to listing_type_enums * Fixing fmt. * Adding a default to ListingType. * Upgrading to use new lemmy-js-client. --------- Co-authored-by: Nutomic Co-authored-by: Dessalines Co-authored-by: Dessalines --- api_tests/package.json | 2 +- api_tests/src/community.spec.ts | 42 ++++++++++++++-- api_tests/src/shared.ts | 15 +++--- api_tests/tsconfig.json | 2 +- api_tests/yarn.lock | 8 +-- crates/api_common/src/post.rs | 1 - crates/apub/src/api/list_posts.rs | 3 -- crates/db_schema/src/lib.rs | 12 ++++- crates/db_views/src/comment_view.rs | 16 +++++- crates/db_views/src/post_view.rs | 16 +++--- .../down.sql | 49 +++++++++++++++++++ .../up.sql | 4 ++ 12 files changed, 140 insertions(+), 30 deletions(-) create mode 100644 migrations/2023-08-29-183053_add_listing_type_moderator_view/down.sql create mode 100644 migrations/2023-08-29-183053_add_listing_type_moderator_view/up.sql diff --git a/api_tests/package.json b/api_tests/package.json index 61c11d00a..06c2c46ae 100644 --- a/api_tests/package.json +++ b/api_tests/package.json @@ -19,7 +19,7 @@ "eslint": "^8.40.0", "eslint-plugin-prettier": "^4.0.0", "jest": "^29.5.0", - "lemmy-js-client": "0.19.0-rc.2", + "lemmy-js-client": "0.19.0-rc.3", "prettier": "^3.0.0", "ts-jest": "^29.1.0", "typescript": "^5.0.4" diff --git a/api_tests/src/community.spec.ts b/api_tests/src/community.spec.ts index 274721d94..6a6195989 100644 --- a/api_tests/src/community.spec.ts +++ b/api_tests/src/community.spec.ts @@ -21,6 +21,8 @@ import { registerUser, API, getPosts, + getComments, + createComment, getCommunityByName, } from "./shared"; @@ -245,12 +247,17 @@ test("moderator view", async () => { client: alpha.client, }; expect(otherUser.auth).not.toBe(""); + let otherCommunity = (await createCommunity(otherUser)).community_view; expect(otherCommunity.community.name).toBeDefined(); let otherPost = (await createPost(otherUser, otherCommunity.community.id)) .post_view; expect(otherPost.post.id).toBeDefined(); + let otherComment = (await createComment(otherUser, otherPost.post.id)) + .comment_view; + expect(otherComment.comment.id).toBeDefined(); + // create a community and post on alpha let alphaCommunity = (await createCommunity(alpha)).community_view; expect(alphaCommunity.community.name).toBeDefined(); @@ -258,27 +265,56 @@ test("moderator view", async () => { .post_view; expect(alphaPost.post.id).toBeDefined(); + let alphaComment = (await createComment(otherUser, alphaPost.post.id)) + .comment_view; + expect(alphaComment.comment.id).toBeDefined(); + // other user also posts on alpha's community let otherAlphaPost = ( await createPost(otherUser, alphaCommunity.community.id) ).post_view; expect(otherAlphaPost.post.id).toBeDefined(); - // alpha lists posts on home page, should contain all posts that were made - let posts = (await getPosts(alpha)).posts; + let otherAlphaComment = ( + await createComment(otherUser, otherAlphaPost.post.id) + ).comment_view; + expect(otherAlphaComment.comment.id).toBeDefined(); + + // alpha lists posts and comments on home page, should contain all posts that were made + let posts = (await getPosts(alpha, "All")).posts; expect(posts).toBeDefined(); let postIds = posts.map(post => post.post.id); + + let comments = (await getComments(alpha, undefined, "All")).comments; + expect(comments).toBeDefined(); + let commentIds = comments.map(comment => comment.comment.id); + expect(postIds).toContain(otherPost.post.id); + expect(commentIds).toContain(otherComment.comment.id); + expect(postIds).toContain(alphaPost.post.id); + expect(commentIds).toContain(alphaComment.comment.id); + expect(postIds).toContain(otherAlphaPost.post.id); + expect(commentIds).toContain(otherAlphaComment.comment.id); // in moderator view, alpha should not see otherPost, wich was posted on a community alpha doesn't moderate - posts = (await getPosts(alpha, true)).posts; + posts = (await getPosts(alpha, "ModeratorView")).posts; expect(posts).toBeDefined(); postIds = posts.map(post => post.post.id); + + comments = (await getComments(alpha, undefined, "ModeratorView")).comments; + expect(comments).toBeDefined(); + commentIds = comments.map(comment => comment.comment.id); + expect(postIds).not.toContain(otherPost.post.id); + expect(commentIds).not.toContain(otherComment.comment.id); + expect(postIds).toContain(alphaPost.post.id); + expect(commentIds).toContain(alphaComment.comment.id); + expect(postIds).toContain(otherAlphaPost.post.id); + expect(commentIds).toContain(otherAlphaComment.comment.id); }); test("Get community for different casing on domain", async () => { diff --git a/api_tests/src/shared.ts b/api_tests/src/shared.ts index bc760fd33..e465d1da2 100644 --- a/api_tests/src/shared.ts +++ b/api_tests/src/shared.ts @@ -4,7 +4,6 @@ import { GetUnreadCount, GetUnreadCountResponse, LemmyHttp, - LocalUser, } from "lemmy-js-client"; import { CreatePost } from "lemmy-js-client/dist/types/CreatePost"; import { DeletePost } from "lemmy-js-client/dist/types/DeletePost"; @@ -69,6 +68,7 @@ import { GetPostsResponse } from "lemmy-js-client/dist/types/GetPostsResponse"; import { GetPosts } from "lemmy-js-client/dist/types/GetPosts"; import { GetPersonDetailsResponse } from "lemmy-js-client/dist/types/GetPersonDetailsResponse"; import { GetPersonDetails } from "lemmy-js-client/dist/types/GetPersonDetails"; +import { ListingType } from "lemmy-js-client/dist/types/ListingType"; export interface API { client: LemmyHttp; @@ -201,7 +201,9 @@ export async function setupLogins() { try { await createCommunity(alpha, "main"); await createCommunity(beta, "main"); - } catch (_) {} + } catch (_) { + console.log("Communities already exist"); + } } export async function createPost( @@ -321,11 +323,12 @@ export async function getPost( export async function getComments( api: API, - post_id: number, + post_id?: number, + listingType: ListingType = "All", ): Promise { let form: GetComments = { post_id: post_id, - type_: "All", + type_: listingType, sort: "New", auth: api.auth, }; @@ -798,11 +801,11 @@ export async function listCommentReports( export function getPosts( api: API, - moderator_view = false, + listingType?: ListingType, ): Promise { let form: GetPosts = { - moderator_view, auth: api.auth, + type_: listingType, }; return api.client.getPosts(form); } diff --git a/api_tests/tsconfig.json b/api_tests/tsconfig.json index 28f5d192c..9e2e0720e 100644 --- a/api_tests/tsconfig.json +++ b/api_tests/tsconfig.json @@ -6,7 +6,7 @@ "noImplicitAny": true, "lib": ["es2017", "es7", "es6", "dom"], "outDir": "./dist", - "target": "ES5", + "target": "ES2015", "strictNullChecks": true, "moduleResolution": "Node" }, diff --git a/api_tests/yarn.lock b/api_tests/yarn.lock index b243c1b65..17c5a2849 100644 --- a/api_tests/yarn.lock +++ b/api_tests/yarn.lock @@ -2174,10 +2174,10 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== -lemmy-js-client@0.19.0-rc.2: - version "0.19.0-rc.2" - resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.19.0-rc.2.tgz#c3cb511b27f92538909a2b91a0f8527b1abad958" - integrity sha512-FXuf8s7bpBVkHL/OGWDb/0aGIrJ7uv3d4Xt1h6zmNDhw6MmmuD8RXgCHiS2jqhxjAEp96Dpl1NFXbpmKpix7tQ== +lemmy-js-client@0.19.0-rc.3: + version "0.19.0-rc.3" + resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.19.0-rc.3.tgz#1efbfd5ce492319227a41cb020fc1cf9b2e7c075" + integrity sha512-RmibQ3+YTvqsQ89II2I29pfPmVAWiSObGAU9Nc/AGYfyvaCya7f5+TirKwHdKA2eWDWLOTnD4rm6WgcgAwvhWw== dependencies: cross-fetch "^3.1.5" form-data "^4.0.0" diff --git a/crates/api_common/src/post.rs b/crates/api_common/src/post.rs index e511d2798..d7838866b 100644 --- a/crates/api_common/src/post.rs +++ b/crates/api_common/src/post.rs @@ -77,7 +77,6 @@ pub struct GetPosts { pub saved_only: Option, pub liked_only: Option, pub disliked_only: Option, - pub moderator_view: Option, pub auth: Option>, } diff --git a/crates/apub/src/api/list_posts.rs b/crates/apub/src/api/list_posts.rs index 76c7c7cec..1c98e699a 100644 --- a/crates/apub/src/api/list_posts.rs +++ b/crates/apub/src/api/list_posts.rs @@ -43,8 +43,6 @@ pub async fn list_posts( return Err(LemmyError::from(LemmyErrorType::ContradictingFilters)); } - let moderator_view = data.moderator_view.unwrap_or_default(); - let listing_type = Some(listing_type_with_default( data.type_, &local_site, @@ -59,7 +57,6 @@ pub async fn list_posts( saved_only, liked_only, disliked_only, - moderator_view, page, limit, ..Default::default() diff --git a/crates/db_schema/src/lib.rs b/crates/db_schema/src/lib.rs index e3232c402..ce1827211 100644 --- a/crates/db_schema/src/lib.rs +++ b/crates/db_schema/src/lib.rs @@ -44,7 +44,9 @@ use strum_macros::{Display, EnumString}; #[cfg(feature = "full")] use ts_rs::TS; -#[derive(EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] +#[derive( + EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default, +)] #[cfg_attr(feature = "full", derive(DbEnum, TS))] #[cfg_attr( feature = "full", @@ -54,6 +56,7 @@ use ts_rs::TS; #[cfg_attr(feature = "full", ts(export))] /// The post sort types. See here for descriptions: https://join-lemmy.org/docs/en/users/03-votes-and-ranking.html pub enum SortType { + #[default] Active, Hot, New, @@ -99,7 +102,9 @@ pub enum PersonSortType { PostCount, } -#[derive(EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] +#[derive( + EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default, +)] #[cfg_attr(feature = "full", derive(DbEnum, TS))] #[cfg_attr( feature = "full", @@ -112,9 +117,12 @@ pub enum ListingType { /// Content from your own site, as well as all connected / federated sites. All, /// Content from your site only. + #[default] Local, /// Content only from communities you've subscribed to. Subscribed, + /// Content that you can moderate (because you are a moderator of the community it is posted to) + ModeratorView, } #[derive(EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] diff --git a/crates/db_views/src/comment_view.rs b/crates/db_views/src/comment_view.rs index 716d563a7..713b8ed27 100644 --- a/crates/db_views/src/comment_view.rs +++ b/crates/db_views/src/comment_view.rs @@ -22,6 +22,7 @@ use lemmy_db_schema::{ community, community_block, community_follower, + community_moderator, community_person_ban, local_user_language, person, @@ -101,6 +102,14 @@ fn queries<'a>() -> Queries< .and(comment_like::person_id.eq(person_id_join)), ), ) + .left_join( + community_moderator::table.on( + post::id + .eq(comment::post_id) + .and(post::community_id.eq(community_moderator::community_id)) + .and(community_moderator::person_id.eq(person_id_join)), + ), + ) }; let selection = ( @@ -186,6 +195,9 @@ fn queries<'a>() -> Queries< .or(community_follower::person_id.eq(person_id_join)), ) } + ListingType::ModeratorView => { + query = query.filter(community_moderator::person_id.is_not_null()); + } } } @@ -222,7 +234,9 @@ fn queries<'a>() -> Queries< query = query.filter(person::bot_account.eq(false)); }; - if options.local_user.is_some() { + if options.local_user.is_some() + && options.listing_type.unwrap_or_default() != ListingType::ModeratorView + { // Filter out the rows with missing languages query = query.filter(local_user_language::language_id.is_not_null()); diff --git a/crates/db_views/src/post_view.rs b/crates/db_views/src/post_view.rs index e2a0672c6..4bd0501ab 100644 --- a/crates/db_views/src/post_view.rs +++ b/crates/db_views/src/post_view.rs @@ -258,6 +258,9 @@ fn queries<'a>() -> Queries< .or(community_follower::person_id.eq(person_id_join)), ) } + ListingType::ModeratorView => { + query = query.filter(community_moderator::person_id.is_not_null()); + } } } @@ -295,10 +298,6 @@ fn queries<'a>() -> Queries< if options.saved_only { query = query.filter(post_saved::id.is_not_null()); } - - if options.moderator_view { - query = query.filter(community_moderator::person_id.is_not_null()); - } // Only hide the read posts, if the saved_only is false. Otherwise ppl with the hide_read // setting wont be able to see saved posts. else if !options @@ -318,15 +317,16 @@ fn queries<'a>() -> Queries< query = query.filter(post_like::score.eq(-1)); } - if options.local_user.is_some() { + // Dont filter blocks or missing languages for moderator view type + if options.local_user.is_some() + && options.listing_type.unwrap_or_default() != ListingType::ModeratorView + { // Filter out the rows with missing languages query = query.filter(local_user_language::language_id.is_not_null()); // Don't show blocked communities or persons query = query.filter(community_block::person_id.is_null()); - if !options.moderator_view { - query = query.filter(person_block::person_id.is_null()); - } + query = query.filter(person_block::person_id.is_null()); } let now = diesel::dsl::now.into_sql::(); diff --git a/migrations/2023-08-29-183053_add_listing_type_moderator_view/down.sql b/migrations/2023-08-29-183053_add_listing_type_moderator_view/down.sql new file mode 100644 index 000000000..838f07d45 --- /dev/null +++ b/migrations/2023-08-29-183053_add_listing_type_moderator_view/down.sql @@ -0,0 +1,49 @@ +ALTER TABLE local_user + ALTER default_listing_type DROP DEFAULT; + +ALTER TABLE local_site + ALTER default_post_listing_type DROP DEFAULT; + +UPDATE + local_user +SET + default_listing_type = 'Local' +WHERE + default_listing_type = 'ModeratorView'; + +UPDATE + local_site +SET + default_post_listing_type = 'Local' +WHERE + default_post_listing_type = 'ModeratorView'; + +-- rename the old enum +ALTER TYPE listing_type_enum RENAME TO listing_type_enum__; + +-- create the new enum +CREATE TYPE listing_type_enum AS ENUM ( + 'All', + 'Local', + 'Subscribed' +); + +-- alter all your enum columns +ALTER TABLE local_user + ALTER COLUMN default_listing_type TYPE listing_type_enum + USING default_listing_type::text::listing_type_enum; + +ALTER TABLE local_site + ALTER COLUMN default_post_listing_type TYPE listing_type_enum + USING default_post_listing_type::text::listing_type_enum; + +-- Add back in the default +ALTER TABLE local_user + ALTER default_listing_type SET DEFAULT 'Local'; + +ALTER TABLE local_site + ALTER default_post_listing_type SET DEFAULT 'Local'; + +-- drop the old enum +DROP TYPE listing_type_enum__; + diff --git a/migrations/2023-08-29-183053_add_listing_type_moderator_view/up.sql b/migrations/2023-08-29-183053_add_listing_type_moderator_view/up.sql new file mode 100644 index 000000000..0f2d5eeae --- /dev/null +++ b/migrations/2023-08-29-183053_add_listing_type_moderator_view/up.sql @@ -0,0 +1,4 @@ +-- Update the listing_type_enum +ALTER TYPE listing_type_enum + ADD VALUE 'ModeratorView'; +