about summary refs log tree commit diff
path: root/src/view/com/composer/photos
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/composer/photos')
-rw-r--r--src/view/com/composer/photos/Gallery.tsx130
-rw-r--r--src/view/com/composer/photos/OpenCameraBtn.tsx58
-rw-r--r--src/view/com/composer/photos/OpenCameraBtn.web.tsx3
-rw-r--r--src/view/com/composer/photos/SelectPhotoBtn.tsx84
-rw-r--r--src/view/com/composer/photos/SelectedPhotos.tsx96
5 files changed, 166 insertions, 205 deletions
diff --git a/src/view/com/composer/photos/Gallery.tsx b/src/view/com/composer/photos/Gallery.tsx
new file mode 100644
index 000000000..f4dfc88fa
--- /dev/null
+++ b/src/view/com/composer/photos/Gallery.tsx
@@ -0,0 +1,130 @@
+import React, {useCallback} from 'react'
+import {GalleryModel} from 'state/models/media/gallery'
+import {observer} from 'mobx-react-lite'
+import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
+import {colors} from 'lib/styles'
+import {StyleSheet, TouchableOpacity, View} from 'react-native'
+import {ImageModel} from 'state/models/media/image'
+import {Image} from 'expo-image'
+
+interface Props {
+  gallery: GalleryModel
+}
+
+export const Gallery = observer(function ({gallery}: Props) {
+  const getImageStyle = useCallback(() => {
+    switch (gallery.size) {
+      case 1:
+        return styles.image250
+      case 2:
+        return styles.image175
+      default:
+        return styles.image85
+    }
+  }, [gallery])
+
+  const imageStyle = getImageStyle()
+  const handleRemovePhoto = useCallback(
+    (image: ImageModel) => {
+      gallery.remove(image)
+    },
+    [gallery],
+  )
+
+  const handleEditPhoto = useCallback(
+    (image: ImageModel) => {
+      gallery.crop(image)
+    },
+    [gallery],
+  )
+
+  return !gallery.isEmpty ? (
+    <View testID="selectedPhotosView" style={styles.gallery}>
+      {gallery.images.map(image =>
+        image.compressed !== undefined ? (
+          <View
+            key={`selected-image-${image.path}`}
+            style={[styles.imageContainer, imageStyle]}>
+            <View style={styles.imageControls}>
+              <TouchableOpacity
+                testID="cropPhotoButton"
+                onPress={() => {
+                  handleEditPhoto(image)
+                }}
+                style={styles.imageControl}>
+                <FontAwesomeIcon
+                  icon="pen"
+                  size={12}
+                  style={{color: colors.white}}
+                />
+              </TouchableOpacity>
+              <TouchableOpacity
+                testID="removePhotoButton"
+                onPress={() => handleRemovePhoto(image)}
+                style={styles.imageControl}>
+                <FontAwesomeIcon
+                  icon="xmark"
+                  size={16}
+                  style={{color: colors.white}}
+                />
+              </TouchableOpacity>
+            </View>
+
+            <Image
+              testID="selectedPhotoImage"
+              style={[styles.image, imageStyle]}
+              source={{
+                uri: image.compressed.path,
+              }}
+            />
+          </View>
+        ) : null,
+      )}
+    </View>
+  ) : null
+})
+
+const styles = StyleSheet.create({
+  gallery: {
+    flex: 1,
+    flexDirection: 'row',
+    marginTop: 16,
+  },
+  imageContainer: {
+    margin: 2,
+  },
+  image: {
+    resizeMode: 'cover',
+    borderRadius: 8,
+  },
+  image250: {
+    width: 250,
+    height: 250,
+  },
+  image175: {
+    width: 175,
+    height: 175,
+  },
+  image85: {
+    width: 85,
+    height: 85,
+  },
+  imageControls: {
+    position: 'absolute',
+    display: 'flex',
+    flexDirection: 'row',
+    gap: 4,
+    top: 8,
+    right: 8,
+    zIndex: 1,
+  },
+  imageControl: {
+    width: 24,
+    height: 24,
+    borderRadius: 12,
+    backgroundColor: 'rgba(0, 0, 0, 0.75)',
+    borderWidth: 0.5,
+    alignItems: 'center',
+    justifyContent: 'center',
+  },
+})
diff --git a/src/view/com/composer/photos/OpenCameraBtn.tsx b/src/view/com/composer/photos/OpenCameraBtn.tsx
index 118728781..809c41783 100644
--- a/src/view/com/composer/photos/OpenCameraBtn.tsx
+++ b/src/view/com/composer/photos/OpenCameraBtn.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import React, {useCallback} from 'react'
 import {TouchableOpacity} from 'react-native'
 import {
   FontAwesomeIcon,
@@ -10,62 +10,44 @@ import {useStores} from 'state/index'
 import {s} from 'lib/styles'
 import {isDesktopWeb} from 'platform/detection'
 import {openCamera} from 'lib/media/picker'
-import {compressIfNeeded} from 'lib/media/manip'
 import {useCameraPermission} from 'lib/hooks/usePermissions'
-import {
-  POST_IMG_MAX_WIDTH,
-  POST_IMG_MAX_HEIGHT,
-  POST_IMG_MAX_SIZE,
-} from 'lib/constants'
+import {POST_IMG_MAX} from 'lib/constants'
+import {GalleryModel} from 'state/models/media/gallery'
 
 const HITSLOP = {left: 10, top: 10, right: 10, bottom: 10}
 
-export function OpenCameraBtn({
-  enabled,
-  selectedPhotos,
-  onSelectPhotos,
-}: {
-  enabled: boolean
-  selectedPhotos: string[]
-  onSelectPhotos: (v: string[]) => void
-}) {
+type Props = {
+  gallery: GalleryModel
+}
+
+export function OpenCameraBtn({gallery}: Props) {
   const pal = usePalette('default')
   const {track} = useAnalytics()
   const store = useStores()
   const {requestCameraAccessIfNeeded} = useCameraPermission()
 
-  const onPressTakePicture = React.useCallback(async () => {
+  const onPressTakePicture = useCallback(async () => {
     track('Composer:CameraOpened')
-    if (!enabled) {
-      return
-    }
     try {
       if (!(await requestCameraAccessIfNeeded())) {
         return
       }
-      const cameraRes = await openCamera(store, {
-        mediaType: 'photo',
-        width: POST_IMG_MAX_WIDTH,
-        height: POST_IMG_MAX_HEIGHT,
+
+      const img = await openCamera(store, {
+        width: POST_IMG_MAX.width,
+        height: POST_IMG_MAX.height,
         freeStyleCropEnabled: true,
       })
-      const img = await compressIfNeeded(cameraRes, POST_IMG_MAX_SIZE)
-      onSelectPhotos([...selectedPhotos, img.path])
+
+      gallery.add(img)
     } catch (err: any) {
       // ignore
       store.log.warn('Error using camera', err)
     }
-  }, [
-    track,
-    store,
-    onSelectPhotos,
-    selectedPhotos,
-    enabled,
-    requestCameraAccessIfNeeded,
-  ])
+  }, [gallery, track, store, requestCameraAccessIfNeeded])
 
   if (isDesktopWeb) {
-    return <></>
+    return null
   }
 
   return (
@@ -76,11 +58,7 @@ export function OpenCameraBtn({
       hitSlop={HITSLOP}>
       <FontAwesomeIcon
         icon="camera"
-        style={
-          (enabled
-            ? pal.link
-            : [pal.textLight, s.dimmed]) as FontAwesomeIconStyle
-        }
+        style={pal.link as FontAwesomeIconStyle}
         size={24}
       />
     </TouchableOpacity>
diff --git a/src/view/com/composer/photos/OpenCameraBtn.web.tsx b/src/view/com/composer/photos/OpenCameraBtn.web.tsx
new file mode 100644
index 000000000..226de1f60
--- /dev/null
+++ b/src/view/com/composer/photos/OpenCameraBtn.web.tsx
@@ -0,0 +1,3 @@
+export function OpenCameraBtn() {
+  return null
+}
diff --git a/src/view/com/composer/photos/SelectPhotoBtn.tsx b/src/view/com/composer/photos/SelectPhotoBtn.tsx
index c0808b85c..9569e08ad 100644
--- a/src/view/com/composer/photos/SelectPhotoBtn.tsx
+++ b/src/view/com/composer/photos/SelectPhotoBtn.tsx
@@ -1,86 +1,36 @@
-import React from 'react'
-import {Platform, TouchableOpacity} from 'react-native'
+import React, {useCallback} from 'react'
+import {TouchableOpacity} from 'react-native'
 import {
   FontAwesomeIcon,
   FontAwesomeIconStyle,
 } from '@fortawesome/react-native-fontawesome'
 import {usePalette} from 'lib/hooks/usePalette'
 import {useAnalytics} from 'lib/analytics'
-import {useStores} from 'state/index'
 import {s} from 'lib/styles'
 import {isDesktopWeb} from 'platform/detection'
-import {openPicker, cropAndCompressFlow, pickImagesFlow} from 'lib/media/picker'
 import {usePhotoLibraryPermission} from 'lib/hooks/usePermissions'
-import {
-  POST_IMG_MAX_WIDTH,
-  POST_IMG_MAX_HEIGHT,
-  POST_IMG_MAX_SIZE,
-} from 'lib/constants'
+import {GalleryModel} from 'state/models/media/gallery'
 
 const HITSLOP = {left: 10, top: 10, right: 10, bottom: 10}
 
-export function SelectPhotoBtn({
-  enabled,
-  selectedPhotos,
-  onSelectPhotos,
-}: {
-  enabled: boolean
-  selectedPhotos: string[]
-  onSelectPhotos: (v: string[]) => void
-}) {
+type Props = {
+  gallery: GalleryModel
+}
+
+export function SelectPhotoBtn({gallery}: Props) {
   const pal = usePalette('default')
   const {track} = useAnalytics()
-  const store = useStores()
   const {requestPhotoAccessIfNeeded} = usePhotoLibraryPermission()
 
-  const onPressSelectPhotos = React.useCallback(async () => {
+  const onPressSelectPhotos = useCallback(async () => {
     track('Composer:GalleryOpened')
-    if (!enabled) {
+
+    if (!isDesktopWeb && !(await requestPhotoAccessIfNeeded())) {
       return
     }
-    if (isDesktopWeb) {
-      const images = await pickImagesFlow(
-        store,
-        4 - selectedPhotos.length,
-        {width: POST_IMG_MAX_WIDTH, height: POST_IMG_MAX_HEIGHT},
-        POST_IMG_MAX_SIZE,
-      )
-      onSelectPhotos([...selectedPhotos, ...images])
-    } else {
-      if (!(await requestPhotoAccessIfNeeded())) {
-        return
-      }
-      const items = await openPicker(store, {
-        multiple: true,
-        maxFiles: 4 - selectedPhotos.length,
-        mediaType: 'photo',
-      })
-      const result = []
-      for (const image of items) {
-        if (Platform.OS === 'android') {
-          result.push(image.path)
-          continue
-        }
-        result.push(
-          await cropAndCompressFlow(
-            store,
-            image.path,
-            image,
-            {width: POST_IMG_MAX_WIDTH, height: POST_IMG_MAX_HEIGHT},
-            POST_IMG_MAX_SIZE,
-          ),
-        )
-      }
-      onSelectPhotos([...selectedPhotos, ...result])
-    }
-  }, [
-    track,
-    store,
-    onSelectPhotos,
-    selectedPhotos,
-    enabled,
-    requestPhotoAccessIfNeeded,
-  ])
+
+    gallery.pick()
+  }, [track, gallery, requestPhotoAccessIfNeeded])
 
   return (
     <TouchableOpacity
@@ -90,11 +40,7 @@ export function SelectPhotoBtn({
       hitSlop={HITSLOP}>
       <FontAwesomeIcon
         icon={['far', 'image']}
-        style={
-          (enabled
-            ? pal.link
-            : [pal.textLight, s.dimmed]) as FontAwesomeIconStyle
-        }
+        style={pal.link as FontAwesomeIconStyle}
         size={24}
       />
     </TouchableOpacity>
diff --git a/src/view/com/composer/photos/SelectedPhotos.tsx b/src/view/com/composer/photos/SelectedPhotos.tsx
deleted file mode 100644
index d22f5d8c4..000000000
--- a/src/view/com/composer/photos/SelectedPhotos.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-import React, {useCallback} from 'react'
-import {StyleSheet, TouchableOpacity, View} from 'react-native'
-import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
-import {Image} from 'expo-image'
-import {colors} from 'lib/styles'
-
-export const SelectedPhotos = ({
-  selectedPhotos,
-  onSelectPhotos,
-}: {
-  selectedPhotos: string[]
-  onSelectPhotos: (v: string[]) => void
-}) => {
-  const imageStyle =
-    selectedPhotos.length === 1
-      ? styles.image250
-      : selectedPhotos.length === 2
-      ? styles.image175
-      : styles.image85
-
-  const handleRemovePhoto = useCallback(
-    item => {
-      onSelectPhotos(selectedPhotos.filter(filterItem => filterItem !== item))
-    },
-    [selectedPhotos, onSelectPhotos],
-  )
-
-  return selectedPhotos.length !== 0 ? (
-    <View testID="selectedPhotosView" style={styles.gallery}>
-      {selectedPhotos.length !== 0 &&
-        selectedPhotos.map((item, index) => (
-          <View
-            key={`selected-image-${index}`}
-            style={[styles.imageContainer, imageStyle]}>
-            <TouchableOpacity
-              testID="removePhotoButton"
-              onPress={() => handleRemovePhoto(item)}
-              style={styles.removePhotoButton}>
-              <FontAwesomeIcon
-                icon="xmark"
-                size={16}
-                style={{color: colors.white}}
-              />
-            </TouchableOpacity>
-
-            <Image
-              testID="selectedPhotoImage"
-              style={[styles.image, imageStyle]}
-              source={{uri: item}}
-            />
-          </View>
-        ))}
-    </View>
-  ) : null
-}
-
-const styles = StyleSheet.create({
-  gallery: {
-    flex: 1,
-    flexDirection: 'row',
-    marginTop: 16,
-  },
-  imageContainer: {
-    margin: 2,
-  },
-  image: {
-    resizeMode: 'cover',
-    borderRadius: 8,
-  },
-  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,
-    borderColor: colors.gray4,
-    borderWidth: 0.5,
-  },
-})