about summary refs log tree commit diff
path: root/src/view/com/composer/photos/PhotoCarouselPicker.web.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/composer/photos/PhotoCarouselPicker.web.tsx')
-rw-r--r--src/view/com/composer/photos/PhotoCarouselPicker.web.tsx158
1 files changed, 158 insertions, 0 deletions
diff --git a/src/view/com/composer/photos/PhotoCarouselPicker.web.tsx b/src/view/com/composer/photos/PhotoCarouselPicker.web.tsx
new file mode 100644
index 000000000..bb2800026
--- /dev/null
+++ b/src/view/com/composer/photos/PhotoCarouselPicker.web.tsx
@@ -0,0 +1,158 @@
+import React, {useCallback} from 'react'
+import {StyleSheet, TouchableOpacity, ScrollView} from 'react-native'
+import {
+  FontAwesomeIcon,
+  FontAwesomeIconStyle,
+} from '@fortawesome/react-native-fontawesome'
+import {
+  openPicker,
+  openCamera,
+  openCropper,
+} from '../../util/images/image-crop-picker/ImageCropPicker'
+import {compressIfNeeded, scaleDownDimensions} from '../../../../lib/images'
+import {usePalette} from '../../../lib/hooks/usePalette'
+import {useStores, RootStoreModel} from '../../../../state'
+
+const MAX_WIDTH = 1000
+const MAX_HEIGHT = 1000
+const MAX_SIZE = 300000
+
+const IMAGE_PARAMS = {
+  width: 1000,
+  height: 1000,
+  freeStyleCropEnabled: true,
+}
+
+export async function cropPhoto(
+  store: RootStoreModel,
+  path: string,
+  imgWidth = MAX_WIDTH,
+  imgHeight = MAX_HEIGHT,
+) {
+  // choose target dimensions based on the original
+  // this causes the photo cropper to start with the full image "selected"
+  const {width, height} = scaleDownDimensions(
+    {width: imgWidth, height: imgHeight},
+    {width: MAX_WIDTH, height: MAX_HEIGHT},
+  )
+  const cropperRes = await openCropper(store, {
+    mediaType: 'photo',
+    path,
+    freeStyleCropEnabled: true,
+    width,
+    height,
+  })
+  const img = await compressIfNeeded(cropperRes, MAX_SIZE)
+  return img.path
+}
+
+export const PhotoCarouselPicker = ({
+  selectedPhotos,
+  onSelectPhotos,
+}: {
+  selectedPhotos: string[]
+  onSelectPhotos: (v: string[]) => void
+}) => {
+  const pal = usePalette('default')
+  const store = useStores()
+
+  const handleOpenCamera = useCallback(async () => {
+    try {
+      const cameraRes = await openCamera(store, {
+        mediaType: 'photo',
+        ...IMAGE_PARAMS,
+      })
+      const img = await compressIfNeeded(cameraRes, MAX_SIZE)
+      onSelectPhotos([...selectedPhotos, img.path])
+    } catch (err: any) {
+      // ignore
+      store.log.warn('Error using camera', err)
+    }
+  }, [store, selectedPhotos, onSelectPhotos])
+
+  const handleOpenGallery = useCallback(() => {
+    openPicker(store, {
+      multiple: true,
+      maxFiles: 4 - selectedPhotos.length,
+      mediaType: 'photo',
+    }).then(async items => {
+      const result = []
+
+      for (const image of items) {
+        // choose target dimensions based on the original
+        // this causes the photo cropper to start with the full image "selected"
+        const {width, height} = scaleDownDimensions(
+          {width: image.width, height: image.height},
+          {width: MAX_WIDTH, height: MAX_HEIGHT},
+        )
+        const cropperRes = await openCropper(store, {
+          mediaType: 'photo',
+          path: image.path,
+          freeStyleCropEnabled: true,
+          width,
+          height,
+        })
+        const finalImg = await compressIfNeeded(cropperRes, MAX_SIZE)
+        result.push(finalImg.path)
+      }
+      onSelectPhotos([...selectedPhotos, ...result])
+    })
+  }, [store, selectedPhotos, onSelectPhotos])
+
+  return (
+    <ScrollView
+      testID="photoCarouselPickerView"
+      horizontal
+      style={[pal.view, styles.photosContainer]}
+      keyboardShouldPersistTaps="always"
+      showsHorizontalScrollIndicator={false}>
+      <TouchableOpacity
+        testID="openCameraButton"
+        style={[styles.galleryButton, pal.border, styles.photo]}
+        onPress={handleOpenCamera}>
+        <FontAwesomeIcon
+          icon="camera"
+          size={24}
+          style={pal.link as FontAwesomeIconStyle}
+        />
+      </TouchableOpacity>
+      <TouchableOpacity
+        testID="openGalleryButton"
+        style={[styles.galleryButton, pal.border, styles.photo]}
+        onPress={handleOpenGallery}>
+        <FontAwesomeIcon
+          icon="image"
+          style={pal.link as FontAwesomeIconStyle}
+          size={24}
+        />
+      </TouchableOpacity>
+    </ScrollView>
+  )
+}
+
+const styles = StyleSheet.create({
+  photosContainer: {
+    width: '100%',
+    maxHeight: 96,
+    padding: 8,
+    overflow: 'hidden',
+  },
+  galleryButton: {
+    borderWidth: 1,
+    alignItems: 'center',
+    justifyContent: 'center',
+  },
+  photoButton: {
+    width: 75,
+    height: 75,
+    marginRight: 8,
+    borderWidth: 1,
+    borderRadius: 16,
+  },
+  photo: {
+    width: 75,
+    height: 75,
+    marginRight: 8,
+    borderRadius: 16,
+  },
+})