about summary refs log tree commit diff
path: root/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/media/manip.ts25
-rw-r--r--src/lib/media/manip.web.ts14
-rw-r--r--src/lib/media/picker.e2e.tsx17
-rw-r--r--src/lib/media/picker.shared.ts11
-rw-r--r--src/lib/media/picker.tsx48
-rw-r--r--src/lib/media/picker.web.tsx26
-rw-r--r--src/lib/media/types.ts7
7 files changed, 77 insertions, 71 deletions
diff --git a/src/lib/media/manip.ts b/src/lib/media/manip.ts
index f6ef8347d..ff5b71ace 100644
--- a/src/lib/media/manip.ts
+++ b/src/lib/media/manip.ts
@@ -1,5 +1,4 @@
 import {Image as RNImage, Share as RNShare} from 'react-native'
-import {Image} from 'react-native-image-crop-picker'
 import uuid from 'react-native-uuid'
 import {
   cacheDirectory,
@@ -20,17 +19,17 @@ import RNFetchBlob from 'rn-fetch-blob'
 import {POST_IMG_MAX} from '#/lib/constants'
 import {logger} from '#/logger'
 import {isAndroid, isIOS} from '#/platform/detection'
-import {Dimensions} from './types'
+import {type PickerImage} from './picker.shared'
+import {type Dimensions} from './types'
 
 export async function compressIfNeeded(
-  img: Image,
+  img: PickerImage,
   maxSize: number = 1000000,
-): Promise<Image> {
-  const origUri = `file://${img.path}`
+): Promise<PickerImage> {
   if (img.size < maxSize) {
     return img
   }
-  const resizedImage = await doResize(origUri, {
+  const resizedImage = await doResize(normalizePath(img.path), {
     width: img.width,
     height: img.height,
     mode: 'stretch',
@@ -166,7 +165,10 @@ interface DoResizeOpts {
   maxSize: number
 }
 
-async function doResize(localUri: string, opts: DoResizeOpts): Promise<Image> {
+async function doResize(
+  localUri: string,
+  opts: DoResizeOpts,
+): Promise<PickerImage> {
   // We need to get the dimensions of the image before we resize it. Previously, the library we used allowed us to enter
   // a "max size", and it would do the "best possible size" calculation for us.
   // Now instead, we have to supply the final dimensions to the manipulation function instead.
@@ -181,6 +183,7 @@ async function doResize(localUri: string, opts: DoResizeOpts): Promise<Image> {
   let minQualityPercentage = 0
   let maxQualityPercentage = 101 // exclusive
   let newDataUri
+  const intermediateUris = []
 
   while (maxQualityPercentage - minQualityPercentage > 1) {
     const qualityPercentage = Math.round(
@@ -195,6 +198,8 @@ async function doResize(localUri: string, opts: DoResizeOpts): Promise<Image> {
       },
     )
 
+    intermediateUris.push(resizeRes.uri)
+
     const fileInfo = await getInfoAsync(resizeRes.uri)
     if (!fileInfo.exists) {
       throw new Error(
@@ -214,8 +219,12 @@ async function doResize(localUri: string, opts: DoResizeOpts): Promise<Image> {
     } else {
       maxQualityPercentage = qualityPercentage
     }
+  }
 
-    safeDeleteAsync(resizeRes.uri)
+  for (const intermediateUri of intermediateUris) {
+    if (newDataUri?.path !== normalizePath(intermediateUri)) {
+      safeDeleteAsync(intermediateUri)
+    }
   }
 
   if (newDataUri) {
diff --git a/src/lib/media/manip.web.ts b/src/lib/media/manip.web.ts
index ffef7314d..ffcf0c533 100644
--- a/src/lib/media/manip.web.ts
+++ b/src/lib/media/manip.web.ts
@@ -1,12 +1,11 @@
-import {Image as RNImage} from 'react-native-image-crop-picker'
-
-import {Dimensions} from './types'
+import {type PickerImage} from './picker.shared'
+import {type Dimensions} from './types'
 import {blobToDataUri, getDataUriSize} from './util'
 
 export async function compressIfNeeded(
-  img: RNImage,
+  img: PickerImage,
   maxSize: number,
-): Promise<RNImage> {
+): Promise<PickerImage> {
   if (img.size < maxSize) {
     return img
   }
@@ -69,7 +68,10 @@ interface DoResizeOpts {
   maxSize: number
 }
 
-async function doResize(dataUri: string, opts: DoResizeOpts): Promise<RNImage> {
+async function doResize(
+  dataUri: string,
+  opts: DoResizeOpts,
+): Promise<PickerImage> {
   let newDataUri
 
   let minQualityPercentage = 0
diff --git a/src/lib/media/picker.e2e.tsx b/src/lib/media/picker.e2e.tsx
index fc6fcde45..a2a9357ec 100644
--- a/src/lib/media/picker.e2e.tsx
+++ b/src/lib/media/picker.e2e.tsx
@@ -1,15 +1,12 @@
 import {
-  Image as RNImage,
-  openCropper as openCropperFn,
-} from 'react-native-image-crop-picker'
-import {
   documentDirectory,
   getInfoAsync,
   readDirectoryAsync,
 } from 'expo-file-system'
+import ExpoImageCropTool, {type OpenCropperOptions} from 'expo-image-crop-tool'
 
 import {compressIfNeeded} from './manip'
-import {CropperOptions} from './types'
+import {type PickerImage} from './picker.shared'
 
 async function getFile() {
   const imagesDir = documentDirectory!
@@ -37,18 +34,18 @@ async function getFile() {
   })
 }
 
-export async function openPicker(): Promise<RNImage[]> {
+export async function openPicker(): Promise<PickerImage[]> {
   return [await getFile()]
 }
 
-export async function openCamera(): Promise<RNImage> {
+export async function openCamera(): Promise<PickerImage> {
   return await getFile()
 }
 
-export async function openCropper(opts: CropperOptions) {
-  const item = await openCropperFn({
+export async function openCropper(opts: OpenCropperOptions) {
+  const item = await ExpoImageCropTool.openCropperAsync({
     ...opts,
-    forceJpg: true, // ios only
+    format: 'jpeg',
   })
 
   return {
diff --git a/src/lib/media/picker.shared.ts b/src/lib/media/picker.shared.ts
index a45bf5c0f..21e680832 100644
--- a/src/lib/media/picker.shared.ts
+++ b/src/lib/media/picker.shared.ts
@@ -1,14 +1,21 @@
 import {
-  ImagePickerOptions,
+  type ImagePickerOptions,
   launchImageLibraryAsync,
   MediaTypeOptions,
 } from 'expo-image-picker'
-// TODO: replace global i18n instance with one returned from useLingui -sfn
 import {t} from '@lingui/macro'
 
 import * as Toast from '#/view/com/util/Toast'
 import {getDataUriSize} from './util'
 
+export type PickerImage = {
+  mime: string
+  height: number
+  width: number
+  path: string
+  size: number
+}
+
 export async function openPicker(opts?: ImagePickerOptions) {
   const response = await launchImageLibraryAsync({
     exif: false,
diff --git a/src/lib/media/picker.tsx b/src/lib/media/picker.tsx
index 37e01e67f..6095730d5 100644
--- a/src/lib/media/picker.tsx
+++ b/src/lib/media/picker.tsx
@@ -1,36 +1,34 @@
-import {
-  Image as RNImage,
-  openCamera as openCameraFn,
-  openCropper as openCropperFn,
-} from 'react-native-image-crop-picker'
+import ExpoImageCropTool, {type OpenCropperOptions} from 'expo-image-crop-tool'
+import {type ImagePickerOptions, launchCameraAsync} from 'expo-image-picker'
 
-import {CameraOpts, CropperOptions} from './types'
-export {openPicker} from './picker.shared'
+export {openPicker, type PickerImage as RNImage} from './picker.shared'
 
-export async function openCamera(opts: CameraOpts): Promise<RNImage> {
-  const item = await openCameraFn({
-    width: opts.width,
-    height: opts.height,
-    freeStyleCropEnabled: opts.freeStyleCropEnabled,
-    cropperCircleOverlay: opts.cropperCircleOverlay,
-    cropping: false,
-    forceJpg: true, // ios only
-    compressImageQuality: 0.8,
-  })
+export async function openCamera(customOpts: ImagePickerOptions) {
+  const opts: ImagePickerOptions = {
+    mediaTypes: 'images',
+    ...customOpts,
+  }
+  const res = await launchCameraAsync(opts)
+
+  if (!res || !res.assets) {
+    throw new Error('Camera was closed before taking a photo')
+  }
+
+  const asset = res?.assets[0]
 
   return {
-    path: item.path,
-    mime: item.mime,
-    size: item.size,
-    width: item.width,
-    height: item.height,
+    path: asset.uri,
+    mime: asset.mimeType ?? 'image/jpeg',
+    size: asset.fileSize ?? 0,
+    width: asset.width,
+    height: asset.height,
   }
 }
 
-export async function openCropper(opts: CropperOptions) {
-  const item = await openCropperFn({
+export async function openCropper(opts: OpenCropperOptions) {
+  const item = await ExpoImageCropTool.openCropperAsync({
     ...opts,
-    forceJpg: true, // ios only
+    format: 'jpeg',
   })
 
   return {
diff --git a/src/lib/media/picker.web.tsx b/src/lib/media/picker.web.tsx
index a53ffc961..b7d0d6f06 100644
--- a/src/lib/media/picker.web.tsx
+++ b/src/lib/media/picker.web.tsx
@@ -1,29 +1,29 @@
 /// <reference lib="dom" />
 
-import {Image as RNImage} from 'react-native-image-crop-picker'
+import {type OpenCropperOptions} from 'expo-image-crop-tool'
 
-import {CameraOpts, CropperOptions} from './types'
-export {openPicker} from './picker.shared'
 import {unstable__openModal} from '#/state/modals'
+import {type PickerImage} from './picker.shared'
+import {type CameraOpts} from './types'
 
-export async function openCamera(_opts: CameraOpts): Promise<RNImage> {
+export {openPicker, type PickerImage as RNImage} from './picker.shared'
+
+export async function openCamera(_opts: CameraOpts): Promise<PickerImage> {
   // const mediaType = opts.mediaType || 'photo' TODO
   throw new Error('TODO')
 }
 
-export async function openCropper(opts: CropperOptions): Promise<RNImage> {
+export async function openCropper(
+  opts: OpenCropperOptions,
+): Promise<PickerImage> {
   // TODO handle more opts
   return new Promise((resolve, reject) => {
     unstable__openModal({
       name: 'crop-image',
-      uri: opts.path,
-      dimensions:
-        opts.width && opts.height
-          ? {width: opts.width, height: opts.height}
-          : undefined,
-      aspect: opts.webAspectRatio,
-      circular: opts.webCircularCrop,
-      onSelect: (img?: RNImage) => {
+      uri: opts.imageUri,
+      aspect: opts.aspectRatio,
+      circular: opts.shape === 'circle',
+      onSelect: (img?: PickerImage) => {
         if (img) {
           resolve(img)
         } else {
diff --git a/src/lib/media/types.ts b/src/lib/media/types.ts
index ec94256ea..c083093ac 100644
--- a/src/lib/media/types.ts
+++ b/src/lib/media/types.ts
@@ -1,5 +1,3 @@
-import {openCropper} from 'react-native-image-crop-picker'
-
 export interface Dimensions {
   width: number
   height: number
@@ -17,8 +15,3 @@ export interface CameraOpts {
   freeStyleCropEnabled?: boolean
   cropperCircleOverlay?: boolean
 }
-
-export type CropperOptions = Parameters<typeof openCropper>[0] & {
-  webAspectRatio?: number
-  webCircularCrop?: boolean
-}