From 02b3afda3c9149a636404656a06fb58178df64d7 Mon Sep 17 00:00:00 2001 From: Gleidson Daniel Silva Date: Mon, 20 May 2024 15:21:31 -0400 Subject: [PATCH] feat: remove rn-fetch-blob (#5669) * feat: remove rn-fetch-blob * fix types * jest --- .../networking/SSLPinningModule.java | 4 - app/containers/Toast.tsx | 5 +- app/containers/message/Reply.tsx | 24 +-- app/containers/message/Video.tsx | 3 +- .../message/helpers/fileDownload/index.ts | 53 ------ app/lib/constants/index.ts | 1 - app/lib/constants/localPath.ts | 4 - app/lib/methods/getServerInfo.ts | 17 +- app/lib/methods/helpers/fileDownload.ts | 33 ++++ app/lib/methods/helpers/fileUpload.ts | 66 ++++++++ .../methods/helpers/fileUpload/index.ios.ts | 60 ------- app/lib/methods/helpers/fileUpload/index.ts | 25 --- .../methods/helpers/fileUpload/interfaces.ts | 7 - app/lib/methods/helpers/index.ts | 1 + app/lib/methods/sendFileMessage.ts | 12 +- app/lib/services/getServerTimeSync.ts | 12 +- app/views/AttachmentView.tsx | 12 +- ios/Podfile.lock | 6 - jest.setup.js | 12 -- package.json | 1 - patches/rn-fetch-blob+0.12.0.patch | 153 ------------------ yarn.lock | 28 +--- 22 files changed, 140 insertions(+), 399 deletions(-) delete mode 100644 app/containers/message/helpers/fileDownload/index.ts delete mode 100644 app/lib/constants/localPath.ts create mode 100644 app/lib/methods/helpers/fileDownload.ts create mode 100644 app/lib/methods/helpers/fileUpload.ts delete mode 100644 app/lib/methods/helpers/fileUpload/index.ios.ts delete mode 100644 app/lib/methods/helpers/fileUpload/index.ts delete mode 100644 app/lib/methods/helpers/fileUpload/interfaces.ts delete mode 100644 patches/rn-fetch-blob+0.12.0.patch diff --git a/android/app/src/main/java/chat/rocket/reactnative/networking/SSLPinningModule.java b/android/app/src/main/java/chat/rocket/reactnative/networking/SSLPinningModule.java index aad807859..b4224bf88 100644 --- a/android/app/src/main/java/chat/rocket/reactnative/networking/SSLPinningModule.java +++ b/android/app/src/main/java/chat/rocket/reactnative/networking/SSLPinningModule.java @@ -32,8 +32,6 @@ import android.security.KeyChainAliasCallback; import java.util.Arrays; import java.util.concurrent.TimeUnit; -import com.RNFetchBlob.RNFetchBlob; - import com.reactnativecommunity.webview.RNCWebViewManager; import com.dylanvann.fastimage.FastImageOkHttpUrlLoader; @@ -104,8 +102,6 @@ public class SSLPinningModule extends ReactContextBaseJavaModule implements KeyC WebSocketModule.setCustomClientBuilder(new CustomClient()); // Image networking react-native layer ReactOkHttpNetworkFetcher.setOkHttpClient(getOkHttpClient()); - // RNFetchBlob networking layer - RNFetchBlob.applyCustomOkHttpClient(getOkHttpClient()); // RNCWebView onReceivedClientCertRequest RNCWebViewManager.setCertificateAlias(data); // FastImage Glide network layer diff --git a/app/containers/Toast.tsx b/app/containers/Toast.tsx index 781ad5b59..cd538734c 100644 --- a/app/containers/Toast.tsx +++ b/app/containers/Toast.tsx @@ -13,8 +13,9 @@ const styles = StyleSheet.create({ }, text: { fontSize: 14, - ...sharedStyles.textRegular, - ...sharedStyles.textAlignCenter + // jest error: TypeError: Cannot read property 'textRegular' of undefined + ...sharedStyles?.textRegular, + ...sharedStyles?.textAlignCenter } }); diff --git a/app/containers/message/Reply.tsx b/app/containers/message/Reply.tsx index bfcd85cbf..f45985bcb 100644 --- a/app/containers/message/Reply.tsx +++ b/app/containers/message/Reply.tsx @@ -1,21 +1,21 @@ +import { dequal } from 'dequal'; +import moment from 'moment'; import React, { useContext, useState } from 'react'; import { StyleSheet, Text, View } from 'react-native'; -import moment from 'moment'; -import { dequal } from 'dequal'; import FastImage from 'react-native-fast-image'; -import Touchable from './Touchable'; -import Markdown from '../markdown'; -import openLink from '../../lib/methods/helpers/openLink'; -import sharedStyles from '../../views/Styles'; -import { themes } from '../../lib/constants'; -import MessageContext from './Context'; -import { fileDownloadAndPreview } from './helpers/fileDownload'; import { IAttachment, TGetCustomEmoji } from '../../definitions'; -import RCActivityIndicator from '../ActivityIndicator'; -import Attachments from './Attachments'; -import { TSupportedThemes, useTheme } from '../../theme'; +import { themes } from '../../lib/constants'; +import { fileDownloadAndPreview } from '../../lib/methods/helpers'; import { formatAttachmentUrl } from '../../lib/methods/helpers/formatAttachmentUrl'; +import openLink from '../../lib/methods/helpers/openLink'; +import { TSupportedThemes, useTheme } from '../../theme'; +import sharedStyles from '../../views/Styles'; +import RCActivityIndicator from '../ActivityIndicator'; +import Markdown from '../markdown'; +import Attachments from './Attachments'; +import MessageContext from './Context'; +import Touchable from './Touchable'; import messageStyles from './styles'; const styles = StyleSheet.create({ diff --git a/app/containers/message/Video.tsx b/app/containers/message/Video.tsx index 54a8bee4a..ff4d52eed 100644 --- a/app/containers/message/Video.tsx +++ b/app/containers/message/Video.tsx @@ -14,7 +14,7 @@ import { isDownloadActive, resumeMediaFile } from '../../lib/methods/handleMediaDownload'; -import { isIOS } from '../../lib/methods/helpers'; +import { fileDownload, isIOS } from '../../lib/methods/helpers'; import EventEmitter from '../../lib/methods/helpers/events'; import { formatAttachmentUrl } from '../../lib/methods/helpers/formatAttachmentUrl'; import { useTheme } from '../../theme'; @@ -24,7 +24,6 @@ import Markdown from '../markdown'; import BlurComponent from './Components/OverlayComponent'; import MessageContext from './Context'; import Touchable from './Touchable'; -import { fileDownload } from './helpers/fileDownload'; import { DEFAULT_MESSAGE_HEIGHT } from './utils'; const SUPPORTED_TYPES = ['video/quicktime', 'video/mp4', ...(isIOS ? [] : ['video/3gp', 'video/mkv'])]; diff --git a/app/containers/message/helpers/fileDownload/index.ts b/app/containers/message/helpers/fileDownload/index.ts deleted file mode 100644 index 73c7faa82..000000000 --- a/app/containers/message/helpers/fileDownload/index.ts +++ /dev/null @@ -1,53 +0,0 @@ -import RNFetchBlob, { FetchBlobResponse } from 'rn-fetch-blob'; -import FileViewer from 'react-native-file-viewer'; - -import EventEmitter from '../../../../lib/methods/helpers/events'; -import { LISTENER } from '../../../Toast'; -import I18n from '../../../../i18n'; -import { DOCUMENTS_PATH, DOWNLOAD_PATH } from '../../../../lib/constants'; -import { IAttachment } from '../../../../definitions'; - -export const getLocalFilePathFromFile = (localPath: string, attachment: IAttachment): string => `${localPath}${attachment.title}`; - -export const fileDownload = (url: string, attachment: IAttachment): Promise => { - const path = getLocalFilePathFromFile(DOWNLOAD_PATH, attachment); - - const options = { - path, - timeout: 10000, - indicator: true, - overwrite: true, - addAndroidDownloads: { - path, - notification: true, - useDownloadManager: true - } - }; - - return RNFetchBlob.config(options).fetch('GET', url); -}; - -export const fileDownloadAndPreview = async (url: string, attachment: IAttachment): Promise => { - try { - const path = getLocalFilePathFromFile(DOCUMENTS_PATH, attachment); - const file = await RNFetchBlob.config({ - timeout: 10000, - indicator: true, - path - }).fetch('GET', url); - - FileViewer.open(file.data, { - showOpenWithDialog: true, - showAppsSuggestions: true - }) - .then(res => res) - .catch(async () => { - const file = await fileDownload(url, attachment); - file - ? EventEmitter.emit(LISTENER, { message: I18n.t('Downloaded_file') }) - : EventEmitter.emit(LISTENER, { message: I18n.t('Error_Download_file') }); - }); - } catch (e) { - EventEmitter.emit(LISTENER, { message: I18n.t('Error_Download_file') }); - } -}; diff --git a/app/lib/constants/index.ts b/app/lib/constants/index.ts index 442c06d6a..5be7d6ec1 100644 --- a/app/lib/constants/index.ts +++ b/app/lib/constants/index.ts @@ -6,7 +6,6 @@ export * from './environment'; export * from './keys'; export * from './links'; export * from './localAuthentication'; -export * from './localPath'; export * from './messagesStatus'; export * from './messageTypeLoad'; export * from './notifications'; diff --git a/app/lib/constants/localPath.ts b/app/lib/constants/localPath.ts deleted file mode 100644 index 704e2b6b6..000000000 --- a/app/lib/constants/localPath.ts +++ /dev/null @@ -1,4 +0,0 @@ -import RNFetchBlob from 'rn-fetch-blob'; - -export const DOCUMENTS_PATH = `${RNFetchBlob.fs.dirs.DocumentDir}/`; -export const DOWNLOAD_PATH = `${RNFetchBlob.fs.dirs.DownloadDir}/`; diff --git a/app/lib/methods/getServerInfo.ts b/app/lib/methods/getServerInfo.ts index c05d4e8f8..7b790c7f6 100644 --- a/app/lib/methods/getServerInfo.ts +++ b/app/lib/methods/getServerInfo.ts @@ -1,4 +1,3 @@ -import RNFetchBlob from 'rn-fetch-blob'; import { settings as RocketChatSettings } from '@rocket.chat/sdk'; import { KJUR } from 'jsrsasign'; import moment from 'moment'; @@ -45,12 +44,12 @@ const verifyJWT = (jwt?: string): ISupportedVersionsData | null => { export async function getServerInfo(server: string): Promise { try { - const response = await RNFetchBlob.fetch('GET', `${server}/api/info`, { + const response = await fetch(`${server}/api/info`, { ...RocketChatSettings.customHeaders }); try { - const jsonRes: IApiServerInfo = response.json(); - if (!jsonRes?.success) { + const serverInfo: IApiServerInfo = await response.json(); + if (!serverInfo?.success) { return { success: false, message: I18n.t('Not_RC_Server', { contact: I18n.t('Contact_your_server_admin') }) @@ -58,7 +57,7 @@ export async function getServerInfo(server: string): Promise } // Makes use of signed JWT to get supported versions - const supportedVersions = verifyJWT(jsonRes.supportedVersions?.signed); + const supportedVersions = verifyJWT(serverInfo.supportedVersions?.signed); // if backend doesn't have supported versions or JWT is invalid, request from cloud if (!supportedVersions) { @@ -69,7 +68,7 @@ export async function getServerInfo(server: string): Promise moment(new Date()).diff(serverRecord?.supportedVersionsUpdatedAt, 'hours') <= SV_CLOUD_UPDATE_INTERVAL ) { return { - ...jsonRes, + ...serverInfo, success: true }; } @@ -79,7 +78,7 @@ export async function getServerInfo(server: string): Promise // Allows airgapped servers to use the app until enforcementStartDate if (!cloudInfo) { return { - ...jsonRes, + ...serverInfo, success: true }; } @@ -88,14 +87,14 @@ export async function getServerInfo(server: string): Promise const supportedVersionsCloud = verifyJWT(cloudInfo?.signed); return { - ...jsonRes, + ...serverInfo, success: true, supportedVersions: supportedVersionsCloud }; } return { - ...jsonRes, + ...serverInfo, success: true, supportedVersions }; diff --git a/app/lib/methods/helpers/fileDownload.ts b/app/lib/methods/helpers/fileDownload.ts new file mode 100644 index 000000000..af255452d --- /dev/null +++ b/app/lib/methods/helpers/fileDownload.ts @@ -0,0 +1,33 @@ +import * as FileSystem from 'expo-file-system'; +import FileViewer from 'react-native-file-viewer'; + +import { LISTENER } from '../../../containers/Toast'; +import { IAttachment } from '../../../definitions'; +import i18n from '../../../i18n'; +import EventEmitter from './events'; + +export const getLocalFilePathFromFile = (localPath: string, attachment: IAttachment): string => `${localPath}${attachment.title}`; + +export const fileDownload = async (url: string, attachment?: IAttachment, fileName?: string): Promise => { + let path = `${FileSystem.documentDirectory}`; + if (fileName) { + path = `${path}${fileName}`; + } + if (attachment) { + path = `${path}${attachment.title}`; + } + const file = await FileSystem.downloadAsync(url, path); + return file.uri; +}; + +export const fileDownloadAndPreview = async (url: string, attachment: IAttachment): Promise => { + try { + const file = await fileDownload(url, attachment); + FileViewer.open(file, { + showOpenWithDialog: true, + showAppsSuggestions: true + }); + } catch (e) { + EventEmitter.emit(LISTENER, { message: i18n.t('Error_Download_file') }); + } +}; diff --git a/app/lib/methods/helpers/fileUpload.ts b/app/lib/methods/helpers/fileUpload.ts new file mode 100644 index 000000000..d711d095b --- /dev/null +++ b/app/lib/methods/helpers/fileUpload.ts @@ -0,0 +1,66 @@ +export interface IFileUpload { + name: string; + uri?: string; + type?: string; + filename?: string; + data?: any; +} + +export class Upload { + public xhr: XMLHttpRequest; + public formData: FormData; + + constructor() { + this.xhr = new XMLHttpRequest(); + this.formData = new FormData(); + } + + public setupRequest(url: string, headers: { [key: string]: string }): void { + this.xhr.open('POST', url); + Object.keys(headers).forEach(key => { + this.xhr.setRequestHeader(key, headers[key]); + }); + } + + public appendFile(item: IFileUpload): void { + if (item.uri) { + this.formData.append(item.name, { + uri: item.uri, + type: item.type, + name: item.filename + } as any); + } else { + this.formData.append(item.name, item.data); + } + } + + public then(callback: (param: { respInfo: XMLHttpRequest }) => void): void { + this.xhr.onload = () => callback({ respInfo: this.xhr }); + this.xhr.send(this.formData); + } + + public catch(callback: ((this: XMLHttpRequest, ev: ProgressEvent) => any) | null): void { + this.xhr.onerror = callback; + } + + public uploadProgress(callback: (param: number, arg1: number) => any): void { + this.xhr.upload.onprogress = ({ total, loaded }) => callback(loaded, total); + } + + public cancel(): Promise { + this.xhr.abort(); + return Promise.resolve(); + } +} + +class FileUpload { + public uploadFile(url: string, headers: { [x: string]: string }, data: IFileUpload[]) { + const upload = new Upload(); + upload.setupRequest(url, headers); + data.forEach(item => upload.appendFile(item)); + return upload; + } +} + +const fileUpload = new FileUpload(); +export default fileUpload; diff --git a/app/lib/methods/helpers/fileUpload/index.ios.ts b/app/lib/methods/helpers/fileUpload/index.ios.ts deleted file mode 100644 index 96c2ae355..000000000 --- a/app/lib/methods/helpers/fileUpload/index.ios.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { IFileUpload } from './interfaces'; - -class Upload { - public xhr: XMLHttpRequest; - - public formData: FormData; - - constructor() { - this.xhr = new XMLHttpRequest(); - this.formData = new FormData(); - } - - then = (callback: (param: { respInfo: XMLHttpRequest }) => XMLHttpRequest) => { - this.xhr.onload = () => callback({ respInfo: this.xhr }); - this.xhr.send(this.formData); - }; - - catch = (callback: ((this: XMLHttpRequest, ev: ProgressEvent) => any) | null) => { - this.xhr.onerror = callback; - }; - - uploadProgress = (callback: (param: number, arg1: number) => any) => { - this.xhr.upload.onprogress = ({ total, loaded }) => callback(loaded, total); - }; - - cancel = () => { - this.xhr.abort(); - return Promise.resolve(); - }; -} - -class FileUpload { - fetch = (method: string, url: string, headers: { [x: string]: string }, data: IFileUpload[]) => { - const upload = new Upload(); - upload.xhr.open(method, url); - - Object.keys(headers).forEach(key => { - upload.xhr.setRequestHeader(key, headers[key]); - }); - - data.forEach(item => { - if (item.uri) { - upload.formData.append(item.name, { - // @ts-ignore - uri: item.uri, - // @ts-ignore - type: item.type, - name: item.filename - }); - } else { - upload.formData.append(item.name, item.data); - } - }); - - return upload; - }; -} - -const fileUpload = new FileUpload(); -export default fileUpload; diff --git a/app/lib/methods/helpers/fileUpload/index.ts b/app/lib/methods/helpers/fileUpload/index.ts deleted file mode 100644 index bf23212c4..000000000 --- a/app/lib/methods/helpers/fileUpload/index.ts +++ /dev/null @@ -1,25 +0,0 @@ -import RNFetchBlob from 'rn-fetch-blob'; - -import { TMethods } from '../fetch'; -import { IFileUpload } from './interfaces'; - -class FileUpload { - fetch = (method: TMethods, url: string, headers: { [key: string]: string }, data: IFileUpload[]) => { - const formData = data.map(item => { - if (item.uri) { - return { - name: item.name, - type: item.type, - filename: item.filename, - data: RNFetchBlob.wrap(decodeURI(item.uri)) - }; - } - return item; - }); - - return RNFetchBlob.fetch(method, url, headers, formData); - }; -} - -const fileUpload = new FileUpload(); -export default fileUpload; diff --git a/app/lib/methods/helpers/fileUpload/interfaces.ts b/app/lib/methods/helpers/fileUpload/interfaces.ts deleted file mode 100644 index 91b0d7d46..000000000 --- a/app/lib/methods/helpers/fileUpload/interfaces.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface IFileUpload { - name: string; - uri?: string; - type?: string; - filename?: string; - data?: any; -} diff --git a/app/lib/methods/helpers/index.ts b/app/lib/methods/helpers/index.ts index 65ae74993..58dfa6fef 100644 --- a/app/lib/methods/helpers/index.ts +++ b/app/lib/methods/helpers/index.ts @@ -18,3 +18,4 @@ export * from './image'; export * from './askAndroidMediaPermissions'; export * from './emitter'; export * from './parseJson'; +export * from './fileDownload'; diff --git a/app/lib/methods/sendFileMessage.ts b/app/lib/methods/sendFileMessage.ts index 8886f6064..43a2aa6cf 100644 --- a/app/lib/methods/sendFileMessage.ts +++ b/app/lib/methods/sendFileMessage.ts @@ -1,17 +1,16 @@ import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; import { settings as RocketChatSettings } from '@rocket.chat/sdk'; import isEmpty from 'lodash/isEmpty'; -import { FetchBlobResponse, StatefulPromise } from 'rn-fetch-blob'; import { Alert } from 'react-native'; import { IUpload, IUser, TUploadModel } from '../../definitions'; import i18n from '../../i18n'; import database from '../database'; +import type { IFileUpload, Upload } from './helpers/fileUpload'; import FileUpload from './helpers/fileUpload'; -import { IFileUpload } from './helpers/fileUpload/interfaces'; import log from './helpers/log'; -const uploadQueue: { [index: string]: StatefulPromise } = {}; +const uploadQueue: { [index: string]: Upload } = {}; const getUploadPath = (path: string, rid: string) => `${path}-${rid}`; @@ -48,7 +47,7 @@ export function sendFileMessage( server: string, user: Partial>, isForceTryAgain?: boolean -): Promise { +): Promise { return new Promise(async (resolve, reject) => { try { const { id, token } = user; @@ -121,7 +120,7 @@ export function sendFileMessage( 'X-User-Id': id }; - uploadQueue[uploadPath] = FileUpload.fetch('POST', uploadUrl, headers, formData); + uploadQueue[uploadPath] = FileUpload.uploadFile(uploadUrl, headers, formData); uploadQueue[uploadPath].uploadProgress(async (loaded: number, total: number) => { try { @@ -137,12 +136,11 @@ export function sendFileMessage( uploadQueue[uploadPath].then(async response => { if (response.respInfo.status >= 200 && response.respInfo.status < 400) { - // If response is all good... try { await db.write(async () => { await uploadRecord.destroyPermanently(); }); - resolve(response); + resolve(); } catch (e) { log(e); } diff --git a/app/lib/services/getServerTimeSync.ts b/app/lib/services/getServerTimeSync.ts index da50c07ed..9bce93451 100644 --- a/app/lib/services/getServerTimeSync.ts +++ b/app/lib/services/getServerTimeSync.ts @@ -1,13 +1,9 @@ -import RNFetchBlob from 'rn-fetch-blob'; - export const getServerTimeSync = async (server: string) => { try { - const response = await Promise.race([ - RNFetchBlob.fetch('GET', `${server}/_timesync`), - new Promise(res => setTimeout(res, 2000)) - ]); - if (response?.data) { - return parseInt(response.data); + const response = await Promise.race([fetch(`${server}/_timesync`), new Promise(res => setTimeout(res, 2000))]); + const data = await response?.json(); + if (data?.data) { + return parseInt(data.data); } return null; } catch { diff --git a/app/views/AttachmentView.tsx b/app/views/AttachmentView.tsx index 28d375d83..ffec75a11 100644 --- a/app/views/AttachmentView.tsx +++ b/app/views/AttachmentView.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { PermissionsAndroid, useWindowDimensions, View } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { shallowEqual } from 'react-redux'; -import RNFetchBlob from 'rn-fetch-blob'; +import * as FileSystem from 'expo-file-system'; import { isImageBase64 } from '../lib/methods'; import RCActivityIndicator from '../containers/ActivityIndicator'; @@ -18,7 +18,7 @@ import { IAttachment } from '../definitions'; import I18n from '../i18n'; import { useAppSelector } from '../lib/hooks'; import { useAppNavigation, useAppRoute } from '../lib/hooks/navigation'; -import { formatAttachmentUrl, isAndroid } from '../lib/methods/helpers'; +import { formatAttachmentUrl, isAndroid, fileDownload } from '../lib/methods/helpers'; import EventEmitter from '../lib/methods/helpers/events'; import { getUserSelector } from '../selectors/login'; import { TNavigation } from '../stacks/stackType'; @@ -177,11 +177,9 @@ const AttachmentView = (): React.ReactElement => { } else { filename = getFilename({ title: attachment.title, type: 'video', mimeType: video_type, url }); } - const documentDir = `${RNFetchBlob.fs.dirs.DocumentDir}/`; - const path = `${documentDir + filename}`; - const file = await RNFetchBlob.config({ path }).fetch('GET', mediaAttachment); - await CameraRoll.save(path, { album: 'Rocket.Chat' }); - file.flush(); + const file = await fileDownload(mediaAttachment, {}, filename); + await CameraRoll.save(file, { album: 'Rocket.Chat' }); + FileSystem.deleteAsync(file, { idempotent: true }); } EventEmitter.emit(LISTENER, { message: I18n.t('saved_to_gallery') }); } catch (e) { diff --git a/ios/Podfile.lock b/ios/Podfile.lock index d67fe6338..7b9eabf1a 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -484,8 +484,6 @@ PODS: - React - rn-extensions-share (2.4.1): - React - - rn-fetch-blob (0.12.0): - - React-Core - RNBootSplash (4.3.3): - React-Core - RNCAsyncStorage (1.17.11): @@ -653,7 +651,6 @@ DEPENDENCIES: - "ReactNativeART (from `../node_modules/@react-native-community/art`)" - ReactNativeUiLib (from `../node_modules/react-native-ui-lib`) - rn-extensions-share (from `../node_modules/rn-extensions-share`) - - rn-fetch-blob (from `../node_modules/rn-fetch-blob`) - RNBootSplash (from `../node_modules/react-native-bootsplash`) - "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)" - "RNCClipboard (from `../node_modules/@react-native-clipboard/clipboard`)" @@ -827,8 +824,6 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-ui-lib" rn-extensions-share: :path: "../node_modules/rn-extensions-share" - rn-fetch-blob: - :path: "../node_modules/rn-fetch-blob" RNBootSplash: :path: "../node_modules/react-native-bootsplash" RNCAsyncStorage: @@ -962,7 +957,6 @@ SPEC CHECKSUMS: ReactNativeART: 78edc68dd4a1e675338cd0cd113319cf3a65f2ab ReactNativeUiLib: 33521c0747ea376d292b62b6415e0f1d75bd3c10 rn-extensions-share: 5fd84a80e6594706f0dfa1884f2d6d591b382cf5 - rn-fetch-blob: f065bb7ab7fb48dd002629f8bdcb0336602d3cba RNBootSplash: 7e91ea56c7010aae487489789dbe212e8c905a0c RNCAsyncStorage: 8616bd5a58af409453ea4e1b246521bb76578d60 RNCClipboard: cc054ad1e8a33d2a74cd13e565588b4ca928d8fd diff --git a/jest.setup.js b/jest.setup.js index e8e2bcfcd..12204dbb8 100644 --- a/jest.setup.js +++ b/jest.setup.js @@ -29,18 +29,6 @@ jest.mock('react-native-reanimated', () => require('react-native-reanimated/mock jest.mock('@react-native-clipboard/clipboard', () => mockClipboard); -jest.mock('rn-fetch-blob', () => ({ - fs: { - dirs: { - DocumentDir: '/data/com.rocket.chat/documents', - DownloadDir: '/data/com.rocket.chat/downloads' - }, - exists: jest.fn(() => null) - }, - fetch: jest.fn(() => null), - config: jest.fn(() => null) -})); - jest.mock('react-native-file-viewer', () => ({ open: jest.fn(() => null) })); diff --git a/package.json b/package.json index 4780969c7..ae0d66ebf 100644 --- a/package.json +++ b/package.json @@ -137,7 +137,6 @@ "remove-markdown": "^0.3.0", "reselect": "4.0.0", "rn-extensions-share": "RocketChat/rn-extensions-share", - "rn-fetch-blob": "^0.12.0", "rn-root-view": "RocketChat/rn-root-view", "semver": "7.3.8", "transliteration": "^2.3.5", diff --git a/patches/rn-fetch-blob+0.12.0.patch b/patches/rn-fetch-blob+0.12.0.patch deleted file mode 100644 index 1269040fb..000000000 --- a/patches/rn-fetch-blob+0.12.0.patch +++ /dev/null @@ -1,153 +0,0 @@ -diff --git a/node_modules/rn-fetch-blob/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java b/node_modules/rn-fetch-blob/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java -index 602d51d..920d975 100644 ---- a/node_modules/rn-fetch-blob/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java -+++ b/node_modules/rn-fetch-blob/android/src/main/java/com/RNFetchBlob/RNFetchBlob.java -@@ -38,7 +38,7 @@ import static com.RNFetchBlob.RNFetchBlobConst.GET_CONTENT_INTENT; - - public class RNFetchBlob extends ReactContextBaseJavaModule { - -- private final OkHttpClient mClient; -+ static private OkHttpClient mClient; - - static ReactApplicationContext RCTContext; - private static LinkedBlockingQueue taskQueue = new LinkedBlockingQueue<>(); -@@ -75,6 +75,10 @@ public class RNFetchBlob extends ReactContextBaseJavaModule { - }); - } - -+ public static void applyCustomOkHttpClient(OkHttpClient client) { -+ mClient = client; -+ } -+ - @Override - public String getName() { - return "RNFetchBlob"; -diff --git a/node_modules/rn-fetch-blob/ios/RNFetchBlobRequest.m b/node_modules/rn-fetch-blob/ios/RNFetchBlobRequest.m -index cdbe6b1..04e5e7b 100644 ---- a/node_modules/rn-fetch-blob/ios/RNFetchBlobRequest.m -+++ b/node_modules/rn-fetch-blob/ios/RNFetchBlobRequest.m -@@ -15,6 +15,9 @@ - #import "IOS7Polyfill.h" - #import - -+#import "SecureStorage.h" -+#import -+ - - typedef NS_ENUM(NSUInteger, ResponseFormat) { - UTF8, -@@ -450,16 +453,108 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSen - } - } - -- --- (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable credantial))completionHandler -+-(NSURLCredential *)getUrlCredential:(NSURLAuthenticationChallenge *)challenge path:(NSString *)path password:(NSString *)password - { -- if ([[options valueForKey:CONFIG_TRUSTY] boolValue]) { -- completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); -- } else { -- completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); -+ NSString *authMethod = [[challenge protectionSpace] authenticationMethod]; -+ SecTrustRef serverTrust = challenge.protectionSpace.serverTrust; -+ -+ if ([authMethod isEqualToString:NSURLAuthenticationMethodServerTrust] || path == nil || password == nil) { -+ return [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; -+ } else if (path && password) { -+ NSMutableArray *policies = [NSMutableArray array]; -+ [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)challenge.protectionSpace.host)]; -+ SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies); -+ -+ SecTrustResultType result; -+ SecTrustEvaluate(serverTrust, &result); -+ -+ if (![[NSFileManager defaultManager] fileExistsAtPath:path]) -+ { -+ return [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; -+ } -+ -+ NSData *p12data = [NSData dataWithContentsOfFile:path]; -+ NSDictionary* options = @{ (id)kSecImportExportPassphrase:password }; -+ CFArrayRef rawItems = NULL; -+ OSStatus status = SecPKCS12Import((__bridge CFDataRef)p12data, -+ (__bridge CFDictionaryRef)options, -+ &rawItems); -+ -+ if (status != noErr) { -+ return [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; -+ } -+ -+ NSArray* items = (NSArray*)CFBridgingRelease(rawItems); -+ NSDictionary* firstItem = nil; -+ if ((status == errSecSuccess) && ([items count]>0)) { -+ firstItem = items[0]; - } -+ -+ SecIdentityRef identity = (SecIdentityRef)CFBridgingRetain(firstItem[(id)kSecImportItemIdentity]); -+ SecCertificateRef certificate = NULL; -+ if (identity) { -+ SecIdentityCopyCertificate(identity, &certificate); -+ if (certificate) { CFRelease(certificate); } -+ } -+ -+ NSMutableArray *certificates = [[NSMutableArray alloc] init]; -+ [certificates addObject:CFBridgingRelease(certificate)]; -+ -+ return [NSURLCredential credentialWithIdentity:identity certificates:certificates persistence:NSURLCredentialPersistenceNone]; -+ } -+ -+ return [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; -+} -+ -+- (NSString *)stringToHex:(NSString *)string -+{ -+ char *utf8 = (char *)[string UTF8String]; -+ NSMutableString *hex = [NSMutableString string]; -+ while (*utf8) [hex appendFormat:@"%02X", *utf8++ & 0x00FF]; -+ -+ return [[NSString stringWithFormat:@"%@", hex] lowercaseString]; - } - -+-(void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler -+{ -+ NSString *host = challenge.protectionSpace.host; -+ -+ // Read the clientSSL info from MMKV -+ __block NSString *clientSSL; -+ SecureStorage *secureStorage = [[SecureStorage alloc] init]; -+ -+ // https://github.com/ammarahm-ed/react-native-mmkv-storage/blob/master/src/loader.js#L31 -+ NSString *key = [secureStorage getSecureKey:[self stringToHex:@"com.MMKV.default"]]; -+ NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; -+ -+ if (key == NULL) { -+ return completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, credential); -+ } -+ -+ NSData *cryptKey = [key dataUsingEncoding:NSUTF8StringEncoding]; -+ MMKV *mmkv = [MMKV mmkvWithID:@"default" cryptKey:cryptKey mode:MMKVMultiProcess]; -+ clientSSL = [mmkv getStringForKey:host]; -+ -+ if ([clientSSL length] != 0) { -+ NSData *data = [clientSSL dataUsingEncoding:NSUTF8StringEncoding]; -+ id dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; -+ NSString *path = [dict objectForKey:@"path"]; -+ NSString *password = [dict objectForKey:@"password"]; -+ credential = [self getUrlCredential:challenge path:path password:password]; -+ } -+ -+ completionHandler(NSURLSessionAuthChallengeUseCredential, credential); -+} -+ -+// - (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable credantial))completionHandler -+// { -+// if ([[options valueForKey:CONFIG_TRUSTY] boolValue]) { -+// completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); -+// } else { -+// completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); -+// } -+// } -+ - - - (void) URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session - { diff --git a/yarn.lock b/yarn.lock index be7c8518f..7f74a6779 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8171,11 +8171,6 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= -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-js@^1.0.2, base64-js@^1.1.2, base64-js@^1.2.3, base64-js@^1.3.0, base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -8619,7 +8614,6 @@ builtins@^1.0.3: resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" integrity sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ== - bunyamin@^1.5.0: version "1.5.2" resolved "https://registry.yarnpkg.com/bunyamin/-/bunyamin-1.5.2.tgz#681db204c0b16531369d5c1f6c89dc8d760b7558" @@ -12208,18 +12202,6 @@ glob-to-regexp@^0.3.0: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= -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@7.1.6, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" @@ -18798,14 +18780,6 @@ rn-extensions-share@RocketChat/rn-extensions-share: version "2.4.1" resolved "https://codeload.github.com/RocketChat/rn-extensions-share/tar.gz/4d7c0e4c2f300e4fb116af7b7cc0dbbc8169150c" -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" - rn-host-detect@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/rn-host-detect/-/rn-host-detect-1.2.0.tgz#8b0396fc05631ec60c1cb8789e5070cdb04d0da0" @@ -19677,6 +19651,7 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: strip-ansi "^6.0.0" string-width@^4.2.3: + name string-width-cjs version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -19840,6 +19815,7 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: ansi-regex "^4.1.0" strip-ansi@^6.0.1: + name strip-ansi-cjs version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==