Add label appeal tool to posts and accounts (#2124)
* Add label appeal tool to posts and accounts * Fix translations
This commit is contained in:
parent
794015aef8
commit
52a0cb8fac
12 changed files with 846 additions and 380 deletions
139
src/view/com/modals/AppealLabel.tsx
Normal file
139
src/view/com/modals/AppealLabel.tsx
Normal file
|
@ -0,0 +1,139 @@
|
|||
import React, {useState} from 'react'
|
||||
import {StyleSheet, TouchableOpacity, View} from 'react-native'
|
||||
import {ComAtprotoModerationDefs} from '@atproto/api'
|
||||
import {ScrollView, TextInput} from './util'
|
||||
import {Text} from '../util/text/Text'
|
||||
import {s, colors} from 'lib/styles'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {Trans, msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {CharProgress} from '../composer/char-progress/CharProgress'
|
||||
import {getAgent} from '#/state/session'
|
||||
import * as Toast from '../util/Toast'
|
||||
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
||||
|
||||
export const snapPoints = ['40%']
|
||||
|
||||
type ReportComponentProps =
|
||||
| {
|
||||
uri: string
|
||||
cid: string
|
||||
}
|
||||
| {
|
||||
did: string
|
||||
}
|
||||
|
||||
export function Component(props: ReportComponentProps) {
|
||||
const pal = usePalette('default')
|
||||
const [details, setDetails] = useState<string>('')
|
||||
const {_} = useLingui()
|
||||
const {closeModal} = useModalControls()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const isAccountReport = 'did' in props
|
||||
|
||||
const submit = async () => {
|
||||
try {
|
||||
const $type = !isAccountReport
|
||||
? 'com.atproto.repo.strongRef'
|
||||
: 'com.atproto.admin.defs#repoRef'
|
||||
await getAgent().createModerationReport({
|
||||
reasonType: ComAtprotoModerationDefs.REASONOTHER,
|
||||
subject: {
|
||||
$type,
|
||||
...props,
|
||||
},
|
||||
reason: details,
|
||||
})
|
||||
Toast.show("We'll look into your appeal promptly.")
|
||||
} finally {
|
||||
closeModal()
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
pal.view,
|
||||
s.flex1,
|
||||
isMobile ? {paddingHorizontal: 12} : undefined,
|
||||
]}
|
||||
testID="appealLabelModal">
|
||||
<Text
|
||||
type="2xl-bold"
|
||||
style={[pal.text, s.textCenter, {paddingBottom: 8}]}>
|
||||
<Trans>Appeal Decision</Trans>
|
||||
</Text>
|
||||
<ScrollView>
|
||||
<View style={[pal.btn, styles.detailsInputContainer]}>
|
||||
<TextInput
|
||||
accessibilityLabel={_(msg`Text input field`)}
|
||||
accessibilityHint={_(
|
||||
msg`Please tell us why you think this decision was incorrect.`,
|
||||
)}
|
||||
placeholder={_(
|
||||
msg`Please tell us why you think this decision was incorrect.`,
|
||||
)}
|
||||
placeholderTextColor={pal.textLight.color}
|
||||
value={details}
|
||||
onChangeText={setDetails}
|
||||
autoFocus={true}
|
||||
numberOfLines={3}
|
||||
multiline={true}
|
||||
textAlignVertical="top"
|
||||
maxLength={300}
|
||||
style={[styles.detailsInput, pal.text]}
|
||||
/>
|
||||
<View style={styles.detailsInputBottomBar}>
|
||||
<View style={styles.charCounter}>
|
||||
<CharProgress count={details?.length || 0} />
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
testID="confirmBtn"
|
||||
onPress={submit}
|
||||
style={styles.btn}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Confirm`)}
|
||||
accessibilityHint="">
|
||||
<Text style={[s.white, s.bold, s.f18]}>
|
||||
<Trans>Submit</Trans>
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</ScrollView>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
detailsInputContainer: {
|
||||
borderRadius: 8,
|
||||
marginBottom: 8,
|
||||
},
|
||||
detailsInput: {
|
||||
paddingHorizontal: 12,
|
||||
paddingTop: 12,
|
||||
paddingBottom: 12,
|
||||
borderRadius: 8,
|
||||
minHeight: 100,
|
||||
fontSize: 16,
|
||||
},
|
||||
detailsInputBottomBar: {
|
||||
alignSelf: 'flex-end',
|
||||
},
|
||||
charCounter: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingRight: 10,
|
||||
paddingBottom: 8,
|
||||
},
|
||||
btn: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
borderRadius: 32,
|
||||
padding: 14,
|
||||
backgroundColor: colors.blue3,
|
||||
},
|
||||
})
|
|
@ -22,6 +22,7 @@ import * as ListAddUserModal from './ListAddRemoveUsers'
|
|||
import * as AltImageModal from './AltImage'
|
||||
import * as EditImageModal from './AltImage'
|
||||
import * as ReportModal from './report/Modal'
|
||||
import * as AppealLabelModal from './AppealLabel'
|
||||
import * as DeleteAccountModal from './DeleteAccount'
|
||||
import * as ChangeHandleModal from './ChangeHandle'
|
||||
import * as WaitlistModal from './Waitlist'
|
||||
|
@ -105,6 +106,9 @@ export function ModalsContainer() {
|
|||
} else if (activeModal?.name === 'report') {
|
||||
snapPoints = ReportModal.snapPoints
|
||||
element = <ReportModal.Component {...activeModal} />
|
||||
} else if (activeModal?.name === 'appeal-label') {
|
||||
snapPoints = AppealLabelModal.snapPoints
|
||||
element = <AppealLabelModal.Component {...activeModal} />
|
||||
} else if (activeModal?.name === 'create-or-edit-list') {
|
||||
snapPoints = CreateOrEditListModal.snapPoints
|
||||
element = <CreateOrEditListModal.Component {...activeModal} />
|
||||
|
|
|
@ -11,6 +11,7 @@ import * as EditProfileModal from './EditProfile'
|
|||
import * as ProfilePreviewModal from './ProfilePreview'
|
||||
import * as ServerInputModal from './ServerInput'
|
||||
import * as ReportModal from './report/Modal'
|
||||
import * as AppealLabelModal from './AppealLabel'
|
||||
import * as CreateOrEditListModal from './CreateOrEditList'
|
||||
import * as UserAddRemoveLists from './UserAddRemoveLists'
|
||||
import * as ListAddUserModal from './ListAddRemoveUsers'
|
||||
|
@ -81,6 +82,8 @@ function Modal({modal}: {modal: ModalIface}) {
|
|||
element = <ServerInputModal.Component {...modal} />
|
||||
} else if (modal.name === 'report') {
|
||||
element = <ReportModal.Component {...modal} />
|
||||
} else if (modal.name === 'appeal-label') {
|
||||
element = <AppealLabelModal.Component {...modal} />
|
||||
} else if (modal.name === 'create-or-edit-list') {
|
||||
element = <CreateOrEditListModal.Component {...modal} />
|
||||
} else if (modal.name === 'user-add-remove-lists') {
|
||||
|
|
|
@ -42,6 +42,8 @@ import {useComposerControls} from '#/state/shell/composer'
|
|||
import {useModerationOpts} from '#/state/queries/preferences'
|
||||
import {Shadow, usePostShadow, POST_TOMBSTONE} from '#/state/cache/post-shadow'
|
||||
import {ThreadPost} from '#/state/queries/post-thread'
|
||||
import {LabelInfo} from '../util/moderation/LabelInfo'
|
||||
import {useSession} from '#/state/session'
|
||||
|
||||
export function PostThreadItem({
|
||||
post,
|
||||
|
@ -158,6 +160,7 @@ let PostThreadItemLoaded = ({
|
|||
const pal = usePalette('default')
|
||||
const langPrefs = useLanguagePrefs()
|
||||
const {openComposer} = useComposerControls()
|
||||
const {currentAccount} = useSession()
|
||||
const [limitLines, setLimitLines] = React.useState(
|
||||
() => countLines(richText?.text) >= MAX_POST_LINES,
|
||||
)
|
||||
|
@ -345,6 +348,13 @@ let PostThreadItemLoaded = ({
|
|||
includeMute
|
||||
style={styles.alert}
|
||||
/>
|
||||
{post.author.did === currentAccount?.did ? (
|
||||
<LabelInfo
|
||||
details={{uri: post.uri, cid: post.cid}}
|
||||
labels={post.labels}
|
||||
style={{marginBottom: 8}}
|
||||
/>
|
||||
) : null}
|
||||
{richText?.text ? (
|
||||
<View
|
||||
style={[
|
||||
|
|
|
@ -54,6 +54,7 @@ import {logger} from '#/logger'
|
|||
import {useSession} from '#/state/session'
|
||||
import {Shadow} from '#/state/cache/types'
|
||||
import {useRequireAuth} from '#/state/session'
|
||||
import {LabelInfo} from '../util/moderation/LabelInfo'
|
||||
|
||||
interface Props {
|
||||
profile: Shadow<AppBskyActorDefs.ProfileViewDetailed> | null
|
||||
|
@ -619,6 +620,9 @@ let ProfileHeaderLoaded = ({
|
|||
</>
|
||||
)}
|
||||
<ProfileHeaderAlerts moderation={moderation} />
|
||||
{isMe && (
|
||||
<LabelInfo details={{did: profile.did}} labels={profile.labels} />
|
||||
)}
|
||||
</View>
|
||||
|
||||
{!isProfilePreview && showSuggestedFollows && (
|
||||
|
|
60
src/view/com/util/moderation/LabelInfo.tsx
Normal file
60
src/view/com/util/moderation/LabelInfo.tsx
Normal file
|
@ -0,0 +1,60 @@
|
|||
import React from 'react'
|
||||
import {Pressable, StyleProp, View, ViewStyle} from 'react-native'
|
||||
import {ComAtprotoLabelDefs} from '@atproto/api'
|
||||
import {Text} from '../text/Text'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
|
||||
export function LabelInfo({
|
||||
details,
|
||||
labels,
|
||||
style,
|
||||
}: {
|
||||
details: {did: string} | {uri: string; cid: string}
|
||||
labels: ComAtprotoLabelDefs.Label[] | undefined
|
||||
style?: StyleProp<ViewStyle>
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const {_} = useLingui()
|
||||
const {openModal} = useModalControls()
|
||||
|
||||
if (!labels) {
|
||||
return null
|
||||
}
|
||||
labels = labels.filter(l => !l.val.startsWith('!'))
|
||||
if (!labels.length) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
pal.viewLight,
|
||||
{
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 10,
|
||||
borderRadius: 8,
|
||||
},
|
||||
style,
|
||||
]}>
|
||||
<Text type="sm" style={pal.text}>
|
||||
<Trans>
|
||||
This {'did' in details ? 'account' : 'post'} has been labeled.
|
||||
</Trans>{' '}
|
||||
</Text>
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg`Appeal this decision`)}
|
||||
accessibilityHint=""
|
||||
onPress={() => openModal({name: 'appeal-label', ...details})}>
|
||||
<Text type="sm" style={pal.link}>
|
||||
<Trans>Appeal this decision.</Trans>
|
||||
</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue