[APP 513] Label tuning (#496)

* Label updates: break out sexual into 3 categories; tune defaults; improve descriptions

* Fix misapplication of warning in notifications
zio/stable
Paul Frazee 2023-04-19 14:27:54 -05:00 committed by GitHub
parent 3cc0fb1d67
commit b40287e4be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 88 additions and 34 deletions

View File

@ -3,6 +3,8 @@ import {LabelPreferencesModel} from 'state/models/ui/preferences'
export interface LabelValGroup { export interface LabelValGroup {
id: keyof LabelPreferencesModel | 'illegal' | 'unknown' id: keyof LabelPreferencesModel | 'illegal' | 'unknown'
title: string title: string
subtitle?: string
warning?: string
values: string[] values: string[]
} }
@ -24,27 +26,50 @@ export const CONFIGURABLE_LABEL_GROUPS: Record<
> = { > = {
nsfw: { nsfw: {
id: 'nsfw', id: 'nsfw',
title: 'Sexual Content', title: 'Explicit Sexual Images',
values: ['porn', 'nudity', 'sexual'], subtitle: 'i.e. Pornography',
warning: 'Sexually Explicit',
values: ['porn'],
},
nudity: {
id: 'nudity',
title: 'Other Nudity',
subtitle: 'Including non-sexual and artistic',
warning: 'Nudity',
values: ['nudity'],
},
suggestive: {
id: 'suggestive',
title: 'Sexually Suggestive',
subtitle: 'Does not include nudity',
warning: 'Sexually Suggestive',
values: ['sexual'],
}, },
gore: { gore: {
id: 'gore', id: 'gore',
title: 'Violent / Bloody', title: 'Violent / Bloody',
subtitle: 'Gore, self-harm, torture',
warning: 'Violence',
values: ['gore', 'self-harm', 'torture'], values: ['gore', 'self-harm', 'torture'],
}, },
hate: { hate: {
id: 'hate', id: 'hate',
title: 'Political Hate-Groups', title: 'Political Hate-Groups',
values: ['icon-kkk', 'icon-nazi', 'icon-confederate'], warning: 'Hate',
values: ['icon-kkk', 'icon-nazi'],
}, },
spam: { spam: {
id: 'spam', id: 'spam',
title: 'Spam', title: 'Spam',
subtitle: 'Excessive low-quality posts',
warning: 'Spam',
values: ['spam'], values: ['spam'],
}, },
impersonation: { impersonation: {
id: 'impersonation', id: 'impersonation',
title: 'Impersonation', title: 'Impersonation',
subtitle: 'Accounts falsely claiming to be people or orgs',
warning: 'Impersonation',
values: ['impersonation'], values: ['impersonation'],
}, },
} }

View File

@ -15,7 +15,9 @@ export type LabelPreference = 'show' | 'warn' | 'hide'
export class LabelPreferencesModel { export class LabelPreferencesModel {
nsfw: LabelPreference = 'warn' nsfw: LabelPreference = 'warn'
gore: LabelPreference = 'hide' nudity: LabelPreference = 'show'
suggestive: LabelPreference = 'show'
gore: LabelPreference = 'warn'
hate: LabelPreference = 'hide' hate: LabelPreference = 'hide'
spam: LabelPreference = 'hide' spam: LabelPreference = 'hide'
impersonation: LabelPreference = 'warn' impersonation: LabelPreference = 'warn'

View File

@ -1,15 +1,17 @@
import React from 'react' import React from 'react'
import {StyleSheet, TouchableOpacity, View} from 'react-native' import {StyleSheet, Pressable, View} from 'react-native'
import LinearGradient from 'react-native-linear-gradient' import LinearGradient from 'react-native-linear-gradient'
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import {ScrollView} from './util'
import {useStores} from 'state/index' import {useStores} from 'state/index'
import {LabelPreference} from 'state/models/ui/preferences' import {LabelPreference} from 'state/models/ui/preferences'
import {s, colors, gradients} from 'lib/styles' import {s, colors, gradients} from 'lib/styles'
import {Text} from '../util/text/Text' import {Text} from '../util/text/Text'
import {usePalette} from 'lib/hooks/usePalette' import {usePalette} from 'lib/hooks/usePalette'
import {CONFIGURABLE_LABEL_GROUPS} from 'lib/labeling/const' import {CONFIGURABLE_LABEL_GROUPS} from 'lib/labeling/const'
import {isDesktopWeb} from 'platform/detection'
export const snapPoints = [500] export const snapPoints = ['90%']
export function Component({}: {}) { export function Component({}: {}) {
const store = useStores() const store = useStores()
@ -20,22 +22,28 @@ export function Component({}: {}) {
return ( return (
<View testID="reportPostModal" style={[pal.view, styles.container]}> <View testID="reportPostModal" style={[pal.view, styles.container]}>
<Text style={[pal.text, styles.title]}>Content Filtering</Text> <Text style={[pal.text, styles.title]}>Content Moderation</Text>
<ContentLabelPref group="nsfw" /> <ScrollView style={styles.scrollContainer}>
<ContentLabelPref group="gore" /> <ContentLabelPref group="nsfw" />
<ContentLabelPref group="hate" /> <ContentLabelPref group="nudity" />
<ContentLabelPref group="spam" /> <ContentLabelPref group="suggestive" />
<ContentLabelPref group="impersonation" /> <ContentLabelPref group="gore" />
<View style={s.flex1} /> <ContentLabelPref group="hate" />
<TouchableOpacity testID="sendReportBtn" onPress={onPressDone}> <ContentLabelPref group="spam" />
<LinearGradient <ContentLabelPref group="impersonation" />
colors={[gradients.blueLight.start, gradients.blueLight.end]} <View style={styles.bottomSpacer} />
start={{x: 0, y: 0}} </ScrollView>
end={{x: 1, y: 1}} <View style={[styles.btnContainer, pal.borderDark]}>
style={[styles.btn]}> <Pressable testID="sendReportBtn" onPress={onPressDone}>
<Text style={[s.white, s.bold, s.f18]}>Done</Text> <LinearGradient
</LinearGradient> colors={[gradients.blueLight.start, gradients.blueLight.end]}
</TouchableOpacity> start={{x: 0, y: 0}}
end={{x: 1, y: 1}}
style={[styles.btn]}>
<Text style={[s.white, s.bold, s.f18]}>Done</Text>
</LinearGradient>
</Pressable>
</View>
</View> </View>
) )
} }
@ -46,9 +54,16 @@ const ContentLabelPref = observer(
const pal = usePalette('default') const pal = usePalette('default')
return ( return (
<View style={[styles.contentLabelPref, pal.border]}> <View style={[styles.contentLabelPref, pal.border]}>
<Text type="md-medium" style={[pal.text]}> <View style={s.flex1}>
{CONFIGURABLE_LABEL_GROUPS[group].title} <Text type="md-medium" style={[pal.text]}>
</Text> {CONFIGURABLE_LABEL_GROUPS[group].title}
</Text>
{typeof CONFIGURABLE_LABEL_GROUPS[group].subtitle === 'string' && (
<Text type="sm" style={[pal.textLight]}>
{CONFIGURABLE_LABEL_GROUPS[group].subtitle}
</Text>
)}
</View>
<SelectGroup <SelectGroup
current={store.preferences.contentLabels[group]} current={store.preferences.contentLabels[group]}
onChange={v => store.preferences.setContentLabelPref(group, v)} onChange={v => store.preferences.setContentLabelPref(group, v)}
@ -109,7 +124,7 @@ function SelectableBtn({
const pal = usePalette('default') const pal = usePalette('default')
const palPrimary = usePalette('inverted') const palPrimary = usePalette('inverted')
return ( return (
<TouchableOpacity <Pressable
style={[ style={[
styles.selectableBtn, styles.selectableBtn,
left && styles.selectableBtnLeft, left && styles.selectableBtnLeft,
@ -121,15 +136,13 @@ function SelectableBtn({
<Text style={current === value ? palPrimary.text : pal.text}> <Text style={current === value ? palPrimary.text : pal.text}>
{label} {label}
</Text> </Text>
</TouchableOpacity> </Pressable>
) )
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
paddingHorizontal: 10,
paddingBottom: 40,
}, },
title: { title: {
textAlign: 'center', textAlign: 'center',
@ -141,19 +154,33 @@ const styles = StyleSheet.create({
paddingHorizontal: 2, paddingHorizontal: 2,
marginBottom: 10, marginBottom: 10,
}, },
scrollContainer: {
flex: 1,
paddingHorizontal: 10,
},
bottomSpacer: {
height: isDesktopWeb ? 0 : 60,
},
btnContainer: {
paddingTop: 10,
paddingHorizontal: 10,
paddingBottom: isDesktopWeb ? 0 : 40,
borderTopWidth: isDesktopWeb ? 0 : 1,
},
contentLabelPref: { contentLabelPref: {
flexDirection: 'row', flexDirection: 'row',
justifyContent: 'space-between', justifyContent: 'space-between',
alignItems: 'center', alignItems: 'center',
paddingTop: 10, paddingTop: 14,
paddingLeft: 4, paddingLeft: 4,
marginBottom: 10, marginBottom: 14,
borderTopWidth: 1, borderTopWidth: 1,
}, },
selectableBtns: { selectableBtns: {
flexDirection: 'row', flexDirection: 'row',
marginLeft: 10,
}, },
selectableBtn: { selectableBtn: {
flexDirection: 'row', flexDirection: 'row',

View File

@ -140,7 +140,7 @@ export const FeedItem = observer(function FeedItem({
handle: item2.author.handle, handle: item2.author.handle,
displayName: item2.author.displayName, displayName: item2.author.displayName,
avatar: item2.author.avatar, avatar: item2.author.avatar,
labels: item.author.labels, labels: item2.author.labels,
})), })),
) )
} }

View File

@ -55,7 +55,7 @@ export function ContentHider({
{isMuted ? ( {isMuted ? (
<>Post from an account you muted.</> <>Post from an account you muted.</>
) : ( ) : (
<>Warning: {labelPref.desc.title}</> <>Warning: {labelPref.desc.warning || labelPref.desc.title}</>
)} )}
</Text> </Text>
<TouchableOpacity <TouchableOpacity

View File

@ -33,7 +33,7 @@ export function ProfileHeaderLabels({
/> />
<Text style={palErr.text}> <Text style={palErr.text}>
This account has been flagged for{' '} This account has been flagged for{' '}
{labelGroup.title.toLocaleLowerCase()}. {(labelGroup.warning || labelGroup.title).toLocaleLowerCase()}.
</Text> </Text>
</View> </View>
) )