about summary refs log tree commit diff
path: root/src/view/com/composer
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/composer')
-rw-r--r--src/view/com/composer/ComposePost.tsx51
-rw-r--r--src/view/com/composer/PhotoCarouselPicker.tsx93
-rw-r--r--src/view/com/composer/SelectedPhoto.tsx83
3 files changed, 222 insertions, 5 deletions
diff --git a/src/view/com/composer/ComposePost.tsx b/src/view/com/composer/ComposePost.tsx
index a61759c24..114586f47 100644
--- a/src/view/com/composer/ComposePost.tsx
+++ b/src/view/com/composer/ComposePost.tsx
@@ -23,6 +23,9 @@ import * as apilib from '../../../state/lib/api'
 import {ComposerOpts} from '../../../state/models/shell-ui'
 import {s, colors, gradients} from '../../lib/styles'
 import {detectLinkables} from '../../../lib/strings'
+import {UserLocalPhotosModel} from '../../../state/models/user-local-photos'
+import {PhotoCarouselPicker} from './PhotoCarouselPicker'
+import {SelectedPhoto} from './SelectedPhoto'
 
 const MAX_TEXT_LENGTH = 256
 const DANGER_TEXT_LENGTH = MAX_TEXT_LENGTH
@@ -41,14 +44,25 @@ export const ComposePost = observer(function ComposePost({
   const [isProcessing, setIsProcessing] = useState(false)
   const [error, setError] = useState('')
   const [text, setText] = useState('')
+  const [selectedPhotos, setSelectedPhotos] = useState<string[]>([])
+
   const autocompleteView = useMemo<UserAutocompleteViewModel>(
     () => new UserAutocompleteViewModel(store),
     [],
   )
+  const localPhotos = useMemo<UserLocalPhotosModel>(
+    () => new UserLocalPhotosModel(store),
+    [],
+  )
 
   useEffect(() => {
     autocompleteView.setup()
   })
+
+  useEffect(() => {
+    localPhotos.setup()
+  }, [localPhotos])
+
   useEffect(() => {
     // HACK
     // wait a moment before focusing the input to resolve some layout bugs with the keyboard-avoiding-view
@@ -60,7 +74,9 @@ export const ComposePost = observer(function ComposePost({
       }, 250)
     }
     return () => {
-      if (to) clearTimeout(to)
+      if (to) {
+        clearTimeout(to)
+      }
     }
   }, [textInput.current])
 
@@ -115,6 +131,10 @@ export const ComposePost = observer(function ComposePost({
 
   const canPost = text.length <= MAX_TEXT_LENGTH
   const progressColor = text.length > DANGER_TEXT_LENGTH ? '#e60000' : undefined
+  const selectTextInputLayout =
+    selectedPhotos.length !== 0
+      ? styles.textInputLayoutWithPhoto
+      : styles.textInputLayoutWithoutPhoto
 
   const textDecorated = useMemo(() => {
     let i = 0
@@ -192,7 +212,7 @@ export const ComposePost = observer(function ComposePost({
             </View>
           </View>
         ) : undefined}
-        <View style={styles.textInputLayout}>
+        <View style={[styles.textInputLayout, selectTextInputLayout]}>
           <UserAvatar
             handle={store.me.handle || ''}
             displayName={store.me.displayName}
@@ -208,8 +228,18 @@ export const ComposePost = observer(function ComposePost({
             {textDecorated}
           </TextInput>
         </View>
-        <View
-          style={[s.flexRow, {alignItems: 'center'}, s.pt10, s.pb10, s.pr5]}>
+        <SelectedPhoto
+          selectedPhotos={selectedPhotos}
+          setSelectedPhotos={setSelectedPhotos}
+        />
+        <PhotoCarouselPicker
+          selectedPhotos={selectedPhotos}
+          setSelectedPhotos={setSelectedPhotos}
+          localPhotos={localPhotos}
+          inputText={text}
+        />
+        <View style={styles.separator} />
+        <View style={[s.flexRow, s.pt10, s.pb10, s.pr5, styles.contentCenter]}>
           <View style={s.flex1} />
           <Text style={[s.mr10, {color: progressColor}]}>
             {MAX_TEXT_LENGTH - text.length}
@@ -282,9 +312,14 @@ const styles = StyleSheet.create({
     justifyContent: 'center',
     marginRight: 5,
   },
+  textInputLayoutWithPhoto: {
+    flexWrap: 'wrap',
+  },
+  textInputLayoutWithoutPhoto: {
+    flex: 1,
+  },
   textInputLayout: {
     flexDirection: 'row',
-    flex: 1,
     borderTopWidth: 1,
     borderTopColor: colors.gray2,
     paddingTop: 16,
@@ -307,4 +342,10 @@ const styles = StyleSheet.create({
     paddingLeft: 13,
     paddingRight: 8,
   },
+  contentCenter: {alignItems: 'center'},
+  separator: {
+    borderBottomColor: 'black',
+    borderBottomWidth: StyleSheet.hairlineWidth,
+    width: '100%',
+  },
 })
diff --git a/src/view/com/composer/PhotoCarouselPicker.tsx b/src/view/com/composer/PhotoCarouselPicker.tsx
new file mode 100644
index 000000000..17be8a5f5
--- /dev/null
+++ b/src/view/com/composer/PhotoCarouselPicker.tsx
@@ -0,0 +1,93 @@
+import React from 'react'
+import {Image, StyleSheet, TouchableOpacity, ScrollView} from 'react-native'
+import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
+import {colors} from '../../lib/styles'
+import {openPicker, openCamera} from 'react-native-image-crop-picker'
+import {observer} from 'mobx-react-lite'
+
+export const PhotoCarouselPicker = observer(function PhotoCarouselPicker({
+  selectedPhotos,
+  setSelectedPhotos,
+  inputText,
+  localPhotos,
+}: {
+  selectedPhotos: string[]
+  setSelectedPhotos: React.Dispatch<React.SetStateAction<string[]>>
+  inputText: string
+  localPhotos: any
+}) {
+  return localPhotos.photos != null &&
+    inputText === '' &&
+    selectedPhotos.length === 0 ? (
+    <ScrollView
+      horizontal
+      style={styles.photosContainer}
+      showsHorizontalScrollIndicator={false}>
+      <TouchableOpacity
+        style={[styles.galleryButton, styles.photo]}
+        onPress={() => {
+          openCamera({multiple: true, maxFiles: 4}).then()
+        }}>
+        <FontAwesomeIcon
+          icon="camera"
+          size={24}
+          style={{color: colors.blue3}}
+        />
+      </TouchableOpacity>
+      {localPhotos.photos.map((item: any, index: number) => (
+        <TouchableOpacity
+          key={`local-image-${index}`}
+          style={styles.photoButton}
+          onPress={() => {
+            setSelectedPhotos([item.node.image.uri, ...selectedPhotos])
+          }}>
+          <Image style={styles.photo} source={{uri: item.node.image.uri}} />
+        </TouchableOpacity>
+      ))}
+      <TouchableOpacity
+        style={[styles.galleryButton, styles.photo]}
+        onPress={() => {
+          openPicker({multiple: true, maxFiles: 4}).then(items => {
+            setSelectedPhotos([
+              ...items.reduce(
+                (accum, cur) => accum.concat(cur.sourceURL!),
+                [] as string[],
+              ),
+              ...selectedPhotos,
+            ])
+          })
+        }}>
+        <FontAwesomeIcon icon="image" style={{color: colors.blue3}} size={24} />
+      </TouchableOpacity>
+    </ScrollView>
+  ) : null
+})
+
+const styles = StyleSheet.create({
+  photosContainer: {
+    width: '100%',
+    maxHeight: 96,
+    padding: 8,
+    overflow: 'hidden',
+  },
+  galleryButton: {
+    borderWidth: 1,
+    borderColor: colors.gray3,
+    alignItems: 'center',
+    justifyContent: 'center',
+  },
+  photoButton: {
+    width: 75,
+    height: 75,
+    marginRight: 8,
+    borderWidth: 1,
+    borderRadius: 16,
+    borderColor: colors.gray3,
+  },
+  photo: {
+    width: 75,
+    height: 75,
+    marginRight: 8,
+    borderRadius: 16,
+  },
+})
diff --git a/src/view/com/composer/SelectedPhoto.tsx b/src/view/com/composer/SelectedPhoto.tsx
new file mode 100644
index 000000000..1fe147483
--- /dev/null
+++ b/src/view/com/composer/SelectedPhoto.tsx
@@ -0,0 +1,83 @@
+import React from 'react'
+import {Image, StyleSheet, TouchableOpacity, View} from 'react-native'
+import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
+import {colors} from '../../lib/styles'
+import {observer} from 'mobx-react-lite'
+
+export const SelectedPhoto = ({
+  selectedPhotos,
+  setSelectedPhotos,
+}: {
+  selectedPhotos: string[]
+  setSelectedPhotos: React.Dispatch<React.SetStateAction<string[]>>
+}) => {
+  const imageStyle =
+    selectedPhotos.length === 1
+      ? styles.image250
+      : selectedPhotos.length === 2
+      ? styles.image175
+      : styles.image85
+
+  return selectedPhotos.length !== 0 ? (
+    <View style={styles.imageContainer}>
+      {selectedPhotos.length !== 0 &&
+        selectedPhotos.map((item, index) => (
+          <View
+            key={`selected-image-${index}`}
+            style={[styles.image, imageStyle]}>
+            <TouchableOpacity
+              onPress={() => {
+                setSelectedPhotos(
+                  selectedPhotos.filter(filterItem => filterItem !== item),
+                )
+              }}
+              style={styles.removePhotoButton}>
+              <FontAwesomeIcon
+                icon="xmark"
+                size={16}
+                style={{color: colors.white}}
+              />
+            </TouchableOpacity>
+
+            <Image style={[styles.image, imageStyle]} source={{uri: item}} />
+          </View>
+        ))}
+    </View>
+  ) : null
+}
+
+const styles = StyleSheet.create({
+  imageContainer: {
+    flex: 1,
+    flexDirection: 'row',
+    marginTop: 16,
+  },
+  image: {
+    borderRadius: 8,
+    margin: 2,
+  },
+  image250: {
+    width: 250,
+    height: 250,
+  },
+  image175: {
+    width: 175,
+    height: 175,
+  },
+  image85: {
+    width: 85,
+    height: 85,
+  },
+  removePhotoButton: {
+    position: 'absolute',
+    top: 8,
+    right: 8,
+    width: 24,
+    height: 24,
+    borderRadius: 12,
+    alignItems: 'center',
+    justifyContent: 'center',
+    backgroundColor: colors.black,
+    zIndex: 1,
+  },
+})