Create moderation inbox reports list (#11570)

This commit is contained in:
DS Boyce
2026-09-02 10:02:21 -07:00
committed by GitHub
parent 87329397a4
commit e08bcd0228
5 changed files with 367 additions and 19 deletions
@@ -0,0 +1,66 @@
import {Pressable} from 'react-native'
import {useLingui} from '@lingui/react/macro'
import {atoms as a, useTheme} from '#/alf'
import {ChevronBottom_Stroke2_Corner0_Rounded as ChevronDownIcon} from '#/components/icons/Chevron'
import * as Menu from '#/components/Menu'
import {Text} from '#/components/Typography'
type ReportFilter = 'all' | 'pending' | 'resolved' | 'unread'
export function FilterMenu({
filter,
setFilter,
}: {
filter: ReportFilter
setFilter: React.Dispatch<React.SetStateAction<ReportFilter>>
}) {
const t = useTheme()
const {t: l} = useLingui()
const labels: Record<ReportFilter, string> = {
all: l({context: 'moderation-report-filter', message: 'All'}),
pending: l({
context: 'moderation-report-filter',
message: 'Under review',
}),
resolved: l({context: 'moderation-report-filter', message: 'Resolved'}),
unread: l({context: 'moderation-report-filter', message: 'Unread'}),
}
const options: {value: ReportFilter; label: string}[] = [
{value: 'all', label: labels.all},
{value: 'pending', label: labels.pending},
{value: 'resolved', label: labels.resolved},
{value: 'unread', label: labels.unread},
]
const currentLabel = labels[filter]
return (
<Menu.Root>
<Menu.Trigger
label={l`Filter moderation reports (currently: ${currentLabel})`}>
{({props}) => (
<Pressable
{...props}
style={[a.flex_row, a.align_center, a.justify_center, a.gap_xs]}>
<Text style={[a.text_md, a.font_medium]}>{currentLabel}</Text>
<ChevronDownIcon size="xs" style={[t.atoms.text]} />
</Pressable>
)}
</Menu.Trigger>
<Menu.Outer>
<Menu.Group>
{options.map(option => (
<Menu.Item
key={option.value}
label={option.label}
onPress={() => setFilter(option.value)}>
<Menu.ItemText>{option.label}</Menu.ItemText>
<Menu.ItemRadio selected={filter === option.value} />
</Menu.Item>
))}
</Menu.Group>
</Menu.Outer>
</Menu.Root>
)
}
@@ -0,0 +1,73 @@
import {View} from 'react-native'
import {useLingui} from '@lingui/react/macro'
import {isBefore, subYears} from 'date-fns'
import {atoms as a, useTheme} from '#/alf'
import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRightIcon} from '#/components/icons/Chevron'
import {Text} from '#/components/Typography'
export function ReportRow({
subject,
action,
date,
unread,
}: {
subject: string
action: string
date: Date
unread?: boolean
}) {
const t = useTheme()
const {i18n} = useLingui()
const shouldShowYear = isBefore(date, subYears(new Date(), 1))
return (
<View
style={[
a.flex_row,
a.w_full,
a.align_center,
a.justify_between,
a.gap_sm,
a.p_lg,
{
backgroundColor: unread ? t.palette.primary_25 : undefined,
},
]}>
<View style={[a.flex_1, a.gap_2xs, {minWidth: 0}]}>
<Text
emoji
style={[a.text_md, unread ? a.font_semi_bold : a.font_medium]}>
{subject}
</Text>
<Text style={[a.text_sm, t.atoms.text_contrast_high]}>{action}</Text>
<Text style={[a.text_sm, t.atoms.text_contrast_medium]}>
{i18n.date(date, {
month: 'short',
day: 'numeric',
year: shouldShowYear ? 'numeric' : undefined,
})}
</Text>
</View>
<View
style={[
a.flex_row,
a.flex_shrink_0,
a.align_center,
a.justify_center,
a.gap_sm,
]}>
{unread ? (
<View
style={[
a.rounded_full,
{height: 8, width: 8, backgroundColor: t.palette.primary_500},
]}
/>
) : undefined}
<ChevronRightIcon size="md" style={[t.atoms.text_contrast_medium]} />
</View>
</View>
)
}