about summary refs log tree commit diff
path: root/src/lib/media/manip.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/media/manip.ts')
-rw-r--r--src/lib/media/manip.ts198
1 files changed, 115 insertions, 83 deletions
diff --git a/src/lib/media/manip.ts b/src/lib/media/manip.ts
index 6ff8b691c..f77b861e2 100644
--- a/src/lib/media/manip.ts
+++ b/src/lib/media/manip.ts
@@ -1,13 +1,77 @@
 import RNFetchBlob from 'rn-fetch-blob'
 import ImageResizer from '@bam.tech/react-native-image-resizer'
 import {Image as RNImage, Share} from 'react-native'
+import {Image} from 'react-native-image-crop-picker'
 import RNFS from 'react-native-fs'
 import uuid from 'react-native-uuid'
 import * as Toast from 'view/com/util/Toast'
+import {Dimensions} from './types'
+import {POST_IMG_MAX} from 'lib/constants'
+import {isAndroid} from 'platform/detection'
 
-export interface Dim {
-  width: number
-  height: number
+export async function compressAndResizeImageForPost(
+  image: Image,
+): Promise<Image> {
+  const uri = `file://${image.path}`
+  let resized: Omit<Image, 'mime'>
+
+  for (let i = 0; i < 9; i++) {
+    const quality = 100 - i * 10
+
+    try {
+      resized = await ImageResizer.createResizedImage(
+        uri,
+        POST_IMG_MAX.width,
+        POST_IMG_MAX.height,
+        'JPEG',
+        quality,
+        undefined,
+        undefined,
+        undefined,
+        {mode: 'cover'},
+      )
+    } catch (err) {
+      throw new Error(`Failed to resize: ${err}`)
+    }
+
+    if (resized.size < POST_IMG_MAX.size) {
+      const path = await moveToPermanentPath(resized.path)
+
+      return {
+        path,
+        mime: 'image/jpeg',
+        size: resized.size,
+        height: resized.height,
+        width: resized.width,
+      }
+    }
+  }
+
+  throw new Error(
+    `This image is too big! We couldn't compress it down to ${POST_IMG_MAX.size} bytes`,
+  )
+}
+
+export async function compressIfNeeded(
+  img: Image,
+  maxSize: number = 1000000,
+): Promise<Image> {
+  const origUri = `file://${img.path}`
+  if (img.size < maxSize) {
+    return img
+  }
+  const resizedImage = await doResize(origUri, {
+    width: img.width,
+    height: img.height,
+    mode: 'stretch',
+    maxSize,
+  })
+  const finalImageMovedPath = await moveToPermanentPath(resizedImage.path)
+  const finalImg = {
+    ...resizedImage,
+    path: finalImageMovedPath,
+  }
+  return finalImg
 }
 
 export interface DownloadAndResizeOpts {
@@ -19,14 +83,6 @@ export interface DownloadAndResizeOpts {
   timeout: number
 }
 
-export interface Image {
-  path: string
-  mime: string
-  size: number
-  width: number
-  height: number
-}
-
 export async function downloadAndResize(opts: DownloadAndResizeOpts) {
   let appendExt = 'jpeg'
   try {
@@ -55,7 +111,7 @@ export async function downloadAndResize(opts: DownloadAndResizeOpts) {
       localUri = `file://${localUri}`
     }
 
-    return await resize(localUri, opts)
+    return await doResize(localUri, opts)
   } finally {
     if (downloadRes) {
       downloadRes.flush()
@@ -63,17 +119,47 @@ export async function downloadAndResize(opts: DownloadAndResizeOpts) {
   }
 }
 
-export interface ResizeOpts {
+export async function saveImageModal({uri}: {uri: string}) {
+  const downloadResponse = await RNFetchBlob.config({
+    fileCache: true,
+  }).fetch('GET', uri)
+
+  const imagePath = downloadResponse.path()
+  const base64Data = await downloadResponse.readFile('base64')
+  const result = await Share.share({
+    url: 'data:image/png;base64,' + base64Data,
+  })
+  if (result.action === Share.sharedAction) {
+    Toast.show('Image saved to gallery')
+  } else if (result.action === Share.dismissedAction) {
+    // dismissed
+  }
+  RNFS.unlink(imagePath)
+}
+
+export function getImageDim(path: string): Promise<Dimensions> {
+  return new Promise((resolve, reject) => {
+    RNImage.getSize(
+      path,
+      (width, height) => {
+        resolve({width, height})
+      },
+      reject,
+    )
+  })
+}
+
+// internal methods
+// =
+
+interface DoResizeOpts {
   width: number
   height: number
   mode: 'contain' | 'cover' | 'stretch'
   maxSize: number
 }
 
-export async function resize(
-  localUri: string,
-  opts: ResizeOpts,
-): Promise<Image> {
+async function doResize(localUri: string, opts: DoResizeOpts): Promise<Image> {
   for (let i = 0; i < 9; i++) {
     const quality = 100 - i * 10
     const resizeRes = await ImageResizer.createResizedImage(
@@ -89,7 +175,7 @@ export async function resize(
     )
     if (resizeRes.size < opts.maxSize) {
       return {
-        path: resizeRes.path,
+        path: normalizePath(resizeRes.path),
         mime: 'image/jpeg',
         size: resizeRes.size,
         width: resizeRes.width,
@@ -102,78 +188,24 @@ export async function resize(
   )
 }
 
-export async function compressIfNeeded(
-  img: Image,
-  maxSize: number,
-): Promise<Image> {
-  const origUri = `file://${img.path}`
-  if (img.size < maxSize) {
-    return img
-  }
-  const resizedImage = await resize(origUri, {
-    width: img.width,
-    height: img.height,
-    mode: 'stretch',
-    maxSize,
-  })
-  const finalImageMovedPath = await moveToPremanantPath(resizedImage.path)
-  const finalImg = {
-    ...resizedImage,
-    path: finalImageMovedPath,
-  }
-  return finalImg
-}
-
-export function scaleDownDimensions(dim: Dim, max: Dim): Dim {
-  if (dim.width < max.width && dim.height < max.height) {
-    return dim
-  }
-  let wScale = dim.width > max.width ? max.width / dim.width : 1
-  let hScale = dim.height > max.height ? max.height / dim.height : 1
-  if (wScale < hScale) {
-    return {width: dim.width * wScale, height: dim.height * wScale}
-  }
-  return {width: dim.width * hScale, height: dim.height * hScale}
-}
-
-export async function saveImageModal({uri}: {uri: string}) {
-  const downloadResponse = await RNFetchBlob.config({
-    fileCache: true,
-  }).fetch('GET', uri)
-
-  const imagePath = downloadResponse.path()
-  const base64Data = await downloadResponse.readFile('base64')
-  const result = await Share.share({
-    url: 'data:image/png;base64,' + base64Data,
-  })
-  if (result.action === Share.sharedAction) {
-    Toast.show('Image saved to gallery')
-  } else if (result.action === Share.dismissedAction) {
-    // dismissed
-  }
-  RNFS.unlink(imagePath)
-}
-
-export async function moveToPremanantPath(path: string) {
+async function moveToPermanentPath(path: string): Promise<string> {
   /*
   Since this package stores images in a temp directory, we need to move the file to a permanent location.
   Relevant: IOS bug when trying to open a second time:
   https://github.com/ivpusic/react-native-image-crop-picker/issues/1199
   */
   const filename = uuid.v4()
+
   const destinationPath = `${RNFS.TemporaryDirectoryPath}/${filename}`
-  RNFS.moveFile(path, destinationPath)
-  return destinationPath
+  await RNFS.moveFile(path, destinationPath)
+  return normalizePath(destinationPath)
 }
 
-export function getImageDim(path: string): Promise<Dim> {
-  return new Promise((resolve, reject) => {
-    RNImage.getSize(
-      path,
-      (width, height) => {
-        resolve({width, height})
-      },
-      reject,
-    )
-  })
+function normalizePath(str: string): string {
+  if (isAndroid) {
+    if (!str.startsWith('file://')) {
+      return `file://${str}`
+    }
+  }
+  return str
 }