3p moderation services [WIP] (#2550)
* Add modservice screen and profile-header-card * Drop the guidelines for now * Remove ununsed constants * Add label & label group descriptions * Not found state * Reorg, add icon * Subheader * Header * Complete header * Clean up * Add all groups * Fix scroll view * Dialogs side quest * Remove log * Add (WIP) debug mod page * Dialog solution * Add note * Clean up and reorganize localized moderation strings * Memoize * Add example * Add first ReportDialog screen * Report dialog step 2 * Submit * Integrate updates * Move moderation screen * Migrate buttons * Migrate everything * Rough sketch * Fix types * Update atoms values * Abstract ModerationServiceCard * Hook up data to settings page * Handle subscription * Rough enablement * Rough enablement * Some validation, fixes * More work on the mod debug screen * Hook up data * Update invalidation * Hook up data to ReportDialog * Fix native error * Refactor/rewrite the entire moderation-application system * Fix toggles * Add copyright and other option to report * Handle reports on profile vs content * Little cleanup * Get post hiding back in gear * Better loading flow on Mod screen * Clean up Mod screen * Clean up ProfileMod screen * Handle muting correctly * Update enablement on ProfileMod screen * Improve Moderation screen and dialog * Styling, handle disabled labelers * Rework list of labels on own content * Use moderateNotification() * ReportDialog updates * Fix button overflow * Simplify the ProfileModerationService ui * Mod screen design * Move moderation card from the profile header to a tab * Small tweaks to the moderation screen * Enable toggle on mod page * Add notifs to debugmod and dont filter notifs from followed users * Add moderator-service profile view * Wire up more of the modservice data to profiles * A bunch of speculative non-working UI * Cleanup: delete old code * Update ModerationDetailsDialog * Update ReportDialog * Update LabelsOnMe dialog * Handle ReportDialog load better * Rename LabelsOnMeDialog, fix close * Experiment to put labeling under a tab of a normal profile * Moderator variation of profile * Remove dead code and start moving toward latest modsdk * Remove a bunch of now-dead label strings * Update ModDebug to be a bit more intuitive and support custom labels * Minor ui tweaks * Improve consistency of display name blurring * Fix profile-card warning rendering * More debugmod UI tuning * Update to use new labeler semantics * Delete some dead code and do some refactoring * Update profile to pull from labeler definition * Implement new label config controls (wip) * Tweak ui * Implement preference controls on labelers * Rework label pref ui * Get moderation screen working * Add asyncstorage query persistence * Implement label handling * Small cleanup * Implement Likes dialog * Fix: remove text outside of text element * Cleanup * Fix likes dialog on mobile * Implement the label appeal flow * Get report flow working again with temporarily fixed report options * Update onboarding * Enforce limit of ten labeler subscriptions * Fix type errors * Fix lint errors * Improve types of RQ * Some work on Likes dialog, needs discussion * Bit of ReportDialog cleanup * Replace non-single-path SVG * Update nudity descriptions * Update to use new sdk updates * Add adult-content-enabled behavior to label config * Use the default setting of custom labels * Handle global moderation label prefs with the global settings * Fix missing postAuthor * Fix empty moderation page * Add mutewords control back to Mod screen * Tweak adult setting styles * Remove deprecated global labels * Handle underage users on mod screen * Adjust font sizes * Swap in RichText * Like button improvements * Tweaks to Labeler profile * Design tweaks for mod pref dialog * Add tertiary button color * Switch moderation UIs to tertiary color * Update mutewords and hiddenposts to use the new sdk * Add test-environment mod authority * Switch 'gore' to 'graphic-media' * Move nudity out of the adult content control * Remove focus styles from buttons - let the browser behavior handle it * Fixes to the adult content age-gating in moderaiton * Ditch tertiary button color, lighten secondary button * Fix some colors * Remove focused overrides from toggles * Liked by screen * Rework the moderationlabelpref * Fix optimistic like * Cleanup * Change how onboarding handles adult content enabled/disabled * Add special handling of the mod authorities * Tweaks * Update the default labeler avatar to a shield * Add route to go server * Avoid dups due to bad config * Fix attrs * Fix: dont try to detect link/label mismatches on post meta * Correctly show the label behavior when adult content is disabled * Readd the local hiddenPosts handling * WIP * Fix bad merge * Conten hider design tweaks * Fix text string breakage * Adjust source text in ContentHider * Fix link bug * Design tweaks to ContentHider and ModDetailsDialog * Adjust spacing of inform badges * Adjust spacing of embeds in posts * Style tweaks to post/profile alerts * Labels on me and dialog * Remove bad focus styles from post dropdown * Better spacing solution * Tune moderation UIs * Moderation UI tweaks for mobile * Move labelers query on Mod screen * Update to use new SDK appLabelers semantics * Implement report submission * Replace the report modal entirely with the report dialog * Add @ to mod details dialog handle * Bump SDK package * Remove silly type * Add to AWS build CI * Fix ToggleButton overflow * Clean up ModServiceCard, rename to LabelingServiceCard * Hackfix to translate gore labels to graphic-media * Tune content hider sizing on web desktop * Handle self labels * Fix spacing below text-only posts * Fix: send appeals to the right labeler * Give mod page links interactive states * Fix references * Remove focus handling * Remove remnant * Remove the like count from the subscribed labeler listing * Bump @atproto/api@0.11.1 * Remove extra @ * Fix: persist labels to local storage to reduce coverage gaps * update dipendencies * revert dipendencies * Add some explainers on how blocking affects labelers * Tweak copy * Fix underline color in header * Fix profile menu * Handle card overflow * Remove metrics from header * Mute 'account' not 'user' * Show metrics if self * Show the labels tab on logged out view * Fix bad merge * Use purple theming on labelers * Tighten space on LabelerCard * Set staleTime to 6hrs for labeler details * Memoize the memoizers * Drop staleTime to 60s * Move label defs into a context to reduce recomputes * Submit view tweaks * Move labeler fetch below auth * Mitigation: hardcode the bluesky moderation labeler name * Bump sdk * Add missing translated string Co-authored-by: Takayuki KUSANO <65759+tkusano@users.noreply.github.com> * Add missing translated string Co-authored-by: Takayuki KUSANO <65759+tkusano@users.noreply.github.com> * Hailey's fix for incorrect profile tabs Co-authored-by: Hailey <me@haileyok.com> * Feedback * Fix borders, add bottom space * Hailey's fix pt 2 Co-authored-by: Hailey <me@haileyok.com> * Fix post tabs * Integrate feedback pt 1 Co-authored-by: Takayuki KUSANO <65759+tkusano@users.noreply.github.com> * Integrate feedback pt 2 Co-authored-by: Takayuki KUSANO <65759+tkusano@users.noreply.github.com> * Integrate feedback pt 3 Co-authored-by: Takayuki KUSANO <65759+tkusano@users.noreply.github.com> * Integrate feedback pt 4 Co-authored-by: Takayuki KUSANO <65759+tkusano@users.noreply.github.com> * Integrate feedback pt 5 Co-authored-by: Takayuki KUSANO <65759+tkusano@users.noreply.github.com> * Integrate feedback pt 6 Co-authored-by: Takayuki KUSANO <65759+tkusano@users.noreply.github.com> * Integrate feedback pt 7 Co-authored-by: Takayuki KUSANO <65759+tkusano@users.noreply.github.com> * Integrate feedback pt 8 Co-authored-by: Takayuki KUSANO <65759+tkusano@users.noreply.github.com> * Format * Integrate new bday modal * Use public agent for getServices * Update casing --------- Co-authored-by: Eric Bailey <git@esb.lol> Co-authored-by: Takayuki KUSANO <65759+tkusano@users.noreply.github.com> Co-authored-by: Hailey <me@haileyok.com>
This commit is contained in:
parent
d5ebbeb3fc
commit
20d463ff2f
165 changed files with 7034 additions and 5009 deletions
183
src/components/ReportDialog/SelectReportOptionView.tsx
Normal file
183
src/components/ReportDialog/SelectReportOptionView.tsx
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
import React from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {AppBskyLabelerDefs} from '@atproto/api'
|
||||
|
||||
import {useReportOptions, ReportOption} from '#/lib/moderation/useReportOptions'
|
||||
import {DMCA_LINK} from '#/components/ReportDialog/const'
|
||||
import {Link} from '#/components/Link'
|
||||
export {useDialogControl as useReportDialogControl} from '#/components/Dialog'
|
||||
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {
|
||||
Button,
|
||||
ButtonIcon,
|
||||
ButtonText,
|
||||
useButtonContext,
|
||||
} from '#/components/Button'
|
||||
import {Divider} from '#/components/Divider'
|
||||
import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron'
|
||||
import {SquareArrowTopRight_Stroke2_Corner0_Rounded as SquareArrowTopRight} from '#/components/icons/SquareArrowTopRight'
|
||||
|
||||
import {ReportDialogProps} from './types'
|
||||
|
||||
export function SelectReportOptionView({
|
||||
...props
|
||||
}: ReportDialogProps & {
|
||||
labelers: AppBskyLabelerDefs.LabelerViewDetailed[]
|
||||
onSelectReportOption: (reportOption: ReportOption) => void
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
const allReportOptions = useReportOptions()
|
||||
const reportOptions = allReportOptions[props.params.type]
|
||||
|
||||
const i18n = React.useMemo(() => {
|
||||
let title = _(msg`Report this content`)
|
||||
let description = _(msg`Why should this content be reviewed?`)
|
||||
|
||||
if (props.params.type === 'account') {
|
||||
title = _(msg`Report this user`)
|
||||
description = _(msg`Why should this user be reviewed?`)
|
||||
} else if (props.params.type === 'post') {
|
||||
title = _(msg`Report this post`)
|
||||
description = _(msg`Why should this post be reviewed?`)
|
||||
} else if (props.params.type === 'list') {
|
||||
title = _(msg`Report this list`)
|
||||
description = _(msg`Why should this list be reviewed?`)
|
||||
} else if (props.params.type === 'feedgen') {
|
||||
title = _(msg`Report this feed`)
|
||||
description = _(msg`Why should this feed be reviewed?`)
|
||||
}
|
||||
|
||||
return {
|
||||
title,
|
||||
description,
|
||||
}
|
||||
}, [_, props.params.type])
|
||||
|
||||
return (
|
||||
<View style={[a.gap_lg]}>
|
||||
<View style={[a.justify_center, a.gap_sm]}>
|
||||
<Text style={[a.text_2xl, a.font_bold]}>{i18n.title}</Text>
|
||||
<Text style={[a.text_md, t.atoms.text_contrast_medium]}>
|
||||
{i18n.description}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<Divider />
|
||||
|
||||
<View style={[a.gap_sm, {marginHorizontal: a.p_md.padding * -1}]}>
|
||||
{reportOptions.map(reportOption => {
|
||||
return (
|
||||
<Button
|
||||
key={reportOption.reason}
|
||||
label={_(msg`Create report for ${reportOption.title}`)}
|
||||
onPress={() => props.onSelectReportOption(reportOption)}>
|
||||
<ReportOptionButton
|
||||
title={reportOption.title}
|
||||
description={reportOption.description}
|
||||
/>
|
||||
</Button>
|
||||
)
|
||||
})}
|
||||
|
||||
{(props.params.type === 'post' || props.params.type === 'account') && (
|
||||
<View style={[a.pt_md, a.px_md]}>
|
||||
<View
|
||||
style={[
|
||||
a.flex_row,
|
||||
a.align_center,
|
||||
a.justify_between,
|
||||
a.gap_lg,
|
||||
a.p_md,
|
||||
a.pl_lg,
|
||||
a.rounded_md,
|
||||
t.atoms.bg_contrast_900,
|
||||
]}>
|
||||
<Text
|
||||
style={[
|
||||
a.flex_1,
|
||||
t.atoms.text_inverted,
|
||||
a.italic,
|
||||
a.leading_snug,
|
||||
]}>
|
||||
<Trans>Need to report a copyright violation?</Trans>
|
||||
</Text>
|
||||
<Link
|
||||
to={DMCA_LINK}
|
||||
label={_(msg`View details for reporting a copyright violation`)}
|
||||
size="small"
|
||||
variant="solid"
|
||||
color="secondary">
|
||||
<ButtonText>
|
||||
<Trans>View details</Trans>
|
||||
</ButtonText>
|
||||
<ButtonIcon position="right" icon={SquareArrowTopRight} />
|
||||
</Link>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
function ReportOptionButton({
|
||||
title,
|
||||
description,
|
||||
}: {
|
||||
title: string
|
||||
description: string
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const {hovered, pressed} = useButtonContext()
|
||||
const interacted = hovered || pressed
|
||||
|
||||
const styles = React.useMemo(() => {
|
||||
return {
|
||||
interacted: {
|
||||
backgroundColor: t.palette.contrast_50,
|
||||
},
|
||||
}
|
||||
}, [t])
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
a.w_full,
|
||||
a.flex_row,
|
||||
a.align_center,
|
||||
a.justify_between,
|
||||
a.p_md,
|
||||
a.rounded_md,
|
||||
{paddingRight: 70},
|
||||
interacted && styles.interacted,
|
||||
]}>
|
||||
<View style={[a.flex_1, a.gap_xs]}>
|
||||
<Text style={[a.text_md, a.font_bold, t.atoms.text_contrast_medium]}>
|
||||
{title}
|
||||
</Text>
|
||||
<Text style={[a.leading_tight, {maxWidth: 400}]}>{description}</Text>
|
||||
</View>
|
||||
|
||||
<View
|
||||
style={[
|
||||
a.absolute,
|
||||
a.inset_0,
|
||||
a.justify_center,
|
||||
a.pr_md,
|
||||
{left: 'auto'},
|
||||
]}>
|
||||
<ChevronRight
|
||||
size="md"
|
||||
fill={
|
||||
hovered ? t.palette.primary_500 : t.atoms.text_contrast_low.color
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
262
src/components/ReportDialog/SubmitView.tsx
Normal file
262
src/components/ReportDialog/SubmitView.tsx
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
import React from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {AppBskyLabelerDefs} from '@atproto/api'
|
||||
|
||||
import {getLabelingServiceTitle} from '#/lib/moderation'
|
||||
import {ReportOption} from '#/lib/moderation/useReportOptions'
|
||||
|
||||
import {atoms as a, useTheme, native} from '#/alf'
|
||||
import {Text} from '#/components/Typography'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||
import {ChevronLeft_Stroke2_Corner0_Rounded as ChevronLeft} from '#/components/icons/Chevron'
|
||||
import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check'
|
||||
import * as Toggle from '#/components/forms/Toggle'
|
||||
import {CharProgress} from '#/view/com/composer/char-progress/CharProgress'
|
||||
import {Loader} from '#/components/Loader'
|
||||
import * as Toast from '#/view/com/util/Toast'
|
||||
|
||||
import {ReportDialogProps} from './types'
|
||||
import {getAgent} from '#/state/session'
|
||||
|
||||
export function SubmitView({
|
||||
params,
|
||||
labelers,
|
||||
selectedReportOption,
|
||||
goBack,
|
||||
onSubmitComplete,
|
||||
}: ReportDialogProps & {
|
||||
labelers: AppBskyLabelerDefs.LabelerViewDetailed[]
|
||||
selectedReportOption: ReportOption
|
||||
goBack: () => void
|
||||
onSubmitComplete: () => void
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
const [details, setDetails] = React.useState<string>('')
|
||||
const [submitting, setSubmitting] = React.useState<boolean>(false)
|
||||
const [selectedServices, setSelectedServices] = React.useState<string[]>(
|
||||
labelers?.map(labeler => labeler.creator.did) || [],
|
||||
)
|
||||
const [error, setError] = React.useState('')
|
||||
|
||||
const submit = React.useCallback(async () => {
|
||||
setSubmitting(true)
|
||||
setError('')
|
||||
|
||||
const $type =
|
||||
params.type === 'account'
|
||||
? 'com.atproto.admin.defs#repoRef'
|
||||
: 'com.atproto.repo.strongRef'
|
||||
const report = {
|
||||
reasonType: selectedReportOption.reason,
|
||||
subject: {
|
||||
$type,
|
||||
...params,
|
||||
},
|
||||
reason: details,
|
||||
}
|
||||
const results = await Promise.all(
|
||||
selectedServices.map(did =>
|
||||
getAgent()
|
||||
.withProxy('atproto_labeler', did)
|
||||
.createModerationReport(report)
|
||||
.then(
|
||||
_ => true,
|
||||
_ => false,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
setSubmitting(false)
|
||||
|
||||
if (results.includes(true)) {
|
||||
Toast.show(_(msg`Thank you. Your report has been sent.`))
|
||||
onSubmitComplete()
|
||||
} else {
|
||||
setError(
|
||||
_(
|
||||
msg`There was an issue sending your report. Please check your internet connection.`,
|
||||
),
|
||||
)
|
||||
}
|
||||
}, [
|
||||
_,
|
||||
params,
|
||||
details,
|
||||
selectedReportOption,
|
||||
selectedServices,
|
||||
onSubmitComplete,
|
||||
setError,
|
||||
])
|
||||
|
||||
return (
|
||||
<View style={[a.gap_2xl]}>
|
||||
<Button
|
||||
size="small"
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
shape="round"
|
||||
label={_(msg`Go back to previous step`)}
|
||||
onPress={goBack}>
|
||||
<ButtonIcon icon={ChevronLeft} />
|
||||
</Button>
|
||||
|
||||
<View
|
||||
style={[
|
||||
a.w_full,
|
||||
a.flex_row,
|
||||
a.align_center,
|
||||
a.justify_between,
|
||||
a.gap_lg,
|
||||
a.p_md,
|
||||
a.rounded_md,
|
||||
a.border,
|
||||
t.atoms.border_contrast_low,
|
||||
]}>
|
||||
<View style={[a.flex_1, a.gap_xs]}>
|
||||
<Text style={[a.text_md, a.font_bold]}>
|
||||
{selectedReportOption.title}
|
||||
</Text>
|
||||
<Text style={[a.leading_tight, {maxWidth: 400}]}>
|
||||
{selectedReportOption.description}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<Check size="md" style={[a.pr_sm, t.atoms.text_contrast_low]} />
|
||||
</View>
|
||||
|
||||
<View style={[a.gap_md]}>
|
||||
<Text style={[t.atoms.text_contrast_medium]}>
|
||||
<Trans>Select the moderation service(s) to report to</Trans>
|
||||
</Text>
|
||||
|
||||
<Toggle.Group
|
||||
label="Select mod services"
|
||||
values={selectedServices}
|
||||
onChange={setSelectedServices}>
|
||||
<View style={[a.flex_row, a.gap_md, a.flex_wrap]}>
|
||||
{labelers.map(labeler => {
|
||||
const title = getLabelingServiceTitle({
|
||||
displayName: labeler.creator.displayName,
|
||||
handle: labeler.creator.handle,
|
||||
})
|
||||
return (
|
||||
<Toggle.Item
|
||||
key={labeler.creator.did}
|
||||
name={labeler.creator.did}
|
||||
label={title}>
|
||||
<LabelerToggle title={title} />
|
||||
</Toggle.Item>
|
||||
)
|
||||
})}
|
||||
</View>
|
||||
</Toggle.Group>
|
||||
</View>
|
||||
<View style={[a.gap_md]}>
|
||||
<Text style={[t.atoms.text_contrast_medium]}>
|
||||
<Trans>Optionally provide additional information below:</Trans>
|
||||
</Text>
|
||||
|
||||
<View style={[a.relative, a.w_full]}>
|
||||
<Dialog.Input
|
||||
multiline
|
||||
value={details}
|
||||
onChangeText={setDetails}
|
||||
label="Text field"
|
||||
style={{paddingRight: 60}}
|
||||
numberOfLines={6}
|
||||
/>
|
||||
|
||||
<View
|
||||
style={[
|
||||
a.absolute,
|
||||
a.flex_row,
|
||||
a.align_center,
|
||||
a.pr_md,
|
||||
a.pb_sm,
|
||||
{
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
},
|
||||
]}>
|
||||
<CharProgress count={details?.length || 0} />
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={[a.flex_row, a.align_center, a.justify_end, a.gap_lg]}>
|
||||
{!selectedServices.length ||
|
||||
(error && (
|
||||
<Text
|
||||
style={[
|
||||
a.flex_1,
|
||||
a.italic,
|
||||
a.leading_snug,
|
||||
t.atoms.text_contrast_medium,
|
||||
]}>
|
||||
{error ? (
|
||||
error
|
||||
) : (
|
||||
<Trans>You must select at least one labeler for a report</Trans>
|
||||
)}
|
||||
</Text>
|
||||
))}
|
||||
|
||||
<Button
|
||||
size="large"
|
||||
variant="solid"
|
||||
color="negative"
|
||||
label={_(msg`Send report`)}
|
||||
onPress={submit}
|
||||
disabled={!selectedServices.length}>
|
||||
<ButtonText>
|
||||
<Trans>Send report</Trans>
|
||||
</ButtonText>
|
||||
{submitting && <ButtonIcon icon={Loader} />}
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
function LabelerToggle({title}: {title: string}) {
|
||||
const t = useTheme()
|
||||
const ctx = Toggle.useItemContext()
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
a.flex_row,
|
||||
a.align_center,
|
||||
a.gap_md,
|
||||
a.p_md,
|
||||
a.pr_lg,
|
||||
a.rounded_sm,
|
||||
a.overflow_hidden,
|
||||
t.atoms.bg_contrast_25,
|
||||
ctx.selected && [t.atoms.bg_contrast_50],
|
||||
]}>
|
||||
<Toggle.Checkbox />
|
||||
<View
|
||||
style={[
|
||||
a.flex_row,
|
||||
a.align_center,
|
||||
a.justify_between,
|
||||
a.gap_lg,
|
||||
a.z_10,
|
||||
]}>
|
||||
<Text
|
||||
style={[
|
||||
native({marginTop: 2}),
|
||||
t.atoms.text_contrast_medium,
|
||||
ctx.selected && t.atoms.text,
|
||||
]}>
|
||||
{title}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
1
src/components/ReportDialog/const.ts
Normal file
1
src/components/ReportDialog/const.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export const DMCA_LINK = 'https://bsky.social/about/support/copyright'
|
||||
73
src/components/ReportDialog/index.tsx
Normal file
73
src/components/ReportDialog/index.tsx
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import React from 'react'
|
||||
import {View, Pressable} from 'react-native'
|
||||
import {Trans} from '@lingui/macro'
|
||||
|
||||
import {useMyLabelersQuery} from '#/state/queries/preferences'
|
||||
import {ReportOption} from '#/lib/moderation/useReportOptions'
|
||||
export {useDialogControl as useReportDialogControl} from '#/components/Dialog'
|
||||
|
||||
import {atoms as a} from '#/alf'
|
||||
import {Loader} from '#/components/Loader'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import {Text} from '#/components/Typography'
|
||||
|
||||
import {ReportDialogProps} from './types'
|
||||
import {SelectReportOptionView} from './SelectReportOptionView'
|
||||
import {SubmitView} from './SubmitView'
|
||||
import {useDelayedLoading} from '#/components/hooks/useDelayedLoading'
|
||||
|
||||
export function ReportDialog(props: ReportDialogProps) {
|
||||
return (
|
||||
<Dialog.Outer control={props.control}>
|
||||
<Dialog.Handle />
|
||||
|
||||
<ReportDialogInner {...props} />
|
||||
</Dialog.Outer>
|
||||
)
|
||||
}
|
||||
|
||||
function ReportDialogInner(props: ReportDialogProps) {
|
||||
const {
|
||||
isLoading: isLabelerLoading,
|
||||
data: labelers,
|
||||
error,
|
||||
} = useMyLabelersQuery()
|
||||
const isLoading = useDelayedLoading(500, isLabelerLoading)
|
||||
const [selectedReportOption, setSelectedReportOption] = React.useState<
|
||||
ReportOption | undefined
|
||||
>()
|
||||
|
||||
return (
|
||||
<Dialog.ScrollableInner label="Report Dialog">
|
||||
{isLoading ? (
|
||||
<View style={[a.align_center, {height: 100}]}>
|
||||
<Loader size="xl" />
|
||||
{/* Here to capture focus for a hot sec to prevent flash */}
|
||||
<Pressable accessible={false} />
|
||||
</View>
|
||||
) : error || !labelers ? (
|
||||
<View>
|
||||
<Text style={[a.text_md]}>
|
||||
<Trans>Something went wrong, please try again.</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
) : selectedReportOption ? (
|
||||
<SubmitView
|
||||
{...props}
|
||||
labelers={labelers}
|
||||
selectedReportOption={selectedReportOption}
|
||||
goBack={() => setSelectedReportOption(undefined)}
|
||||
onSubmitComplete={() => props.control.close()}
|
||||
/>
|
||||
) : (
|
||||
<SelectReportOptionView
|
||||
{...props}
|
||||
labelers={labelers}
|
||||
onSelectReportOption={setSelectedReportOption}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Dialog.Close />
|
||||
</Dialog.ScrollableInner>
|
||||
)
|
||||
}
|
||||
15
src/components/ReportDialog/types.ts
Normal file
15
src/components/ReportDialog/types.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import * as Dialog from '#/components/Dialog'
|
||||
|
||||
export type ReportDialogProps = {
|
||||
control: Dialog.DialogOuterProps['control']
|
||||
params:
|
||||
| {
|
||||
type: 'post' | 'list' | 'feedgen' | 'other'
|
||||
uri: string
|
||||
cid: string
|
||||
}
|
||||
| {
|
||||
type: 'account'
|
||||
did: string
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue