Create mod inbox report details screen (#11641)

This commit is contained in:
DS Boyce
2026-09-03 10:20:46 -07:00
committed by GitHub
parent 3e8a48989a
commit 23d8fcf5ea
12 changed files with 347 additions and 6 deletions
@@ -0,0 +1,22 @@
import {View} from 'react-native'
import {atoms as a, useTheme} from '#/alf'
import {H1, Text} from '#/components/Typography'
export function ActionSummaryText({
header,
children,
}: React.PropsWithChildren<{header: string}>) {
const t = useTheme()
// Cleaner to call this ActionSummaryText and suppress lint in one location vs every call site.
// oxlint-disable-next-line bsky-internal/avoid-unwrapped-text
return (
<View style={[a.gap_xs]}>
<H1 accessibilityRole="header" style={[a.text_xl, a.font_semi_bold]}>
{header}
</H1>
<Text style={[a.text_md, t.atoms.text_contrast_high]}>{children}</Text>
</View>
)
}
@@ -0,0 +1,28 @@
import {View} from 'react-native'
import {atoms as a, useTheme} from '#/alf'
import {H2} from '#/components/Typography'
export function ContentBlock({
header,
children,
}: React.PropsWithChildren<{header: string}>) {
const t = useTheme()
return (
<View
style={[
a.p_md,
a.gap_md,
a.rounded_md,
a.border,
t.atoms.bg_contrast_25,
t.atoms.border_contrast_low,
]}>
<H2 accessibilityRole="header" style={[a.text_md, a.font_semi_bold]}>
{header}
</H2>
{children}
</View>
)
}
@@ -4,26 +4,36 @@ 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 {Link} from '#/components/Link'
import {Text} from '#/components/Typography'
export function ReportRow({
subject,
action,
date,
to,
unread,
}: {
subject: string
action: string
date: Date
to: React.ComponentProps<typeof Link>['to']
unread?: boolean
}) {
const t = useTheme()
const {i18n} = useLingui()
const {i18n, t: l} = useLingui()
const shouldShowYear = isBefore(date, subYears(new Date(), 1))
return (
<View
<Link
label={l`View report details: ${subject}`}
to={to}
hoverStyle={
unread
? {backgroundColor: t.palette.primary_50}
: t.atoms.bg_contrast_25
}
style={[
a.flex_row,
a.w_full,
@@ -68,6 +78,6 @@ export function ReportRow({
) : undefined}
<ChevronRightIcon size="md" style={[t.atoms.text_contrast_medium]} />
</View>
</View>
</Link>
)
}
@@ -0,0 +1,57 @@
import {View} from 'react-native'
import {Trans, useLingui} from '@lingui/react/macro'
import {atoms as a, useTheme} from '#/alf'
import {Text} from '#/components/Typography'
// TODO These are placeholder values. -dsb
const POSTED_AT = new Date(2026, 7, 8)
export function SubjectPreview() {
const t = useTheme()
const {i18n} = useLingui()
// TODO These are placeholder values. -dsb
const handle = '@deleteme01.bsky.social'
const post = '@deleteme01.bsky.social lorem ipsum dolor sit amet…'
return (
<View
style={[
a.p_lg,
a.gap_sm,
a.rounded_md,
a.border,
t.atoms.bg,
t.atoms.border_contrast_low,
]}>
<Text emoji style={[a.text_md, a.font_semi_bold]}>
<Trans>Reply to {handle}</Trans>
</Text>
<Text emoji style={[a.text_md]}>
<Trans>“{post}”</Trans>
</Text>
<Text style={[a.text_sm, t.atoms.text_contrast_medium]}>
<Trans>
Posted{' '}
{i18n.date(POSTED_AT, {
month: 'short',
day: 'numeric',
year: 'numeric',
})}
</Trans>
</Text>
<View style={[a.align_start]}>
{/* TODO This is a placeholder for a button. -dsb */}
<View
accessibilityElementsHidden
importantForAccessibility="no-hide-descendants"
style={[a.py_sm, a.px_md, a.rounded_full, t.atoms.bg_contrast_50]}>
<Text style={[a.text_sm, t.atoms.text_contrast_high]}>
<Trans>Open post</Trans>
</Text>
</View>
</View>
</View>
)
}
@@ -0,0 +1,34 @@
import {View} from 'react-native'
import {useLingui} from '@lingui/react/macro'
import {ContentBlock} from './ContentBlock'
import {TimelineItem} from './TimelineItem'
export function Timeline({items}: {items: {title: string; date?: Date}[]}) {
const {i18n, t: l} = useLingui()
return (
<ContentBlock header={l`Timeline`}>
<View>
{items.map(({title, date}, index) => (
<TimelineItem
key={index}
title={title}
date={
date
? i18n.date(date, {
month: 'short',
day: 'numeric',
year: 'numeric',
hour: 'numeric',
minute: 'numeric',
})
: undefined
}
last={index + 1 === items.length}
/>
))}
</View>
</ContentBlock>
)
}
@@ -0,0 +1,72 @@
import {View} from 'react-native'
import {atoms as a, useTheme} from '#/alf'
import {Text} from '#/components/Typography'
export function TimelineItem({
title,
date,
last,
}: {
title: string
date?: string
last?: boolean
}) {
const t = useTheme()
return (
<View style={[a.flex_row, {gap: 10}]}>
<View style={[a.align_center]}>
{last ? (
<View
style={[
a.mt_xs,
a.rounded_full,
{
width: 8,
height: 8,
borderColor: t.palette.primary_500,
borderWidth: 2,
},
]}
/>
) : (
<View
style={[
a.mt_xs,
a.rounded_full,
{
width: 8,
height: 8,
backgroundColor: t.palette.contrast_300,
},
]}
/>
)}
{last ? undefined : (
<View
style={[
a.mt_xs,
a.flex_1,
a.rounded_full,
{
width: 2,
backgroundColor: t.palette.contrast_100,
minHeight: 12,
},
]}
/>
)}
</View>
<View style={[a.flex_1]}>
<Text style={[a.text_sm, a.font_medium]}>{title}</Text>
{date ? (
<Text
style={[!last && a.mb_md, a.text_sm, t.atoms.text_contrast_medium]}>
{date}
</Text>
) : undefined}
</View>
</View>
)
}