about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/view/com/composer/Composer.tsx102
-rw-r--r--src/view/com/composer/state/composer.ts4
2 files changed, 40 insertions, 66 deletions
diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx
index 214f3605e..10169b706 100644
--- a/src/view/com/composer/Composer.tsx
+++ b/src/view/com/composer/Composer.tsx
@@ -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>
   )
diff --git a/src/view/com/composer/state/composer.ts b/src/view/com/composer/state/composer.ts
index 958718eaf..353600ef6 100644
--- a/src/view/com/composer/state/composer.ts
+++ b/src/view/com/composer/state/composer.ts
@@ -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,