Rework the moderationlabelpref

This commit is contained in:
Paul Frazee
2024-03-09 17:50:29 -08:00
parent af124516b3
commit c7e20a4b3d
5 changed files with 112 additions and 202 deletions
+1 -1
View File
@@ -103,7 +103,7 @@ function ButtonInner({children}: React.PropsWithChildren<{}>) {
native({ native({
paddingBottom: 10, paddingBottom: 10,
}), }),
a.px_sm, a.px_md,
t.atoms.bg, t.atoms.bg,
t.atoms.border_contrast_low, t.atoms.border_contrast_low,
baseStyles, baseStyles,
+89 -184
View File
@@ -5,10 +5,7 @@ import {useLingui} from '@lingui/react'
import {msg, Trans} from '@lingui/macro' import {msg, Trans} from '@lingui/macro'
import {useGlobalLabelStrings} from '#/lib/moderation/useGlobalLabelStrings' import {useGlobalLabelStrings} from '#/lib/moderation/useGlobalLabelStrings'
import { import {useLabelBehaviorDescription} from '#/lib/moderation/useLabelBehaviorDescription'
useLabelBehaviorDescription,
useLabelLongBehaviorDescription,
} from '#/lib/moderation/useLabelBehaviorDescription'
import { import {
usePreferencesQuery, usePreferencesQuery,
usePreferencesSetContentLabelMutation, usePreferencesSetContentLabelMutation,
@@ -19,11 +16,8 @@ import {useTheme, atoms as a} from '#/alf'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {InlineLink} from '#/components/Link' import {InlineLink} from '#/components/Link'
import * as Dialog from '#/components/Dialog' import * as Dialog from '#/components/Dialog'
import {Button} from '#/components/Button'
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '../icons/CircleInfo' import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '../icons/CircleInfo'
import {SettingsGear2_Stroke2_Corner0_Rounded as Gear} from '../icons/Gear' import * as ToggleButton from '#/components/forms/ToggleButton'
import * as Toggle from '#/components/forms/Toggle'
import {Divider} from '#/components/Divider'
export function ModerationLabelPref({ export function ModerationLabelPref({
labelValueDefinition, labelValueDefinition,
@@ -52,18 +46,34 @@ export function ModerationLabelPref({
savedPref ?? savedPref ??
labelValueDefinition.defaultSetting ?? labelValueDefinition.defaultSetting ??
'warn' 'warn'
const [selected, setSelected] = React.useState<LabelPreference[]>([pref])
const settingDesc = useLabelBehaviorDescription(labelValueDefinition, pref) // does the 'warn' setting make sense for this label?
const hideLabel = useLabelLongBehaviorDescription( const canWarn = !(
labelValueDefinition, labelValueDefinition.blurs === 'none' &&
'hide', labelValueDefinition.severity === 'none'
) )
const warnLabel = useLabelLongBehaviorDescription( // is this label adult only?
const adultOnly = labelValueDefinition.flags.includes('adult')
// is this label disabled because it's adult only?
const adultDisabled =
adultOnly && !preferences?.moderationPrefs.adultContentEnabled
// are there any reasons we cant configure this label here?
const cantConfigure = isGlobalLabel || adultDisabled
// adjust the pref based on whether warn is available
let prefAdjusted = pref
if (!canWarn && pref === 'warn') {
prefAdjusted = 'ignore'
}
// grab localized descriptions of the label and its settings
const currentPrefLabel = useLabelBehaviorDescription(
labelValueDefinition, labelValueDefinition,
'warn', prefAdjusted,
) )
const ignoreLabel = useLabelLongBehaviorDescription( const hideLabel = useLabelBehaviorDescription(labelValueDefinition, 'hide')
const warnLabel = useLabelBehaviorDescription(labelValueDefinition, 'warn')
const ignoreLabel = useLabelBehaviorDescription(
labelValueDefinition, labelValueDefinition,
'ignore', 'ignore',
) )
@@ -74,178 +84,73 @@ export function ModerationLabelPref({
labelValueDefinition, labelValueDefinition,
) )
const canWarn = !(
labelValueDefinition.blurs === 'none' &&
labelValueDefinition.severity === 'none'
)
const adultOnly = labelValueDefinition.flags.includes('adult')
const adultDisabled =
adultOnly && !preferences?.moderationPrefs.adultContentEnabled
const cantConfigure = isGlobalLabel || adultDisabled
const onSettingChange = React.useCallback(
(newPrefs: LabelPreference[]) => {
setSelected(newPrefs)
mutate({label: identifier, visibility: newPrefs[0], labelerDid})
},
[mutate, labelerDid, identifier],
)
const settings = [
{
pref: 'hide',
label: hideLabel,
},
canWarn && {
pref: 'warn',
label: warnLabel,
},
{
pref: 'ignore',
label: ignoreLabel,
},
].filter(Boolean) as {
pref: LabelPreference
label: string
}[]
return ( return (
<> <View style={[a.flex_row, a.gap_sm, a.px_lg, a.py_lg, a.justify_between]}>
<Button <View style={[a.gap_xs, a.flex_1]}>
onPress={() => control.open()} <Text style={[a.font_bold]}>{labelStrings.name}</Text>
label={settingDesc} <Text style={[t.atoms.text_contrast_medium, a.leading_snug]}>
disabled={disabled}> {labelStrings.description}
{({hovered, focused, pressed}) => ( </Text>
<View
style={[
a.w_full,
a.flex_row,
a.justify_between,
a.gap_sm,
a.align_start,
a.px_lg,
a.py_lg,
a.rounded_sm,
t.atoms.bg_contrast_25,
(hovered || focused || pressed) && [t.atoms.bg_contrast_50],
]}>
<View style={[a.gap_xs]}>
<Text style={[a.font_bold]}>{labelStrings.name}</Text>
<Text
style={[t.atoms.text_contrast_medium, a.leading_snug]}
numberOfLines={1}>
{labelStrings.description}
</Text>
</View>
{!disabled && ( {cantConfigure && (
<View <View style={[a.flex_row, a.gap_xs, a.align_center, a.mt_xs]}>
style={[ <CircleInfo size="sm" fill={t.atoms.text_contrast_high.color} />
a.flex_row,
a.align_center, <Text style={[t.atoms.text_contrast_high, a.font_semibold]}>
a.justify_end, {adultDisabled ? (
a.gap_xs, <Trans>Adult content is disabled.</Trans>
a.rounded_2xs, ) : isGlobalLabel ? (
]}> <Trans>
<Text style={[t.atoms.text_contrast_medium, a.font_semibold]}> Configured in{' '}
{settingDesc} <InlineLink
</Text> to="/moderation"
<Gear size="sm" fill={t.atoms.text_contrast_low.color} /> onPress={() => control.close()}
</View> style={a.text_sm}>
)} moderation settings
</InlineLink>
.
</Trans>
) : null}
</Text>
</View> </View>
)} )}
</Button> </View>
{disabled ? (
<Dialog.Outer control={control}> <></>
<Dialog.Handle /> ) : cantConfigure ? (
<View style={[{minHeight: 35}, a.px_sm, a.py_md]}>
<Dialog.Inner <Text style={[a.font_bold, t.atoms.text_contrast_medium]}>
label={_(msg`Settings for ${labelValueDefinition.identifier}`)} {currentPrefLabel}
style={[a.gap_sm]}>
<Text style={[a.text_2xl, a.font_bold, a.pb_xs, t.atoms.text]}>
{labelStrings.name}
</Text> </Text>
<Text style={[a.text_md, a.pb_sm, t.atoms.text_contrast_medium]}> </View>
{labelStrings.description} ) : (
</Text> <View style={[{minHeight: 35}]}>
<ToggleButton.Group
{cantConfigure && ( label={_(
<View msg`Configure content filtering setting for category: ${labelStrings.name.toLowerCase()}`,
style={[ )}
a.flex_row, values={[prefAdjusted]}
a.gap_xs, onChange={newPref =>
a.align_center, mutate({
a.py_md, label: identifier,
a.px_lg, visibility: newPref[0] as LabelPreference,
a.rounded_sm, labelerDid,
t.atoms.bg_contrast_25, })
]}> }>
<CircleInfo size="md" fill={t.atoms.text_contrast_medium.color} /> <ToggleButton.Button name="ignore" label={ignoreLabel}>
{ignoreLabel}
<Text style={[t.atoms.text_contrast_medium]}> </ToggleButton.Button>
{adultDisabled ? ( {canWarn && (
<Trans> <ToggleButton.Button name="warn" label={warnLabel}>
Adult content must be enabled to configure this label. {warnLabel}
</Trans> </ToggleButton.Button>
) : isGlobalLabel ? ( )}
<Trans> <ToggleButton.Button name="hide" label={hideLabel}>
{labelStrings.name} is configured in your{' '} {hideLabel}
<InlineLink </ToggleButton.Button>
to="/moderation" </ToggleButton.Group>
onPress={() => control.close()} </View>
style={a.text_md}> )}
global moderation settings </View>
</InlineLink>
.
</Trans>
) : null}
</Text>
</View>
)}
{!cantConfigure && (
<Toggle.Group<LabelPreference>
type="radio"
values={selected}
onChange={onSettingChange}
label={_(
msg`Configure filtering settings for ${labelStrings.name}`,
)}>
<View
style={[
a.rounded_md,
a.overflow_hidden,
t.atoms.bg_contrast_25,
]}>
{settings.map((s, i) => (
<>
{i !== 0 && <Divider />}
<Toggle.Item name={s.pref} label={s.label}>
<View
style={[
a.flex_1,
a.flex_row,
a.align_center,
a.gap_md,
a.py_md,
a.px_lg,
t.atoms.bg_contrast_25,
]}>
<Toggle.Radio />
<Text>{s.label}</Text>
</View>
</Toggle.Item>
</>
))}
</View>
</Toggle.Group>
)}
<Dialog.Close />
</Dialog.Inner>
</Dialog.Outer>
</>
) )
} }
@@ -8,23 +8,18 @@ export function useLabelBehaviorDescription(
) { ) {
const {_} = useLingui() const {_} = useLingui()
if (pref === 'ignore') { if (pref === 'ignore') {
return _(msg`Disabled`) return _(msg`Off`)
} }
if (labelValueDef.blurs === 'content') { if (labelValueDef.blurs === 'content' || labelValueDef.blurs === 'media') {
if (pref === 'hide') { if (pref === 'hide') {
return _(msg`Hide`) return _(msg`Hide`)
} }
return _(msg`Warn`) return _(msg`Warn`)
} else if (labelValueDef.blurs === 'media') {
if (pref === 'hide') {
return _(msg`Hide`)
}
return _(msg`Blur images`)
} else if (labelValueDef.severity === 'alert') { } else if (labelValueDef.severity === 'alert') {
if (pref === 'hide') { if (pref === 'hide') {
return _(msg`Hide`) return _(msg`Hide`)
} }
return _(msg`Show warning`) return _(msg`Warn`)
} else if (labelValueDef.severity === 'inform') { } else if (labelValueDef.severity === 'inform') {
if (pref === 'hide') { if (pref === 'hide') {
return _(msg`Hide`) return _(msg`Hide`)
@@ -67,7 +67,7 @@ export function ModerationOption({
]} ]}
layout={Layout.easing(Easing.ease).duration(200)} layout={Layout.easing(Easing.ease).duration(200)}
entering={isMounted.current ? FadeIn : undefined}> entering={isMounted.current ? FadeIn : undefined}>
<View style={[a.gap_xs, {width: '50%'}]}> <View style={[a.gap_xs, a.flex_1]}>
<Text style={[a.font_bold]}>{labelStrings.name}</Text> <Text style={[a.font_bold]}>{labelStrings.name}</Text>
<Text style={[t.atoms.text_contrast_medium, a.leading_snug]}> <Text style={[t.atoms.text_contrast_medium, a.leading_snug]}>
{labelStrings.description} {labelStrings.description}
+18 -8
View File
@@ -20,6 +20,7 @@ import {isNative} from '#/platform/detection'
import {useTheme, atoms as a} from '#/alf' import {useTheme, atoms as a} from '#/alf'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {Loader} from '#/components/Loader' import {Loader} from '#/components/Loader'
import {Divider} from '#/components/Divider'
import {CenteredView, ScrollView} from '#/view/com/util/Views' import {CenteredView, ScrollView} from '#/view/com/util/Views'
import {ErrorState} from '../ErrorState' import {ErrorState} from '../ErrorState'
import {ModerationLabelPref} from '#/components/moderation/ModerationLabelPref' import {ModerationLabelPref} from '#/components/moderation/ModerationLabelPref'
@@ -191,15 +192,24 @@ export function ProfileLabelsSectionInner({
) : null} ) : null}
</View> </View>
{labelDefs.length > 0 && ( {labelDefs.length > 0 && (
<View style={[a.mt_xl, a.gap_sm]}> <View
{labelDefs.map(labelDef => { style={[
a.mt_xl,
a.w_full,
a.rounded_md,
a.overflow_hidden,
t.atoms.bg_contrast_25,
]}>
{labelDefs.map((labelDef, i) => {
return ( return (
<ModerationLabelPref <React.Fragment key={labelDef.identifier}>
key={labelDef.identifier} {i !== 0 && <Divider />}
disabled={isSubscribed ? undefined : true} <ModerationLabelPref
labelValueDefinition={labelDef} disabled={isSubscribed ? undefined : true}
labelerDid={labelerInfo.creator.did} labelValueDefinition={labelDef}
/> labelerDid={labelerInfo.creator.did}
/>
</React.Fragment>
) )
})} })}
</View> </View>