Add self-labeling controls (#1141)
* Add self-label modal * Use the shield-exclamation icon consistently on post moderation * Wire up self-labeling * Bump @atproto/api@0.6.0 * Bump @atproto/dev-env@^0.2.3 * Add e2e test for self-labeling * Fix types
This commit is contained in:
parent
48813a96d6
commit
03d152675e
21 changed files with 443 additions and 124 deletions
|
@ -41,6 +41,7 @@ import {isDesktopWeb, isAndroid, isIOS} from 'platform/detection'
|
|||
import {GalleryModel} from 'state/models/media/gallery'
|
||||
import {Gallery} from './photos/Gallery'
|
||||
import {MAX_GRAPHEME_LENGTH} from 'lib/constants'
|
||||
import {LabelsBtn} from './labels/LabelsBtn'
|
||||
import {SelectLangBtn} from './select-language/SelectLangBtn'
|
||||
|
||||
type Props = ComposerOpts & {
|
||||
|
@ -67,6 +68,7 @@ export const ComposePost = observer(function ComposePost({
|
|||
initQuote,
|
||||
)
|
||||
const {extLink, setExtLink} = useExternalLinkFetch({setQuote})
|
||||
const [labels, setLabels] = useState<string[]>([])
|
||||
const [suggestedLinks, setSuggestedLinks] = useState<Set<string>>(new Set())
|
||||
const gallery = useMemo(() => new GalleryModel(store), [store])
|
||||
|
||||
|
@ -145,75 +147,59 @@ export const ComposePost = observer(function ComposePost({
|
|||
[gallery, track],
|
||||
)
|
||||
|
||||
const onPressPublish = useCallback(
|
||||
async (rt: RichText) => {
|
||||
if (isProcessing || rt.graphemeLength > MAX_GRAPHEME_LENGTH) {
|
||||
return
|
||||
}
|
||||
if (store.preferences.requireAltTextEnabled && gallery.needsAltText) {
|
||||
return
|
||||
}
|
||||
const onPressPublish = async (rt: RichText) => {
|
||||
if (isProcessing || rt.graphemeLength > MAX_GRAPHEME_LENGTH) {
|
||||
return
|
||||
}
|
||||
if (store.preferences.requireAltTextEnabled && gallery.needsAltText) {
|
||||
return
|
||||
}
|
||||
|
||||
setError('')
|
||||
setError('')
|
||||
|
||||
if (rt.text.trim().length === 0 && gallery.isEmpty) {
|
||||
setError('Did you want to say anything?')
|
||||
return
|
||||
}
|
||||
if (rt.text.trim().length === 0 && gallery.isEmpty) {
|
||||
setError('Did you want to say anything?')
|
||||
return
|
||||
}
|
||||
|
||||
setIsProcessing(true)
|
||||
setIsProcessing(true)
|
||||
|
||||
try {
|
||||
await apilib.post(store, {
|
||||
rawText: rt.text,
|
||||
replyTo: replyTo?.uri,
|
||||
images: gallery.images,
|
||||
quote: quote,
|
||||
extLink: extLink,
|
||||
onStateChange: setProcessingState,
|
||||
knownHandles: autocompleteView.knownHandles,
|
||||
langs: store.preferences.postLanguages,
|
||||
})
|
||||
} catch (e: any) {
|
||||
if (extLink) {
|
||||
setExtLink({
|
||||
...extLink,
|
||||
isLoading: true,
|
||||
localThumb: undefined,
|
||||
} as apilib.ExternalEmbedDraft)
|
||||
}
|
||||
setError(cleanError(e.message))
|
||||
setIsProcessing(false)
|
||||
return
|
||||
} finally {
|
||||
track('Create Post', {
|
||||
imageCount: gallery.size,
|
||||
})
|
||||
if (replyTo && replyTo.uri) track('Post:Reply')
|
||||
try {
|
||||
await apilib.post(store, {
|
||||
rawText: rt.text,
|
||||
replyTo: replyTo?.uri,
|
||||
images: gallery.images,
|
||||
quote,
|
||||
extLink,
|
||||
labels,
|
||||
onStateChange: setProcessingState,
|
||||
knownHandles: autocompleteView.knownHandles,
|
||||
langs: store.preferences.postLanguages,
|
||||
})
|
||||
} catch (e: any) {
|
||||
if (extLink) {
|
||||
setExtLink({
|
||||
...extLink,
|
||||
isLoading: true,
|
||||
localThumb: undefined,
|
||||
} as apilib.ExternalEmbedDraft)
|
||||
}
|
||||
if (!replyTo) {
|
||||
store.me.mainFeed.onPostCreated()
|
||||
}
|
||||
onPost?.()
|
||||
onClose()
|
||||
Toast.show(`Your ${replyTo ? 'reply' : 'post'} has been published`)
|
||||
},
|
||||
[
|
||||
isProcessing,
|
||||
setError,
|
||||
setIsProcessing,
|
||||
replyTo,
|
||||
autocompleteView.knownHandles,
|
||||
extLink,
|
||||
onClose,
|
||||
onPost,
|
||||
quote,
|
||||
setExtLink,
|
||||
store,
|
||||
track,
|
||||
gallery,
|
||||
],
|
||||
)
|
||||
setError(cleanError(e.message))
|
||||
setIsProcessing(false)
|
||||
return
|
||||
} finally {
|
||||
track('Create Post', {
|
||||
imageCount: gallery.size,
|
||||
})
|
||||
if (replyTo && replyTo.uri) track('Post:Reply')
|
||||
}
|
||||
if (!replyTo) {
|
||||
store.me.mainFeed.onPostCreated()
|
||||
}
|
||||
onPost?.()
|
||||
onClose()
|
||||
Toast.show(`Your ${replyTo ? 'reply' : 'post'} has been published`)
|
||||
}
|
||||
|
||||
const canPost = useMemo(
|
||||
() =>
|
||||
|
@ -246,6 +232,7 @@ export const ComposePost = observer(function ComposePost({
|
|||
<Text style={[pal.link, s.f18]}>Cancel</Text>
|
||||
</TouchableOpacity>
|
||||
<View style={s.flex1} />
|
||||
<LabelsBtn labels={labels} onChange={setLabels} />
|
||||
{isProcessing ? (
|
||||
<View style={styles.postBtn}>
|
||||
<ActivityIndicator />
|
||||
|
@ -407,7 +394,7 @@ const styles = StyleSheet.create({
|
|||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingTop: isDesktopWeb ? 10 : undefined,
|
||||
paddingBottom: 10,
|
||||
paddingBottom: isDesktopWeb ? 10 : 4,
|
||||
paddingHorizontal: 20,
|
||||
height: 55,
|
||||
},
|
||||
|
|
53
src/view/com/composer/labels/LabelsBtn.tsx
Normal file
53
src/view/com/composer/labels/LabelsBtn.tsx
Normal file
|
@ -0,0 +1,53 @@
|
|||
import React from 'react'
|
||||
import {StyleSheet} from 'react-native'
|
||||
import {observer} from 'mobx-react-lite'
|
||||
import {Button} from 'view/com/util/forms/Button'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useStores} from 'state/index'
|
||||
import {ShieldExclamation} from 'lib/icons'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome'
|
||||
|
||||
export const LabelsBtn = observer(function LabelsBtn({
|
||||
labels,
|
||||
onChange,
|
||||
}: {
|
||||
labels: string[]
|
||||
onChange: (v: string[]) => void
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
|
||||
return (
|
||||
<Button
|
||||
type="default-light"
|
||||
testID="labelsBtn"
|
||||
style={styles.button}
|
||||
accessibilityLabel="Content warnings"
|
||||
accessibilityHint=""
|
||||
onPress={() =>
|
||||
store.shell.openModal({name: 'self-label', labels, onChange})
|
||||
}>
|
||||
<ShieldExclamation style={pal.link} size={26} />
|
||||
{labels.length > 0 ? (
|
||||
<FontAwesomeIcon
|
||||
icon="check"
|
||||
size={16}
|
||||
style={pal.link as FontAwesomeIconStyle}
|
||||
/>
|
||||
) : null}
|
||||
</Button>
|
||||
)
|
||||
})
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
button: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: 14,
|
||||
marginRight: 4,
|
||||
},
|
||||
label: {
|
||||
maxWidth: 100,
|
||||
},
|
||||
})
|
Loading…
Add table
Add a link
Reference in a new issue