diff options
Diffstat (limited to 'src/lib/media/manip.ts')
-rw-r--r-- | src/lib/media/manip.ts | 198 |
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 } |