Rework list of labels on own content
This commit is contained in:
@@ -0,0 +1,92 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import {View} from 'react-native'
|
||||||
|
import {msg, Trans} from '@lingui/macro'
|
||||||
|
import {useLingui} from '@lingui/react'
|
||||||
|
import {ComAtprotoLabelDefs} from '@atproto/api'
|
||||||
|
|
||||||
|
import {atoms as a, useBreakpoints} from '#/alf'
|
||||||
|
import {Text} from '#/components/Typography'
|
||||||
|
import * as Dialog from '#/components/Dialog'
|
||||||
|
import {GlobalDialogProps} from '#/components/dialogs'
|
||||||
|
import {Button} from '#/components/Button'
|
||||||
|
|
||||||
|
type Subject =
|
||||||
|
| {
|
||||||
|
uri: string
|
||||||
|
cid: string
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
did: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LabelsOnMeDialogProps {
|
||||||
|
subject: Subject
|
||||||
|
labels: ComAtprotoLabelDefs.Label[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function LabelsOnMeDialog({
|
||||||
|
params,
|
||||||
|
cleanup,
|
||||||
|
}: GlobalDialogProps<LabelsOnMeDialogProps>) {
|
||||||
|
const {_} = useLingui()
|
||||||
|
const control = Dialog.useDialogControl()
|
||||||
|
const {gtMobile} = useBreakpoints()
|
||||||
|
const {subject, labels} = params
|
||||||
|
const isAccount = 'did' in subject
|
||||||
|
|
||||||
|
// REQUIRED CLEANUP
|
||||||
|
const onClose = React.useCallback(() => cleanup(), [cleanup])
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// const submit = async () => {
|
||||||
|
// try {
|
||||||
|
// const $type = !isAccountReport
|
||||||
|
// ? 'com.atproto.repo.strongRef'
|
||||||
|
// : 'com.atproto.admin.defs#repoRef'
|
||||||
|
// await getAgent().createModerationReport({
|
||||||
|
// reasonType: ComAtprotoModerationDefs.REASONAPPEAL,
|
||||||
|
// subject: {
|
||||||
|
// $type,
|
||||||
|
// ...props,
|
||||||
|
// },
|
||||||
|
// reason: details,
|
||||||
|
// })
|
||||||
|
// Toast.show(_(msg`We'll look into your appeal promptly.`))
|
||||||
|
// } finally {
|
||||||
|
// closeModal()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog.Outer defaultOpen control={control} onClose={onClose}>
|
||||||
|
<Dialog.Handle />
|
||||||
|
|
||||||
|
<Dialog.ScrollableInner
|
||||||
|
accessibilityDescribedBy="dialog-description"
|
||||||
|
accessibilityLabelledBy="dialog-title">
|
||||||
|
<Text nativeID="dialog-title" style={[a.text_2xl, a.font_bold]}>
|
||||||
|
<Trans>Labels on my {isAccount ? 'account' : 'content'}</Trans>
|
||||||
|
</Text>
|
||||||
|
<Text nativeID="dialog-description" style={[a.text_sm]}>
|
||||||
|
<Trans>
|
||||||
|
You may appeal these labels if you feel they were placed in error.
|
||||||
|
</Trans>
|
||||||
|
</Text>
|
||||||
|
{labels.map(label => (
|
||||||
|
<Text key={`${label.src}-${label.val}`}>{label.val} TODO</Text>
|
||||||
|
))}
|
||||||
|
<View style={gtMobile && [a.flex_row, a.justify_end]}>
|
||||||
|
<Button
|
||||||
|
testID="doneBtn"
|
||||||
|
variant="outline"
|
||||||
|
color="primary"
|
||||||
|
size="small"
|
||||||
|
onPress={() => control.close()}
|
||||||
|
label={_(msg`Done`)}>
|
||||||
|
{_(msg`Done`)}
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</Dialog.ScrollableInner>
|
||||||
|
</Dialog.Outer>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -36,16 +36,6 @@ export type ReportModal = {
|
|||||||
| {did: string}
|
| {did: string}
|
||||||
)
|
)
|
||||||
|
|
||||||
export type AppealLabelModal = {
|
|
||||||
name: 'appeal-label'
|
|
||||||
} & (
|
|
||||||
| {
|
|
||||||
uri: string
|
|
||||||
cid: string
|
|
||||||
}
|
|
||||||
| {did: string}
|
|
||||||
)
|
|
||||||
|
|
||||||
export interface CreateOrEditListModal {
|
export interface CreateOrEditListModal {
|
||||||
name: 'create-or-edit-list'
|
name: 'create-or-edit-list'
|
||||||
purpose?: string
|
purpose?: string
|
||||||
@@ -198,7 +188,6 @@ export type Modal =
|
|||||||
|
|
||||||
// Moderation
|
// Moderation
|
||||||
| ReportModal
|
| ReportModal
|
||||||
| AppealLabelModal
|
|
||||||
|
|
||||||
// Lists
|
// Lists
|
||||||
| CreateOrEditListModal
|
| CreateOrEditListModal
|
||||||
|
|||||||
@@ -1,139 +0,0 @@
|
|||||||
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.REASONAPPEAL,
|
|
||||||
subject: {
|
|
||||||
$type,
|
|
||||||
...props,
|
|
||||||
},
|
|
||||||
reason: details,
|
|
||||||
})
|
|
||||||
Toast.show(_(msg`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 Content Warning</Trans>
|
|
||||||
</Text>
|
|
||||||
<ScrollView>
|
|
||||||
<View style={[pal.btn, styles.detailsInputContainer]}>
|
|
||||||
<TextInput
|
|
||||||
accessibilityLabel={_(msg`Text input field`)}
|
|
||||||
accessibilityHint={_(
|
|
||||||
msg`Please tell us why you think this content warning was incorrectly applied!`,
|
|
||||||
)}
|
|
||||||
placeholder={_(
|
|
||||||
msg`Please tell us why you think this content warning was incorrectly applied!`,
|
|
||||||
)}
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
@@ -17,7 +17,6 @@ import * as ListAddUserModal from './ListAddRemoveUsers'
|
|||||||
import * as AltImageModal from './AltImage'
|
import * as AltImageModal from './AltImage'
|
||||||
import * as EditImageModal from './AltImage'
|
import * as EditImageModal from './AltImage'
|
||||||
import * as ReportModal from './report/Modal'
|
import * as ReportModal from './report/Modal'
|
||||||
import * as AppealLabelModal from './AppealLabel'
|
|
||||||
import * as DeleteAccountModal from './DeleteAccount'
|
import * as DeleteAccountModal from './DeleteAccount'
|
||||||
import * as ChangeHandleModal from './ChangeHandle'
|
import * as ChangeHandleModal from './ChangeHandle'
|
||||||
import * as WaitlistModal from './Waitlist'
|
import * as WaitlistModal from './Waitlist'
|
||||||
@@ -75,9 +74,6 @@ export function ModalsContainer() {
|
|||||||
} else if (activeModal?.name === 'report') {
|
} else if (activeModal?.name === 'report') {
|
||||||
snapPoints = ReportModal.snapPoints
|
snapPoints = ReportModal.snapPoints
|
||||||
element = <ReportModal.Component {...activeModal} />
|
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') {
|
} else if (activeModal?.name === 'create-or-edit-list') {
|
||||||
snapPoints = CreateOrEditListModal.snapPoints
|
snapPoints = CreateOrEditListModal.snapPoints
|
||||||
element = <CreateOrEditListModal.Component {...activeModal} />
|
element = <CreateOrEditListModal.Component {...activeModal} />
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import type {Modal as ModalIface} from '#/state/modals'
|
|||||||
import * as ConfirmModal from './Confirm'
|
import * as ConfirmModal from './Confirm'
|
||||||
import * as EditProfileModal from './EditProfile'
|
import * as EditProfileModal from './EditProfile'
|
||||||
import * as ReportModal from './report/Modal'
|
import * as ReportModal from './report/Modal'
|
||||||
import * as AppealLabelModal from './AppealLabel'
|
|
||||||
import * as CreateOrEditListModal from './CreateOrEditList'
|
import * as CreateOrEditListModal from './CreateOrEditList'
|
||||||
import * as UserAddRemoveLists from './UserAddRemoveLists'
|
import * as UserAddRemoveLists from './UserAddRemoveLists'
|
||||||
import * as ListAddUserModal from './ListAddRemoveUsers'
|
import * as ListAddUserModal from './ListAddRemoveUsers'
|
||||||
@@ -84,8 +83,6 @@ function Modal({modal}: {modal: ModalIface}) {
|
|||||||
element = <EditProfileModal.Component {...modal} />
|
element = <EditProfileModal.Component {...modal} />
|
||||||
} else if (modal.name === 'report') {
|
} else if (modal.name === 'report') {
|
||||||
element = <ReportModal.Component {...modal} />
|
element = <ReportModal.Component {...modal} />
|
||||||
} else if (modal.name === 'appeal-label') {
|
|
||||||
element = <AppealLabelModal.Component {...modal} />
|
|
||||||
} else if (modal.name === 'create-or-edit-list') {
|
} else if (modal.name === 'create-or-edit-list') {
|
||||||
element = <CreateOrEditListModal.Component {...modal} />
|
element = <CreateOrEditListModal.Component {...modal} />
|
||||||
} else if (modal.name === 'user-add-remove-lists') {
|
} else if (modal.name === 'user-add-remove-lists') {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import {PostCtrls} from '../util/post-ctrls/PostCtrls'
|
|||||||
import {PostHider} from '../util/moderation/PostHider'
|
import {PostHider} from '../util/moderation/PostHider'
|
||||||
import {ContentHider} from '../util/moderation/ContentHider'
|
import {ContentHider} from '../util/moderation/ContentHider'
|
||||||
import {PostAlerts} from '../util/moderation/PostAlerts'
|
import {PostAlerts} from '../util/moderation/PostAlerts'
|
||||||
|
import {LabelsOnMyPost} from '../util/moderation/LabelsOnMe'
|
||||||
import {PostSandboxWarning} from '../util/PostSandboxWarning'
|
import {PostSandboxWarning} from '../util/PostSandboxWarning'
|
||||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
@@ -286,6 +287,7 @@ let PostThreadItemLoaded = ({
|
|||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
<View style={[s.pl10, s.pr10, s.pb10]}>
|
<View style={[s.pl10, s.pr10, s.pb10]}>
|
||||||
|
<LabelsOnMyPost post={post} />
|
||||||
<ContentHider
|
<ContentHider
|
||||||
modui={moderation.ui('contentView')}
|
modui={moderation.ui('contentView')}
|
||||||
ignoreMute
|
ignoreMute
|
||||||
@@ -476,6 +478,7 @@ let PostThreadItemLoaded = ({
|
|||||||
displayNameStyle={isThreadedChild && s.ml2}
|
displayNameStyle={isThreadedChild && s.ml2}
|
||||||
style={isThreadedChild && s.mb2}
|
style={isThreadedChild && s.mb2}
|
||||||
/>
|
/>
|
||||||
|
<LabelsOnMyPost post={post} />
|
||||||
<PostAlerts
|
<PostAlerts
|
||||||
modui={moderation.ui('contentList')}
|
modui={moderation.ui('contentList')}
|
||||||
style={styles.alert}
|
style={styles.alert}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {PostEmbeds} from '../util/post-embeds'
|
|||||||
import {PostCtrls} from '../util/post-ctrls/PostCtrls'
|
import {PostCtrls} from '../util/post-ctrls/PostCtrls'
|
||||||
import {ContentHider} from '../util/moderation/ContentHider'
|
import {ContentHider} from '../util/moderation/ContentHider'
|
||||||
import {PostAlerts} from '../util/moderation/PostAlerts'
|
import {PostAlerts} from '../util/moderation/PostAlerts'
|
||||||
|
import {LabelsOnMyPost} from '../util/moderation/LabelsOnMe'
|
||||||
import {Text} from '../util/text/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {RichText} from '../util/text/RichText'
|
import {RichText} from '../util/text/RichText'
|
||||||
import {PreviewableUserAvatar} from '../util/UserAvatar'
|
import {PreviewableUserAvatar} from '../util/UserAvatar'
|
||||||
@@ -175,6 +176,7 @@ function PostInner({
|
|||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
<LabelsOnMyPost post={post} />
|
||||||
<ContentHider
|
<ContentHider
|
||||||
modui={moderation.ui('contentView')}
|
modui={moderation.ui('contentView')}
|
||||||
style={styles.contentHider}
|
style={styles.contentHider}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import {PostCtrls} from '../util/post-ctrls/PostCtrls'
|
|||||||
import {PostEmbeds} from '../util/post-embeds'
|
import {PostEmbeds} from '../util/post-embeds'
|
||||||
import {ContentHider} from '../util/moderation/ContentHider'
|
import {ContentHider} from '../util/moderation/ContentHider'
|
||||||
import {PostAlerts} from '../util/moderation/PostAlerts'
|
import {PostAlerts} from '../util/moderation/PostAlerts'
|
||||||
|
import {LabelsOnMyPost} from '../util/moderation/LabelsOnMe'
|
||||||
import {RichText} from '../util/text/RichText'
|
import {RichText} from '../util/text/RichText'
|
||||||
import {PostSandboxWarning} from '../util/PostSandboxWarning'
|
import {PostSandboxWarning} from '../util/PostSandboxWarning'
|
||||||
import {PreviewableUserAvatar} from '../util/UserAvatar'
|
import {PreviewableUserAvatar} from '../util/UserAvatar'
|
||||||
@@ -298,6 +299,7 @@ let FeedItemInner = ({
|
|||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
<LabelsOnMyPost post={post} />
|
||||||
<PostContent
|
<PostContent
|
||||||
moderation={moderation}
|
moderation={moderation}
|
||||||
richText={richText}
|
richText={richText}
|
||||||
@@ -309,9 +311,6 @@ let FeedItemInner = ({
|
|||||||
record={record}
|
record={record}
|
||||||
richText={richText}
|
richText={richText}
|
||||||
onPressReply={onPressReply}
|
onPressReply={onPressReply}
|
||||||
showAppealLabelItem={
|
|
||||||
post.author.did === currentAccount?.did && isModeratedPost
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@@ -324,12 +323,10 @@ let PostContent = ({
|
|||||||
moderation,
|
moderation,
|
||||||
richText,
|
richText,
|
||||||
postEmbed,
|
postEmbed,
|
||||||
postAuthor,
|
|
||||||
}: {
|
}: {
|
||||||
moderation: ModerationDecision
|
moderation: ModerationDecision
|
||||||
richText: RichTextAPI
|
richText: RichTextAPI
|
||||||
postEmbed: AppBskyFeedDefs.PostView['embed']
|
postEmbed: AppBskyFeedDefs.PostView['embed']
|
||||||
postAuthor: AppBskyFeedDefs.PostView['author']
|
|
||||||
}): React.ReactNode => {
|
}): React.ReactNode => {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ import {logger} from '#/logger'
|
|||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
import {Shadow} from '#/state/cache/types'
|
import {Shadow} from '#/state/cache/types'
|
||||||
import {useRequireAuth} from '#/state/session'
|
import {useRequireAuth} from '#/state/session'
|
||||||
import {LabelInfo} from '../util/moderation/LabelInfo'
|
import {LabelsOnMe} from '../util/moderation/LabelsOnMe'
|
||||||
import {useProfileShadow} from 'state/cache/profile-shadow'
|
import {useProfileShadow} from 'state/cache/profile-shadow'
|
||||||
import * as ModerationServiceCard from '#/components/ModerationServiceCard'
|
import * as ModerationServiceCard from '#/components/ModerationServiceCard'
|
||||||
import {getModerationServiceTitle} from '#/lib/moderation'
|
import {getModerationServiceTitle} from '#/lib/moderation'
|
||||||
@@ -635,7 +635,7 @@ let ProfileHeader = ({
|
|||||||
)}
|
)}
|
||||||
<ProfileHeaderAlerts moderation={moderation} />
|
<ProfileHeaderAlerts moderation={moderation} />
|
||||||
{isMe && (
|
{isMe && (
|
||||||
<LabelInfo details={{did: profile.did}} labels={profile.labels} />
|
<LabelsOnMe details={{did: profile.did}} labels={profile.labels} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<ModerationServiceCard.Loader
|
<ModerationServiceCard.Loader
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ let PostDropdownBtn = ({
|
|||||||
record,
|
record,
|
||||||
richText,
|
richText,
|
||||||
style,
|
style,
|
||||||
showAppealLabelItem,
|
|
||||||
}: {
|
}: {
|
||||||
testID: string
|
testID: string
|
||||||
postAuthor: AppBskyActorDefs.ProfileViewBasic
|
postAuthor: AppBskyActorDefs.ProfileViewBasic
|
||||||
@@ -52,7 +51,6 @@ let PostDropdownBtn = ({
|
|||||||
record: AppBskyFeedPost.Record
|
record: AppBskyFeedPost.Record
|
||||||
richText: RichTextAPI
|
richText: RichTextAPI
|
||||||
style?: StyleProp<ViewStyle>
|
style?: StyleProp<ViewStyle>
|
||||||
showAppealLabelItem?: boolean
|
|
||||||
}): React.ReactNode => {
|
}): React.ReactNode => {
|
||||||
const {hasSession, currentAccount} = useSession()
|
const {hasSession, currentAccount} = useSession()
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
@@ -255,23 +253,6 @@ let PostDropdownBtn = ({
|
|||||||
web: ['far', 'trash-can'],
|
web: ['far', 'trash-can'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
showAppealLabelItem && {
|
|
||||||
label: 'separator',
|
|
||||||
},
|
|
||||||
showAppealLabelItem && {
|
|
||||||
label: _(msg`Appeal content warning`),
|
|
||||||
onPress() {
|
|
||||||
openModal({name: 'appeal-label', uri: postUri, cid: postCid})
|
|
||||||
},
|
|
||||||
testID: 'postDropdownAppealBtn',
|
|
||||||
icon: {
|
|
||||||
ios: {
|
|
||||||
name: 'exclamationmark.triangle',
|
|
||||||
},
|
|
||||||
android: 'ic_menu_report_image',
|
|
||||||
web: 'circle-exclamation',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
].filter(Boolean) as NativeDropdownItem[]
|
].filter(Boolean) as NativeDropdownItem[]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
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>
|
|
||||||
A content warning has been applied to this{' '}
|
|
||||||
{'did' in details ? 'account' : 'post'}.
|
|
||||||
</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,0 +1,86 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import {StyleProp, View, ViewStyle} from 'react-native'
|
||||||
|
import {AppBskyFeedDefs, ComAtprotoLabelDefs} from '@atproto/api'
|
||||||
|
import {msg, Trans} from '@lingui/macro'
|
||||||
|
import {useLingui} from '@lingui/react'
|
||||||
|
import {useSession} from '#/state/session'
|
||||||
|
|
||||||
|
import {atoms as a} from '#/alf'
|
||||||
|
import {Button, ButtonText, ButtonIcon, ButtonSize} from '#/components/Button'
|
||||||
|
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
|
||||||
|
import {useOpenGlobalDialog} from '#/components/dialogs'
|
||||||
|
import {LabelsOnMeDialog} from '#/components/dialogs/LabelsOnMe'
|
||||||
|
|
||||||
|
export function LabelsOnMe({
|
||||||
|
details,
|
||||||
|
labels,
|
||||||
|
size,
|
||||||
|
style,
|
||||||
|
}: {
|
||||||
|
details: {did: string} | {uri: string; cid: string}
|
||||||
|
labels: ComAtprotoLabelDefs.Label[] | undefined
|
||||||
|
size?: ButtonSize
|
||||||
|
style?: StyleProp<ViewStyle>
|
||||||
|
}) {
|
||||||
|
const {_} = useLingui()
|
||||||
|
const {currentAccount} = useSession()
|
||||||
|
const isAccount = 'did' in details
|
||||||
|
const openDialog = useOpenGlobalDialog()
|
||||||
|
|
||||||
|
if (!labels || !currentAccount) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
labels = labels.filter(
|
||||||
|
l => !l.val.startsWith('!') && l.src !== currentAccount.did,
|
||||||
|
)
|
||||||
|
if (!labels.length) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={[a.flex_row, style]}>
|
||||||
|
<Button
|
||||||
|
variant="solid"
|
||||||
|
color="secondary"
|
||||||
|
size={size || 'small'}
|
||||||
|
label={_(msg`View information about these labels`)}
|
||||||
|
onPress={() => {
|
||||||
|
openDialog(LabelsOnMeDialog, {
|
||||||
|
subject: details,
|
||||||
|
labels: labels!,
|
||||||
|
})
|
||||||
|
}}>
|
||||||
|
<ButtonIcon position="left" icon={CircleInfo} />
|
||||||
|
<ButtonText>
|
||||||
|
{labels.length}{' '}
|
||||||
|
{labels.length === 1 ? (
|
||||||
|
<Trans>
|
||||||
|
label has been placed on this {isAccount ? 'account' : 'content'}
|
||||||
|
</Trans>
|
||||||
|
) : (
|
||||||
|
<Trans>
|
||||||
|
labels have been placed on this{' '}
|
||||||
|
{isAccount ? 'account' : 'content'}
|
||||||
|
</Trans>
|
||||||
|
)}
|
||||||
|
</ButtonText>
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function LabelsOnMyPost({
|
||||||
|
post,
|
||||||
|
style,
|
||||||
|
}: {
|
||||||
|
post: AppBskyFeedDefs.PostView
|
||||||
|
style?: StyleProp<ViewStyle>
|
||||||
|
}) {
|
||||||
|
const {currentAccount} = useSession()
|
||||||
|
if (post.author.did !== currentAccount?.did) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<LabelsOnMe details={post} labels={post.labels} size="tiny" style={style} />
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -41,7 +41,6 @@ let PostCtrls = ({
|
|||||||
post,
|
post,
|
||||||
record,
|
record,
|
||||||
richText,
|
richText,
|
||||||
showAppealLabelItem,
|
|
||||||
style,
|
style,
|
||||||
onPressReply,
|
onPressReply,
|
||||||
}: {
|
}: {
|
||||||
@@ -49,7 +48,6 @@ let PostCtrls = ({
|
|||||||
post: Shadow<AppBskyFeedDefs.PostView>
|
post: Shadow<AppBskyFeedDefs.PostView>
|
||||||
record: AppBskyFeedPost.Record
|
record: AppBskyFeedPost.Record
|
||||||
richText: RichTextAPI
|
richText: RichTextAPI
|
||||||
showAppealLabelItem?: boolean
|
|
||||||
style?: StyleProp<ViewStyle>
|
style?: StyleProp<ViewStyle>
|
||||||
onPressReply: () => void
|
onPressReply: () => void
|
||||||
}): React.ReactNode => {
|
}): React.ReactNode => {
|
||||||
@@ -229,7 +227,6 @@ let PostCtrls = ({
|
|||||||
postUri={post.uri}
|
postUri={post.uri}
|
||||||
record={record}
|
record={record}
|
||||||
richText={richText}
|
richText={richText}
|
||||||
showAppealLabelItem={showAppealLabelItem}
|
|
||||||
style={styles.btnPad}
|
style={styles.btnPad}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
RichText,
|
RichText,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
import {moderationOptsOverrideContext} from '#/state/queries/preferences'
|
import {moderationOptsOverrideContext} from '#/state/queries/preferences'
|
||||||
|
import {useSession} from '#/state/session'
|
||||||
|
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import {CenteredView, ScrollView} from '#/view/com/util/Views'
|
import {CenteredView, ScrollView} from '#/view/com/util/Views'
|
||||||
@@ -41,7 +42,7 @@ const LABEL_VALUES: (keyof typeof LABELS)[] = Object.keys(
|
|||||||
) as (keyof typeof LABELS)[]
|
) as (keyof typeof LABELS)[]
|
||||||
|
|
||||||
const MOCK_MOD_OPTS = {
|
const MOCK_MOD_OPTS = {
|
||||||
userDid: 'did:web:alice.test',
|
userDid: '',
|
||||||
adultContentEnabled: true,
|
adultContentEnabled: true,
|
||||||
labelGroups: {},
|
labelGroups: {},
|
||||||
mods: [
|
mods: [
|
||||||
@@ -63,6 +64,7 @@ export const DebugModScreen = ({}: NativeStackScreenProps<
|
|||||||
const [target, setTarget] = React.useState<string[]>(['account'])
|
const [target, setTarget] = React.useState<string[]>(['account'])
|
||||||
const [visibility, setVisiblity] = React.useState<string[]>(['hide'])
|
const [visibility, setVisiblity] = React.useState<string[]>(['hide'])
|
||||||
const labelStrings = useLabelStrings()
|
const labelStrings = useLabelStrings()
|
||||||
|
const {currentAccount} = useSession()
|
||||||
|
|
||||||
const isTargetMe =
|
const isTargetMe =
|
||||||
scenario[0] === 'label' && scenarioSwitches.includes('targetMe')
|
scenario[0] === 'label' && scenarioSwitches.includes('targetMe')
|
||||||
@@ -73,6 +75,9 @@ export const DebugModScreen = ({}: NativeStackScreenProps<
|
|||||||
const isLoggedOut =
|
const isLoggedOut =
|
||||||
scenario[0] === 'label' && scenarioSwitches.includes('loggedOut')
|
scenario[0] === 'label' && scenarioSwitches.includes('loggedOut')
|
||||||
|
|
||||||
|
const did =
|
||||||
|
isTargetMe && currentAccount ? currentAccount.did : 'did:web:bob.test'
|
||||||
|
|
||||||
const profile = React.useMemo(() => {
|
const profile = React.useMemo(() => {
|
||||||
const mockedProfile = mock.profileViewBasic({
|
const mockedProfile = mock.profileViewBasic({
|
||||||
handle: `bob.test`,
|
handle: `bob.test`,
|
||||||
@@ -82,17 +87,17 @@ export const DebugModScreen = ({}: NativeStackScreenProps<
|
|||||||
scenario[0] === 'label' && target[0] === 'account'
|
scenario[0] === 'label' && target[0] === 'account'
|
||||||
? [
|
? [
|
||||||
mock.label({
|
mock.label({
|
||||||
src: isSelfLabel ? 'did:web:bob.test' : undefined,
|
src: isSelfLabel ? did : undefined,
|
||||||
val: label[0],
|
val: label[0],
|
||||||
uri: `at://did:web:bob.test/`,
|
uri: `at://${did}/`,
|
||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
: scenario[0] === 'label' && target[0] === 'profile'
|
: scenario[0] === 'label' && target[0] === 'profile'
|
||||||
? [
|
? [
|
||||||
mock.label({
|
mock.label({
|
||||||
src: isSelfLabel ? 'did:web:bob.test' : undefined,
|
src: isSelfLabel ? did : undefined,
|
||||||
val: label[0],
|
val: label[0],
|
||||||
uri: `at://did:web:bob.test/app.bsky.actor.profile/self`,
|
uri: `at://${did}/app.bsky.actor.profile/self`,
|
||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
: undefined,
|
: undefined,
|
||||||
@@ -102,16 +107,17 @@ export const DebugModScreen = ({}: NativeStackScreenProps<
|
|||||||
blockedBy: undefined,
|
blockedBy: undefined,
|
||||||
blocking:
|
blocking:
|
||||||
scenario[0] === 'block'
|
scenario[0] === 'block'
|
||||||
? 'did://did:web:alice/app.bsky.actor.block/fake'
|
? `at://did:web:alice.test/app.bsky.actor.block/fake`
|
||||||
: undefined,
|
: undefined,
|
||||||
blockingByList: undefined,
|
blockingByList: undefined,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
mockedProfile.did = did
|
||||||
mockedProfile.avatar = 'https://bsky.social/about/images/favicon-32x32.png'
|
mockedProfile.avatar = 'https://bsky.social/about/images/favicon-32x32.png'
|
||||||
mockedProfile.banner =
|
mockedProfile.banner =
|
||||||
'https://bsky.social/about/images/social-card-default-gradient.png'
|
'https://bsky.social/about/images/social-card-default-gradient.png'
|
||||||
return mockedProfile
|
return mockedProfile
|
||||||
}, [scenario, target, label, isSelfLabel])
|
}, [scenario, target, label, isSelfLabel, did])
|
||||||
|
|
||||||
const post = React.useMemo(() => {
|
const post = React.useMemo(() => {
|
||||||
return mock.postView({
|
return mock.postView({
|
||||||
@@ -123,9 +129,9 @@ export const DebugModScreen = ({}: NativeStackScreenProps<
|
|||||||
scenario[0] === 'label' && target[0] === 'post'
|
scenario[0] === 'label' && target[0] === 'post'
|
||||||
? [
|
? [
|
||||||
mock.label({
|
mock.label({
|
||||||
src: isSelfLabel ? 'did:web:bob.test' : undefined,
|
src: isSelfLabel ? did : undefined,
|
||||||
val: label[0],
|
val: label[0],
|
||||||
uri: `at://bob.test/app.bsky.feed.post/fake`,
|
uri: `at:/${did}/app.bsky.feed.post/fake`,
|
||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
: undefined,
|
: undefined,
|
||||||
@@ -139,9 +145,9 @@ export const DebugModScreen = ({}: NativeStackScreenProps<
|
|||||||
scenario[0] === 'label' && target[0] === 'embed'
|
scenario[0] === 'label' && target[0] === 'embed'
|
||||||
? [
|
? [
|
||||||
mock.label({
|
mock.label({
|
||||||
src: isSelfLabel ? 'did:web:bob.test' : undefined,
|
src: isSelfLabel ? did : undefined,
|
||||||
val: label[0],
|
val: label[0],
|
||||||
uri: `at://bob.test/app.bsky.feed.post/fake`,
|
uri: `at://${did}/app.bsky.feed.post/fake`,
|
||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
: undefined,
|
: undefined,
|
||||||
@@ -160,23 +166,19 @@ export const DebugModScreen = ({}: NativeStackScreenProps<
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}, [scenario, label, target, profile, isSelfLabel])
|
}, [scenario, label, target, profile, isSelfLabel, did])
|
||||||
|
|
||||||
const modOpts = React.useMemo(() => {
|
const modOpts = React.useMemo(() => {
|
||||||
return {
|
return {
|
||||||
...MOCK_MOD_OPTS,
|
...MOCK_MOD_OPTS,
|
||||||
userDid: isLoggedOut
|
userDid: isLoggedOut ? '' : isTargetMe ? did : 'did:web:alice.test',
|
||||||
? ''
|
|
||||||
: isTargetMe
|
|
||||||
? 'did:web:bob.test'
|
|
||||||
: 'did:web:alice.test',
|
|
||||||
adultContentEnabled: !noAdult,
|
adultContentEnabled: !noAdult,
|
||||||
labelGroups: {
|
labelGroups: {
|
||||||
[LABELS[label[0] as keyof typeof LABELS].groupId]:
|
[LABELS[label[0] as keyof typeof LABELS].groupId]:
|
||||||
visibility[0] as LabelPreference,
|
visibility[0] as LabelPreference,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}, [label, visibility, noAdult, isLoggedOut, isTargetMe])
|
}, [label, visibility, noAdult, isLoggedOut, isTargetMe, did])
|
||||||
|
|
||||||
const profileModeration = React.useMemo(() => {
|
const profileModeration = React.useMemo(() => {
|
||||||
return moderateProfile(profile, modOpts)
|
return moderateProfile(profile, modOpts)
|
||||||
|
|||||||
Reference in New Issue
Block a user