[new arch] remove rn-fetch-blob dependency (#8294)
* remove fetchblob dependency * give error a name * fix timeout * give qr code dialog a handle * fix error toast text * more bumps that let the thing build on ios * bumps that let it build for ios * [new arch] experiment - use animatedview/animatedref to measure rect for lightbox (#8298) * more upgrades * use animatedview/animatedref for measurements in lightbox * rm usehandleref * downgrade dynamic app icon * more bumps that let the thing build on ios * bumps that let it build for ios * bump expo --------- Co-authored-by: Samuel Newman <mozzius@protonmail.com> --------- Co-authored-by: Samuel Newman <mozzius@protonmail.com>
This commit is contained in:
@@ -1,10 +1,9 @@
|
|||||||
import {deleteAsync} from 'expo-file-system'
|
import {createDownloadResumable, deleteAsync} from 'expo-file-system'
|
||||||
import {manipulateAsync, SaveFormat} from 'expo-image-manipulator'
|
import {manipulateAsync, SaveFormat} from 'expo-image-manipulator'
|
||||||
import RNFetchBlob from 'rn-fetch-blob'
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
downloadAndResize,
|
downloadAndResize,
|
||||||
DownloadAndResizeOpts,
|
type DownloadAndResizeOpts,
|
||||||
getResizedDimensions,
|
getResizedDimensions,
|
||||||
} from '../../src/lib/media/manip'
|
} from '../../src/lib/media/manip'
|
||||||
|
|
||||||
@@ -32,11 +31,12 @@ describe('downloadAndResize', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should return resized image for valid URI and options', async () => {
|
it('should return resized image for valid URI and options', async () => {
|
||||||
const mockedFetch = RNFetchBlob.fetch as jest.Mock
|
const mockedFetch = createDownloadResumable as jest.Mock
|
||||||
mockedFetch.mockResolvedValueOnce({
|
mockedFetch.mockReturnValue({
|
||||||
path: jest.fn().mockReturnValue('file://downloaded-image.jpg'),
|
cancelAsync: jest.fn(),
|
||||||
info: jest.fn().mockReturnValue({status: 200}),
|
downloadAsync: jest
|
||||||
flush: jest.fn(),
|
.fn()
|
||||||
|
.mockResolvedValue({uri: 'file://resized-image.jpg'}),
|
||||||
})
|
})
|
||||||
|
|
||||||
const opts: DownloadAndResizeOpts = {
|
const opts: DownloadAndResizeOpts = {
|
||||||
@@ -50,13 +50,12 @@ describe('downloadAndResize', () => {
|
|||||||
|
|
||||||
const result = await downloadAndResize(opts)
|
const result = await downloadAndResize(opts)
|
||||||
expect(result).toEqual(mockResizedImage)
|
expect(result).toEqual(mockResizedImage)
|
||||||
expect(RNFetchBlob.config).toHaveBeenCalledWith({
|
expect(createDownloadResumable).toHaveBeenCalledWith(
|
||||||
fileCache: true,
|
opts.uri,
|
||||||
appendExt: 'jpeg',
|
expect.anything(),
|
||||||
})
|
{
|
||||||
expect(RNFetchBlob.fetch).toHaveBeenCalledWith(
|
cache: true,
|
||||||
'GET',
|
},
|
||||||
'https://example.com/image.jpg',
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// First time it gets called is to get dimensions
|
// First time it gets called is to get dimensions
|
||||||
@@ -86,28 +85,6 @@ describe('downloadAndResize', () => {
|
|||||||
expect(result).toBeUndefined()
|
expect(result).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return undefined for non-200 response', async () => {
|
|
||||||
const mockedFetch = RNFetchBlob.fetch as jest.Mock
|
|
||||||
mockedFetch.mockResolvedValueOnce({
|
|
||||||
path: jest.fn().mockReturnValue('file://downloaded-image'),
|
|
||||||
info: jest.fn().mockReturnValue({status: 400}),
|
|
||||||
flush: jest.fn(),
|
|
||||||
})
|
|
||||||
|
|
||||||
const opts: DownloadAndResizeOpts = {
|
|
||||||
uri: 'https://example.com/image',
|
|
||||||
width: 100,
|
|
||||||
height: 100,
|
|
||||||
maxSize: 500000,
|
|
||||||
mode: 'cover',
|
|
||||||
timeout: 10000,
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await downloadAndResize(opts)
|
|
||||||
expect(errorSpy).not.toHaveBeenCalled()
|
|
||||||
expect(result).toBeUndefined()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should not downsize whenever dimensions are below the max dimensions', () => {
|
it('should not downsize whenever dimensions are below the max dimensions', () => {
|
||||||
const initialDimensionsOne = {
|
const initialDimensionsOne = {
|
||||||
width: 1200,
|
width: 1200,
|
||||||
|
|||||||
+2
-7
@@ -33,15 +33,10 @@ jest.mock('react-native-safe-area-context', () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
jest.mock('rn-fetch-blob', () => ({
|
|
||||||
config: jest.fn().mockReturnThis(),
|
|
||||||
cancel: jest.fn(),
|
|
||||||
fetch: jest.fn(),
|
|
||||||
}))
|
|
||||||
|
|
||||||
jest.mock('expo-file-system', () => ({
|
jest.mock('expo-file-system', () => ({
|
||||||
getInfoAsync: jest.fn().mockResolvedValue({exists: true, size: 100}),
|
getInfoAsync: jest.fn().mockResolvedValue({exists: true, size: 100}),
|
||||||
deleteAsync: jest.fn(),
|
deleteAsync: jest.fn(),
|
||||||
|
createDownloadResumable: jest.fn(),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
jest.mock('expo-image-manipulator', () => ({
|
jest.mock('expo-image-manipulator', () => ({
|
||||||
@@ -101,7 +96,7 @@ jest.mock('expo-modules-core', () => ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
requireNativeViewManager: jest.fn().mockImplementation(moduleName => {
|
requireNativeViewManager: jest.fn().mockImplementation(_ => {
|
||||||
return () => null
|
return () => null
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -210,7 +210,6 @@
|
|||||||
"react-remove-scroll-bar": "^2.3.8",
|
"react-remove-scroll-bar": "^2.3.8",
|
||||||
"react-responsive": "^9.0.2",
|
"react-responsive": "^9.0.2",
|
||||||
"react-textarea-autosize": "^8.5.3",
|
"react-textarea-autosize": "^8.5.3",
|
||||||
"rn-fetch-blob": "^0.12.0",
|
|
||||||
"statsig-react-native-expo": "^4.6.1",
|
"statsig-react-native-expo": "^4.6.1",
|
||||||
"tippy.js": "^6.3.7",
|
"tippy.js": "^6.3.7",
|
||||||
"tlds": "^1.234.0",
|
"tlds": "^1.234.0",
|
||||||
|
|||||||
+37
-50
@@ -1,8 +1,9 @@
|
|||||||
import {Image as RNImage, Share as RNShare} from 'react-native'
|
import {Image as RNImage} from 'react-native'
|
||||||
import uuid from 'react-native-uuid'
|
import uuid from 'react-native-uuid'
|
||||||
import {
|
import {
|
||||||
cacheDirectory,
|
cacheDirectory,
|
||||||
copyAsync,
|
copyAsync,
|
||||||
|
createDownloadResumable,
|
||||||
deleteAsync,
|
deleteAsync,
|
||||||
EncodingType,
|
EncodingType,
|
||||||
getInfoAsync,
|
getInfoAsync,
|
||||||
@@ -14,7 +15,6 @@ import {manipulateAsync, SaveFormat} from 'expo-image-manipulator'
|
|||||||
import * as MediaLibrary from 'expo-media-library'
|
import * as MediaLibrary from 'expo-media-library'
|
||||||
import * as Sharing from 'expo-sharing'
|
import * as Sharing from 'expo-sharing'
|
||||||
import {Buffer} from 'buffer'
|
import {Buffer} from 'buffer'
|
||||||
import RNFetchBlob from 'rn-fetch-blob'
|
|
||||||
|
|
||||||
import {POST_IMG_MAX} from '#/lib/constants'
|
import {POST_IMG_MAX} from '#/lib/constants'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
@@ -68,28 +68,13 @@ export async function downloadAndResize(opts: DownloadAndResizeOpts) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let downloadRes
|
const path = createPath(appendExt)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const downloadResPromise = RNFetchBlob.config({
|
await downloadImage(opts.uri, path, opts.timeout)
|
||||||
fileCache: true,
|
return await doResize(path, opts)
|
||||||
appendExt,
|
|
||||||
}).fetch('GET', opts.uri)
|
|
||||||
const to1 = setTimeout(() => downloadResPromise.cancel(), opts.timeout)
|
|
||||||
downloadRes = await downloadResPromise
|
|
||||||
clearTimeout(to1)
|
|
||||||
|
|
||||||
const status = downloadRes.info().status
|
|
||||||
if (status !== 200) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const localUri = normalizePath(downloadRes.path(), true)
|
|
||||||
return await doResize(localUri, opts)
|
|
||||||
} finally {
|
} finally {
|
||||||
// TODO Whenever we remove `rn-fetch-blob`, we will need to replace this `flush()` with a `deleteAsync()` -hailey
|
safeDeleteAsync(path)
|
||||||
if (downloadRes) {
|
|
||||||
downloadRes.flush()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,32 +83,16 @@ export async function shareImageModal({uri}: {uri: string}) {
|
|||||||
// TODO might need to give an error to the user in this case -prf
|
// TODO might need to give an error to the user in this case -prf
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const downloadResponse = await RNFetchBlob.config({
|
|
||||||
fileCache: true,
|
|
||||||
}).fetch('GET', uri)
|
|
||||||
|
|
||||||
// NOTE
|
|
||||||
// assuming PNG
|
|
||||||
// we're currently relying on the fact our CDN only serves pngs
|
// we're currently relying on the fact our CDN only serves pngs
|
||||||
// -prf
|
// -prf
|
||||||
|
const imageUri = await downloadImage(uri, createPath('png'), 5e3)
|
||||||
let imagePath = downloadResponse.path()
|
const imagePath = await moveToPermanentPath(imageUri, '.png')
|
||||||
imagePath = normalizePath(await moveToPermanentPath(imagePath, '.png'), true)
|
safeDeleteAsync(imageUri)
|
||||||
|
await Sharing.shareAsync(imagePath, {
|
||||||
// NOTE
|
mimeType: 'image/png',
|
||||||
// for some reason expo-sharing refuses to work on iOS
|
UTI: 'image/png',
|
||||||
// ...and visa versa
|
})
|
||||||
// -prf
|
|
||||||
if (isIOS) {
|
|
||||||
await RNShare.share({url: imagePath})
|
|
||||||
} else {
|
|
||||||
await Sharing.shareAsync(imagePath, {
|
|
||||||
mimeType: 'image/png',
|
|
||||||
UTI: 'image/png',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
safeDeleteAsync(imagePath)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ALBUM_NAME = 'Bluesky'
|
const ALBUM_NAME = 'Bluesky'
|
||||||
@@ -134,11 +103,8 @@ export async function saveImageToMediaLibrary({uri}: {uri: string}) {
|
|||||||
// assuming PNG
|
// assuming PNG
|
||||||
// we're currently relying on the fact our CDN only serves pngs
|
// we're currently relying on the fact our CDN only serves pngs
|
||||||
// -prf
|
// -prf
|
||||||
const downloadResponse = await RNFetchBlob.config({
|
const imageUri = await downloadImage(uri, createPath('png'), 5e3)
|
||||||
fileCache: true,
|
const imagePath = await moveToPermanentPath(imageUri, '.png')
|
||||||
}).fetch('GET', uri)
|
|
||||||
let imagePath = downloadResponse.path()
|
|
||||||
imagePath = normalizePath(await moveToPermanentPath(imagePath, '.png'), true)
|
|
||||||
|
|
||||||
// save
|
// save
|
||||||
try {
|
try {
|
||||||
@@ -403,3 +369,24 @@ export function getResizedDimensions(originalDims: {
|
|||||||
height: Math.round(originalDims.height * ratio),
|
height: Math.round(originalDims.height * ratio),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createPath(ext: string) {
|
||||||
|
// cacheDirectory will never be null on native, so the null check here is not necessary except for typescript.
|
||||||
|
// we use a web-only function for downloadAndResize on web
|
||||||
|
return `${cacheDirectory ?? ''}/${uuid.v4()}.${ext}`
|
||||||
|
}
|
||||||
|
|
||||||
|
async function downloadImage(uri: string, path: string, timeout: number) {
|
||||||
|
const dlResumable = createDownloadResumable(uri, path, {cache: true})
|
||||||
|
|
||||||
|
const to1 = setTimeout(() => dlResumable.cancelAsync(), timeout)
|
||||||
|
|
||||||
|
const dlRes = await dlResumable.downloadAsync()
|
||||||
|
clearTimeout(to1)
|
||||||
|
|
||||||
|
if (!dlRes?.uri) {
|
||||||
|
throw new Error('Failed to download image - dlRes is undefined')
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizePath(dlRes.uri)
|
||||||
|
}
|
||||||
|
|||||||
@@ -6184,7 +6184,7 @@
|
|||||||
"@babel/traverse" "^7.25.3"
|
"@babel/traverse" "^7.25.3"
|
||||||
"@react-native/codegen" "0.79.2"
|
"@react-native/codegen" "0.79.2"
|
||||||
|
|
||||||
"@react-native/babel-preset@0.79.0-rc.4", "@react-native/babel-preset@0.79.2":
|
"@react-native/babel-preset@0.79.2":
|
||||||
version "0.79.2"
|
version "0.79.2"
|
||||||
resolved "https://registry.yarnpkg.com/@react-native/babel-preset/-/babel-preset-0.79.2.tgz#5a683a6efeea357a326f70c84a881be2bafbeae3"
|
resolved "https://registry.yarnpkg.com/@react-native/babel-preset/-/babel-preset-0.79.2.tgz#5a683a6efeea357a326f70c84a881be2bafbeae3"
|
||||||
integrity sha512-/HNu869oUq4FUXizpiNWrIhucsYZqu0/0spudJEzk9SEKar0EjVDP7zkg/sKK+KccNypDQGW7nFXT8onzvQ3og==
|
integrity sha512-/HNu869oUq4FUXizpiNWrIhucsYZqu0/0spudJEzk9SEKar0EjVDP7zkg/sKK+KccNypDQGW7nFXT8onzvQ3og==
|
||||||
@@ -8779,11 +8779,6 @@ balanced-match@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||||
|
|
||||||
base-64@0.1.0:
|
|
||||||
version "0.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb"
|
|
||||||
integrity sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==
|
|
||||||
|
|
||||||
base64-arraybuffer@^1.0.2:
|
base64-arraybuffer@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc"
|
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc"
|
||||||
@@ -12109,18 +12104,6 @@ glob-to-regexp@^0.4.1:
|
|||||||
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
|
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
|
||||||
integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
|
integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
|
||||||
|
|
||||||
glob@7.0.6:
|
|
||||||
version "7.0.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a"
|
|
||||||
integrity sha512-f8c0rE8JiCxpa52kWPAOa3ZaYEnzofDzCQLCn3Vdk0Z5OVLq3BsRFJI4S4ykpeVW6QMGBUkMeUpoEgWnMTnw5Q==
|
|
||||||
dependencies:
|
|
||||||
fs.realpath "^1.0.0"
|
|
||||||
inflight "^1.0.4"
|
|
||||||
inherits "2"
|
|
||||||
minimatch "^3.0.2"
|
|
||||||
once "^1.3.0"
|
|
||||||
path-is-absolute "^1.0.0"
|
|
||||||
|
|
||||||
glob@^10.3.10:
|
glob@^10.3.10:
|
||||||
version "10.3.12"
|
version "10.3.12"
|
||||||
resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.12.tgz#3a65c363c2e9998d220338e88a5f6ac97302960b"
|
resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.12.tgz#3a65c363c2e9998d220338e88a5f6ac97302960b"
|
||||||
@@ -14876,7 +14859,7 @@ minimalistic-crypto-utils@^1.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
|
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
|
||||||
integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==
|
integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==
|
||||||
|
|
||||||
minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
|
minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
|
||||||
version "3.1.2"
|
version "3.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
||||||
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
|
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
|
||||||
@@ -17571,14 +17554,6 @@ rimraf@^3.0.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
glob "^7.1.3"
|
glob "^7.1.3"
|
||||||
|
|
||||||
rn-fetch-blob@^0.12.0:
|
|
||||||
version "0.12.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/rn-fetch-blob/-/rn-fetch-blob-0.12.0.tgz#ec610d2f9b3f1065556b58ab9c106eeb256f3cba"
|
|
||||||
integrity sha512-+QnR7AsJ14zqpVVUbzbtAjq0iI8c9tCg49tIoKO2ezjzRunN7YL6zFSFSWZm6d+mE/l9r+OeDM3jmb2tBb2WbA==
|
|
||||||
dependencies:
|
|
||||||
base-64 "0.1.0"
|
|
||||||
glob "7.0.6"
|
|
||||||
|
|
||||||
roarr@^7.0.4:
|
roarr@^7.0.4:
|
||||||
version "7.15.1"
|
version "7.15.1"
|
||||||
resolved "https://registry.yarnpkg.com/roarr/-/roarr-7.15.1.tgz#e4d93105c37b5ea7dd1200d96a3500f757ddc39f"
|
resolved "https://registry.yarnpkg.com/roarr/-/roarr-7.15.1.tgz#e4d93105c37b5ea7dd1200d96a3500f757ddc39f"
|
||||||
|
|||||||
Reference in New Issue
Block a user