Update ModerationDetailsDialog

This commit is contained in:
Eric Bailey
2024-02-26 17:41:51 -06:00
parent dbd42ae0c5
commit 67fea6d317
8 changed files with 219 additions and 147 deletions
@@ -7,29 +7,39 @@ import {ModerationCause} from '@atproto/api'
import {atoms as a, useBreakpoints} from '#/alf' import {atoms as a, useBreakpoints} from '#/alf'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import * as Dialog from '#/components/Dialog' import * as Dialog from '#/components/Dialog'
import {GlobalDialogProps} from '#/components/dialogs'
import {Button} from '#/components/Button' import {Button} from '#/components/Button'
import {InlineLink} from '#/components/Link' import {InlineLink} from '#/components/Link'
import {useLabelStrings} from '#/lib/moderation/useLabelStrings' import {useLabelStrings} from '#/lib/moderation/useLabelStrings'
import {listUriToHref} from '#/lib/strings/url-helpers' import {listUriToHref} from '#/lib/strings/url-helpers'
export {useDialogControl as useModerationDetailsDialogControl} from '#/components/Dialog'
export interface ModerationDetailsDialogProps { export interface ModerationDetailsDialogProps {
control: Dialog.DialogOuterProps['control']
context: 'account' | 'content' context: 'account' | 'content'
modcause: ModerationCause modcause: ModerationCause
} }
export function ModerationDetailsDialog({ export function ModerationDetailsDialog(props: ModerationDetailsDialogProps) {
params, return (
cleanup, <Dialog.Outer control={props.control}>
}: GlobalDialogProps<ModerationDetailsDialogProps>) { <Dialog.Handle />
<ModerationDetailsDialogInner {...props} />
</Dialog.Outer>
)
}
function ModerationDetailsDialogInner({
context,
modcause,
}: ModerationDetailsDialogProps & {
control: Dialog.DialogOuterProps['control']
}) {
const {_} = useLingui() const {_} = useLingui()
const labelStrings = useLabelStrings() const labelStrings = useLabelStrings()
const control = Dialog.useDialogControl() const control = Dialog.useDialogControl()
const {gtMobile} = useBreakpoints() const {gtMobile} = useBreakpoints()
const {context, modcause} = params
// REQUIRED CLEANUP
const onClose = React.useCallback(() => cleanup(), [cleanup])
let name let name
let description let description
@@ -99,30 +109,26 @@ export function ModerationDetailsDialog({
} }
return ( return (
<Dialog.Outer defaultOpen control={control} onClose={onClose}> <Dialog.ScrollableInner
<Dialog.Handle /> accessibilityDescribedBy="dialog-description"
accessibilityLabelledBy="dialog-title">
<Dialog.ScrollableInner <Text nativeID="dialog-title" style={[a.text_2xl, a.font_bold]}>
accessibilityDescribedBy="dialog-description" {name}
accessibilityLabelledBy="dialog-title"> </Text>
<Text nativeID="dialog-title" style={[a.text_2xl, a.font_bold]}> <Text nativeID="dialog-description" style={[a.text_sm]}>
{name} {description}
</Text> </Text>
<Text nativeID="dialog-description" style={[a.text_sm]}> <View style={gtMobile && [a.flex_row, a.justify_end]}>
{description} <Button
</Text> testID="doneBtn"
<View style={gtMobile && [a.flex_row, a.justify_end]}> variant="outline"
<Button color="primary"
testID="doneBtn" size="small"
variant="outline" onPress={() => control.close()}
color="primary" label={_(msg`Done`)}>
size="small" {_(msg`Done`)}
onPress={() => control.close()} </Button>
label={_(msg`Done`)}> </View>
{_(msg`Done`)} </Dialog.ScrollableInner>
</Button>
</View>
</Dialog.ScrollableInner>
</Dialog.Outer>
) )
} }
+29
View File
@@ -0,0 +1,29 @@
import React from 'react'
import * as Dialog from '#/components/Dialog'
type Control = Dialog.DialogOuterProps['control']
type ControlsContext = {
mutedWordsDialogControl: Control
}
const ControlsContext = React.createContext({
mutedWordsDialogControl: {} as Control,
})
export function useGlobalDialogsControlContext() {
return React.useContext(ControlsContext)
}
export function Provider({children}: React.PropsWithChildren<{}>) {
const mutedWordsDialogControl = Dialog.useDialogControl()
const ctx = React.useMemo<ControlsContext>(
() => ({mutedWordsDialogControl}),
[mutedWordsDialogControl],
)
return (
<ControlsContext.Provider value={ctx}>{children}</ControlsContext.Provider>
)
}
+5 -2
View File
@@ -1,6 +1,7 @@
import React from 'react' import React from 'react'
import {DialogControlProps} from '#/components/Dialog' import {DialogControlProps} from '#/components/Dialog'
import {Provider as GlobalDialogsProvider} from '#/components/dialogs' import {Provider as GlobalDialogsProvider} from '#/components/dialogs'
import {Provider as GlobalDialogsProviderV2} from '#/components/dialogs/Context'
const DialogContext = React.createContext<{ const DialogContext = React.createContext<{
activeDialogs: React.MutableRefObject< activeDialogs: React.MutableRefObject<
@@ -28,7 +29,7 @@ export function useDialogStateControlContext() {
export function Provider({children}: React.PropsWithChildren<{}>) { export function Provider({children}: React.PropsWithChildren<{}>) {
const activeDialogs = React.useRef< const activeDialogs = React.useRef<
Map<string, React.MutableRefObject<DialogControlProps<{}>>> Map<string, React.MutableRefObject<DialogControlProps>>
>(new Map()) >(new Map())
const closeAllDialogs = React.useCallback(() => { const closeAllDialogs = React.useCallback(() => {
activeDialogs.current.forEach(dialog => dialog.current.close()) activeDialogs.current.forEach(dialog => dialog.current.close())
@@ -38,7 +39,9 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
return ( return (
<DialogContext.Provider value={context}> <DialogContext.Provider value={context}>
<DialogControlContext.Provider value={controls}> <DialogControlContext.Provider value={controls}>
<GlobalDialogsProvider>{children}</GlobalDialogsProvider> <GlobalDialogsProvider>
<GlobalDialogsProviderV2>{children}</GlobalDialogsProviderV2>
</GlobalDialogsProvider>
</DialogControlContext.Provider> </DialogControlContext.Provider>
</DialogContext.Provider> </DialogContext.Provider>
) )
+13 -11
View File
@@ -11,8 +11,10 @@ import {Button, ButtonText, ButtonIcon} from '#/components/Button'
import {Shield_Stroke2_Corner0_Rounded as Shield} from '#/components/icons/Shield' import {Shield_Stroke2_Corner0_Rounded as Shield} from '#/components/icons/Shield'
import {EyeSlash_Stroke2_Corner0_Rounded as EyeSlash} from '#/components/icons/EyeSlash' import {EyeSlash_Stroke2_Corner0_Rounded as EyeSlash} from '#/components/icons/EyeSlash'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {ModerationDetailsDialog} from '#/components/dialogs/ModerationDetails' import {
import {useOpenGlobalDialog} from '#/components/dialogs' ModerationDetailsDialog,
useModerationDetailsDialogControl,
} from '#/components/ModerationDetailsDialog'
export function ContentHider({ export function ContentHider({
testID, testID,
@@ -32,7 +34,7 @@ export function ContentHider({
const {_} = useLingui() const {_} = useLingui()
const [override, setOverride] = React.useState(false) const [override, setOverride] = React.useState(false)
const {gtMobile} = useBreakpoints() const {gtMobile} = useBreakpoints()
const openDialog = useOpenGlobalDialog() const control = useModerationDetailsDialogControl()
const blur = modui?.blurs[0] const blur = modui?.blurs[0]
const desc = useModerationCauseDescription(blur, 'content') const desc = useModerationCauseDescription(blur, 'content')
@@ -47,6 +49,12 @@ export function ContentHider({
return ( return (
<View testID={testID} style={[a.overflow_hidden, style]}> <View testID={testID} style={[a.overflow_hidden, style]}>
<ModerationDetailsDialog
control={control}
context="content"
modcause={blur}
/>
<View style={[a.flex_col, a.gap_xs]}> <View style={[a.flex_col, a.gap_xs]}>
<Button <Button
variant="solid" variant="solid"
@@ -57,10 +65,7 @@ export function ContentHider({
if (!modui.noOverride) { if (!modui.noOverride) {
setOverride(v => !v) setOverride(v => !v)
} else { } else {
openDialog(ModerationDetailsDialog, { control.open()
context: 'content',
modcause: blur,
})
} }
}} }}
label={desc.name} label={desc.name}
@@ -83,10 +88,7 @@ export function ContentHider({
variant="ghost" variant="ghost"
size="tiny" size="tiny"
onPress={() => { onPress={() => {
openDialog(ModerationDetailsDialog, { control.open()
context: 'content',
modcause: blur,
})
}} }}
label={_(msg`Learn more`)}> label={_(msg`Learn more`)}>
<ButtonText <ButtonText
+55 -43
View File
@@ -11,8 +11,10 @@ import {Button, ButtonText, ButtonIcon} from '#/components/Button'
import {Shield_Stroke2_Corner0_Rounded as Shield} from '#/components/icons/Shield' import {Shield_Stroke2_Corner0_Rounded as Shield} from '#/components/icons/Shield'
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
import {EyeSlash_Stroke2_Corner0_Rounded as EyeSlash} from '#/components/icons/EyeSlash' import {EyeSlash_Stroke2_Corner0_Rounded as EyeSlash} from '#/components/icons/EyeSlash'
import {ModerationDetailsDialog} from '#/components/dialogs/ModerationDetails' import {
import {useOpenGlobalDialog} from '#/components/dialogs' ModerationDetailsDialog,
useModerationDetailsDialogControl,
} from '#/components/ModerationDetailsDialog'
export function PostAlerts({ export function PostAlerts({
modui, modui,
@@ -43,57 +45,67 @@ export function PostAlerts({
} }
function PostInform({cause}: {cause: ModerationCause}) { function PostInform({cause}: {cause: ModerationCause}) {
const openDialog = useOpenGlobalDialog() const control = useModerationDetailsDialogControl()
const desc = useModerationCauseDescription(cause, 'content') const desc = useModerationCauseDescription(cause, 'content')
return ( return (
<Button <>
label={desc.name} <Button
variant="solid" label={desc.name}
color="secondary" variant="solid"
size="tiny" color="secondary"
shape="default" size="tiny"
onPress={() => { shape="default"
openDialog(ModerationDetailsDialog, { onPress={() => {
context: 'content', control.open()
modcause: cause, }}>
}) <ButtonIcon
}}> icon={cause.type === 'muted' ? EyeSlash : CircleInfo}
<ButtonIcon position="left"
icon={cause.type === 'muted' ? EyeSlash : CircleInfo} />{' '}
position="left" <ButtonText>{desc.name}</ButtonText>
/>{' '} </Button>
<ButtonText>{desc.name}</ButtonText>
</Button> <ModerationDetailsDialog
control={control}
context="content"
modcause={cause}
/>
</>
) )
} }
function PostAlert({cause}: {cause: ModerationCause}) { function PostAlert({cause}: {cause: ModerationCause}) {
const t = useTheme() const t = useTheme()
const openDialog = useOpenGlobalDialog() const control = useModerationDetailsDialogControl()
const desc = useModerationCauseDescription(cause, 'content') const desc = useModerationCauseDescription(cause, 'content')
return ( return (
<Button <>
label={desc.name} <Button
variant="solid" label={desc.name}
color="secondary" variant="solid"
size="small" color="secondary"
shape="default" size="small"
onPress={() => { shape="default"
openDialog(ModerationDetailsDialog, { onPress={() => {
context: 'content', control.open()
modcause: cause, }}>
}) <ButtonIcon icon={Shield} position="left" />
}}> <ButtonText style={[a.flex_1, a.text_left]}>
<ButtonIcon icon={Shield} position="left" /> {desc.name}
<ButtonText style={[a.flex_1, a.text_left]}> <Text style={[a.text_sm, t.atoms.text_contrast_medium]}>
{desc.name} {' — ' /* TODO get actual labeler */}
<Text style={[a.text_sm, t.atoms.text_contrast_medium]}> <Trans>Bluesky Safety</Trans>
{' — ' /* TODO get actual labeler */} </Text>
<Trans>Bluesky Safety</Trans> </ButtonText>
</Text> </Button>
</ButtonText>
</Button> <ModerationDetailsDialog
control={control}
context="content"
modcause={cause}
/>
</>
) )
} }
+11 -7
View File
@@ -11,8 +11,10 @@ import {useLingui} from '@lingui/react'
import {Trans, msg} from '@lingui/macro' import {Trans, msg} from '@lingui/macro'
import {useModerationCauseDescription} from '#/lib/moderation/useModerationCauseDescription' import {useModerationCauseDescription} from '#/lib/moderation/useModerationCauseDescription'
import {ModerationDetailsDialog} from '#/components/dialogs/ModerationDetails' import {
import {useOpenGlobalDialog} from '#/components/dialogs' ModerationDetailsDialog,
useModerationDetailsDialogControl,
} from '#/components/ModerationDetailsDialog'
interface Props extends ComponentProps<typeof Link> { interface Props extends ComponentProps<typeof Link> {
iconSize: number iconSize: number
@@ -33,7 +35,7 @@ export function PostHider({
const pal = usePalette('default') const pal = usePalette('default')
const {_} = useLingui() const {_} = useLingui()
const [override, setOverride] = React.useState(false) const [override, setOverride] = React.useState(false)
const openDialog = useOpenGlobalDialog() const control = useModerationDetailsDialogControl()
const blur = modui.blurs[0] const blur = modui.blurs[0]
const desc = useModerationCauseDescription(blur, 'content') const desc = useModerationCauseDescription(blur, 'content')
@@ -69,12 +71,14 @@ export function PostHider({
override ? {paddingBottom: 0} : undefined, override ? {paddingBottom: 0} : undefined,
pal.view, pal.view,
]}> ]}>
<ModerationDetailsDialog
control={control}
context="content"
modcause={blur}
/>
<Pressable <Pressable
onPress={() => { onPress={() => {
openDialog(ModerationDetailsDialog, { control.open()
context: 'content',
modcause: blur,
})
}} }}
accessibilityRole="button" accessibilityRole="button"
accessibilityLabel={_(msg`Learn more about this warning`)} accessibilityLabel={_(msg`Learn more about this warning`)}
@@ -11,8 +11,10 @@ import {Button, ButtonText, ButtonIcon} from '#/components/Button'
import {Shield_Stroke2_Corner0_Rounded as Shield} from '#/components/icons/Shield' import {Shield_Stroke2_Corner0_Rounded as Shield} from '#/components/icons/Shield'
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
import {EyeSlash_Stroke2_Corner0_Rounded as EyeSlash} from '#/components/icons/EyeSlash' import {EyeSlash_Stroke2_Corner0_Rounded as EyeSlash} from '#/components/icons/EyeSlash'
import {ModerationDetailsDialog} from '#/components/dialogs/ModerationDetails' import {
import {useOpenGlobalDialog} from '#/components/dialogs' ModerationDetailsDialog,
useModerationDetailsDialogControl,
} from '#/components/ModerationDetailsDialog'
export function ProfileHeaderAlerts({ export function ProfileHeaderAlerts({
moderation, moderation,
@@ -43,57 +45,66 @@ export function ProfileHeaderAlerts({
} }
function ProfileInform({cause}: {cause: ModerationCause}) { function ProfileInform({cause}: {cause: ModerationCause}) {
const openDialog = useOpenGlobalDialog() const control = useModerationDetailsDialogControl()
const desc = useModerationCauseDescription(cause, 'account') const desc = useModerationCauseDescription(cause, 'account')
return ( return (
<Button <>
label={desc.name} <Button
variant="solid" label={desc.name}
color="secondary" variant="solid"
size="tiny" color="secondary"
shape="default" size="tiny"
onPress={() => { shape="default"
openDialog(ModerationDetailsDialog, { onPress={() => {
context: 'account', control.open()
modcause: cause, }}>
}) <ButtonIcon
}}> icon={cause.type === 'muted' ? EyeSlash : CircleInfo}
<ButtonIcon position="left"
icon={cause.type === 'muted' ? EyeSlash : CircleInfo} />{' '}
position="left" <ButtonText>{desc.name}</ButtonText>
/>{' '} </Button>
<ButtonText>{desc.name}</ButtonText>
</Button> <ModerationDetailsDialog
control={control}
context="account"
modcause={cause}
/>
</>
) )
} }
function ProfileAlert({cause}: {cause: ModerationCause}) { function ProfileAlert({cause}: {cause: ModerationCause}) {
const t = useTheme() const t = useTheme()
const openDialog = useOpenGlobalDialog() const control = useModerationDetailsDialogControl()
const desc = useModerationCauseDescription(cause, 'account') const desc = useModerationCauseDescription(cause, 'account')
return ( return (
<Button <>
label={desc.name} <Button
variant="solid" label={desc.name}
color="secondary" variant="solid"
size="small" color="secondary"
shape="default" size="small"
onPress={() => { shape="default"
openDialog(ModerationDetailsDialog, { onPress={() => {
context: 'account', control.open()
modcause: cause, }}>
}) <ButtonIcon icon={Shield} position="left" />
}}> <ButtonText style={[a.flex_1, a.text_left]}>
<ButtonIcon icon={Shield} position="left" /> {desc.name}
<ButtonText style={[a.flex_1, a.text_left]}> <Text style={[a.text_sm, t.atoms.text_contrast_medium]}>
{desc.name} {' — ' /* TODO get actual labeler */}
<Text style={[a.text_sm, t.atoms.text_contrast_medium]}> <Trans>Bluesky Safety</Trans>
{' — ' /* TODO get actual labeler */} </Text>
<Trans>Bluesky Safety</Trans> </ButtonText>
</Text> </Button>
</ButtonText> <ModerationDetailsDialog
</Button> control={control}
context="account"
modcause={cause}
/>
</>
) )
} }
+12 -7
View File
@@ -23,8 +23,10 @@ import {useModerationCauseDescription} from '#/lib/moderation/useModerationCause
import {s} from '#/lib/styles' import {s} from '#/lib/styles'
import {CenteredView} from '../Views' import {CenteredView} from '../Views'
import {ModerationDetailsDialog} from '#/components/dialogs/ModerationDetails' import {
import {useOpenGlobalDialog} from '#/components/dialogs' ModerationDetailsDialog,
useModerationDetailsDialogControl,
} from '#/components/ModerationDetailsDialog'
export function ScreenHider({ export function ScreenHider({
testID, testID,
@@ -46,7 +48,7 @@ export function ScreenHider({
const [override, setOverride] = React.useState(false) const [override, setOverride] = React.useState(false)
const navigation = useNavigation<NavigationProp>() const navigation = useNavigation<NavigationProp>()
const {isMobile} = useWebMediaQueries() const {isMobile} = useWebMediaQueries()
const openDialog = useOpenGlobalDialog() const control = useModerationDetailsDialogControl()
const blur = modui.blurs[0] const blur = modui.blurs[0]
const desc = useModerationCauseDescription(blur, 'content') const desc = useModerationCauseDescription(blur, 'content')
@@ -95,10 +97,7 @@ export function ScreenHider({
</Text> </Text>
<TouchableWithoutFeedback <TouchableWithoutFeedback
onPress={() => { onPress={() => {
openDialog(ModerationDetailsDialog, { control.open()
context: 'account',
modcause: blur,
})
}} }}
accessibilityRole="button" accessibilityRole="button"
accessibilityLabel={_(msg`Learn more about this warning`)} accessibilityLabel={_(msg`Learn more about this warning`)}
@@ -107,6 +106,12 @@ export function ScreenHider({
<Trans>Learn More</Trans> <Trans>Learn More</Trans>
</Text> </Text>
</TouchableWithoutFeedback> </TouchableWithoutFeedback>
<ModerationDetailsDialog
control={control}
context="account"
modcause={blur}
/>
</> </>
)}{' '} )}{' '}
</Text> </Text>