[APP 513] Label tuning (#496)
* Label updates: break out sexual into 3 categories; tune defaults; improve descriptions * Fix misapplication of warning in notificationszio/stable
parent
3cc0fb1d67
commit
b40287e4be
|
@ -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'],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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,
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue