[IMPROVEMENT] Use Rest API for file upload (#1005)

* removed rn-fetch-blob and use native XMLHttpRequest instead

* removed unnessary changes

* fix android bug

* fix android bug

* added tmid support

* fix bug

* fixed isssue with cacel model

* fix problems with audio

* done requested changes

* fix bug with android
This commit is contained in:
pranavpandey1998official 2019-06-29 00:37:20 +05:30 committed by Diego Mello
parent 3b43cb3fb7
commit 481458285b
8 changed files with 120 additions and 166 deletions

View File

@ -190,7 +190,6 @@ dependencies {
implementation project(":reactnativekeyboardinput")
implementation project(':react-native-video')
implementation project(':react-native-vector-icons')
implementation project(':rn-fetch-blob')
implementation project(':react-native-fast-image')
implementation project(':realm')
implementation project(':reactnativenotifications')

View File

@ -17,7 +17,6 @@ import com.facebook.soloader.SoLoader;
import com.AlexanderZaytsev.RNI18n.RNI18nPackage;
import com.reactnative.ivpusic.imagepicker.PickerPackage;
import com.RNFetchBlob.RNFetchBlobPackage;
import com.brentvatne.react.ReactVideoPackage;
import com.dylanvann.fastimage.FastImageViewPackage;
import com.oblador.vectoricons.VectorIconsPackage;
@ -74,7 +73,6 @@ public class MainApplication extends Application implements ReactApplication, IN
new RNDeviceInfo(),
new PickerPackage(),
new VectorIconsPackage(),
new RNFetchBlobPackage(),
new RealmReactPackage(),
new ReactVideoPackage(),
new ReactNativeAudioPackage(),

View File

@ -18,8 +18,6 @@ include ':react-native-device-info'
project(':react-native-device-info').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-device-info/android')
include ':react-native-gesture-handler'
project(':react-native-gesture-handler').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-gesture-handler/android')
include ':rn-fetch-blob'
project(':rn-fetch-blob').projectDir = new File(rootProject.projectDir, '../node_modules/rn-fetch-blob/android')
include ':react-native-image-crop-picker'
project(':react-native-image-crop-picker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-image-crop-picker/android')
include ':react-native-i18n'

View File

@ -44,13 +44,14 @@ export default class extends React.PureComponent {
this.recordingCanceled = false;
this.recording = true;
this.name = `${ Date.now() }.aac`;
this.state = {
currentTime: '00:00'
};
}
componentDidMount() {
const audioPath = `${ AudioUtils.CachesDirectoryPath }/${ Date.now() }.aac`;
const audioPath = `${ AudioUtils.CachesDirectoryPath }/${ this.name }`;
AudioRecorder.prepareRecordingAtPath(audioPath, {
SampleRate: 22050,
@ -84,12 +85,14 @@ export default class extends React.PureComponent {
if (!didSucceed) {
return onFinish && onFinish(didSucceed);
}
const path = filePath.startsWith('file://') ? filePath.split('file://')[1] : filePath;
if (isAndroid) {
filePath = filePath.startsWith('file://') ? filePath : `file://${ filePath }`;
}
const fileInfo = {
name: this.name,
type: 'audio/aac',
store: 'Uploads',
path
path: filePath
};
return onFinish && onFinish(fileInfo);
}

View File

@ -1,111 +1,129 @@
import RNFetchBlob from 'rn-fetch-blob';
import reduxStore from '../createStore';
import database from '../realm';
import log from '../../utils/log';
const promises = {};
function _ufsCreate(fileInfo) {
return this.sdk.methodCall('ufsCreate', fileInfo);
}
function _ufsComplete(fileId, store, token) {
return this.sdk.methodCall('ufsComplete', fileId, store, token);
}
function _sendFileMessage(rid, data, msg = {}) {
// RC 0.22.0
return this.sdk.methodCall('sendFileMessage', rid, null, data, msg);
}
const uploadQueue = {};
export function isUploadActive(path) {
return !!promises[path];
return !!uploadQueue[path];
}
export async function cancelUpload(path) {
if (promises[path]) {
await promises[path].cancel();
}
}
export async function sendFileMessage(rid, fileInfo, tmid) {
try {
const data = await RNFetchBlob.wrap(fileInfo.path);
if (!fileInfo.size) {
const fileStat = await RNFetchBlob.fs.stat(fileInfo.path);
fileInfo.size = fileStat.size;
fileInfo.name = fileStat.filename;
}
const { FileUpload_MaxFileSize } = reduxStore.getState().settings;
// -1 maxFileSize means there is no limit
if (FileUpload_MaxFileSize > -1 && fileInfo.size > FileUpload_MaxFileSize) {
return Promise.reject({ error: 'error-file-too-large' }); // eslint-disable-line
}
fileInfo.rid = rid;
export function cancelUpload(path) {
if (uploadQueue[path]) {
uploadQueue[path].abort();
database.write(() => {
try {
database.create('uploads', fileInfo, true);
} catch (e) {
return log('err_send_file_message_create_upload_1', e);
}
});
const result = await _ufsCreate.call(this, fileInfo);
promises[fileInfo.path] = RNFetchBlob.fetch('POST', result.url, {
'Content-Type': 'octet-stream'
}, data);
// Workaround for https://github.com/joltup/rn-fetch-blob/issues/96
setTimeout(() => {
if (promises[fileInfo.path] && promises[fileInfo.path].uploadProgress) {
promises[fileInfo.path].uploadProgress((loaded, total) => {
database.write(() => {
fileInfo.progress = Math.floor((loaded / total) * 100);
try {
database.create('uploads', fileInfo, true);
} catch (e) {
return log('err_send_file_message_create_upload_2', e);
}
});
});
}
});
await promises[fileInfo.path];
const completeResult = await _ufsComplete.call(this, result.fileId, fileInfo.store, result.token);
await _sendFileMessage.call(this, completeResult.rid, {
_id: completeResult._id,
type: completeResult.type,
size: completeResult.size,
name: completeResult.name,
description: completeResult.description,
url: completeResult.path
}, {
tmid
});
database.write(() => {
const upload = database.objects('uploads').filtered('path = $0', fileInfo.path);
const upload = database.objects('uploads').filtered('path = $0', path);
try {
database.delete(upload);
} catch (e) {
log('err_send_file_message_delete_upload', e);
}
});
} catch (e) {
database.write(() => {
fileInfo.error = true;
try {
database.create('uploads', fileInfo, true);
} catch (err) {
log('err_send_file_message_create_upload_3', err);
}
});
delete uploadQueue[path];
}
}
export function sendFileMessage(rid, fileInfo, tmid) {
return new Promise((resolve, reject) => {
try {
const { FileUpload_MaxFileSize, Site_Url } = reduxStore.getState().settings;
const { id, token } = reduxStore.getState().login.user;
// -1 maxFileSize means there is no limit
if (FileUpload_MaxFileSize > -1 && fileInfo.size > FileUpload_MaxFileSize) {
return reject({ error: 'error-file-too-large' }); // eslint-disable-line
}
const uploadUrl = `${ Site_Url }/api/v1/rooms.upload/${ rid }`;
const xhr = new XMLHttpRequest();
const formData = new FormData();
fileInfo.rid = rid;
database.write(() => {
try {
database.create('uploads', fileInfo, true);
} catch (e) {
return log('err_send_file_message_create_upload_1', e);
}
});
uploadQueue[fileInfo.path] = xhr;
xhr.open('POST', uploadUrl);
formData.append('file', {
uri: fileInfo.path,
type: fileInfo.type,
name: fileInfo.name || 'fileMessage'
});
if (fileInfo.description) {
formData.append('description', fileInfo.description);
}
if (tmid) {
formData.append('tmid', tmid);
}
xhr.setRequestHeader('X-Auth-Token', token);
xhr.setRequestHeader('X-User-Id', id);
xhr.upload.onprogress = ({ total, loaded }) => {
database.write(() => {
fileInfo.progress = Math.floor((loaded / total) * 100);
try {
database.create('uploads', fileInfo, true);
} catch (e) {
return log('err_send_file_message_create_upload_2', e);
}
});
};
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 400) { // If response is all good...
database.write(() => {
const upload = database.objects('uploads').filtered('path = $0', fileInfo.path);
try {
database.delete(upload);
const response = JSON.parse(xhr.response);
resolve(response);
} catch (e) {
reject(e);
log('err_send_file_message_delete_upload', e);
}
});
} else {
database.write(() => {
fileInfo.error = true;
try {
database.create('uploads', fileInfo, true);
const response = JSON.parse(xhr.response);
reject(response);
} catch (err) {
reject(err);
log('err_send_file_message_create_upload_3', err);
}
});
}
};
xhr.onerror = (e) => {
database.write(() => {
fileInfo.error = true;
try {
database.create('uploads', fileInfo, true);
reject(e);
} catch (err) {
reject(err);
log('err_send_file_message_create_upload_3', err);
}
});
};
xhr.send(formData);
} catch (err) {
log('err_send_file_message_create_upload_4', err);
}
});
}

View File

@ -5,7 +5,6 @@
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; };
00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; };
@ -38,7 +37,6 @@
B88F586F1FBF57F600B352B8 /* libRCTPushNotification.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B88F58461FBF55E200B352B8 /* libRCTPushNotification.a */; };
B8971BB2202A093B0000D245 /* libKeyboardTrackingView.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B8971BB1202A091D0000D245 /* libKeyboardTrackingView.a */; };
BAB7DC22804246F3923A1833 /* libFastImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FD2E2837F110483CA29EE0D4 /* libFastImage.a */; };
BED2B77AA660460E8BC9F8E0 /* libRNFetchBlob.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6533FB90166345D29F1B91C0 /* libRNFetchBlob.a */; };
EF736EF520A64AE8820E684A /* libRealmReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DF26CC845883492D8AC8869B /* libRealmReact.a */; };
F5BF54DC78E1411B8343933B /* libRNI18n.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 921481B47B50490CA761932E /* libRNI18n.a */; };
/* End PBXBuildFile section */
@ -282,13 +280,6 @@
remoteGlobalIDString = 641E28441F0EEC8500443AF6;
remoteInfo = "RCTVideo-tvOS";
};
7A8C915220F39A8000C8F5EE /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0B82BCC462E84F308C5B5CD1 /* RNFetchBlob.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = A15C300E1CD25C330074CB35;
remoteInfo = RNFetchBlob;
};
7A8DEB5120ED0BDE00C5DCE4 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 7A8DEB1B20ED0BDE00C5DCE4 /* RNNotifications.xcodeproj */;
@ -445,7 +436,6 @@
00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = "<group>"; };
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = "<group>"; };
06BB44DD4855498082A744AD /* libz.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
0B82BCC462E84F308C5B5CD1 /* RNFetchBlob.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNFetchBlob.xcodeproj; path = "../node_modules/rn-fetch-blob/ios/RNFetchBlob.xcodeproj"; sourceTree = "<group>"; };
1132AD7934954A958E143199 /* RNFirebase.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNFirebase.xcodeproj; path = "../node_modules/react-native-firebase/ios/RNFirebase.xcodeproj"; sourceTree = "<group>"; };
1142E3442BA94B19BCF52814 /* libRNAudio.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNAudio.a; sourceTree = "<group>"; };
139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = "<group>"; };
@ -470,7 +460,6 @@
5A8684E7C27E426C9206E980 /* RealmReact.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RealmReact.xcodeproj; path = "../node_modules/realm/react-native/ios/RealmReact.xcodeproj"; sourceTree = "<group>"; };
5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = "<group>"; };
60B2A6A31FC4588700BD58E5 /* RocketChatRN.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = RocketChatRN.entitlements; path = RocketChatRN/RocketChatRN.entitlements; sourceTree = "<group>"; };
6533FB90166345D29F1B91C0 /* libRNFetchBlob.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNFetchBlob.a; sourceTree = "<group>"; };
66D6B1D0567051BE541450C9 /* Pods-RocketChatRN.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RocketChatRN.release.xcconfig"; path = "Pods/Target Support Files/Pods-RocketChatRN/Pods-RocketChatRN.release.xcconfig"; sourceTree = "<group>"; };
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = "<group>"; };
7A006F13229C83B600803143 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
@ -521,7 +510,6 @@
EF736EF520A64AE8820E684A /* libRealmReact.a in Frameworks */,
0C6E2DE448364EA896869ADF /* libc++.tbd in Frameworks */,
24A2AEF2383D44B586D31C01 /* libz.tbd in Frameworks */,
BED2B77AA660460E8BC9F8E0 /* libRNFetchBlob.a in Frameworks */,
77C35F50C01C43668188886C /* libRNVectorIcons.a in Frameworks */,
8ECBD927DDAC4987B98E102E /* libRCTVideo.a in Frameworks */,
74815BBCB91147C08C8F7B3D /* libRNAudio.a in Frameworks */,
@ -713,14 +701,6 @@
name = Products;
sourceTree = "<group>";
};
7A8C912120F39A8000C8F5EE /* Products */ = {
isa = PBXGroup;
children = (
7A8C915320F39A8000C8F5EE /* libRNFetchBlob.a */,
);
name = Products;
sourceTree = "<group>";
};
7A8DEB1C20ED0BDE00C5DCE4 /* Products */ = {
isa = PBXGroup;
children = (
@ -771,7 +751,6 @@
C21010507E5B4B37BA0E4C9D /* RNAudio.xcodeproj */,
1845C223DA364898A8400573 /* FastImage.xcodeproj */,
22D3971EAF2E4660B4FAB3DD /* RNI18n.xcodeproj */,
0B82BCC462E84F308C5B5CD1 /* RNFetchBlob.xcodeproj */,
B1A58A7ACB0E4453A44AEC38 /* RNGestureHandler.xcodeproj */,
1132AD7934954A958E143199 /* RNFirebase.xcodeproj */,
);
@ -1015,10 +994,6 @@
ProductGroup = A9A6C941204DD556006B7D9D /* Products */;
ProjectRef = C21010507E5B4B37BA0E4C9D /* RNAudio.xcodeproj */;
},
{
ProductGroup = 7A8C912120F39A8000C8F5EE /* Products */;
ProjectRef = 0B82BCC462E84F308C5B5CD1 /* RNFetchBlob.xcodeproj */;
},
{
ProductGroup = 7AB8E714229C6145006B474A /* Products */;
ProjectRef = 1132AD7934954A958E143199 /* RNFirebase.xcodeproj */;
@ -1290,13 +1265,6 @@
remoteRef = 7A7F5C9A1FCC982500024129 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
7A8C915320F39A8000C8F5EE /* libRNFetchBlob.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRNFetchBlob.a;
remoteRef = 7A8C915220F39A8000C8F5EE /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
7A8DEB5220ED0BDE00C5DCE4 /* libRNNotifications.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
@ -1561,7 +1529,6 @@
"$(SRCROOT)/../node_modules/react-native-fast-image/ios/FastImage/**",
"$(SRCROOT)/../node_modules/react-native-i18n/ios",
"$(SRCROOT)/../node_modules/react-native-notifications/RNNotifications",
"$(SRCROOT)/../node_modules/rn-fetch-blob/ios/**",
"$(SRCROOT)/../node_modules/react-native-gesture-handler/ios/**",
"$(SRCROOT)/../node_modules/react-native-firebase/ios/RNFirebase/**",
);
@ -1569,7 +1536,6 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/RocketChatRN\"",
);
OTHER_LDFLAGS = (
"$(inherited)",
@ -1612,7 +1578,6 @@
"$(SRCROOT)/../node_modules/react-native-fast-image/ios/FastImage/**",
"$(SRCROOT)/../node_modules/react-native-i18n/ios",
"$(SRCROOT)/../node_modules/react-native-notifications/RNNotifications",
"$(SRCROOT)/../node_modules/rn-fetch-blob/ios/**",
"$(SRCROOT)/../node_modules/react-native-gesture-handler/ios/**",
"$(SRCROOT)/../node_modules/react-native-firebase/ios/RNFirebase/**",
);
@ -1620,7 +1585,6 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/RocketChatRN\"",
);
OTHER_LDFLAGS = (
"$(inherited)",

View File

@ -78,7 +78,6 @@
"redux-immutable-state-invariant": "^2.1.0",
"redux-saga": "^0.16.2",
"remove-markdown": "^0.3.0",
"rn-fetch-blob": "^0.10.15",
"rn-user-defaults": "^1.3.4",
"semver": "6.0.0",
"snyk": "^1.156.0",

View File

@ -3469,11 +3469,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 sha1-eAqZyE59YAJgNhURxId2E78k9rs=
base64-js@^1.0.2, base64-js@^1.1.2, base64-js@^1.2.3:
version "1.3.0"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3"
@ -6501,18 +6496,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 sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=
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.2:
version "7.1.2"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
@ -9642,7 +9625,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4:
"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.3, minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
@ -12547,14 +12530,6 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
hash-base "^3.0.0"
inherits "^2.0.1"
rn-fetch-blob@^0.10.15:
version "0.10.15"
resolved "https://registry.yarnpkg.com/rn-fetch-blob/-/rn-fetch-blob-0.10.15.tgz#3526860c1c57e0bf4c09bdbe409c919264afffba"
integrity sha512-/m/gurTaPAvR3J843uehHhznj5k89x7XClyO5ejmbspNLNQ4ByF+kZs80wiiKGWntj+Wqo0jJu1goArXEfc0kA==
dependencies:
base-64 "0.1.0"
glob "7.0.6"
rn-user-defaults@^1.3.4:
version "1.3.4"
resolved "https://registry.yarnpkg.com/rn-user-defaults/-/rn-user-defaults-1.3.4.tgz#1fbdd1bf29d9f853918dca5219e45db54d19fe37"