diff options
Diffstat (limited to 'src/lib/media/manip.ts')
-rw-r--r-- | src/lib/media/manip.ts | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/src/lib/media/manip.ts b/src/lib/media/manip.ts new file mode 100644 index 000000000..e44ee3907 --- /dev/null +++ b/src/lib/media/manip.ts @@ -0,0 +1,178 @@ +import RNFetchBlob from 'rn-fetch-blob' +import ImageResizer from '@bam.tech/react-native-image-resizer' +import {Image as RNImage, Share} from 'react-native' +import RNFS from 'react-native-fs' +import uuid from 'react-native-uuid' +import * as Toast from 'view/com/util/Toast' + +export interface DownloadAndResizeOpts { + uri: string + width: number + height: number + mode: 'contain' | 'cover' | 'stretch' + maxSize: number + 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 { + const urip = new URL(opts.uri) + const ext = urip.pathname.split('.').pop() + if (ext === 'png') { + appendExt = 'png' + } + } catch (e: any) { + console.error('Invalid URI', opts.uri, e) + return + } + + let downloadRes + try { + const downloadResPromise = RNFetchBlob.config({ + fileCache: true, + appendExt, + }).fetch('GET', opts.uri) + const to1 = setTimeout(() => downloadResPromise.cancel(), opts.timeout) + downloadRes = await downloadResPromise + clearTimeout(to1) + + let localUri = downloadRes.path() + if (!localUri.startsWith('file://')) { + localUri = `file://${localUri}` + } + + return await resize(localUri, opts) + } finally { + if (downloadRes) { + downloadRes.flush() + } + } +} + +export interface ResizeOpts { + width: number + height: number + mode: 'contain' | 'cover' | 'stretch' + maxSize: number +} + +export async function resize( + localUri: string, + opts: ResizeOpts, +): Promise<Image> { + for (let i = 0; i < 9; i++) { + const quality = 100 - i * 10 + const resizeRes = await ImageResizer.createResizedImage( + localUri, + opts.width, + opts.height, + 'JPEG', + quality, + undefined, + undefined, + undefined, + {mode: opts.mode}, + ) + if (resizeRes.size < opts.maxSize) { + return { + path: resizeRes.path, + mime: 'image/jpeg', + size: resizeRes.size, + width: resizeRes.width, + height: resizeRes.height, + } + } + } + throw new Error( + `This image is too big! We couldn't compress it down to ${opts.maxSize} bytes`, + ) +} + +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 interface Dim { + width: number + height: number +} +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) { + /* + 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 +} + +export function getImageDim(path: string): Promise<Dim> { + return new Promise((resolve, reject) => { + RNImage.getSize( + path, + (width, height) => { + resolve({width, height}) + }, + reject, + ) + }) +} |