From c3f4714f9593e8538f0acdeda2fe920d888f8e97 Mon Sep 17 00:00:00 2001 From: Amar Trebinjac Date: Fri, 24 Jan 2025 11:38:57 +0100 Subject: [PATCH 01/18] feat: make squad id optional for sourcePostModerations --- packages/shared/src/graphql/squads.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shared/src/graphql/squads.ts b/packages/shared/src/graphql/squads.ts index f971f7b183..a6da87f6a1 100644 --- a/packages/shared/src/graphql/squads.ts +++ b/packages/shared/src/graphql/squads.ts @@ -633,7 +633,7 @@ const SOURCE_POST_MODERATION_FRAGMENT = gql` export const SQUAD_PENDING_POSTS_QUERY = gql` query sourcePostModerations( - $sourceId: ID! + $sourceId: ID $status: [String] $first: Int $after: String From a9cdeb397c98803d74706b27fececafe3df5ddbc Mon Sep 17 00:00:00 2001 From: Amar Trebinjac Date: Fri, 24 Jan 2025 11:39:32 +0100 Subject: [PATCH 02/18] feat: move moderate page to squad folder --- packages/webapp/pages/squads/moderate.tsx | 71 +++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 packages/webapp/pages/squads/moderate.tsx diff --git a/packages/webapp/pages/squads/moderate.tsx b/packages/webapp/pages/squads/moderate.tsx new file mode 100644 index 0000000000..3707b0a0bc --- /dev/null +++ b/packages/webapp/pages/squads/moderate.tsx @@ -0,0 +1,71 @@ +import type { ReactElement } from 'react'; +import React, { useEffect } from 'react'; +import type { SquadSettingsProps } from '@dailydotdev/shared/src/components/squads/utils'; +import { ManageSquadPageContainer } from '@dailydotdev/shared/src/components/squads/utils'; +import { + SquadTab, + SquadTabs, +} from '@dailydotdev/shared/src/components/squads/SquadTabs'; +import { SquadModerationList } from '@dailydotdev/shared/src/components/squads/moderation/SquadModerationList'; +import { + PageHeader, + PageHeaderTitle, +} from '@dailydotdev/shared/src/components/layout/common'; +import { + Button, + ButtonVariant, +} from '@dailydotdev/shared/src/components/buttons/Button'; +import { ArrowIcon } from '@dailydotdev/shared/src/components/icons'; +import { useSquad } from '@dailydotdev/shared/src/hooks'; +import { useRouter } from 'next/router'; +import { verifyPermission } from '@dailydotdev/shared/src/graphql/squads'; +import { SourcePermissions } from '@dailydotdev/shared/src/graphql/sources'; +import { TypographyType } from '@dailydotdev/shared/src/components/typography/Typography'; +import { getLayout as getMainLayout } from '../../components/layouts/MainLayout'; + +export default function ModerateSquadPage({ + handle, +}: SquadSettingsProps): ReactElement { + const router = useRouter(); + const { squad, isLoading, isFetched } = useSquad({ + handle: router.query.handle as string, + }); + const isModerator = + verifyPermission(squad, SourcePermissions.ModeratePost) || !handle; + + useEffect(() => { + if (isLoading || !isFetched) { + return; + } + + if (handle && !squad.moderationRequired) { + router.push(`/squads/${handle}`); + } + }, [handle, isFetched, isLoading, router, squad]); + + if (isLoading) { + return null; + } + + return ( + + + {handle && ( + + )} {squads?.map((squad) => ( ))} From 9adabe8295024a5dad27b3d409ced46cd0cc81d2 Mon Sep 17 00:00:00 2001 From: Amar Trebinjac Date: Mon, 27 Jan 2025 00:28:16 +0100 Subject: [PATCH 10/18] feat: bulk approval for multiple squads --- .../squads/moderation/SquadModerationList.tsx | 5 +---- packages/shared/src/graphql/squads.ts | 12 +++--------- .../src/hooks/squads/useSourceModerationList.ts | 11 ++++------- 3 files changed, 8 insertions(+), 20 deletions(-) diff --git a/packages/shared/src/components/squads/moderation/SquadModerationList.tsx b/packages/shared/src/components/squads/moderation/SquadModerationList.tsx index 40ba4d2981..37583863bb 100644 --- a/packages/shared/src/components/squads/moderation/SquadModerationList.tsx +++ b/packages/shared/src/components/squads/moderation/SquadModerationList.tsx @@ -55,10 +55,7 @@ export function SquadModerationList({ variant={ButtonVariant.Primary} size={ButtonSize.Small} onClick={() => - moderate.onApprove( - list.map((request) => request.id), - squad.id, - ) + moderate.onApprove(list.map((request) => request.id)) } > Approve all {list.length} posts diff --git a/packages/shared/src/graphql/squads.ts b/packages/shared/src/graphql/squads.ts index a6da87f6a1..9d1f0063aa 100644 --- a/packages/shared/src/graphql/squads.ts +++ b/packages/shared/src/graphql/squads.ts @@ -662,14 +662,12 @@ export const SQUAD_MODERATE_POST_MUTATION = gql` mutation ModerateSourcePost( $postIds: [ID]! $status: String - $sourceId: ID! $rejectionReason: String $moderatorMessage: String ) { moderateSourcePosts( postIds: $postIds status: $status - sourceId: $sourceId rejectionReason: $rejectionReason moderatorMessage: $moderatorMessage ) { @@ -702,16 +700,14 @@ export interface SquadPostRejectionProps extends SquadPostModerationProps { note?: string; } -export const squadApproveMutation = ({ - postIds, - sourceId, -}: SquadPostModerationProps): Promise => { +export const squadApproveMutation = ( + postIds: string[], +): Promise => { return gqlClient .request<{ moderateSourcePosts: SourcePostModeration[]; }>(SQUAD_MODERATE_POST_MUTATION, { postIds, - sourceId, status: SourcePostModerationStatus.Approved, }) .then((res) => res.moderateSourcePosts); @@ -719,7 +715,6 @@ export const squadApproveMutation = ({ export const squadRejectMutation = ({ postIds, - sourceId, reason, note, }: SquadPostRejectionProps): Promise => { @@ -728,7 +723,6 @@ export const squadRejectMutation = ({ moderateSourcePosts: SourcePostModeration[]; }>(SQUAD_MODERATE_POST_MUTATION, { postIds, - sourceId, status: SourcePostModerationStatus.Rejected, rejectionReason: reason, moderatorMessage: note, diff --git a/packages/shared/src/hooks/squads/useSourceModerationList.ts b/packages/shared/src/hooks/squads/useSourceModerationList.ts index 0536cb4b06..4ac0c55fa0 100644 --- a/packages/shared/src/hooks/squads/useSourceModerationList.ts +++ b/packages/shared/src/hooks/squads/useSourceModerationList.ts @@ -63,12 +63,12 @@ export const rejectReasons: { value: PostModerationReason; label: string }[] = [ export interface UseSourceModerationList { onApprove: ( ids: string[], - sourceId: string, + sourceId?: string, onSuccess?: MouseEventHandler, ) => Promise; onReject: ( id: string, - sourceId: string, + sourceId?: string, onSuccess?: MouseEventHandler, ) => void; onDelete: (postId: string) => Promise; @@ -154,11 +154,8 @@ export const useSourceModerationList = ({ isPending: isPendingApprove, isSuccess: isSuccessApprove, } = useMutation({ - mutationFn: ({ postIds, sourceId }: SquadPostModerationProps) => - squadApproveMutation({ - postIds, - sourceId, - }), + mutationFn: ({ postIds }: SquadPostModerationProps) => + squadApproveMutation(postIds), onMutate: (data) => handleOptimistic(data), onSuccess: (data) => { displayToast('Post(s) approved successfully'); From a2ba4bddad52524162619402b7f2d8ef2289b994 Mon Sep 17 00:00:00 2001 From: Amar Trebinjac Date: Mon, 27 Jan 2025 10:19:32 +0100 Subject: [PATCH 11/18] feat: update squad tabs --- .../src/components/squads/SquadTabs.tsx | 5 ++- .../squads/moderation/SquadModerationItem.tsx | 31 +++++++++++-------- packages/webapp/pages/squads/moderate.tsx | 4 +-- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/packages/shared/src/components/squads/SquadTabs.tsx b/packages/shared/src/components/squads/SquadTabs.tsx index 076ed18411..b8076b6966 100644 --- a/packages/shared/src/components/squads/SquadTabs.tsx +++ b/packages/shared/src/components/squads/SquadTabs.tsx @@ -33,7 +33,10 @@ export function SquadTabs({ active, squad }: SquadTabsProps): ReactElement { }, ] : []), - { label: pendingTabLabel, url: `${squadLink}/moderate` }, + { + label: pendingTabLabel, + url: `${webappUrl}squads/moderate?handle=${handle}`, + }, ]; const controlledActive = diff --git a/packages/shared/src/components/squads/moderation/SquadModerationItem.tsx b/packages/shared/src/components/squads/moderation/SquadModerationItem.tsx index 99257306cb..fe6745dea4 100644 --- a/packages/shared/src/components/squads/moderation/SquadModerationItem.tsx +++ b/packages/shared/src/components/squads/moderation/SquadModerationItem.tsx @@ -1,5 +1,6 @@ import type { ReactElement } from 'react'; import React from 'react'; +import { useSearchParams } from 'next/navigation'; import { Typography, TypographyColor, @@ -26,6 +27,8 @@ import SourceProfilePicture from '../../profile/SourceProfilePicture'; export function SquadModerationItem( props: SquadModerationItemProps, ): ReactElement { + const searchParams = useSearchParams(); + const handle = searchParams?.get('handle'); const { context, modal, user } = useSourceModerationItem(props); const { data, squad, onApprove, onReject, isPending } = props; const { rejectionReason, createdBy, createdAt, image, status, source } = data; @@ -46,19 +49,21 @@ export function SquadModerationItem( onClick={modal.open} type="button" /> -
- - - {source.name} - -
+ {!handle && ( +
+ + + {source.name} + +
+ )}
{handle && (