mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import RNFetchBlob from 'rn-fetch-blob'
2import ImageResizer from '@bam.tech/react-native-image-resizer'
3import {Share} from 'react-native'
4import RNFS from 'react-native-fs'
5
6import * as Toast from '../view/com/util/Toast'
7
8export interface DownloadAndResizeOpts {
9 uri: string
10 width: number
11 height: number
12 mode: 'contain' | 'cover' | 'stretch'
13 maxSize: number
14 timeout: number
15}
16
17export interface Image {
18 path: string
19 mime: string
20 size: number
21 width: number
22 height: number
23}
24
25export async function downloadAndResize(opts: DownloadAndResizeOpts) {
26 let appendExt
27 try {
28 const urip = new URL(opts.uri)
29 const ext = urip.pathname.split('.').pop()
30 if (ext === 'jpg' || ext === 'jpeg') {
31 appendExt = 'jpeg'
32 } else if (ext === 'png') {
33 appendExt = 'png'
34 } else {
35 return
36 }
37 } catch (e: any) {
38 console.error('Invalid URI', opts.uri, e)
39 return
40 }
41
42 let downloadRes
43 try {
44 const downloadResPromise = RNFetchBlob.config({
45 fileCache: true,
46 appendExt,
47 }).fetch('GET', opts.uri)
48 const to1 = setTimeout(() => downloadResPromise.cancel(), opts.timeout)
49 downloadRes = await downloadResPromise
50 clearTimeout(to1)
51
52 let localUri = downloadRes.path()
53 if (!localUri.startsWith('file://')) {
54 localUri = `file://${localUri}`
55 }
56
57 return await resize(localUri, opts)
58 } finally {
59 if (downloadRes) {
60 downloadRes.flush()
61 }
62 }
63}
64
65export interface ResizeOpts {
66 width: number
67 height: number
68 mode: 'contain' | 'cover' | 'stretch'
69 maxSize: number
70}
71
72export async function resize(
73 localUri: string,
74 opts: ResizeOpts,
75): Promise<Image> {
76 for (let i = 0; i < 9; i++) {
77 const quality = 100 - i * 10
78 const resizeRes = await ImageResizer.createResizedImage(
79 localUri,
80 opts.width,
81 opts.height,
82 'JPEG',
83 quality,
84 undefined,
85 undefined,
86 undefined,
87 {mode: opts.mode},
88 )
89 if (resizeRes.size < opts.maxSize) {
90 return {
91 path: resizeRes.path,
92 mime: 'image/jpeg',
93 size: resizeRes.size,
94 width: resizeRes.width,
95 height: resizeRes.height,
96 }
97 }
98 }
99 throw new Error(
100 `This image is too big! We couldn't compress it down to ${opts.maxSize} bytes`,
101 )
102}
103
104export async function compressIfNeeded(
105 img: Image,
106 maxSize: number,
107): Promise<Image> {
108 const origUri = `file://${img.path}`
109 if (img.size < maxSize) {
110 return img
111 }
112 return await resize(origUri, {
113 width: img.width,
114 height: img.height,
115 mode: 'stretch',
116 maxSize,
117 })
118}
119
120export interface Dim {
121 width: number
122 height: number
123}
124export function scaleDownDimensions(dim: Dim, max: Dim): Dim {
125 if (dim.width < max.width && dim.height < max.height) {
126 return dim
127 }
128 let wScale = dim.width > max.width ? max.width / dim.width : 1
129 let hScale = dim.height > max.height ? max.height / dim.height : 1
130 if (wScale < hScale) {
131 return {width: dim.width * wScale, height: dim.height * wScale}
132 }
133 return {width: dim.width * hScale, height: dim.height * hScale}
134}
135
136export const saveImageModal = async ({uri}: {uri: string}) => {
137 const downloadResponse = await RNFetchBlob.config({
138 fileCache: true,
139 }).fetch('GET', uri)
140
141 const imagePath = downloadResponse.path()
142 const base64Data = await downloadResponse.readFile('base64')
143 const result = await Share.share({
144 url: 'data:image/png;base64,' + base64Data,
145 })
146 if (result.action === Share.sharedAction) {
147 Toast.show('Image saved to gallery')
148 } else if (result.action === Share.dismissedAction) {
149 // dismissed
150 }
151 RNFS.unlink(imagePath)
152}