about summary refs log tree commit diff
path: root/src/view/com/composer/Composer.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/composer/Composer.tsx')
-rw-r--r--src/view/com/composer/Composer.tsx132
1 files changed, 54 insertions, 78 deletions
diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx
index 7d72899fc..f77005b5e 100644
--- a/src/view/com/composer/Composer.tsx
+++ b/src/view/com/composer/Composer.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'
 import {observer} from 'mobx-react-lite'
 import {
   ActivityIndicator,
@@ -30,47 +30,42 @@ import {sanitizeDisplayName} from 'lib/strings/display-names'
 import {cleanError} from 'lib/strings/errors'
 import {SelectPhotoBtn} from './photos/SelectPhotoBtn'
 import {OpenCameraBtn} from './photos/OpenCameraBtn'
-import {SelectedPhotos} from './photos/SelectedPhotos'
 import {usePalette} from 'lib/hooks/usePalette'
 import QuoteEmbed from '../util/post-embeds/QuoteEmbed'
 import {useExternalLinkFetch} from './useExternalLinkFetch'
 import {isDesktopWeb} from 'platform/detection'
+import {GalleryModel} from 'state/models/media/gallery'
+import {Gallery} from './photos/Gallery'
 
 const MAX_GRAPHEME_LENGTH = 300
 
+type Props = ComposerOpts & {
+  onClose: () => void
+}
+
 export const ComposePost = observer(function ComposePost({
   replyTo,
   onPost,
   onClose,
   quote: initQuote,
-}: {
-  replyTo?: ComposerOpts['replyTo']
-  onPost?: ComposerOpts['onPost']
-  onClose: () => void
-  quote?: ComposerOpts['quote']
-}) {
+}: Props) {
   const {track} = useAnalytics()
   const pal = usePalette('default')
   const store = useStores()
-  const textInput = React.useRef<TextInputRef>(null)
-  const [isProcessing, setIsProcessing] = React.useState(false)
-  const [processingState, setProcessingState] = React.useState('')
-  const [error, setError] = React.useState('')
-  const [richtext, setRichText] = React.useState(new RichText({text: ''}))
-  const graphemeLength = React.useMemo(
-    () => richtext.graphemeLength,
-    [richtext],
-  )
-  const [quote, setQuote] = React.useState<ComposerOpts['quote'] | undefined>(
+  const textInput = useRef<TextInputRef>(null)
+  const [isProcessing, setIsProcessing] = useState(false)
+  const [processingState, setProcessingState] = useState('')
+  const [error, setError] = useState('')
+  const [richtext, setRichText] = useState(new RichText({text: ''}))
+  const graphemeLength = useMemo(() => richtext.graphemeLength, [richtext])
+  const [quote, setQuote] = useState<ComposerOpts['quote'] | undefined>(
     initQuote,
   )
   const {extLink, setExtLink} = useExternalLinkFetch({setQuote})
-  const [suggestedLinks, setSuggestedLinks] = React.useState<Set<string>>(
-    new Set(),
-  )
-  const [selectedPhotos, setSelectedPhotos] = React.useState<string[]>([])
+  const [suggestedLinks, setSuggestedLinks] = useState<Set<string>>(new Set())
+  const gallery = useMemo(() => new GalleryModel(store), [store])
 
-  const autocompleteView = React.useMemo<UserAutocompleteModel>(
+  const autocompleteView = useMemo<UserAutocompleteModel>(
     () => new UserAutocompleteModel(store),
     [store],
   )
@@ -82,17 +77,17 @@ export const ComposePost = observer(function ComposePost({
   // is focused during unmount, an exception will throw (seems that a blur method isnt implemented)
   // manually blurring before closing gets around that
   // -prf
-  const hackfixOnClose = React.useCallback(() => {
+  const hackfixOnClose = useCallback(() => {
     textInput.current?.blur()
     onClose()
   }, [textInput, onClose])
 
   // initial setup
-  React.useEffect(() => {
+  useEffect(() => {
     autocompleteView.setup()
   }, [autocompleteView])
 
-  React.useEffect(() => {
+  useEffect(() => {
     // HACK
     // wait a moment before focusing the input to resolve some layout bugs with the keyboard-avoiding-view
     // -prf
@@ -109,60 +104,51 @@ export const ComposePost = observer(function ComposePost({
     }
   }, [])
 
-  const onPressContainer = React.useCallback(() => {
+  const onPressContainer = useCallback(() => {
     textInput.current?.focus()
   }, [textInput])
 
-  const onSelectPhotos = React.useCallback(
-    (photos: string[]) => {
-      track('Composer:SelectedPhotos')
-      setSelectedPhotos(photos)
-    },
-    [track, setSelectedPhotos],
-  )
-
-  const onPressAddLinkCard = React.useCallback(
+  const onPressAddLinkCard = useCallback(
     (uri: string) => {
       setExtLink({uri, isLoading: true})
     },
     [setExtLink],
   )
 
-  const onPhotoPasted = React.useCallback(
+  const onPhotoPasted = useCallback(
     async (uri: string) => {
-      if (selectedPhotos.length >= 4) {
-        return
-      }
-      onSelectPhotos([...selectedPhotos, uri])
+      track('Composer:PastedPhotos')
+      gallery.paste(uri)
     },
-    [selectedPhotos, onSelectPhotos],
+    [gallery, track],
   )
 
-  const onPressPublish = React.useCallback(async () => {
-    if (isProcessing) {
-      return
-    }
-    if (richtext.graphemeLength > MAX_GRAPHEME_LENGTH) {
+  const onPressPublish = useCallback(async () => {
+    if (isProcessing || richtext.graphemeLength > MAX_GRAPHEME_LENGTH) {
       return
     }
+
     setError('')
-    if (richtext.text.trim().length === 0 && selectedPhotos.length === 0) {
+
+    if (richtext.text.trim().length === 0 && gallery.isEmpty) {
       setError('Did you want to say anything?')
       return false
     }
+
     setIsProcessing(true)
+
     try {
       await apilib.post(store, {
         rawText: richtext.text,
         replyTo: replyTo?.uri,
-        images: selectedPhotos,
+        images: gallery.paths,
         quote: quote,
         extLink: extLink,
         onStateChange: setProcessingState,
         knownHandles: autocompleteView.knownHandles,
       })
       track('Create Post', {
-        imageCount: selectedPhotos.length,
+        imageCount: gallery.size,
       })
     } catch (e: any) {
       if (extLink) {
@@ -191,34 +177,33 @@ export const ComposePost = observer(function ComposePost({
     hackfixOnClose,
     onPost,
     quote,
-    selectedPhotos,
     setExtLink,
     store,
     track,
+    gallery,
   ])
 
   const canPost = graphemeLength <= MAX_GRAPHEME_LENGTH
 
   const selectTextInputPlaceholder = replyTo
     ? 'Write your reply'
-    : selectedPhotos.length !== 0
+    : gallery.isEmpty
     ? 'Write a comment'
     : "What's up?"
 
+  const canSelectImages = gallery.size <= 4
+  const viewStyles = {
+    paddingBottom: Platform.OS === 'android' ? insets.bottom : 0,
+    paddingTop: Platform.OS === 'android' ? insets.top : 15,
+  }
+
   return (
     <KeyboardAvoidingView
       testID="composePostView"
       behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
       style={styles.outer}>
       <TouchableWithoutFeedback onPressIn={onPressContainer}>
-        <View
-          style={[
-            s.flex1,
-            {
-              paddingBottom: Platform.OS === 'android' ? insets.bottom : 0,
-              paddingTop: Platform.OS === 'android' ? insets.top : 15,
-            },
-          ]}>
+        <View style={[s.flex1, viewStyles]}>
           <View style={styles.topbar}>
             <TouchableOpacity
               testID="composerCancelButton"
@@ -301,11 +286,8 @@ export const ComposePost = observer(function ComposePost({
               />
             </View>
 
-            <SelectedPhotos
-              selectedPhotos={selectedPhotos}
-              onSelectPhotos={onSelectPhotos}
-            />
-            {selectedPhotos.length === 0 && extLink && (
+            <Gallery gallery={gallery} />
+            {gallery.isEmpty && extLink && (
               <ExternalEmbed
                 link={extLink}
                 onRemove={() => setExtLink(undefined)}
@@ -317,9 +299,7 @@ export const ComposePost = observer(function ComposePost({
               </View>
             ) : undefined}
           </ScrollView>
-          {!extLink &&
-          selectedPhotos.length === 0 &&
-          suggestedLinks.size > 0 ? (
+          {!extLink && suggestedLinks.size > 0 ? (
             <View style={s.mb5}>
               {Array.from(suggestedLinks).map(url => (
                 <TouchableOpacity
@@ -335,16 +315,12 @@ export const ComposePost = observer(function ComposePost({
             </View>
           ) : null}
           <View style={[pal.border, styles.bottomBar]}>
-            <SelectPhotoBtn
-              enabled={selectedPhotos.length < 4}
-              selectedPhotos={selectedPhotos}
-              onSelectPhotos={setSelectedPhotos}
-            />
-            <OpenCameraBtn
-              enabled={selectedPhotos.length < 4}
-              selectedPhotos={selectedPhotos}
-              onSelectPhotos={setSelectedPhotos}
-            />
+            {canSelectImages ? (
+              <>
+                <SelectPhotoBtn gallery={gallery} />
+                <OpenCameraBtn gallery={gallery} />
+              </>
+            ) : null}
             <View style={s.flex1} />
             <CharProgress count={graphemeLength} />
           </View>