Clean up and reorganize localized moderation strings

This commit is contained in:
Paul Frazee
2024-02-08 16:40:50 -08:00
parent fe20b4d575
commit 667729f7db
13 changed files with 1350 additions and 1332 deletions
+1 -1224
View File
File diff suppressed because it is too large Load Diff
+122
View File
@@ -0,0 +1,122 @@
import {LABEL_GROUPS} from '@atproto/api'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useMemo} from 'react'
export type LabelGroupStrings = Record<
keyof typeof LABEL_GROUPS,
{name: string; description: string}
>
export function useLabelGroupStrings(): LabelGroupStrings {
const {_} = useLingui()
return useMemo(
() => ({
system: {
name: _(msg`System`),
description: _(msg`Moderator overrides for special cases.`),
},
legal: {
name: _(msg`Legal`),
description: _(msg`Content removed for legal reasons.`),
},
'intellectual-property': {
name: _(msg`Intellectual Property`),
description: _(msg`Plagiarism, copying without attribution.`),
},
porn: {
name: _(msg`Explicit Sexual Images`),
description: _(msg`i.e. pornography.`),
},
suggestive: {
name: _(msg`Sexually Suggestive`),
description: _(msg`Does not include nudity.`),
},
nudity: {
name: _(msg`Other Nudity`),
description: _(msg`Including non-sexual and artistic.`),
},
violence: {
name: _(msg`Violent / Bloody`),
description: _(msg`Gore, self-harm, torture.`),
},
'drugs-alcohol': {
name: _(msg`Substance Abuse`),
description: _(msg`Use of drugs or alcohol.`),
},
'self-harm': {
name: _(msg`Self Harm`),
description: _(msg`Suicide, self-harm, eating disorders.`),
},
intolerance: {
name: _(msg`Intolerance`),
description: _(
msg`Content or behavior which is hateful or intolerant toward a group of people.`,
),
},
'bad-behavior': {
name: _(msg`Bad Behavior`),
description: _(
msg`Harassment, bullying, and threats toward other users.`,
),
},
rude: {
name: _(msg`Rude`),
description: _(msg`Behavior which is rude toward other users.`),
},
upsetting: {
name: _(msg`Upsetting`),
description: _(
msg`Shocking, disgusting, or generally upsetting content.`,
),
},
troubling: {
name: _(msg`Troubling`),
description: _(
msg`Bad news, troubling information, or dispiriting content.`,
),
},
'hate-group-mention': {
name: _(msg`Hate Group Coverage`),
description: _(
msg`Images of terror groups, articles covering events, etc.`,
),
},
discourse: {
name: _(msg`Discourse / Drama`),
description: _(
msg`On-going discussions or debates that may be frustrating.`,
),
},
curation: {
name: _(msg`Curation`),
description: _(
msg`Judgment of the moderators to remove content not worth showing.`,
),
},
spam: {
name: _(msg`Spam`),
description: _(msg`Content which doesn't add to the conversation.`),
},
misrepresentation: {
name: _(msg`Misrepresentation`),
description: _(msg`Impersonations, scams.`),
},
security: {
name: _(msg`Security`),
description: _(msg`Potential security attacks.`),
},
misinfo: {
name: _(msg`Misinformation`),
description: _(msg`Content which misleads or defrauds users.`),
},
context: {
name: _(msg`Context`),
description: _(
msg`Helpful annotations to explain intent, such as satire or parody.`,
),
},
}),
[_],
)
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,106 @@
import {ModerationCause} from '@atproto/api'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useLabelStrings} from './useLabelStrings'
export interface ModerationCauseDescription {
name: string
description: string
}
export function useModerationCauseDescription(
cause: ModerationCause | undefined,
context: 'account' | 'content',
): ModerationCauseDescription {
const {_} = useLingui()
const labelStrings = useLabelStrings()
if (!cause) {
return {
name: _(msg`Content Warning`),
description: _(
msg`Moderator has chosen to set a general warning on the content.`,
),
}
}
if (cause.type === 'blocking') {
if (cause.source.type === 'list') {
return {
name: _(msg`User Blocked by "${cause.source.list.name}"`),
description: _(
msg`You have blocked this user. You cannot view their content.`,
),
}
} else {
return {
name: _(msg`User Blocked`),
description: _(
msg`You have blocked this user. You cannot view their content.`,
),
}
}
}
if (cause.type === 'blocked-by') {
return {
name: _(msg`User Blocking You`),
description: _(
msg`This user has blocked you. You cannot view their content.`,
),
}
}
if (cause.type === 'block-other') {
return {
name: _(msg`Content Not Available`),
description: _(
msg`This content is not available because one of the users involved has blocked the other.`,
),
}
}
if (cause.type === 'muted') {
if (cause.source.type === 'list') {
return {
name:
context === 'account'
? _(msg`Muted by "${cause.source.list.name}"`)
: _(msg`Post by muted user ("${cause.source.list.name}")`),
description: _(msg`You have muted this user`),
}
} else {
return {
name:
context === 'account'
? _(msg`Muted User`)
: _(msg`Post by muted user`),
description: _(msg`You have muted this user`),
}
}
}
// @ts-ignore Temporary extension to the moderation system -prf
if (cause.type === 'post-hidden') {
return {
name: _(msg`Post Hidden by You`),
description: _(msg`You have hidden this post`),
}
}
if (cause.type === 'label') {
if (cause.labelDef.id in labelStrings) {
const strings = labelStrings[cause.labelDef.id]
return {
name:
context === 'account' ? strings.account.name : strings.content.name,
description:
context === 'account'
? strings.account.description
: strings.content.description,
}
}
return {
name: cause.labelDef.id,
description: _(msg`Labeled ${cause.labelDef.id}`),
}
}
// should never happen
return {
name: '',
description: ``,
}
}
@@ -3,7 +3,7 @@ import {View} from 'react-native'
import {useLingui} from '@lingui/react'
import {msg} from '@lingui/macro'
import {useLabelGroupStrings} from '#/lib/moderation'
import {useLabelGroupStrings} from '#/lib/moderation/useLabelGroupStrings'
import {useTheme, atoms as a} from '#/alf'
import {Text} from '#/components/Typography'
+14 -11
View File
@@ -1,6 +1,6 @@
import React from 'react'
import {StyleSheet, View} from 'react-native'
import {ModerationUI, LABELS} from '@atproto/api'
import {ModerationUI} from '@atproto/api'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {s} from 'lib/styles'
import {Text} from '../util/text/Text'
@@ -12,7 +12,7 @@ import {Button} from '../util/forms/Button'
import {useModalControls} from '#/state/modals'
import {useLingui} from '@lingui/react'
import {Trans, msg} from '@lingui/macro'
import {useLabelStrings} from '#/lib/moderation'
import {useLabelStrings} from '#/lib/moderation/useLabelStrings'
export const snapPoints = [300]
@@ -88,16 +88,19 @@ export function Component({
name = _(msg`Account Muted`)
description = _(msg`You have muted this user.`)
}
} else if (moderation.cause.labelDef.id in labelStrings) {
name =
labelStrings[moderation.cause.labelDef.id as keyof typeof LABELS][context]
.name
description =
labelStrings[moderation.cause.labelDef.id as keyof typeof LABELS][context]
.description
} else if (moderation.cause.type === 'label') {
if (moderation.cause.labelDef.id in labelStrings) {
name = labelStrings[moderation.cause.labelDef.id][context].name
description =
labelStrings[moderation.cause.labelDef.id][context].description
} else {
name = moderation.cause.labelDef.id
description = _(msg`Labeled ${moderation.cause.labelDef.id}`)
}
} else {
name = 'TODO'
description = 'TODO'
// should never happen
name = ''
description = ''
}
return (
+24 -20
View File
@@ -3,6 +3,7 @@ import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
import {
AppBskyActorDefs,
moderateProfile,
ModerationCause,
ProfileModeration,
} from '@atproto/api'
import {Link} from '../util/Link'
@@ -14,17 +15,13 @@ import {FollowButton} from './FollowButton'
import {sanitizeDisplayName} from 'lib/strings/display-names'
import {sanitizeHandle} from 'lib/strings/handles'
import {makeProfileLink} from 'lib/routes/links'
import {
describeModerationCause,
getProfileModerationCauses,
getModerationCauseKey,
useLabelStrings,
} from 'lib/moderation'
import {getProfileModerationCauses, getModerationCauseKey} from 'lib/moderation'
import {Shadow} from '#/state/cache/types'
import {useModerationOpts} from '#/state/queries/preferences'
import {useProfileShadow} from '#/state/cache/profile-shadow'
import {useSession} from '#/state/session'
import {Trans} from '@lingui/macro'
import {useModerationCauseDescription} from '#/lib/moderation/useModerationCauseDescription'
export function ProfileCard({
testID,
@@ -128,7 +125,6 @@ export function ProfileCardPills({
moderation: ProfileModeration
}) {
const pal = usePalette('default')
const labelStrings = useLabelStrings()
const causes = getProfileModerationCauses(moderation)
if (!followedBy && !causes.length) {
@@ -144,19 +140,27 @@ export function ProfileCardPills({
</Text>
</View>
)}
{causes.map(cause => {
const desc = describeModerationCause(cause, 'account', labelStrings)
return (
<View
style={[s.mt5, pal.btn, styles.pill]}
key={getModerationCauseKey(cause)}>
<Text type="xs" style={pal.text}>
{cause?.type === 'label' ? '⚠' : ''}
{desc.name}
</Text>
</View>
)
})}
{causes.map(cause => (
<ProfileCardPillModerationCause
key={getModerationCauseKey(cause)}
cause={cause}
/>
))}
</View>
)
}
function ProfileCardPillModerationCause({cause}: {cause: ModerationCause}) {
const pal = usePalette('default')
const {name} = useModerationCauseDescription(cause, 'account')
return (
<View
style={[s.mt5, pal.btn, styles.pill]}
key={getModerationCauseKey(cause)}>
<Text type="xs" style={pal.text}>
{cause?.type === 'label' ? '⚠' : ''}
{name}
</Text>
</View>
)
}
@@ -5,10 +5,10 @@ import {usePalette} from 'lib/hooks/usePalette'
import {ModerationUI, PostModeration} from '@atproto/api'
import {Text} from '../text/Text'
import {ShieldExclamation} from 'lib/icons'
import {describeModerationCause, useLabelStrings} from 'lib/moderation'
import {useLingui} from '@lingui/react'
import {msg, Trans} from '@lingui/macro'
import {useModalControls} from '#/state/modals'
import {useModerationCauseDescription} from '#/lib/moderation/useModerationCauseDescription'
import {isPostMediaBlurred} from 'lib/moderation'
export function ContentHider({
@@ -33,7 +33,7 @@ export function ContentHider({
const {_} = useLingui()
const [override, setOverride] = React.useState(false)
const {openModal} = useModalControls()
const labelStrings = useLabelStrings()
const desc = useModerationCauseDescription(moderation.cause, 'content')
if (
!moderation.blur ||
@@ -48,11 +48,6 @@ export function ContentHider({
}
const isMute = moderation.cause?.type === 'muted'
const desc = describeModerationCause(
moderation.cause,
'content',
labelStrings,
)
return (
<View testID={testID} style={[styles.outer, style]}>
<Pressable
+2 -7
View File
@@ -4,10 +4,10 @@ import {ModerationUI} from '@atproto/api'
import {Text} from '../text/Text'
import {usePalette} from 'lib/hooks/usePalette'
import {ShieldExclamation} from 'lib/icons'
import {describeModerationCause, useLabelStrings} from 'lib/moderation'
import {Trans, msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useModalControls} from '#/state/modals'
import {useModerationCauseDescription} from '#/lib/moderation/useModerationCauseDescription'
export function PostAlerts({
moderation,
@@ -20,18 +20,13 @@ export function PostAlerts({
const pal = usePalette('default')
const {_} = useLingui()
const {openModal} = useModalControls()
const labelStrings = useLabelStrings()
const desc = useModerationCauseDescription(moderation.cause, 'content')
const shouldAlert = !!moderation.cause && moderation.alert
if (!shouldAlert) {
return null
}
const desc = describeModerationCause(
moderation.cause,
'content',
labelStrings,
)
return (
<Pressable
onPress={() => {
+2 -7
View File
@@ -6,11 +6,11 @@ import {usePalette} from 'lib/hooks/usePalette'
import {Link} from '../Link'
import {Text} from '../text/Text'
import {addStyle} from 'lib/styles'
import {describeModerationCause, useLabelStrings} from 'lib/moderation'
import {ShieldExclamation} from 'lib/icons'
import {useLingui} from '@lingui/react'
import {Trans, msg} from '@lingui/macro'
import {useModalControls} from '#/state/modals'
import {useModerationCauseDescription} from '#/lib/moderation/useModerationCauseDescription'
interface Props extends ComponentProps<typeof Link> {
iconSize: number
@@ -32,7 +32,7 @@ export function PostHider({
const {_} = useLingui()
const [override, setOverride] = React.useState(false)
const {openModal} = useModalControls()
const labelStrings = useLabelStrings()
const desc = useModerationCauseDescription(moderation.cause, 'content')
if (!moderation.blur) {
return (
@@ -49,11 +49,6 @@ export function PostHider({
}
const isMute = moderation.cause?.type === 'muted'
const desc = describeModerationCause(
moderation.cause,
'content',
labelStrings,
)
return !override ? (
<Pressable
onPress={() => {
@@ -1,18 +1,15 @@
import React from 'react'
import {Pressable, StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
import {ProfileModeration} from '@atproto/api'
import {ModerationCause, ProfileModeration} from '@atproto/api'
import {Text} from '../text/Text'
import {usePalette} from 'lib/hooks/usePalette'
import {ShieldExclamation} from 'lib/icons'
import {
describeModerationCause,
getProfileModerationCauses,
useLabelStrings,
} from 'lib/moderation'
import {getModerationCauseKey, getProfileModerationCauses} from 'lib/moderation'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {useModalControls} from '#/state/modals'
import {useModerationCauseDescription} from '#/lib/moderation/useModerationCauseDescription'
export function ProfileHeaderAlerts({
moderation,
@@ -21,11 +18,6 @@ export function ProfileHeaderAlerts({
moderation: ProfileModeration
style?: StyleProp<ViewStyle>
}) {
const pal = usePalette('default')
const {_} = useLingui()
const {openModal} = useModalControls()
const labelStrings = useLabelStrings()
const causes = getProfileModerationCauses(moderation)
if (!causes.length) {
return null
@@ -33,46 +25,64 @@ export function ProfileHeaderAlerts({
return (
<View style={styles.grid}>
{causes.map(cause => {
const isMute = cause.type === 'muted'
const desc = describeModerationCause(cause, 'account', labelStrings)
return (
<Pressable
testID="profileHeaderAlert"
key={desc.name}
onPress={() => {
openModal({
name: 'moderation-details',
context: 'content',
moderation: {cause},
})
}}
accessibilityRole="button"
accessibilityLabel={_(msg`Learn more about this warning`)}
accessibilityHint=""
style={[styles.container, pal.viewLight, style]}>
{isMute ? (
<FontAwesomeIcon
icon={['far', 'eye-slash']}
size={14}
color={pal.colors.textLight}
/>
) : (
<ShieldExclamation style={pal.text} size={18} />
)}
<Text type="sm" style={[{flex: 1}, pal.text]}>
{desc.name}
</Text>
<Text type="sm" style={[pal.link, styles.learnMoreBtn]}>
<Trans>Learn More</Trans>
</Text>
</Pressable>
)
})}
{causes.map(cause => (
<ProfileHeaderAlert
key={getModerationCauseKey(cause)}
cause={cause}
style={style}
/>
))}
</View>
)
}
function ProfileHeaderAlert({
cause,
style,
}: {
cause: ModerationCause
style?: StyleProp<ViewStyle>
}) {
const pal = usePalette('default')
const {_} = useLingui()
const {openModal} = useModalControls()
const desc = useModerationCauseDescription(cause, 'account')
const isMute = cause.type === 'muted'
return (
<Pressable
testID="profileHeaderAlert"
key={desc.name}
onPress={() => {
openModal({
name: 'moderation-details',
context: 'content',
moderation: {cause},
})
}}
accessibilityRole="button"
accessibilityLabel={_(msg`Learn more about this warning`)}
accessibilityHint=""
style={[styles.container, pal.viewLight, style]}>
{isMute ? (
<FontAwesomeIcon
icon={['far', 'eye-slash']}
size={14}
color={pal.colors.textLight}
/>
) : (
<ShieldExclamation style={pal.text} size={18} />
)}
<Text type="sm" style={[{flex: 1}, pal.text]}>
{desc.name}
</Text>
<Text type="sm" style={[pal.link, styles.learnMoreBtn]}>
<Trans>Learn More</Trans>
</Text>
</Pressable>
)
}
const styles = StyleSheet.create({
grid: {
gap: 4,
+2 -7
View File
@@ -17,10 +17,10 @@ import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {NavigationProp} from 'lib/routes/types'
import {Text} from '../text/Text'
import {Button} from '../forms/Button'
import {describeModerationCause, useLabelStrings} from 'lib/moderation'
import {Trans, msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useModalControls} from '#/state/modals'
import {useModerationCauseDescription} from '#/lib/moderation/useModerationCauseDescription'
import {s} from '#/lib/styles'
import {CenteredView} from '../Views'
@@ -45,7 +45,7 @@ export function ScreenHider({
const navigation = useNavigation<NavigationProp>()
const {isMobile} = useWebMediaQueries()
const {openModal} = useModalControls()
const labelStrings = useLabelStrings()
const desc = useModerationCauseDescription(moderation.cause, 'account')
if (!moderation.blur || override) {
return (
@@ -58,11 +58,6 @@ export function ScreenHider({
const isNoPwi =
moderation.cause?.type === 'label' &&
moderation.cause?.labelDef.id === '!no-unauthenticated'
const desc = describeModerationCause(
moderation.cause,
'account',
labelStrings,
)
return (
<CenteredView
style={[styles.container, pal.view, containerStyle]}
+1 -1
View File
@@ -18,7 +18,7 @@ import {
import {atoms as a, useTheme} from '#/alf'
import {CenteredView, ScrollView} from '#/view/com/util/Views'
import {H1, H3, P} from '#/components/Typography'
import {useLabelStrings} from '#/lib/moderation'
import {useLabelStrings} from '#/lib/moderation/useLabelStrings'
import * as Toggle from '#/components/forms/Toggle'
import * as ToggleButton from '#/components/forms/ToggleButton'
import {UserAvatar} from '../com/util/UserAvatar'