Remove indirection when rendering composer state (#5954)
* Simplify onPressCancel dismiss condition * Remove alias variables * Move grapheme length calculation to reducer
This commit is contained in:
@@ -59,7 +59,6 @@ import {usePalette} from '#/lib/hooks/usePalette'
|
||||
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
||||
import {logEvent} from '#/lib/statsig/statsig'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {shortenLinks} from '#/lib/strings/rich-text-manip'
|
||||
import {colors, s} from '#/lib/styles'
|
||||
import {logger} from '#/logger'
|
||||
import {isAndroid, isIOS, isNative, isWeb} from '#/platform/detection'
|
||||
@@ -128,8 +127,6 @@ type CancelRef = {
|
||||
onPressCancel: () => void
|
||||
}
|
||||
|
||||
const NO_IMAGES: ComposerImage[] = []
|
||||
|
||||
type Props = ComposerOpts
|
||||
export const ComposePost = ({
|
||||
replyTo,
|
||||
@@ -178,33 +175,10 @@ export const ComposePost = ({
|
||||
})
|
||||
}, [])
|
||||
|
||||
const richtext = draft.richtext
|
||||
let quote: string | undefined
|
||||
if (draft.embed.quote) {
|
||||
quote = draft.embed.quote.uri
|
||||
}
|
||||
let images = NO_IMAGES
|
||||
if (draft.embed.media?.type === 'images') {
|
||||
images = draft.embed.media.images
|
||||
}
|
||||
let videoState: VideoState | NoVideoState = NO_VIDEO
|
||||
if (draft.embed.media?.type === 'video') {
|
||||
videoState = draft.embed.media.video
|
||||
}
|
||||
let extGif: Gif | undefined
|
||||
let extGifAlt: string | undefined
|
||||
if (draft.embed.media?.type === 'gif') {
|
||||
extGif = draft.embed.media.gif
|
||||
extGifAlt = draft.embed.media.alt
|
||||
}
|
||||
let extLink: string | undefined
|
||||
if (draft.embed.link) {
|
||||
extLink = draft.embed.link.uri
|
||||
}
|
||||
|
||||
const graphemeLength = useMemo(() => {
|
||||
return shortenLinks(richtext).graphemeLength
|
||||
}, [richtext])
|
||||
|
||||
const selectVideo = React.useCallback(
|
||||
(asset: ImagePickerAsset) => {
|
||||
@@ -252,10 +226,9 @@ export const ComposePost = ({
|
||||
|
||||
const onPressCancel = useCallback(() => {
|
||||
if (
|
||||
graphemeLength > 0 ||
|
||||
images.length !== 0 ||
|
||||
extGif ||
|
||||
videoState.status !== 'idle'
|
||||
draft.shortenedGraphemeLength > 0 ||
|
||||
draft.embed.media ||
|
||||
draft.embed.link
|
||||
) {
|
||||
closeAllDialogs()
|
||||
Keyboard.dismiss()
|
||||
@@ -263,15 +236,7 @@ export const ComposePost = ({
|
||||
} else {
|
||||
onClose()
|
||||
}
|
||||
}, [
|
||||
extGif,
|
||||
graphemeLength,
|
||||
images.length,
|
||||
closeAllDialogs,
|
||||
discardPromptControl,
|
||||
onClose,
|
||||
videoState.status,
|
||||
])
|
||||
}, [draft, closeAllDialogs, discardPromptControl, onClose])
|
||||
|
||||
useImperativeHandle(cancelRef, () => ({onPressCancel}))
|
||||
|
||||
@@ -296,25 +261,27 @@ export const ComposePost = ({
|
||||
}, [onPressCancel, closeAllDialogs, closeAllModals])
|
||||
|
||||
const isAltTextRequiredAndMissing = useMemo(() => {
|
||||
if (!requireAltTextEnabled) return false
|
||||
|
||||
if (images.some(img => img.alt === '')) return true
|
||||
|
||||
if (extGif && !extGifAlt) return true
|
||||
|
||||
const media = draft.embed.media
|
||||
if (!requireAltTextEnabled || !media) {
|
||||
return false
|
||||
}
|
||||
if (media.type === 'images' && media.images.some(img => !img.alt)) {
|
||||
return true
|
||||
}
|
||||
if (media.type === 'gif' && !media.alt) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}, [images, extGifAlt, extGif, requireAltTextEnabled])
|
||||
}, [draft.embed.media, requireAltTextEnabled])
|
||||
|
||||
const isEmptyPost =
|
||||
richtext.text.trim().length === 0 &&
|
||||
images.length === 0 &&
|
||||
!extLink &&
|
||||
!extGif &&
|
||||
!quote &&
|
||||
videoState.status === 'idle'
|
||||
draft.richtext.text.trim().length === 0 &&
|
||||
!draft.embed.link &&
|
||||
!draft.embed.media &&
|
||||
!draft.embed.quote
|
||||
|
||||
const canPost =
|
||||
graphemeLength <= MAX_GRAPHEME_LENGTH &&
|
||||
draft.shortenedGraphemeLength <= MAX_GRAPHEME_LENGTH &&
|
||||
!isAltTextRequiredAndMissing &&
|
||||
!isEmptyPost &&
|
||||
videoState.status !== 'error'
|
||||
@@ -341,6 +308,11 @@ export const ComposePost = ({
|
||||
setError('')
|
||||
setIsPublishing(true)
|
||||
|
||||
const imageCount =
|
||||
draft.embed.media?.type === 'images'
|
||||
? draft.embed.media.images.length
|
||||
: 0
|
||||
|
||||
let postUri
|
||||
try {
|
||||
postUri = (
|
||||
@@ -365,7 +337,7 @@ export const ComposePost = ({
|
||||
} catch (e: any) {
|
||||
logger.error(e, {
|
||||
message: `Composer: create post failed`,
|
||||
hasImages: images.length > 0,
|
||||
hasImages: imageCount > 0,
|
||||
})
|
||||
|
||||
let err = cleanError(e.message)
|
||||
@@ -382,10 +354,10 @@ export const ComposePost = ({
|
||||
} finally {
|
||||
if (postUri) {
|
||||
logEvent('post:create', {
|
||||
imageCount: images.length,
|
||||
isReply: replyTo != null,
|
||||
hasLink: extLink != null,
|
||||
hasQuote: quote != null,
|
||||
imageCount,
|
||||
isReply: !!replyTo,
|
||||
hasLink: !!draft.embed.link,
|
||||
hasQuote: !!draft.embed.quote,
|
||||
langs: langPrefs.postLanguage,
|
||||
logContext: 'Composer',
|
||||
})
|
||||
@@ -422,14 +394,12 @@ export const ComposePost = ({
|
||||
_,
|
||||
agent,
|
||||
composerState.thread,
|
||||
extLink,
|
||||
images,
|
||||
draft,
|
||||
canPost,
|
||||
isPublishing,
|
||||
langPrefs.postLanguage,
|
||||
onClose,
|
||||
onPost,
|
||||
quote,
|
||||
initQuote,
|
||||
replyTo,
|
||||
setLangPrefs,
|
||||
@@ -512,7 +482,7 @@ export const ComposePost = ({
|
||||
/>
|
||||
</Animated.ScrollView>
|
||||
|
||||
<SuggestedLanguage text={richtext.text} />
|
||||
<SuggestedLanguage text={draft.richtext.text} />
|
||||
|
||||
<ComposerPills
|
||||
isReply={!!replyTo}
|
||||
@@ -524,7 +494,6 @@ export const ComposePost = ({
|
||||
|
||||
<ComposerFooter
|
||||
draft={draft}
|
||||
graphemeLength={graphemeLength}
|
||||
dispatch={dispatch}
|
||||
onError={setError}
|
||||
onEmojiButtonPress={onEmojiButtonPress}
|
||||
@@ -930,14 +899,12 @@ function ComposerPills({
|
||||
function ComposerFooter({
|
||||
draft,
|
||||
dispatch,
|
||||
graphemeLength,
|
||||
onEmojiButtonPress,
|
||||
onError,
|
||||
onSelectVideo,
|
||||
}: {
|
||||
draft: PostDraft
|
||||
dispatch: (action: PostAction) => void
|
||||
graphemeLength: number
|
||||
onEmojiButtonPress: () => void
|
||||
onError: (error: string) => void
|
||||
onSelectVideo: (asset: ImagePickerAsset) => void
|
||||
@@ -1017,7 +984,10 @@ function ComposerFooter({
|
||||
</View>
|
||||
<View style={[a.flex_row, a.align_center, a.justify_between]}>
|
||||
<SelectLangBtn />
|
||||
<CharProgress count={graphemeLength} style={{width: 65}} />
|
||||
<CharProgress
|
||||
count={draft.shortenedGraphemeLength}
|
||||
style={{width: 65}}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
|
||||
@@ -3,6 +3,7 @@ import {AppBskyFeedPostgate, RichText} from '@atproto/api'
|
||||
|
||||
import {SelfLabel} from '#/lib/moderation'
|
||||
import {insertMentionAt} from '#/lib/strings/mention-manip'
|
||||
import {shortenLinks} from '#/lib/strings/rich-text-manip'
|
||||
import {
|
||||
isBskyPostUrl,
|
||||
postUriToRelativePath,
|
||||
@@ -51,6 +52,7 @@ export type PostDraft = {
|
||||
richtext: RichText
|
||||
labels: SelfLabel[]
|
||||
embed: EmbedDraft
|
||||
shortenedGraphemeLength: number
|
||||
}
|
||||
|
||||
export type PostAction =
|
||||
@@ -137,6 +139,7 @@ function postReducer(state: PostDraft, action: PostAction): PostDraft {
|
||||
return {
|
||||
...state,
|
||||
richtext: action.richtext,
|
||||
shortenedGraphemeLength: shortenLinks(action.richtext).graphemeLength,
|
||||
}
|
||||
}
|
||||
case 'update_labels': {
|
||||
@@ -425,6 +428,7 @@ export function createComposerState({
|
||||
posts: [
|
||||
{
|
||||
richtext: initRichText,
|
||||
shortenedGraphemeLength: 0,
|
||||
labels: [],
|
||||
embed: {
|
||||
quote,
|
||||
|
||||
Reference in New Issue
Block a user