From 57b69120fd297179e0624012b7903019e3ea8e4e Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 29 Dec 2023 00:58:22 +0200 Subject: [PATCH 1/2] TW-1208: [research] GoogleDrive for iOS. + react-native-google-drive-api-wrapper --- package.json | 1 + src/utils/cloud-backup/google-drive.ts | 109 +++++++++++++++---------- src/utils/cloud-backup/index.ts | 1 + yarn.lock | 16 +++- 4 files changed, 84 insertions(+), 43 deletions(-) diff --git a/package.json b/package.json index a10cb1619..5854f376e 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "@react-navigation/native": "6.1.8", "@react-navigation/stack": "6.3.18", "@reduxjs/toolkit": "^1.8.5", + "@robinbobin/react-native-google-drive-api-wrapper": "^1.2.3", "@sentry/react-native": "5.9.1", "@shopify/flash-list": "^1.6.1", "@taquito/local-forging": "17.1.1", diff --git a/src/utils/cloud-backup/google-drive.ts b/src/utils/cloud-backup/google-drive.ts index 6c4a74cc4..5debd29c0 100644 --- a/src/utils/cloud-backup/google-drive.ts +++ b/src/utils/cloud-backup/google-drive.ts @@ -3,18 +3,15 @@ * See: https://github.com/react-native-google-signin/google-signin/blob/master/docs/android-guide.md */ import { GoogleSignin, statusCodes, NativeModuleError } from '@react-native-google-signin/google-signin'; -import RNCloudFs from 'react-native-cloud-fs'; -import * as RNFS from 'react-native-fs'; +import { GDrive, MimeTypes } from '@robinbobin/react-native-google-drive-api-wrapper'; import { isString } from 'src/utils/is-string'; -import { rejectOnTimeout } from 'src/utils/timeouts.util'; import { CLOUD_REQUEST_TIMEOUT, EncryptedBackupObject, buildAndEncryptBackup, parseBackup } from './common'; -const scope = 'hidden'; const filename = 'wallet-backup.json'; -export const isCloudAvailable = async () => Boolean(RNCloudFs); +export const isCloudAvailable = async () => true; export const requestSignInToCloud = async () => { await ensureGooglePlayServicesAvailable(); @@ -50,10 +47,11 @@ export const requestSignInToCloud = async () => { } try { - /* Syncing signed-in state to RNCloudFS */ - return await RNCloudFs.loginIfNeeded(); + await buildGDrive(); + + return true; } catch (error) { - console.error('RNCloudFs.loginIfNeeded() error:', { error }); + console.error(error); throw new Error('Failed to sync sign-in status'); } @@ -68,15 +66,18 @@ export const fetchCloudBackup = async (): Promise { - console.error('RNCloudFs.getGoogleDriveDocument() error:', error); + const gDrive = await buildGDrive(); + gDrive.fetchTimeout = CLOUD_REQUEST_TIMEOUT; - throw new Error("Failed to read cloud. See if it's enabled"); - }), - CLOUD_REQUEST_TIMEOUT, - new Error('Reading cloud took too long') - ); + const encryptedBackup = await gDrive.files.getText(details.id).catch(error => { + console.error(error); + + if (error?.name === 'AbortError') { + throw new Error('Reading cloud took too long'); + } + + throw new Error("Failed to read cloud. See if it's enabled"); + }); return parseBackup(encryptedBackup); }; @@ -84,24 +85,23 @@ export const fetchCloudBackup = async (): Promise { const encryptedData = await buildAndEncryptBackup(mnemonic, password); - const localPath = `${RNFS.DocumentDirectoryPath}/${filename}`; + const gDrive = await buildGDrive(); - await RNFS.writeFile(localPath, encryptedData, 'utf8'); + const uploader = gDrive.files + .newMultipartUploader() + .setRequestBody({ + name: filename, + parents: ['appDataFolder'] + }) + .setData(encryptedData, MimeTypes.JSON); - const fileId = await RNCloudFs.copyToCloud({ - scope, - targetPath: filename, - mimeType: 'application/json', - sourcePath: { path: localPath } - }) - .catch(error => { - console.error('RNCloudFs.copyToCloud() error:', error); + const details: FileDetails = await uploader.execute().catch(error => { + console.error(error); - throw new Error('Failed to upload to cloud'); - }) - .finally(() => RNFS.unlink(localPath).catch(console.error)); + throw new Error('Failed to upload to cloud'); + }); - const fileExists = await checkIfBackupExists(fileId); + const fileExists = await checkIfBackupExists(details.id); if (fileExists === false) { throw new Error('File not found after saving'); @@ -117,11 +117,20 @@ const checkIfBackupExists = async (fileId?: string) => { return false; } - return await RNCloudFs.fileExists({ scope, fileId }).catch(error => { - console.error('RNCloudFs.fileExists() error:', error); + const gDrive = await buildGDrive(); - return false; - }); + return await gDrive.files.getMetadata(fileId).then( + info => { + console.log('Backup file metadata:', info); + + return true; + }, + error => { + console.error(error); + + return false; + } + ); }; const ensureGooglePlayServicesAvailable = async () => { @@ -139,8 +148,6 @@ const ensureGooglePlayServicesAvailable = async () => { const preLogOut = async () => { try { await GoogleSignin.signOut(); - /* Syncing signed-in state to RNCloudFS */ - await RNCloudFs.logout(); } catch (error) { console.error('preLogOut() error:', { error }); @@ -148,15 +155,33 @@ const preLogOut = async () => { } }; +interface FileDetails { + id: string; + name: string; + mimeType: string; + kind: string; +} + const fetchCloudBackupDetails = async () => { - const data = await RNCloudFs.listFiles<'Android'>({ - scope, - targetPath: '' - }).catch(error => { - console.error("NCloudFs.listFiles<'Android'> error:", error); + const gDrive = await buildGDrive(); - throw error; + const data: { files: FileDetails[]; incompleteSearch: boolean } = await gDrive.files.list({ + spaces: 'appDataFolder' }); return data.files?.find(file => file.name.endsWith(filename)); }; + +const buildGDrive = async () => { + const gDrive = new GDrive(); + + const accessInfo = await GoogleSignin.getTokens().catch(error => { + console.error(error); + + throw new Error('First, sign-in to Google account'); + }); + + gDrive.accessToken = accessInfo.accessToken; + + return gDrive; +}; diff --git a/src/utils/cloud-backup/index.ts b/src/utils/cloud-backup/index.ts index 569392d2f..9e885a7bf 100644 --- a/src/utils/cloud-backup/index.ts +++ b/src/utils/cloud-backup/index.ts @@ -11,6 +11,7 @@ export { keepRestoredCloudBackup, useRestoredCloudBackup } from './keeper'; export const cloudTitle = isIOS ? 'iCloud' : 'Google Drive'; export const FAILED_TO_LOGIN_ERR_TITLE = isIOS ? 'Failed to sync cloud' : 'Failed to log-in'; +/** Keep it async for possible future needs */ export const isCloudAvailable = (): Promise => isIOS ? ICloudAPI.isCloudAvailable() : GoogleDriveAPI.isCloudAvailable(); diff --git a/yarn.lock b/yarn.lock index c1ed049d1..8742bef42 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2962,6 +2962,15 @@ redux-thunk "^2.4.1" reselect "^4.1.5" +"@robinbobin/react-native-google-drive-api-wrapper@^1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@robinbobin/react-native-google-drive-api-wrapper/-/react-native-google-drive-api-wrapper-1.2.3.tgz#c4cfbfb027785589d53841ab667078f69266988c" + integrity sha512-WCbkb9zzKIR4Wg5xmX33eDGkfDH6wS1LUhnGET/FNRpJiRC+Lnofnrk7JrrmXg7cLjjJ3J/HkW15bsp2CwbjjQ== + dependencies: + base64-js "^1.5.1" + simple-common-utils "^2.3.0" + utf8 "^3.0.0" + "@sentry-internal/tracing@7.63.0": version "7.63.0" resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.63.0.tgz#58903b2205456034611cc5bc1b5b2479275f89c7" @@ -4859,7 +4868,7 @@ base16@^1.0.0: resolved "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz" integrity sha1-4pf2DX7BAUp6lxo568ipjAtoHnA= -base64-js@^1.0.2, base64-js@^1.1.2, base64-js@^1.3.1: +base64-js@^1.0.2, base64-js@^1.1.2, 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" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -10931,6 +10940,11 @@ signal-exit@^3.0.7: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +simple-common-utils@^2.3.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/simple-common-utils/-/simple-common-utils-2.6.0.tgz#457f126c20c82ba10212427886f4c8ebe86cc6d7" + integrity sha512-K0gtyfMQ1+iDQvZKgdY2L+pbaG0MHp1ke6ho7IL6UKR8f59st+QJgw27FADF+3Hut19C3WUb7IhgENdY/OJG8w== + simple-concat@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" From 4e435be024d16a4a25ff6ce6de9212ef7cab61e4 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 29 Dec 2023 01:56:57 +0200 Subject: [PATCH 2/2] TW-1208: [research] GoogleDrive for iOS. + iOS support --- ios/TempleWallet/Debug-Info.plist | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ios/TempleWallet/Debug-Info.plist b/ios/TempleWallet/Debug-Info.plist index f8e7827d3..78aa682a3 100644 --- a/ios/TempleWallet/Debug-Info.plist +++ b/ios/TempleWallet/Debug-Info.plist @@ -52,6 +52,14 @@ com.madfish.temple-wallet + + CFBundleTypeRole + Editor + CFBundleURLSchemes + + com.googleusercontent.apps.14863818751-1seoo2foi73kbog7okgo6joffpacrp8q + + CFBundleVersion $(CURRENT_PROJECT_VERSION)