diff options
Diffstat (limited to 'src/components/moderation')
-rw-r--r-- | src/components/moderation/GlobalModerationLabelPref.tsx | 93 | ||||
-rw-r--r-- | src/components/moderation/LabelPreference.tsx | 294 | ||||
-rw-r--r-- | src/components/moderation/ModerationLabelPref.tsx | 177 |
3 files changed, 294 insertions, 270 deletions
diff --git a/src/components/moderation/GlobalModerationLabelPref.tsx b/src/components/moderation/GlobalModerationLabelPref.tsx deleted file mode 100644 index 7633cb9f2..000000000 --- a/src/components/moderation/GlobalModerationLabelPref.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import React from 'react' -import {View} from 'react-native' -import {InterpretedLabelValueDefinition, LabelPreference} from '@atproto/api' -import {useLingui} from '@lingui/react' -import {msg} from '@lingui/macro' - -import {useGlobalLabelStrings} from '#/lib/moderation/useGlobalLabelStrings' -import { - usePreferencesQuery, - usePreferencesSetContentLabelMutation, -} from '#/state/queries/preferences' - -import {useTheme, atoms as a} from '#/alf' -import {Text} from '#/components/Typography' -import * as ToggleButton from '#/components/forms/ToggleButton' - -export function GlobalModerationLabelPref({ - labelValueDefinition, - disabled, -}: { - labelValueDefinition: InterpretedLabelValueDefinition - disabled?: boolean -}) { - const {_} = useLingui() - const t = useTheme() - - const {identifier} = labelValueDefinition - const {data: preferences} = usePreferencesQuery() - const {mutate, variables} = usePreferencesSetContentLabelMutation() - const savedPref = preferences?.moderationPrefs.labels[identifier] - const pref = variables?.visibility ?? savedPref ?? 'warn' - - const allLabelStrings = useGlobalLabelStrings() - const labelStrings = - labelValueDefinition.identifier in allLabelStrings - ? allLabelStrings[labelValueDefinition.identifier] - : { - name: labelValueDefinition.identifier, - description: `Labeled "${labelValueDefinition.identifier}"`, - } - - const labelOptions = { - hide: _(msg`Hide`), - warn: _(msg`Warn`), - ignore: _(msg`Show`), - } - - return ( - <View - style={[ - a.flex_row, - a.justify_between, - a.gap_sm, - a.py_md, - a.pl_lg, - a.pr_md, - a.align_center, - ]}> - <View style={[a.gap_xs, a.flex_1]}> - <Text style={[a.font_bold]}>{labelStrings.name}</Text> - <Text style={[t.atoms.text_contrast_medium, a.leading_snug]}> - {labelStrings.description} - </Text> - </View> - <View style={[a.justify_center, {minHeight: 35}]}> - {!disabled && ( - <ToggleButton.Group - label={_( - msg`Configure content filtering setting for category: ${labelStrings.name.toLowerCase()}`, - )} - values={[pref]} - onChange={newPref => - mutate({ - label: identifier, - visibility: newPref[0] as LabelPreference, - labelerDid: undefined, - }) - }> - <ToggleButton.Button name="ignore" label={labelOptions.ignore}> - {labelOptions.ignore} - </ToggleButton.Button> - <ToggleButton.Button name="warn" label={labelOptions.warn}> - {labelOptions.warn} - </ToggleButton.Button> - <ToggleButton.Button name="hide" label={labelOptions.hide}> - {labelOptions.hide} - </ToggleButton.Button> - </ToggleButton.Group> - )} - </View> - </View> - ) -} diff --git a/src/components/moderation/LabelPreference.tsx b/src/components/moderation/LabelPreference.tsx new file mode 100644 index 000000000..7d4bd9c32 --- /dev/null +++ b/src/components/moderation/LabelPreference.tsx @@ -0,0 +1,294 @@ +import React from 'react' +import {View} from 'react-native' +import {InterpretedLabelValueDefinition, LabelPreference} from '@atproto/api' +import {useLingui} from '@lingui/react' +import {msg, Trans} from '@lingui/macro' + +import {useGlobalLabelStrings} from '#/lib/moderation/useGlobalLabelStrings' +import { + usePreferencesQuery, + usePreferencesSetContentLabelMutation, +} from '#/state/queries/preferences' +import {useLabelBehaviorDescription} from '#/lib/moderation/useLabelBehaviorDescription' +import {getLabelStrings} from '#/lib/moderation/useLabelInfo' + +import {useTheme, atoms as a, useBreakpoints} from '#/alf' +import {Text} from '#/components/Typography' +import {InlineLink} from '#/components/Link' +import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '../icons/CircleInfo' +import * as ToggleButton from '#/components/forms/ToggleButton' + +export function Outer({children}: React.PropsWithChildren<{}>) { + return ( + <View + style={[ + a.flex_row, + a.gap_md, + a.px_lg, + a.py_lg, + a.justify_between, + a.flex_wrap, + ]}> + {children} + </View> + ) +} + +export function Content({ + children, + name, + description, +}: React.PropsWithChildren<{ + name: string + description: string +}>) { + const t = useTheme() + const {gtPhone} = useBreakpoints() + + return ( + <View style={[a.gap_xs, a.flex_1]}> + <Text style={[a.font_bold, gtPhone ? a.text_sm : a.text_md]}>{name}</Text> + <Text style={[t.atoms.text_contrast_medium, a.leading_snug]}> + {description} + </Text> + + {children} + </View> + ) +} + +export function Buttons({ + name, + values, + onChange, + ignoreLabel, + warnLabel, + hideLabel, +}: { + name: string + values: ToggleButton.GroupProps['values'] + onChange: ToggleButton.GroupProps['onChange'] + ignoreLabel?: string + warnLabel?: string + hideLabel?: string +}) { + const {_} = useLingui() + const {gtPhone} = useBreakpoints() + + return ( + <View style={[{minHeight: 35}, gtPhone ? undefined : a.w_full]}> + <ToggleButton.Group + label={_( + msg`Configure content filtering setting for category: ${name}`, + )} + values={values} + onChange={onChange}> + {ignoreLabel && ( + <ToggleButton.Button name="ignore" label={ignoreLabel}> + {ignoreLabel} + </ToggleButton.Button> + )} + {warnLabel && ( + <ToggleButton.Button name="warn" label={warnLabel}> + {warnLabel} + </ToggleButton.Button> + )} + {hideLabel && ( + <ToggleButton.Button name="hide" label={hideLabel}> + {hideLabel} + </ToggleButton.Button> + )} + </ToggleButton.Group> + </View> + ) +} + +/** + * For use on the global Moderation screen to set prefs for a "global" label, + * not scoped to a single labeler. + */ +export function GlobalLabelPreference({ + labelDefinition, + disabled, +}: { + labelDefinition: InterpretedLabelValueDefinition + disabled?: boolean +}) { + const {_} = useLingui() + + const {identifier} = labelDefinition + const {data: preferences} = usePreferencesQuery() + const {mutate, variables} = usePreferencesSetContentLabelMutation() + const savedPref = preferences?.moderationPrefs.labels[identifier] + const pref = variables?.visibility ?? savedPref ?? 'warn' + + const allLabelStrings = useGlobalLabelStrings() + const labelStrings = + labelDefinition.identifier in allLabelStrings + ? allLabelStrings[labelDefinition.identifier] + : { + name: labelDefinition.identifier, + description: `Labeled "${labelDefinition.identifier}"`, + } + + const labelOptions = { + hide: _(msg`Hide`), + warn: _(msg`Warn`), + ignore: _(msg`Show`), + } + + return ( + <Outer> + <Content + name={labelStrings.name} + description={labelStrings.description} + /> + {!disabled && ( + <Buttons + name={labelStrings.name.toLowerCase()} + values={[pref]} + onChange={values => { + mutate({ + label: identifier, + visibility: values[0] as LabelPreference, + labelerDid: undefined, + }) + }} + ignoreLabel={labelOptions.ignore} + warnLabel={labelOptions.warn} + hideLabel={labelOptions.hide} + /> + )} + </Outer> + ) +} + +/** + * For use on individual labeler pages + */ +export function LabelerLabelPreference({ + labelDefinition, + disabled, + labelerDid, +}: { + labelDefinition: InterpretedLabelValueDefinition + disabled?: boolean + labelerDid?: string +}) { + const {i18n} = useLingui() + const t = useTheme() + const {gtPhone} = useBreakpoints() + + const isGlobalLabel = !labelDefinition.definedBy + const {identifier} = labelDefinition + const {data: preferences} = usePreferencesQuery() + const {mutate, variables} = usePreferencesSetContentLabelMutation() + const savedPref = + labelerDid && !isGlobalLabel + ? preferences?.moderationPrefs.labelers.find(l => l.did === labelerDid) + ?.labels[identifier] + : preferences?.moderationPrefs.labels[identifier] + const pref = + variables?.visibility ?? + savedPref ?? + labelDefinition.defaultSetting ?? + 'warn' + + // does the 'warn' setting make sense for this label? + const canWarn = !( + labelDefinition.blurs === 'none' && labelDefinition.severity === 'none' + ) + // is this label adult only? + const adultOnly = labelDefinition.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 + const showConfig = !disabled && (gtPhone || !cantConfigure) + + // adjust the pref based on whether warn is available + let prefAdjusted = pref + if (adultDisabled) { + prefAdjusted = 'hide' + } else if (!canWarn && pref === 'warn') { + prefAdjusted = 'ignore' + } + + // grab localized descriptions of the label and its settings + const currentPrefLabel = useLabelBehaviorDescription( + labelDefinition, + prefAdjusted, + ) + const hideLabel = useLabelBehaviorDescription(labelDefinition, 'hide') + const warnLabel = useLabelBehaviorDescription(labelDefinition, 'warn') + const ignoreLabel = useLabelBehaviorDescription(labelDefinition, 'ignore') + const globalLabelStrings = useGlobalLabelStrings() + const labelStrings = getLabelStrings( + i18n.locale, + globalLabelStrings, + labelDefinition, + ) + + return ( + <Outer> + <Content name={labelStrings.name} description={labelStrings.description}> + {cantConfigure && ( + <View style={[a.flex_row, a.gap_xs, a.align_center, a.mt_xs]}> + <CircleInfo size="sm" fill={t.atoms.text_contrast_high.color} /> + + <Text + style={[t.atoms.text_contrast_medium, a.font_semibold, a.italic]}> + {adultDisabled ? ( + <Trans>Adult content is disabled.</Trans> + ) : isGlobalLabel ? ( + <Trans> + Configured in{' '} + <InlineLink to="/moderation" style={a.text_sm}> + moderation settings + </InlineLink> + . + </Trans> + ) : null} + </Text> + </View> + )} + </Content> + + {showConfig && ( + <View style={[gtPhone ? undefined : a.w_full]}> + {cantConfigure ? ( + <View + style={[ + {minHeight: 35}, + a.px_md, + a.py_md, + a.rounded_sm, + a.border, + t.atoms.border_contrast_low, + ]}> + <Text style={[a.font_bold, t.atoms.text_contrast_low]}> + {currentPrefLabel} + </Text> + </View> + ) : ( + <Buttons + name={labelStrings.name.toLowerCase()} + values={[pref]} + onChange={values => { + mutate({ + label: identifier, + visibility: values[0] as LabelPreference, + labelerDid, + }) + }} + ignoreLabel={ignoreLabel} + warnLabel={canWarn ? warnLabel : undefined} + hideLabel={hideLabel} + /> + )} + </View> + )} + </Outer> + ) +} diff --git a/src/components/moderation/ModerationLabelPref.tsx b/src/components/moderation/ModerationLabelPref.tsx deleted file mode 100644 index b16c24859..000000000 --- a/src/components/moderation/ModerationLabelPref.tsx +++ /dev/null @@ -1,177 +0,0 @@ -import React from 'react' -import {View} from 'react-native' -import {InterpretedLabelValueDefinition, LabelPreference} from '@atproto/api' -import {useLingui} from '@lingui/react' -import {msg, Trans} from '@lingui/macro' - -import {useGlobalLabelStrings} from '#/lib/moderation/useGlobalLabelStrings' -import {useLabelBehaviorDescription} from '#/lib/moderation/useLabelBehaviorDescription' -import { - usePreferencesQuery, - usePreferencesSetContentLabelMutation, -} from '#/state/queries/preferences' -import {getLabelStrings} from '#/lib/moderation/useLabelInfo' - -import {useTheme, atoms as a, useBreakpoints} from '#/alf' -import {Text} from '#/components/Typography' -import {InlineLink} from '#/components/Link' -import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '../icons/CircleInfo' -import * as ToggleButton from '#/components/forms/ToggleButton' - -export function ModerationLabelPref({ - labelValueDefinition, - labelerDid, - disabled, -}: { - labelValueDefinition: InterpretedLabelValueDefinition - labelerDid: string | undefined - disabled?: boolean -}) { - const {_, i18n} = useLingui() - const t = useTheme() - const {gtPhone} = useBreakpoints() - - const isGlobalLabel = !labelValueDefinition.definedBy - const {identifier} = labelValueDefinition - const {data: preferences} = usePreferencesQuery() - const {mutate, variables} = usePreferencesSetContentLabelMutation() - const savedPref = - labelerDid && !isGlobalLabel - ? preferences?.moderationPrefs.labelers.find(l => l.did === labelerDid) - ?.labels[identifier] - : preferences?.moderationPrefs.labels[identifier] - const pref = - variables?.visibility ?? - savedPref ?? - labelValueDefinition.defaultSetting ?? - 'warn' - - // does the 'warn' setting make sense for this label? - const canWarn = !( - labelValueDefinition.blurs === 'none' && - labelValueDefinition.severity === 'none' - ) - // 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 - const showConfig = !disabled && (gtPhone || !cantConfigure) - - // adjust the pref based on whether warn is available - let prefAdjusted = pref - if (adultDisabled) { - prefAdjusted = 'hide' - } else if (!canWarn && pref === 'warn') { - prefAdjusted = 'ignore' - } - - // grab localized descriptions of the label and its settings - const currentPrefLabel = useLabelBehaviorDescription( - labelValueDefinition, - prefAdjusted, - ) - const hideLabel = useLabelBehaviorDescription(labelValueDefinition, 'hide') - const warnLabel = useLabelBehaviorDescription(labelValueDefinition, 'warn') - const ignoreLabel = useLabelBehaviorDescription( - labelValueDefinition, - 'ignore', - ) - const globalLabelStrings = useGlobalLabelStrings() - const labelStrings = getLabelStrings( - i18n.locale, - globalLabelStrings, - labelValueDefinition, - ) - - return ( - <View - style={[ - a.flex_row, - a.gap_md, - a.px_lg, - a.py_lg, - a.justify_between, - a.flex_wrap, - ]}> - <View style={[a.gap_xs, a.flex_1]}> - <Text style={[a.font_bold, gtPhone ? a.text_sm : a.text_md]}> - {labelStrings.name} - </Text> - <Text style={[t.atoms.text_contrast_medium, a.leading_snug]}> - {labelStrings.description} - </Text> - - {cantConfigure && ( - <View style={[a.flex_row, a.gap_xs, a.align_center, a.mt_xs]}> - <CircleInfo size="sm" fill={t.atoms.text_contrast_high.color} /> - - <Text - style={[t.atoms.text_contrast_medium, a.font_semibold, a.italic]}> - {adultDisabled ? ( - <Trans>Adult content is disabled.</Trans> - ) : isGlobalLabel ? ( - <Trans> - Configured in{' '} - <InlineLink to="/moderation" style={a.text_sm}> - moderation settings - </InlineLink> - . - </Trans> - ) : null} - </Text> - </View> - )} - </View> - - {showConfig && ( - <View style={[gtPhone ? undefined : a.w_full]}> - {cantConfigure ? ( - <View - style={[ - {minHeight: 35}, - a.px_md, - a.py_md, - a.rounded_sm, - a.border, - t.atoms.border_contrast_low, - ]}> - <Text style={[a.font_bold, t.atoms.text_contrast_low]}> - {currentPrefLabel} - </Text> - </View> - ) : ( - <View style={[{minHeight: 35}]}> - <ToggleButton.Group - label={_( - msg`Configure content filtering setting for category: ${labelStrings.name.toLowerCase()}`, - )} - values={[prefAdjusted]} - onChange={newPref => - mutate({ - label: identifier, - visibility: newPref[0] as LabelPreference, - labelerDid, - }) - }> - <ToggleButton.Button name="ignore" label={ignoreLabel}> - {ignoreLabel} - </ToggleButton.Button> - {canWarn && ( - <ToggleButton.Button name="warn" label={warnLabel}> - {warnLabel} - </ToggleButton.Button> - )} - <ToggleButton.Button name="hide" label={hideLabel}> - {hideLabel} - </ToggleButton.Button> - </ToggleButton.Group> - </View> - )} - </View> - )} - </View> - ) -} |