Skip to content

Commit

Permalink
Merge pull request #140 from 0xriyadh/flag-dabi
Browse files Browse the repository at this point in the history
Integration of frontend logic for flagging dabi
  • Loading branch information
riya09 authored Nov 9, 2024
2 parents 4edac74 + d828e1a commit 1616eb6
Show file tree
Hide file tree
Showing 12 changed files with 234 additions and 54 deletions.
30 changes: 23 additions & 7 deletions apps/jonogon-core/src/api/trpc/procedures/petitions/moderation.mts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {protectedProcedure} from '../../middleware/protected.mjs';
import {z} from 'zod';
import { calculateNoveltyBoost } from '../../../utility/feed-algorithm.mjs';
import {calculateNoveltyBoost} from '../../../utility/feed-algorithm.mjs';
import {TRPCError} from '@trpc/server';

export const approve = protectedProcedure
.input(
Expand All @@ -10,7 +11,7 @@ export const approve = protectedProcedure
)
.mutation(async ({input, ctx}) => {
// set initial score when the petition appears on feed
const { logScore, newScore } = calculateNoveltyBoost()
const {logScore, newScore} = calculateNoveltyBoost();
const result = await ctx.services.postgresQueryBuilder
.updateTable('petitions')
.set({
Expand All @@ -23,7 +24,7 @@ export const approve = protectedProcedure
approved_at: new Date(),
moderated_by: ctx.auth.user_id,
score: newScore,
log_score: logScore
log_score: logScore,
})
.where('id', '=', `${input.petition_id}`)
.returning(['id', 'created_by'])
Expand Down Expand Up @@ -55,6 +56,20 @@ export const reject = protectedProcedure
}),
)
.mutation(async ({input, ctx}) => {
// Check if the petition is already approved
const petition = await ctx.services.postgresQueryBuilder
.selectFrom('petitions')
.select(['approved_at'])
.where('id', '=', `${input.petition_id}`)
.executeTakeFirst();

if (petition?.approved_at) {
throw new TRPCError({
code: 'FORBIDDEN',
message: 'Voting is not allowed on flagged petitions.',
});
}

const result = await ctx.services.postgresQueryBuilder
.updateTable('petitions')
.set({
Expand Down Expand Up @@ -93,7 +108,8 @@ export const flag = protectedProcedure
.input(
z.object({
petition_id: z.number(),
reason: z.string(),
reason: z.string().optional(),
flagged: z.boolean(), // Flag or unflag, if flagged === true, then unflag the petition and vice versa
}),
)
.mutation(async ({input, ctx}) => {
Expand All @@ -105,9 +121,9 @@ export const flag = protectedProcedure
rejection_reason: null, // Reset the rejection_reason
formalized_at: null, // Reset the formalized_at timestamp

flagged_at: new Date(), // Set the current timestamp
flagged_reason: input.reason, // Set the reason for flagging
moderated_by: ctx.auth.user_id, // Set the user who flagged
flagged_at: input.flagged ? null : new Date(), // Set the current timestamp
flagged_reason: input.flagged ? null : input.reason, // Set the reason for flagging
moderated_by: ctx.auth.user_id, // Set the user who flagged or unflagged
})
.where('id', '=', `${input.petition_id}`)
.returning(['id', 'created_by'])
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
export default function AnimatedBadge({count}: {count: number}) {
export default function AnimatedBadge({
count,
isActive,
}: {
count: number;
isActive: boolean;
}) {
return count ? (
<div className="ab-container" id="badge">
<a className="entypo-bell"></a>
<div className="badge-num">{count > 99 ? '99+' : count}</div>
<div
className={`badge ${isActive ? 'badge-active' : 'badge-inactive'}`}>
{count > 99 ? '99+' : count}
</div>
) : null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export default function PetitionCard(props: {
downvotes: number;
comments: number;

mode: 'request' | 'formalized' | 'own';
mode: 'request' | 'formalized' | 'own' | 'flagged';
}) {
const isAuthenticated = useAuthState();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {Card, CardFooter, CardHeader, CardTitle} from '@/components/ui/card';
export default function PetitionCardSkeleton({
mode,
}: {
mode: 'formalized' | 'request' | 'own';
mode: 'formalized' | 'request' | 'own' | 'flagged';
}) {
return (
<Card>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {MondayCountdown} from '@/app/(interactive)/_components/MondayCountdown';
import AnimatedBadge from './AnimatedBadge';
import {useAuthState} from '@/auth/token-manager';
import PetitionCardSkeleton from './PetitionCardSkeleton';
import {cn} from '@/lib/utils';

const NoPetitionsView = () => (
<div>
Expand Down Expand Up @@ -110,12 +111,26 @@ const PetitionList = () => {

return (
<div className="relative">
<div className="absolute -top-[50px] left-[244px]">
<div
className={cn(
`absolute left-[244px]`,
{
'-top-[43px]':
type === 'request' || type === 'formalized',
},
{
'-top-[95px]':
type !== 'request' && type !== 'formalized',
},
)}>
<AnimatedBadge
{...{
count:
petitionRequestListResponse?.unvoted_formalized_petitions_count ??
0,
isActive:
getDabiType(params.get('type')) === 'request' ||
getDabiType(params.get('type')) === 'formalized',
}}
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export function getSortType(sortStr: string | null) {
export function getDabiType(typeStr: string | null) {
switch (typeStr) {
case 'own':
case 'flagged':
case 'formalized':
case 'request':
return typeStr;
Expand Down
66 changes: 65 additions & 1 deletion apps/jonogon-web-next/src/app/(interactive)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';

Expand Down Expand Up @@ -51,6 +52,33 @@ function SortOption({
);
}

function FilterOption({
filter,
children,
}: PropsWithChildren<{filter: ReturnType<typeof getDabiType>}>) {
const router = useRouter();
const params = useSearchParams();

// current selected type
const selectedType = getDabiType(params.get('type'));

// updating the params
const updateParams = () => {
const nextSearchParams = new URLSearchParams(params);
nextSearchParams.set('type', filter);
router.replace('/?' + nextSearchParams.toString());
};

return (
<DropdownMenuItem
className="capitalize flex items-center justify-between"
onSelect={updateParams}>
<span>{children}</span>
{selectedType === filter ? <RxCheck /> : null}
</DropdownMenuItem>
);
}

function Tab({
type,
children,
Expand Down Expand Up @@ -78,6 +106,17 @@ function Tab({
'border-red-500 text-red-500':
getDabiType(params.get('type')) === type,
},
{
'text-gray-400':
getDabiType(params.get('type')) !== 'request' &&
getDabiType(params.get('type')) !== 'formalized',
},
{
'border-gray-400':
getDabiType(params.get('type')) !== 'request' &&
getDabiType(params.get('type')) !== 'formalized' &&
children === 'সব দাবি',
},
)}
onClick={updateParams}>
{children}
Expand Down Expand Up @@ -141,7 +180,7 @@ export default function Home() {
/>
)}
</h1>
<div className="flex items-center justify-between my-2">
<div className="flex items-center justify-between mt-2">
{type === 'own' ? null : (
<div>
<Tab type={'request'}>সব দাবি</Tab>
Expand All @@ -158,12 +197,37 @@ export default function Home() {
</div>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem
disabled
className="text-sm font-semibold">
Sort By
</DropdownMenuItem>
<SortOption sort={'votes'}>বেশি Votes</SortOption>
<SortOption sort={'score'}>Popular</SortOption>
<SortOption sort={'time'}>Latest</SortOption>
{type !== 'own' && (
<>
<DropdownMenuSeparator className="mb-3" />
<DropdownMenuItem
disabled
className="text-sm font-semibold">
Filter By
</DropdownMenuItem>
<FilterOption filter={'flagged'}>
Flagged দাবিs
</FilterOption>
</>
)}
</DropdownMenuContent>
</DropdownMenu>
</div>

{getDabiType(params.get('type')) === 'flagged' && (
<h2 className="text-3xl font-semibold text-red-500">
Flagged দাবিs
</h2>
)}

<PetitionList />
</div>
<div className="fixed bottom-0 left-0 w-full bg-background/50">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ export default function Petition() {
const isMod = !!selfResponse?.meta.token.is_user_moderator;
const status = petition?.data.status ?? 'draft';

const isFlagged = petition?.data.flagged_at !== null;

const {mutate: approve} = trpc.petitions.approve.useMutation({
onSuccess: async () => {
await utils.petitions.get.invalidate({id: petition_id});
Expand All @@ -166,11 +168,29 @@ export default function Petition() {
},
});

const {mutate: flag} = trpc.petitions.flag.useMutation({
onSuccess: async () => {
await utils.petitions.get.invalidate({id: petition_id});
},
});

return isLoading ? (
<Loading />
) : (
<>
<div className="max-w-screen-sm mx-auto px-4 pt-12 mb-28 flex flex-col gap-4">
{status === 'flagged' &&
!isOwnPetition &&
!isMod &&
!isAdmin ? (
<div className={'bg-border px-3 py-2 rounded-lg'}>
<div className={'font-bold text-red-500 text-sm'}>
This petition was flagged.
</div>
<div>{petition?.data.flagged_reason ?? ''}</div>
</div>
) : null}

{isOwnPetition || isMod || isAdmin ? (
<div
className={
Expand All @@ -189,6 +209,17 @@ export default function Petition() {
</div>
</div>
) : null}
{status === 'flagged' ? (
<div className={'flex-1'}>
<div
className={
'font-bold text-red-500 text-sm'
}>
Your petition was flagged.
</div>
<div>{petition?.data.flagged_reason ?? ''}</div>
</div>
) : null}
{isMod || isAdmin ? (
<div className={'flex flex-row gap-2 items-center'}>
{status === 'submitted' ? (
Expand Down Expand Up @@ -258,7 +289,48 @@ export default function Petition() {
Approve
</Button>
) : null}
{status !== 'rejected' && status !== 'draft' ? (
{status === 'flagged' && (
<Button
size={'sm'}
intent={'success'}
onClick={() =>
window.confirm(
'You sure you wanna unflag?',
) &&
flag({
petition_id:
Number(petition_id),
flagged: true,
})
}>
Unflag
</Button>
)}
{status !== 'rejected' &&
status !== 'draft' &&
status !== 'flagged' &&
status !== 'submitted' ? (
<Button
size={'sm'}
intent={'default'}
onClick={() => {
const flagReason = window.prompt(
'Why you flagging this petition? (leave empty to cancel)',
);

flagReason &&
flag({
petition_id:
Number(petition_id),
reason: flagReason,
flagged: false,
});
}}>
Flag
</Button>
) : null}

{status === 'submitted' ? (
<Button
size={'sm'}
intent={'default'}
Expand Down Expand Up @@ -432,6 +504,7 @@ export default function Petition() {
intent={'success'}
size={'lg'}
className="flex-1 w-full"
disabled={isFlagged}
onClick={clickThumbsUp}>
{status === 'formalized' ? (
<>
Expand Down Expand Up @@ -461,6 +534,7 @@ export default function Petition() {
}
intent={'default'}
className="flex-1 w-full"
disabled={isFlagged}
size={'lg'}
onClick={clickThumbsDown}>
<ThumbsDown
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ export async function generateMetadata({params}: {params: {id: string}}) {
const ctx = getRequestContext();

const encodedSymmetricKeyString = encoder.encode(
ctx.env.RENDERING_SYMMETRIC_KEY,
process.env.NODE_ENV === 'development'
? 'development-symmetric-key-1234567890'
: ctx.env.RENDERING_SYMMETRIC_KEY,
);

const signingKey = await crypto.subtle.importKey(
Expand Down
Loading

0 comments on commit 1616eb6

Please sign in to comment.