Skip to content

Commit

Permalink
Add label appeal tool to posts and accounts (#2124)
Browse files Browse the repository at this point in the history
* Add label appeal tool to posts and accounts

* Fix translations
  • Loading branch information
pfrazee authored Dec 7, 2023
1 parent 794015a commit 52a0cb8
Show file tree
Hide file tree
Showing 12 changed files with 846 additions and 380 deletions.
199 changes: 123 additions & 76 deletions src/locale/locales/cs/messages.po

Large diffs are not rendered by default.

199 changes: 123 additions & 76 deletions src/locale/locales/en/messages.po

Large diffs are not rendered by default.

199 changes: 123 additions & 76 deletions src/locale/locales/es/messages.po

Large diffs are not rendered by default.

199 changes: 123 additions & 76 deletions src/locale/locales/fr/messages.po

Large diffs are not rendered by default.

199 changes: 123 additions & 76 deletions src/locale/locales/hi/messages.po

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions src/state/modals/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@ export type ReportModal = {
| {did: string}
)

export type AppealLabelModal = {
name: 'appeal-label'
} & (
| {
uri: string
cid: string
}
| {did: string}
)

export interface CreateOrEditListModal {
name: 'create-or-edit-list'
purpose?: string
Expand Down Expand Up @@ -183,6 +193,7 @@ export type Modal =
// Moderation
| ModerationDetailsModal
| ReportModal
| AppealLabelModal

// Lists
| CreateOrEditListModal
Expand Down
139 changes: 139 additions & 0 deletions src/view/com/modals/AppealLabel.tsx
Original file line number Diff line number Diff line change
@@ -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,
},
})
4 changes: 4 additions & 0 deletions src/view/com/modals/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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} />
Expand Down
3 changes: 3 additions & 0 deletions src/view/com/modals/Modal.web.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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') {
Expand Down
10 changes: 10 additions & 0 deletions src/view/com/post-thread/PostThreadItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
)
Expand Down Expand Up @@ -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={[
Expand Down
4 changes: 4 additions & 0 deletions src/view/com/profile/ProfileHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -619,6 +620,9 @@ let ProfileHeaderLoaded = ({
</>
)}
<ProfileHeaderAlerts moderation={moderation} />
{isMe && (
<LabelInfo details={{did: profile.did}} labels={profile.labels} />
)}
</View>

{!isProfilePreview && showSuggestedFollows && (
Expand Down
60 changes: 60 additions & 0 deletions src/view/com/util/moderation/LabelInfo.tsx
Original file line number Diff line number Diff line change
@@ -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>
)
}

0 comments on commit 52a0cb8

Please sign in to comment.