about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorJoão Ferreiro <ferreiro@pinkroom.dev>2022-11-28 16:46:58 +0000
committerJoão Ferreiro <ferreiro@pinkroom.dev>2022-11-28 16:46:58 +0000
commit5ea750599d08229d4b5b10d0e724ca14c73735f5 (patch)
tree4e6f7511b054613b6d5ba189aeca692f95188953 /src
parentf5ff0fd2749b5f9ebd9c6b4129513ff8ad577c00 (diff)
downloadvoidsky-5ea750599d08229d4b5b10d0e724ca14c73735f5.tar.zst
upload images in composer v1
Diffstat (limited to 'src')
-rw-r--r--src/state/models/user-local-photos.ts25
-rw-r--r--src/view/com/composer/ComposePost.tsx181
-rw-r--r--src/view/index.ts6
3 files changed, 209 insertions, 3 deletions
diff --git a/src/state/models/user-local-photos.ts b/src/state/models/user-local-photos.ts
new file mode 100644
index 000000000..4e01f1d94
--- /dev/null
+++ b/src/state/models/user-local-photos.ts
@@ -0,0 +1,25 @@
+import {PhotoIdentifier} from './../../../node_modules/@react-native-camera-roll/camera-roll/src/CameraRoll'
+import {makeAutoObservable} from 'mobx'
+import {CameraRoll} from '@react-native-camera-roll/camera-roll'
+import {RootStoreModel} from './root-store'
+
+export class UserLocalPhotosModel {
+  // state
+  photos: PhotoIdentifier[] = []
+
+  constructor(public rootStore: RootStoreModel) {
+    makeAutoObservable(this, {
+      rootStore: false,
+    })
+  }
+
+  async setup() {
+    await this._getPhotos()
+  }
+
+  private async _getPhotos() {
+    CameraRoll.getPhotos({first: 20}).then(r => {
+      this.photos = r.edges
+    })
+  }
+}
diff --git a/src/view/com/composer/ComposePost.tsx b/src/view/com/composer/ComposePost.tsx
index bb175f166..10305adb6 100644
--- a/src/view/com/composer/ComposePost.tsx
+++ b/src/view/com/composer/ComposePost.tsx
@@ -9,10 +9,13 @@ import {
   TextInput,
   TouchableOpacity,
   View,
+  ScrollView,
+  Image,
 } from 'react-native'
 import LinearGradient from 'react-native-linear-gradient'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {UserAutocompleteViewModel} from '../../../state/models/user-autocomplete-view'
+import {UserLocalPhotosModel} from '../../../state/models/user-local-photos'
 import {Autocomplete} from './Autocomplete'
 import Toast from '../util/Toast'
 import ProgressCircle from '../util/ProgressCircle'
@@ -22,6 +25,7 @@ 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 {openPicker, openCamera} from 'react-native-image-crop-picker'
 
 const MAX_TEXT_LENGTH = 256
 const WARNING_TEXT_LENGTH = 200
@@ -40,15 +44,24 @@ export const ComposePost = observer(function ComposePost({
   const [isProcessing, setIsProcessing] = useState(false)
   const [error, setError] = useState('')
   const [text, setText] = useState('')
+  const [photoUris, setPhotoUris] = useState<string[]>([])
   const autocompleteView = useMemo<UserAutocompleteViewModel>(
     () => new UserAutocompleteViewModel(store),
     [],
   )
+  const localPhotos = useMemo<UserLocalPhotosModel>(
+    () => new UserLocalPhotosModel(store),
+    [],
+  )
 
   useEffect(() => {
     autocompleteView.setup()
   })
 
+  useEffect(() => {
+    localPhotos.setup()
+  }, [])
+
   const onChangeText = (newText: string) => {
     setText(newText)
 
@@ -183,12 +196,109 @@ export const ComposePost = observer(function ComposePost({
           multiline
           scrollEnabled
           onChangeText={(text: string) => onChangeText(text)}
-          placeholder={replyTo ? 'Write your reply' : "What's up?"}
+          placeholder={
+            replyTo
+              ? 'Write your reply'
+              : photoUris.length === 0
+              ? "What's up?"
+              : 'Add a comment...'
+          }
           style={styles.textInput}>
           {textDecorated}
         </TextInput>
-        <View
-          style={[s.flexRow, {alignItems: 'center'}, s.pt10, s.pb10, s.pr5]}>
+        {photoUris.length !== 0 && (
+          <View style={styles.selectedImageContainer}>
+            {photoUris.length !== 0 &&
+              photoUris.map(item => (
+                <View
+                  style={[
+                    styles.selectedImage,
+                    photoUris.length === 1
+                      ? styles.selectedImage250
+                      : photoUris.length === 2
+                      ? styles.selectedImage175
+                      : styles.selectedImage85,
+                  ]}>
+                  <TouchableOpacity
+                    onPress={() => {
+                      setPhotoUris(
+                        photoUris.filter(filterItem => filterItem !== item),
+                      )
+                    }}
+                    style={styles.removePhotoButton}>
+                    <FontAwesomeIcon
+                      icon="xmark"
+                      size={16}
+                      style={{color: colors.white}}
+                    />
+                  </TouchableOpacity>
+
+                  <Image
+                    style={[
+                      styles.selectedImage,
+                      photoUris.length === 1
+                        ? styles.selectedImage250
+                        : photoUris.length === 2
+                        ? styles.selectedImage175
+                        : styles.selectedImage85,
+                    ]}
+                    source={{uri: item}}
+                  />
+                </View>
+              ))}
+          </View>
+        )}
+        {localPhotos.photos != null && text === '' && photoUris.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 => (
+              <TouchableOpacity
+                style={styles.photoButton}
+                onPress={() => {
+                  setPhotoUris([item.node.image.uri, ...photoUris])
+                }}>
+                <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 => {
+                  setPhotoUris([
+                    ...items.reduce(
+                      (accum, cur) => accum.concat(cur.sourceURL!),
+                      [] as string[],
+                    ),
+                    ...photoUris,
+                  ])
+                })
+              }}>
+              <FontAwesomeIcon
+                icon="image"
+                style={{color: colors.blue3}}
+                size={24}
+              />
+            </TouchableOpacity>
+          </ScrollView>
+        )}
+        <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}
@@ -275,4 +385,69 @@ const styles = StyleSheet.create({
     marginTop: 5,
     marginBottom: 10,
   },
+  contentCenter: {alignItems: 'center'},
+  selectedImageContainer: {
+    flex: 10,
+    flexDirection: 'row',
+  },
+  selectedImage: {
+    borderRadius: 8,
+    margin: 2,
+  },
+  selectedImage250: {
+    width: 250,
+    height: 250,
+  },
+  selectedImage175: {
+    width: 175,
+    height: 175,
+  },
+  selectedImage85: {
+    width: 85,
+    height: 85,
+  },
+  photosContainer: {
+    width: '100%',
+    maxHeight: 96,
+    padding: 8,
+    overflow: 'hidden',
+  },
+  removePhotoButton: {
+    position: 'absolute',
+    top: 8,
+    right: 8,
+    width: 24,
+    height: 24,
+    borderRadius: 12,
+    alignItems: 'center',
+    justifyContent: 'center',
+    backgroundColor: colors.black,
+    zIndex: 1,
+  },
+  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,
+  },
+  separator: {
+    borderBottomColor: 'black',
+    borderBottomWidth: StyleSheet.hairlineWidth,
+    width: '110%',
+    marginLeft: -16,
+  },
 })
diff --git a/src/view/index.ts b/src/view/index.ts
index e38e1debf..bd0e33cbe 100644
--- a/src/view/index.ts
+++ b/src/view/index.ts
@@ -56,6 +56,9 @@ import {faUserXmark} from '@fortawesome/free-solid-svg-icons/faUserXmark'
 import {faTicket} from '@fortawesome/free-solid-svg-icons/faTicket'
 import {faTrashCan} from '@fortawesome/free-regular-svg-icons/faTrashCan'
 import {faX} from '@fortawesome/free-solid-svg-icons/faX'
+import {faCamera} from '@fortawesome/free-solid-svg-icons/faCamera'
+import {faImage} from '@fortawesome/free-solid-svg-icons/faImage'
+import {faXmark} from '@fortawesome/free-solid-svg-icons/faXmark'
 
 export function setup() {
   library.add(
@@ -115,5 +118,8 @@ export function setup() {
     faTicket,
     faTrashCan,
     faX,
+    faCamera,
+    faImage,
+    faXmark,
   )
 }