@dynamic-labs/sdk-react-core 4.78.1 → 4.79.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/package.cjs +4 -4
- package/package.js +4 -4
- package/package.json +15 -15
- package/src/index.cjs +8 -0
- package/src/index.d.ts +2 -1
- package/src/index.js +3 -0
- package/src/lib/utils/hooks/index.d.ts +3 -2
- package/src/lib/utils/hooks/useGoogleDriveBackupReadiness/index.d.ts +1 -0
- package/src/lib/utils/hooks/useGoogleDriveBackupReadiness/useGoogleDriveBackupReadiness.cjs +222 -0
- package/src/lib/utils/hooks/useGoogleDriveBackupReadiness/useGoogleDriveBackupReadiness.d.ts +85 -0
- package/src/lib/utils/hooks/useGoogleDriveBackupReadiness/useGoogleDriveBackupReadiness.js +218 -0
- package/src/lib/utils/hooks/useWalletBackup/googleDriveBackupErrors.cjs +33 -0
- package/src/lib/utils/hooks/useWalletBackup/googleDriveBackupErrors.d.ts +8 -0
- package/src/lib/utils/hooks/useWalletBackup/googleDriveBackupErrors.js +29 -0
- package/src/lib/utils/hooks/useWalletBackup/googleDriveScopes.cjs +22 -0
- package/src/lib/utils/hooks/useWalletBackup/googleDriveScopes.d.ts +10 -0
- package/src/lib/utils/hooks/useWalletBackup/googleDriveScopes.js +16 -0
- package/src/lib/utils/hooks/useWalletBackup/index.d.ts +2 -0
- package/src/lib/utils/hooks/useWalletBackup/useWalletBackup.cjs +8 -0
- package/src/lib/utils/hooks/useWalletBackup/useWalletBackup.d.ts +2 -0
- package/src/lib/utils/hooks/useWalletBackup/useWalletBackup.js +9 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
|
|
2
|
+
## [4.79.0](https://github.com/dynamic-labs/dynamic-auth/compare/v4.78.1...v4.79.0) (2026-04-30)
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
* **sdk-react-core:** two-layer Google Drive backup access defense [DYNT-754] ([#11069](https://github.com/dynamic-labs/dynamic-auth/issues/11069)) ([1ff8b83](https://github.com/dynamic-labs/dynamic-auth/commit/1ff8b83f9c7ba077c586513a250f6aed114e4b07))
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Bug Fixes
|
|
11
|
+
|
|
12
|
+
* **solana-core,sui-core:** prevent RangeError on send balance ([#11071](https://github.com/dynamic-labs/dynamic-auth/issues/11071)) ([21f5f97](https://github.com/dynamic-labs/dynamic-auth/commit/21f5f9724c788053774ce5eaa19f073af9a33d27))
|
|
13
|
+
|
|
2
14
|
### [4.78.1](https://github.com/dynamic-labs/dynamic-auth/compare/v4.78.0...v4.78.1) (2026-04-29)
|
|
3
15
|
|
|
4
16
|
|
package/package.cjs
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
|
|
4
4
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
5
5
|
|
|
6
|
-
var version = "4.
|
|
6
|
+
var version = "4.79.0";
|
|
7
7
|
var dependencies = {
|
|
8
|
-
"@dynamic-labs/sdk-api-core": "0.0.
|
|
9
|
-
"@dynamic-labs-sdk/client": "0.
|
|
10
|
-
"@dynamic-labs-wallet/browser-wallet-client": "0.0.
|
|
8
|
+
"@dynamic-labs/sdk-api-core": "0.0.956",
|
|
9
|
+
"@dynamic-labs-sdk/client": "0.26.2",
|
|
10
|
+
"@dynamic-labs-wallet/browser-wallet-client": "0.0.325",
|
|
11
11
|
"@hcaptcha/react-hcaptcha": "1.4.4",
|
|
12
12
|
"@thumbmarkjs/thumbmarkjs": "0.16.0",
|
|
13
13
|
"country-list": "2.3.0",
|
package/package.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use client'
|
|
2
|
-
var version = "4.
|
|
2
|
+
var version = "4.79.0";
|
|
3
3
|
var dependencies = {
|
|
4
|
-
"@dynamic-labs/sdk-api-core": "0.0.
|
|
5
|
-
"@dynamic-labs-sdk/client": "0.
|
|
6
|
-
"@dynamic-labs-wallet/browser-wallet-client": "0.0.
|
|
4
|
+
"@dynamic-labs/sdk-api-core": "0.0.956",
|
|
5
|
+
"@dynamic-labs-sdk/client": "0.26.2",
|
|
6
|
+
"@dynamic-labs-wallet/browser-wallet-client": "0.0.325",
|
|
7
7
|
"@hcaptcha/react-hcaptcha": "1.4.4",
|
|
8
8
|
"@thumbmarkjs/thumbmarkjs": "0.16.0",
|
|
9
9
|
"country-list": "2.3.0",
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dynamic-labs/sdk-react-core",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.79.0",
|
|
4
4
|
"dependencies": {
|
|
5
|
-
"@dynamic-labs/sdk-api-core": "0.0.
|
|
6
|
-
"@dynamic-labs-sdk/client": "0.
|
|
7
|
-
"@dynamic-labs-wallet/browser-wallet-client": "0.0.
|
|
5
|
+
"@dynamic-labs/sdk-api-core": "0.0.956",
|
|
6
|
+
"@dynamic-labs-sdk/client": "0.26.2",
|
|
7
|
+
"@dynamic-labs-wallet/browser-wallet-client": "0.0.325",
|
|
8
8
|
"@hcaptcha/react-hcaptcha": "1.4.4",
|
|
9
9
|
"@thumbmarkjs/thumbmarkjs": "0.16.0",
|
|
10
10
|
"country-list": "2.3.0",
|
|
@@ -16,17 +16,17 @@
|
|
|
16
16
|
"yup": "0.32.11",
|
|
17
17
|
"react-international-phone": "4.5.0",
|
|
18
18
|
"bs58": "5.0.0",
|
|
19
|
-
"@dynamic-labs/assert-package-version": "4.
|
|
20
|
-
"@dynamic-labs/iconic": "4.
|
|
21
|
-
"@dynamic-labs/locale": "4.
|
|
22
|
-
"@dynamic-labs/logger": "4.
|
|
23
|
-
"@dynamic-labs/multi-wallet": "4.
|
|
24
|
-
"@dynamic-labs/rpc-providers": "4.
|
|
25
|
-
"@dynamic-labs/store": "4.
|
|
26
|
-
"@dynamic-labs/types": "4.
|
|
27
|
-
"@dynamic-labs/utils": "4.
|
|
28
|
-
"@dynamic-labs/wallet-book": "4.
|
|
29
|
-
"@dynamic-labs/wallet-connector-core": "4.
|
|
19
|
+
"@dynamic-labs/assert-package-version": "4.79.0",
|
|
20
|
+
"@dynamic-labs/iconic": "4.79.0",
|
|
21
|
+
"@dynamic-labs/locale": "4.79.0",
|
|
22
|
+
"@dynamic-labs/logger": "4.79.0",
|
|
23
|
+
"@dynamic-labs/multi-wallet": "4.79.0",
|
|
24
|
+
"@dynamic-labs/rpc-providers": "4.79.0",
|
|
25
|
+
"@dynamic-labs/store": "4.79.0",
|
|
26
|
+
"@dynamic-labs/types": "4.79.0",
|
|
27
|
+
"@dynamic-labs/utils": "4.79.0",
|
|
28
|
+
"@dynamic-labs/wallet-book": "4.79.0",
|
|
29
|
+
"@dynamic-labs/wallet-connector-core": "4.79.0",
|
|
30
30
|
"eventemitter3": "5.0.1"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
package/src/index.cjs
CHANGED
|
@@ -138,6 +138,8 @@ require('./lib/widgets/DynamicWidget/components/PasskeyCard/PasskeyCard.cjs');
|
|
|
138
138
|
var useEmbeddedReveal = require('./lib/utils/hooks/useEmbeddedReveal/useEmbeddedReveal.cjs');
|
|
139
139
|
var useWalletBackup = require('./lib/utils/hooks/useWalletBackup/useWalletBackup.cjs');
|
|
140
140
|
var types = require('./lib/utils/hooks/useWalletBackup/types.cjs');
|
|
141
|
+
var googleDriveScopes = require('./lib/utils/hooks/useWalletBackup/googleDriveScopes.cjs');
|
|
142
|
+
var googleDriveBackupErrors = require('./lib/utils/hooks/useWalletBackup/googleDriveBackupErrors.cjs');
|
|
141
143
|
var useEmbeddedWalletAuthenticator = require('./lib/utils/hooks/useEmbeddedWalletAuthenticator/useEmbeddedWalletAuthenticator.cjs');
|
|
142
144
|
require('./lib/utils/hooks/useWalletBackup/cloudProviders.cjs');
|
|
143
145
|
require('./lib/widgets/DynamicWidget/views/CryptoComOnramp/CryptoComOnramp.cjs');
|
|
@@ -188,6 +190,7 @@ var useGetUserMfaMethods = require('./lib/utils/hooks/useGetUserMfaMethods/useGe
|
|
|
188
190
|
var usePromptMfaAuth = require('./lib/utils/hooks/usePromptMfaAuth/usePromptMfaAuth.cjs');
|
|
189
191
|
var useUpgradeToDynamicWaasFlow = require('./lib/utils/hooks/useUpgradeToDynamicWaasFlow/useUpgradeToDynamicWaasFlow.cjs');
|
|
190
192
|
var useIsMfaRequiredForAction = require('./lib/utils/hooks/useIsMfaRequiredForAction/useIsMfaRequiredForAction.cjs');
|
|
193
|
+
var useGoogleDriveBackupReadiness = require('./lib/utils/hooks/useGoogleDriveBackupReadiness/useGoogleDriveBackupReadiness.cjs');
|
|
191
194
|
var useRefreshAuth = require('./lib/utils/hooks/useRefreshAuth/useRefreshAuth.cjs');
|
|
192
195
|
var useWalletPassword = require('./lib/utils/hooks/useWalletPassword/useWalletPassword.cjs');
|
|
193
196
|
var useGetWalletPassword = require('./lib/utils/hooks/useGetWalletPassword/useGetWalletPassword.cjs');
|
|
@@ -304,6 +307,10 @@ exports.isWalletBackedUp = useWalletBackup.isWalletBackedUp;
|
|
|
304
307
|
exports.useBackupWallets = useWalletBackup.useBackupWallets;
|
|
305
308
|
exports.useWalletBackup = useWalletBackup.useWalletBackup;
|
|
306
309
|
exports.CloudBackupProvider = types.CloudBackupProvider;
|
|
310
|
+
exports.GOOGLE_DRIVE_BACKUP_REQUIRED_SCOPES = googleDriveScopes.GOOGLE_DRIVE_BACKUP_REQUIRED_SCOPES;
|
|
311
|
+
exports.findMissingGoogleDriveBackupScopes = googleDriveScopes.findMissingGoogleDriveBackupScopes;
|
|
312
|
+
exports.hasAllGoogleDriveBackupScopes = googleDriveScopes.hasAllGoogleDriveBackupScopes;
|
|
313
|
+
exports.isInsufficientGoogleDriveScopesError = googleDriveBackupErrors.isInsufficientGoogleDriveScopesError;
|
|
307
314
|
exports.useEmbeddedWalletAuthenticator = useEmbeddedWalletAuthenticator.useEmbeddedWalletAuthenticator;
|
|
308
315
|
exports.FilterAndSortWallets = index$2.FilterAndSortWallets;
|
|
309
316
|
exports.FilterBridgeChainsName = index$2.FilterBridgeChainsName;
|
|
@@ -354,6 +361,7 @@ exports.useGetUserMfaMethods = useGetUserMfaMethods.useGetUserMfaMethods;
|
|
|
354
361
|
exports.usePromptMfaAuth = usePromptMfaAuth.usePromptMfaAuth;
|
|
355
362
|
exports.useUpgradeToDynamicWaasFlow = useUpgradeToDynamicWaasFlow.useUpgradeToDynamicWaasFlow;
|
|
356
363
|
exports.useIsMfaRequiredForAction = useIsMfaRequiredForAction.useIsMfaRequiredForAction;
|
|
364
|
+
exports.useGoogleDriveBackupReadiness = useGoogleDriveBackupReadiness.useGoogleDriveBackupReadiness;
|
|
357
365
|
exports.useRefreshAuth = useRefreshAuth.useRefreshAuth;
|
|
358
366
|
exports.useWalletPassword = useWalletPassword.useWalletPassword;
|
|
359
367
|
exports.useGetWalletPassword = useGetWalletPassword.useGetWalletPassword;
|
package/src/index.d.ts
CHANGED
|
@@ -123,7 +123,8 @@ export { FilterAndSortWallets, FilterBridgeChainsName, FilterChain, FilterWallet
|
|
|
123
123
|
export {
|
|
124
124
|
/** @deprecated */
|
|
125
125
|
DynamicWidgetContextProvider, } from './lib/widgets/DynamicWidget/context';
|
|
126
|
-
export { useWalletItemActions, useAuthenticateConnectedUser, useSocialAccounts, useEmbeddedWallet, useEmbeddedWalletAuthenticator, usePasskeyRecovery, useEmbeddedReveal, useIsLoggedIn, useDynamicModals, useMfa, useTokenBalances, useMultichainTokenBalances, useSwitchWallet, useRpcProviders, useRefreshUser, useRefreshAuth, useResetWaasSession, useWalletOptions, useSmartWallets, useSignEip7702Authorization, EmbeddedWalletVersion, useTelegramLogin, useUpgradeEmbeddedWallet, useEVMTransactionSimulation, useSVMTransactionSimulation, useDeleteUserAccount, useDynamicWaas, useGetPasskeys, useDeletePasskey, useRegisterPasskey, useAuthenticatePasskeyMFA, useGetUserMfaMethods, usePromptMfaAuth, useUpgradeToDynamicWaasFlow, useGetMfaToken, useGetWalletPassword, useWalletPassword, useIsMfaRequiredForAction, useWalletDelegation, useWalletBackup, useBackupWallets, isWalletBackedUp, CloudBackupProvider, useExchangeAccounts, useStepUpAuthentication, } from './lib/utils/hooks';
|
|
126
|
+
export { useWalletItemActions, useAuthenticateConnectedUser, useSocialAccounts, useEmbeddedWallet, useEmbeddedWalletAuthenticator, usePasskeyRecovery, useEmbeddedReveal, useIsLoggedIn, useDynamicModals, useMfa, useTokenBalances, useMultichainTokenBalances, useSwitchWallet, useRpcProviders, useRefreshUser, useRefreshAuth, useResetWaasSession, useWalletOptions, useSmartWallets, useSignEip7702Authorization, EmbeddedWalletVersion, useTelegramLogin, useUpgradeEmbeddedWallet, useEVMTransactionSimulation, useSVMTransactionSimulation, useDeleteUserAccount, useDynamicWaas, useGetPasskeys, useDeletePasskey, useRegisterPasskey, useAuthenticatePasskeyMFA, useGetUserMfaMethods, usePromptMfaAuth, useUpgradeToDynamicWaasFlow, useGetMfaToken, useGetWalletPassword, useWalletPassword, useIsMfaRequiredForAction, useWalletDelegation, useWalletBackup, useBackupWallets, isWalletBackedUp, CloudBackupProvider, GOOGLE_DRIVE_BACKUP_REQUIRED_SCOPES, findMissingGoogleDriveBackupScopes, hasAllGoogleDriveBackupScopes, isInsufficientGoogleDriveScopesError, useGoogleDriveBackupReadiness, useExchangeAccounts, useStepUpAuthentication, } from './lib/utils/hooks';
|
|
127
|
+
export type { GoogleDriveBackupAccessError, GoogleDriveBackupReadiness, GoogleDriveBackupReadinessStatus, UseGoogleDriveBackupReadinessReturn, } from './lib/utils/hooks';
|
|
127
128
|
export type { IsStepUpRequiredParams, PromptMfaParams, StepUpAuthenticationState, UseStepUpAuthenticationParams, UseStepUpAuthenticationReturn, VerifyOtpParams, VerifyPasskeyMfaParams, VerifyRecoveryCodeParams, VerifySocialParams, VerifyTotpMfaParams, VerifyWalletParams, } from './lib/utils/hooks';
|
|
128
129
|
export {
|
|
129
130
|
/** @deprecated use useOnramp instead */
|
package/src/index.js
CHANGED
|
@@ -134,6 +134,8 @@ import './lib/widgets/DynamicWidget/components/PasskeyCard/PasskeyCard.js';
|
|
|
134
134
|
export { useEmbeddedReveal } from './lib/utils/hooks/useEmbeddedReveal/useEmbeddedReveal.js';
|
|
135
135
|
export { isWalletBackedUp, useBackupWallets, useWalletBackup } from './lib/utils/hooks/useWalletBackup/useWalletBackup.js';
|
|
136
136
|
export { CloudBackupProvider } from './lib/utils/hooks/useWalletBackup/types.js';
|
|
137
|
+
export { GOOGLE_DRIVE_BACKUP_REQUIRED_SCOPES, findMissingGoogleDriveBackupScopes, hasAllGoogleDriveBackupScopes } from './lib/utils/hooks/useWalletBackup/googleDriveScopes.js';
|
|
138
|
+
export { isInsufficientGoogleDriveScopesError } from './lib/utils/hooks/useWalletBackup/googleDriveBackupErrors.js';
|
|
137
139
|
export { useEmbeddedWalletAuthenticator } from './lib/utils/hooks/useEmbeddedWalletAuthenticator/useEmbeddedWalletAuthenticator.js';
|
|
138
140
|
import './lib/utils/hooks/useWalletBackup/cloudProviders.js';
|
|
139
141
|
import './lib/widgets/DynamicWidget/views/CryptoComOnramp/CryptoComOnramp.js';
|
|
@@ -184,6 +186,7 @@ export { useGetUserMfaMethods } from './lib/utils/hooks/useGetUserMfaMethods/use
|
|
|
184
186
|
export { usePromptMfaAuth } from './lib/utils/hooks/usePromptMfaAuth/usePromptMfaAuth.js';
|
|
185
187
|
export { useUpgradeToDynamicWaasFlow } from './lib/utils/hooks/useUpgradeToDynamicWaasFlow/useUpgradeToDynamicWaasFlow.js';
|
|
186
188
|
export { useIsMfaRequiredForAction } from './lib/utils/hooks/useIsMfaRequiredForAction/useIsMfaRequiredForAction.js';
|
|
189
|
+
export { useGoogleDriveBackupReadiness } from './lib/utils/hooks/useGoogleDriveBackupReadiness/useGoogleDriveBackupReadiness.js';
|
|
187
190
|
export { useRefreshAuth } from './lib/utils/hooks/useRefreshAuth/useRefreshAuth.js';
|
|
188
191
|
export { useWalletPassword } from './lib/utils/hooks/useWalletPassword/useWalletPassword.js';
|
|
189
192
|
export { useGetWalletPassword } from './lib/utils/hooks/useGetWalletPassword/useGetWalletPassword.js';
|
|
@@ -98,8 +98,9 @@ export { useUpgradeToDynamicWaasFlow } from './useUpgradeToDynamicWaasFlow';
|
|
|
98
98
|
export { useGetMfaToken } from './useGetMfaToken';
|
|
99
99
|
export { useIsMfaRequiredForAction } from './useIsMfaRequiredForAction';
|
|
100
100
|
export { useWalletDelegation } from './useWalletDelegation';
|
|
101
|
-
export { CloudBackupProvider, isWalletBackedUp, useBackupWallets, useWalletBackup, } from './useWalletBackup';
|
|
102
|
-
export type { WalletBackupStatus, WalletOperationState, WalletToBackup, WalletWithBackupStatus, } from './useWalletBackup';
|
|
101
|
+
export { CloudBackupProvider, GOOGLE_DRIVE_BACKUP_REQUIRED_SCOPES, findMissingGoogleDriveBackupScopes, hasAllGoogleDriveBackupScopes, isInsufficientGoogleDriveScopesError, isWalletBackedUp, useBackupWallets, useWalletBackup, } from './useWalletBackup';
|
|
102
|
+
export type { GoogleDriveBackupAccessError, WalletBackupStatus, WalletOperationState, WalletToBackup, WalletWithBackupStatus, } from './useWalletBackup';
|
|
103
|
+
export { useGoogleDriveBackupReadiness, type GoogleDriveBackupReadiness, type GoogleDriveBackupReadinessStatus, type UseGoogleDriveBackupReadinessReturn, } from './useGoogleDriveBackupReadiness';
|
|
103
104
|
export { useRefreshAuth } from './useRefreshAuth';
|
|
104
105
|
export { useSetupPassword, PASSWORD_SETUP_CANCELLED_ERROR, } from './useSetupPassword';
|
|
105
106
|
export { useWalletUnlock } from './useWalletUnlock';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useGoogleDriveBackupReadiness, type GoogleDriveBackupReadiness, type GoogleDriveBackupReadinessStatus, type UseGoogleDriveBackupReadinessReturn, } from './useGoogleDriveBackupReadiness';
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
5
|
+
|
|
6
|
+
var _tslib = require('../../../../../_virtual/_tslib.cjs');
|
|
7
|
+
var React = require('react');
|
|
8
|
+
var sdkApiCore = require('@dynamic-labs/sdk-api-core');
|
|
9
|
+
require('@dynamic-labs-sdk/client/core');
|
|
10
|
+
require('eventemitter3');
|
|
11
|
+
require('@dynamic-labs/utils');
|
|
12
|
+
require('@dynamic-labs-sdk/client');
|
|
13
|
+
require('../../../config/ApiEndpoint.cjs');
|
|
14
|
+
require('@dynamic-labs/iconic');
|
|
15
|
+
require('@dynamic-labs/wallet-connector-core');
|
|
16
|
+
require('react/jsx-runtime');
|
|
17
|
+
require('../../../context/ViewContext/ViewContext.cjs');
|
|
18
|
+
require('../../../shared/logger.cjs');
|
|
19
|
+
require('@dynamic-labs/wallet-book');
|
|
20
|
+
require('../../constants/colors.cjs');
|
|
21
|
+
require('../../constants/values.cjs');
|
|
22
|
+
require('../../../shared/consts/index.cjs');
|
|
23
|
+
require('@dynamic-labs/multi-wallet');
|
|
24
|
+
require('react-international-phone');
|
|
25
|
+
require('../../../store/state/nonce/nonce.cjs');
|
|
26
|
+
var api = require('../../../data/api/api.cjs');
|
|
27
|
+
require('@dynamic-labs/locale');
|
|
28
|
+
var dynamicContextProps = require('../../../store/state/dynamicContextProps/dynamicContextProps.cjs');
|
|
29
|
+
require('../../../store/state/primaryWalletId/primaryWalletId.cjs');
|
|
30
|
+
require('../../../store/state/connectedWalletsInfo/connectedWalletsInfo.cjs');
|
|
31
|
+
require('../../functions/getWaasAddressTypeLabel/getWaasAddressTypeLabel.cjs');
|
|
32
|
+
require('../../../events/dynamicEvents.cjs');
|
|
33
|
+
var useUser = require('../../../client/extension/user/useUser/useUser.cjs');
|
|
34
|
+
var useRefreshAuth = require('../useRefreshAuth/useRefreshAuth.cjs');
|
|
35
|
+
var useSocialAccounts = require('../useSocialAccounts/useSocialAccounts.cjs');
|
|
36
|
+
var googleDriveScopes = require('../useWalletBackup/googleDriveScopes.cjs');
|
|
37
|
+
|
|
38
|
+
const IDLE = {
|
|
39
|
+
isChecking: false,
|
|
40
|
+
missingScopes: [],
|
|
41
|
+
status: 'idle',
|
|
42
|
+
};
|
|
43
|
+
// Returns the UUID of the user_oauth_accounts row backing the linked Google
|
|
44
|
+
// credential. The path param of /sdk/.../oauthAccounts/:id/accessToken expects
|
|
45
|
+
// this UUID — *not* the third-party `oauthAccountId` (Google's numeric profile
|
|
46
|
+
// ID), which is what JwtVerifiedCredential.oauthAccountId stores.
|
|
47
|
+
const findGoogleOauthAccountId = (credentials) => {
|
|
48
|
+
var _a, _b;
|
|
49
|
+
return (_b = (_a = credentials === null || credentials === void 0 ? void 0 : credentials.find((cred) => cred.format === 'oauth' && cred.oauthProvider === sdkApiCore.ProviderEnum.Google)) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : undefined;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Pre-flight readiness check for backing up MPC keyshares to Google Drive.
|
|
53
|
+
*
|
|
54
|
+
* Recommended pattern: call `check()` when the user opens the "Backup to
|
|
55
|
+
* Google Drive" CTA. If `status` is `'needs-access'`, prompt the user and
|
|
56
|
+
* call `requestAccess()` to re-prompt the Google consent screen. Then
|
|
57
|
+
* proceed with `useWalletBackup().backupWallet(...)`.
|
|
58
|
+
*
|
|
59
|
+
* Status values:
|
|
60
|
+
* - `'idle'`: never checked / after `reset()`.
|
|
61
|
+
* - `'ready'`: stored scopes include both required Drive scopes; backup
|
|
62
|
+
* should succeed (barring transient errors).
|
|
63
|
+
* - `'needs-access'`: backup likely won't succeed without user intervention.
|
|
64
|
+
* Covers (a) missing scopes — `missingScopes` lists the gap, (b) no Google
|
|
65
|
+
* account linked — `missingScopes` is populated with the full required set
|
|
66
|
+
* so callers can render copy directly, and (c) legacy tokens captured
|
|
67
|
+
* before scope tracking shipped (`scopes: []` from the API) — indicated
|
|
68
|
+
* by an empty `missingScopes` array. Call `requestAccess()` to recover.
|
|
69
|
+
* - `'error'`: the underlying fetch or relink rejected. `error` holds
|
|
70
|
+
* the cause.
|
|
71
|
+
*
|
|
72
|
+
* `isChecking` is true while a `check()` or `requestAccess()` is in flight.
|
|
73
|
+
*
|
|
74
|
+
* `missingScopes` lists the scopes the user still needs to grant. When no
|
|
75
|
+
* Google account is linked, this is the full required set; when scopes are
|
|
76
|
+
* known to be partially missing, only the missing ones; for legacy
|
|
77
|
+
* "unknown scopes" tokens, the array is empty.
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```tsx
|
|
81
|
+
* function BackupButton({ wallet }) {
|
|
82
|
+
* const { status, missingScopes, isChecking, check, requestAccess } =
|
|
83
|
+
* useGoogleDriveBackupReadiness();
|
|
84
|
+
* const { backupWallet, lastBackupError, clearBackupError } = useWalletBackup();
|
|
85
|
+
*
|
|
86
|
+
* // Layer 2: post-flight fallback — needed for legacy users where the stored
|
|
87
|
+
* // `scopes` is empty so pre-flight returned 'unknown'. Because `lastBackupError`
|
|
88
|
+
* // is React state it won't update until the next render; read it in a
|
|
89
|
+
* // useEffect rather than synchronously after `await backupWallet(...)`.
|
|
90
|
+
* useEffect(() => {
|
|
91
|
+
* if (lastBackupError && isInsufficientGoogleDriveScopesError(lastBackupError)) {
|
|
92
|
+
* clearBackupError();
|
|
93
|
+
* requestAccess(); // re-prompt consent, then caller can retry backupWallet
|
|
94
|
+
* }
|
|
95
|
+
* }, [lastBackupError]);
|
|
96
|
+
*
|
|
97
|
+
* const onBackup = async () => {
|
|
98
|
+
* // Layer 1: pre-flight readiness — saves an MPC reshare ceremony when
|
|
99
|
+
* // we can already tell the upload will fail.
|
|
100
|
+
* let r = await check();
|
|
101
|
+
* if (r.status === 'needs-access') {
|
|
102
|
+
* r = await requestAccess(); // popup re-consent
|
|
103
|
+
* if (r.status !== 'ready') return; // user cancelled or relink failed
|
|
104
|
+
* }
|
|
105
|
+
*
|
|
106
|
+
* await backupWallet(wallet, 'GoogleDrive');
|
|
107
|
+
* // If the backup failed due to missing scopes, the useEffect above will
|
|
108
|
+
* // fire on the next render with the updated lastBackupError.
|
|
109
|
+
* };
|
|
110
|
+
*
|
|
111
|
+
* return (
|
|
112
|
+
* <>
|
|
113
|
+
* {status === 'needs-access' && (
|
|
114
|
+
* <p>We need access to: {missingScopes.join(', ')}</p>
|
|
115
|
+
* )}
|
|
116
|
+
* <button onClick={onBackup} disabled={isChecking}>Backup to Drive</button>
|
|
117
|
+
* </>
|
|
118
|
+
* );
|
|
119
|
+
* }
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
const useGoogleDriveBackupReadiness = () => {
|
|
123
|
+
const user = useUser.useUser();
|
|
124
|
+
const environmentId = dynamicContextProps.useEnvironmentId();
|
|
125
|
+
const refresh = useRefreshAuth.useRefreshAuth();
|
|
126
|
+
const { linkSocialAccount } = useSocialAccounts.useSocialAccounts();
|
|
127
|
+
const [readiness, setReadiness] = React.useState(IDLE);
|
|
128
|
+
const performCheck = React.useCallback((verifiedCredentials) => _tslib.__awaiter(void 0, void 0, void 0, function* () {
|
|
129
|
+
const oauthAccountId = findGoogleOauthAccountId(verifiedCredentials);
|
|
130
|
+
if (!oauthAccountId) {
|
|
131
|
+
// No Google credential linked — every required scope is effectively
|
|
132
|
+
// missing, so populate the full set so callers can render
|
|
133
|
+
// "we need access to ..." UX without checking the empty case.
|
|
134
|
+
const result = {
|
|
135
|
+
isChecking: false,
|
|
136
|
+
missingScopes: googleDriveScopes.GOOGLE_DRIVE_BACKUP_REQUIRED_SCOPES,
|
|
137
|
+
status: 'needs-access',
|
|
138
|
+
};
|
|
139
|
+
setReadiness(result);
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
const { accessToken, scopes } = yield api.sdkApi().getEndUserOauthAccessToken({
|
|
144
|
+
environmentId,
|
|
145
|
+
oauthAccountId,
|
|
146
|
+
});
|
|
147
|
+
// Empty scopes = legacy token captured before scope tracking; we
|
|
148
|
+
// can't confirm the upload will work, so surface as needs-access
|
|
149
|
+
// (with empty missingScopes to differentiate from the explicit
|
|
150
|
+
// missing case). requestAccess() forces a fresh token so the
|
|
151
|
+
// column populates.
|
|
152
|
+
if (scopes.length === 0) {
|
|
153
|
+
const result = {
|
|
154
|
+
accessToken,
|
|
155
|
+
isChecking: false,
|
|
156
|
+
missingScopes: [],
|
|
157
|
+
status: 'needs-access',
|
|
158
|
+
};
|
|
159
|
+
setReadiness(result);
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
162
|
+
const missing = googleDriveScopes.findMissingGoogleDriveBackupScopes(scopes);
|
|
163
|
+
const result = missing.length === 0
|
|
164
|
+
? {
|
|
165
|
+
accessToken,
|
|
166
|
+
isChecking: false,
|
|
167
|
+
missingScopes: [],
|
|
168
|
+
status: 'ready',
|
|
169
|
+
}
|
|
170
|
+
: {
|
|
171
|
+
accessToken,
|
|
172
|
+
isChecking: false,
|
|
173
|
+
missingScopes: missing,
|
|
174
|
+
status: 'needs-access',
|
|
175
|
+
};
|
|
176
|
+
setReadiness(result);
|
|
177
|
+
return result;
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
const result = {
|
|
181
|
+
error,
|
|
182
|
+
isChecking: false,
|
|
183
|
+
missingScopes: [],
|
|
184
|
+
status: 'error',
|
|
185
|
+
};
|
|
186
|
+
setReadiness(result);
|
|
187
|
+
return result;
|
|
188
|
+
}
|
|
189
|
+
}), [environmentId]);
|
|
190
|
+
const check = React.useCallback(() => _tslib.__awaiter(void 0, void 0, void 0, function* () {
|
|
191
|
+
setReadiness((prev) => (Object.assign(Object.assign({}, prev), { isChecking: true })));
|
|
192
|
+
return performCheck(user === null || user === void 0 ? void 0 : user.verifiedCredentials);
|
|
193
|
+
}), [performCheck, user]);
|
|
194
|
+
const requestAccess = React.useCallback(() => _tslib.__awaiter(void 0, void 0, void 0, function* () {
|
|
195
|
+
var _a;
|
|
196
|
+
setReadiness((prev) => (Object.assign(Object.assign({}, prev), { isChecking: true })));
|
|
197
|
+
try {
|
|
198
|
+
yield linkSocialAccount(sdkApiCore.ProviderEnum.Google, {
|
|
199
|
+
forcePopup: true,
|
|
200
|
+
showWidgetAfterConnection: false,
|
|
201
|
+
});
|
|
202
|
+
const updatedUser = yield refresh();
|
|
203
|
+
return performCheck((_a = updatedUser === null || updatedUser === void 0 ? void 0 : updatedUser.verifiedCredentials) !== null && _a !== void 0 ? _a : user === null || user === void 0 ? void 0 : user.verifiedCredentials);
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
const result = {
|
|
207
|
+
error,
|
|
208
|
+
isChecking: false,
|
|
209
|
+
missingScopes: [],
|
|
210
|
+
status: 'error',
|
|
211
|
+
};
|
|
212
|
+
setReadiness(result);
|
|
213
|
+
return result;
|
|
214
|
+
}
|
|
215
|
+
}), [linkSocialAccount, performCheck, refresh, user]);
|
|
216
|
+
const reset = React.useCallback(() => setReadiness(IDLE), []);
|
|
217
|
+
return Object.assign(Object.assign({}, readiness), { check,
|
|
218
|
+
requestAccess,
|
|
219
|
+
reset });
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
exports.useGoogleDriveBackupReadiness = useGoogleDriveBackupReadiness;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export type GoogleDriveBackupReadinessStatus = 'idle' | 'ready' | 'needs-access' | 'error';
|
|
2
|
+
export type GoogleDriveBackupReadiness = {
|
|
3
|
+
status: GoogleDriveBackupReadinessStatus;
|
|
4
|
+
isChecking: boolean;
|
|
5
|
+
missingScopes: readonly string[];
|
|
6
|
+
accessToken?: string;
|
|
7
|
+
error?: unknown;
|
|
8
|
+
};
|
|
9
|
+
export type UseGoogleDriveBackupReadinessReturn = GoogleDriveBackupReadiness & {
|
|
10
|
+
check: () => Promise<GoogleDriveBackupReadiness>;
|
|
11
|
+
requestAccess: () => Promise<GoogleDriveBackupReadiness>;
|
|
12
|
+
reset: () => void;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Pre-flight readiness check for backing up MPC keyshares to Google Drive.
|
|
16
|
+
*
|
|
17
|
+
* Recommended pattern: call `check()` when the user opens the "Backup to
|
|
18
|
+
* Google Drive" CTA. If `status` is `'needs-access'`, prompt the user and
|
|
19
|
+
* call `requestAccess()` to re-prompt the Google consent screen. Then
|
|
20
|
+
* proceed with `useWalletBackup().backupWallet(...)`.
|
|
21
|
+
*
|
|
22
|
+
* Status values:
|
|
23
|
+
* - `'idle'`: never checked / after `reset()`.
|
|
24
|
+
* - `'ready'`: stored scopes include both required Drive scopes; backup
|
|
25
|
+
* should succeed (barring transient errors).
|
|
26
|
+
* - `'needs-access'`: backup likely won't succeed without user intervention.
|
|
27
|
+
* Covers (a) missing scopes — `missingScopes` lists the gap, (b) no Google
|
|
28
|
+
* account linked — `missingScopes` is populated with the full required set
|
|
29
|
+
* so callers can render copy directly, and (c) legacy tokens captured
|
|
30
|
+
* before scope tracking shipped (`scopes: []` from the API) — indicated
|
|
31
|
+
* by an empty `missingScopes` array. Call `requestAccess()` to recover.
|
|
32
|
+
* - `'error'`: the underlying fetch or relink rejected. `error` holds
|
|
33
|
+
* the cause.
|
|
34
|
+
*
|
|
35
|
+
* `isChecking` is true while a `check()` or `requestAccess()` is in flight.
|
|
36
|
+
*
|
|
37
|
+
* `missingScopes` lists the scopes the user still needs to grant. When no
|
|
38
|
+
* Google account is linked, this is the full required set; when scopes are
|
|
39
|
+
* known to be partially missing, only the missing ones; for legacy
|
|
40
|
+
* "unknown scopes" tokens, the array is empty.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```tsx
|
|
44
|
+
* function BackupButton({ wallet }) {
|
|
45
|
+
* const { status, missingScopes, isChecking, check, requestAccess } =
|
|
46
|
+
* useGoogleDriveBackupReadiness();
|
|
47
|
+
* const { backupWallet, lastBackupError, clearBackupError } = useWalletBackup();
|
|
48
|
+
*
|
|
49
|
+
* // Layer 2: post-flight fallback — needed for legacy users where the stored
|
|
50
|
+
* // `scopes` is empty so pre-flight returned 'unknown'. Because `lastBackupError`
|
|
51
|
+
* // is React state it won't update until the next render; read it in a
|
|
52
|
+
* // useEffect rather than synchronously after `await backupWallet(...)`.
|
|
53
|
+
* useEffect(() => {
|
|
54
|
+
* if (lastBackupError && isInsufficientGoogleDriveScopesError(lastBackupError)) {
|
|
55
|
+
* clearBackupError();
|
|
56
|
+
* requestAccess(); // re-prompt consent, then caller can retry backupWallet
|
|
57
|
+
* }
|
|
58
|
+
* }, [lastBackupError]);
|
|
59
|
+
*
|
|
60
|
+
* const onBackup = async () => {
|
|
61
|
+
* // Layer 1: pre-flight readiness — saves an MPC reshare ceremony when
|
|
62
|
+
* // we can already tell the upload will fail.
|
|
63
|
+
* let r = await check();
|
|
64
|
+
* if (r.status === 'needs-access') {
|
|
65
|
+
* r = await requestAccess(); // popup re-consent
|
|
66
|
+
* if (r.status !== 'ready') return; // user cancelled or relink failed
|
|
67
|
+
* }
|
|
68
|
+
*
|
|
69
|
+
* await backupWallet(wallet, 'GoogleDrive');
|
|
70
|
+
* // If the backup failed due to missing scopes, the useEffect above will
|
|
71
|
+
* // fire on the next render with the updated lastBackupError.
|
|
72
|
+
* };
|
|
73
|
+
*
|
|
74
|
+
* return (
|
|
75
|
+
* <>
|
|
76
|
+
* {status === 'needs-access' && (
|
|
77
|
+
* <p>We need access to: {missingScopes.join(', ')}</p>
|
|
78
|
+
* )}
|
|
79
|
+
* <button onClick={onBackup} disabled={isChecking}>Backup to Drive</button>
|
|
80
|
+
* </>
|
|
81
|
+
* );
|
|
82
|
+
* }
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export declare const useGoogleDriveBackupReadiness: () => UseGoogleDriveBackupReadinessReturn;
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import { __awaiter } from '../../../../../_virtual/_tslib.js';
|
|
3
|
+
import { useState, useCallback } from 'react';
|
|
4
|
+
import { ProviderEnum } from '@dynamic-labs/sdk-api-core';
|
|
5
|
+
import '@dynamic-labs-sdk/client/core';
|
|
6
|
+
import 'eventemitter3';
|
|
7
|
+
import '@dynamic-labs/utils';
|
|
8
|
+
import '@dynamic-labs-sdk/client';
|
|
9
|
+
import '../../../config/ApiEndpoint.js';
|
|
10
|
+
import '@dynamic-labs/iconic';
|
|
11
|
+
import '@dynamic-labs/wallet-connector-core';
|
|
12
|
+
import 'react/jsx-runtime';
|
|
13
|
+
import '../../../context/ViewContext/ViewContext.js';
|
|
14
|
+
import '../../../shared/logger.js';
|
|
15
|
+
import '@dynamic-labs/wallet-book';
|
|
16
|
+
import '../../constants/colors.js';
|
|
17
|
+
import '../../constants/values.js';
|
|
18
|
+
import '../../../shared/consts/index.js';
|
|
19
|
+
import '@dynamic-labs/multi-wallet';
|
|
20
|
+
import 'react-international-phone';
|
|
21
|
+
import '../../../store/state/nonce/nonce.js';
|
|
22
|
+
import { sdkApi } from '../../../data/api/api.js';
|
|
23
|
+
import '@dynamic-labs/locale';
|
|
24
|
+
import { useEnvironmentId } from '../../../store/state/dynamicContextProps/dynamicContextProps.js';
|
|
25
|
+
import '../../../store/state/primaryWalletId/primaryWalletId.js';
|
|
26
|
+
import '../../../store/state/connectedWalletsInfo/connectedWalletsInfo.js';
|
|
27
|
+
import '../../functions/getWaasAddressTypeLabel/getWaasAddressTypeLabel.js';
|
|
28
|
+
import '../../../events/dynamicEvents.js';
|
|
29
|
+
import { useUser } from '../../../client/extension/user/useUser/useUser.js';
|
|
30
|
+
import { useRefreshAuth } from '../useRefreshAuth/useRefreshAuth.js';
|
|
31
|
+
import { useSocialAccounts } from '../useSocialAccounts/useSocialAccounts.js';
|
|
32
|
+
import { findMissingGoogleDriveBackupScopes, GOOGLE_DRIVE_BACKUP_REQUIRED_SCOPES } from '../useWalletBackup/googleDriveScopes.js';
|
|
33
|
+
|
|
34
|
+
const IDLE = {
|
|
35
|
+
isChecking: false,
|
|
36
|
+
missingScopes: [],
|
|
37
|
+
status: 'idle',
|
|
38
|
+
};
|
|
39
|
+
// Returns the UUID of the user_oauth_accounts row backing the linked Google
|
|
40
|
+
// credential. The path param of /sdk/.../oauthAccounts/:id/accessToken expects
|
|
41
|
+
// this UUID — *not* the third-party `oauthAccountId` (Google's numeric profile
|
|
42
|
+
// ID), which is what JwtVerifiedCredential.oauthAccountId stores.
|
|
43
|
+
const findGoogleOauthAccountId = (credentials) => {
|
|
44
|
+
var _a, _b;
|
|
45
|
+
return (_b = (_a = credentials === null || credentials === void 0 ? void 0 : credentials.find((cred) => cred.format === 'oauth' && cred.oauthProvider === ProviderEnum.Google)) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : undefined;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Pre-flight readiness check for backing up MPC keyshares to Google Drive.
|
|
49
|
+
*
|
|
50
|
+
* Recommended pattern: call `check()` when the user opens the "Backup to
|
|
51
|
+
* Google Drive" CTA. If `status` is `'needs-access'`, prompt the user and
|
|
52
|
+
* call `requestAccess()` to re-prompt the Google consent screen. Then
|
|
53
|
+
* proceed with `useWalletBackup().backupWallet(...)`.
|
|
54
|
+
*
|
|
55
|
+
* Status values:
|
|
56
|
+
* - `'idle'`: never checked / after `reset()`.
|
|
57
|
+
* - `'ready'`: stored scopes include both required Drive scopes; backup
|
|
58
|
+
* should succeed (barring transient errors).
|
|
59
|
+
* - `'needs-access'`: backup likely won't succeed without user intervention.
|
|
60
|
+
* Covers (a) missing scopes — `missingScopes` lists the gap, (b) no Google
|
|
61
|
+
* account linked — `missingScopes` is populated with the full required set
|
|
62
|
+
* so callers can render copy directly, and (c) legacy tokens captured
|
|
63
|
+
* before scope tracking shipped (`scopes: []` from the API) — indicated
|
|
64
|
+
* by an empty `missingScopes` array. Call `requestAccess()` to recover.
|
|
65
|
+
* - `'error'`: the underlying fetch or relink rejected. `error` holds
|
|
66
|
+
* the cause.
|
|
67
|
+
*
|
|
68
|
+
* `isChecking` is true while a `check()` or `requestAccess()` is in flight.
|
|
69
|
+
*
|
|
70
|
+
* `missingScopes` lists the scopes the user still needs to grant. When no
|
|
71
|
+
* Google account is linked, this is the full required set; when scopes are
|
|
72
|
+
* known to be partially missing, only the missing ones; for legacy
|
|
73
|
+
* "unknown scopes" tokens, the array is empty.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```tsx
|
|
77
|
+
* function BackupButton({ wallet }) {
|
|
78
|
+
* const { status, missingScopes, isChecking, check, requestAccess } =
|
|
79
|
+
* useGoogleDriveBackupReadiness();
|
|
80
|
+
* const { backupWallet, lastBackupError, clearBackupError } = useWalletBackup();
|
|
81
|
+
*
|
|
82
|
+
* // Layer 2: post-flight fallback — needed for legacy users where the stored
|
|
83
|
+
* // `scopes` is empty so pre-flight returned 'unknown'. Because `lastBackupError`
|
|
84
|
+
* // is React state it won't update until the next render; read it in a
|
|
85
|
+
* // useEffect rather than synchronously after `await backupWallet(...)`.
|
|
86
|
+
* useEffect(() => {
|
|
87
|
+
* if (lastBackupError && isInsufficientGoogleDriveScopesError(lastBackupError)) {
|
|
88
|
+
* clearBackupError();
|
|
89
|
+
* requestAccess(); // re-prompt consent, then caller can retry backupWallet
|
|
90
|
+
* }
|
|
91
|
+
* }, [lastBackupError]);
|
|
92
|
+
*
|
|
93
|
+
* const onBackup = async () => {
|
|
94
|
+
* // Layer 1: pre-flight readiness — saves an MPC reshare ceremony when
|
|
95
|
+
* // we can already tell the upload will fail.
|
|
96
|
+
* let r = await check();
|
|
97
|
+
* if (r.status === 'needs-access') {
|
|
98
|
+
* r = await requestAccess(); // popup re-consent
|
|
99
|
+
* if (r.status !== 'ready') return; // user cancelled or relink failed
|
|
100
|
+
* }
|
|
101
|
+
*
|
|
102
|
+
* await backupWallet(wallet, 'GoogleDrive');
|
|
103
|
+
* // If the backup failed due to missing scopes, the useEffect above will
|
|
104
|
+
* // fire on the next render with the updated lastBackupError.
|
|
105
|
+
* };
|
|
106
|
+
*
|
|
107
|
+
* return (
|
|
108
|
+
* <>
|
|
109
|
+
* {status === 'needs-access' && (
|
|
110
|
+
* <p>We need access to: {missingScopes.join(', ')}</p>
|
|
111
|
+
* )}
|
|
112
|
+
* <button onClick={onBackup} disabled={isChecking}>Backup to Drive</button>
|
|
113
|
+
* </>
|
|
114
|
+
* );
|
|
115
|
+
* }
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
const useGoogleDriveBackupReadiness = () => {
|
|
119
|
+
const user = useUser();
|
|
120
|
+
const environmentId = useEnvironmentId();
|
|
121
|
+
const refresh = useRefreshAuth();
|
|
122
|
+
const { linkSocialAccount } = useSocialAccounts();
|
|
123
|
+
const [readiness, setReadiness] = useState(IDLE);
|
|
124
|
+
const performCheck = useCallback((verifiedCredentials) => __awaiter(void 0, void 0, void 0, function* () {
|
|
125
|
+
const oauthAccountId = findGoogleOauthAccountId(verifiedCredentials);
|
|
126
|
+
if (!oauthAccountId) {
|
|
127
|
+
// No Google credential linked — every required scope is effectively
|
|
128
|
+
// missing, so populate the full set so callers can render
|
|
129
|
+
// "we need access to ..." UX without checking the empty case.
|
|
130
|
+
const result = {
|
|
131
|
+
isChecking: false,
|
|
132
|
+
missingScopes: GOOGLE_DRIVE_BACKUP_REQUIRED_SCOPES,
|
|
133
|
+
status: 'needs-access',
|
|
134
|
+
};
|
|
135
|
+
setReadiness(result);
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
const { accessToken, scopes } = yield sdkApi().getEndUserOauthAccessToken({
|
|
140
|
+
environmentId,
|
|
141
|
+
oauthAccountId,
|
|
142
|
+
});
|
|
143
|
+
// Empty scopes = legacy token captured before scope tracking; we
|
|
144
|
+
// can't confirm the upload will work, so surface as needs-access
|
|
145
|
+
// (with empty missingScopes to differentiate from the explicit
|
|
146
|
+
// missing case). requestAccess() forces a fresh token so the
|
|
147
|
+
// column populates.
|
|
148
|
+
if (scopes.length === 0) {
|
|
149
|
+
const result = {
|
|
150
|
+
accessToken,
|
|
151
|
+
isChecking: false,
|
|
152
|
+
missingScopes: [],
|
|
153
|
+
status: 'needs-access',
|
|
154
|
+
};
|
|
155
|
+
setReadiness(result);
|
|
156
|
+
return result;
|
|
157
|
+
}
|
|
158
|
+
const missing = findMissingGoogleDriveBackupScopes(scopes);
|
|
159
|
+
const result = missing.length === 0
|
|
160
|
+
? {
|
|
161
|
+
accessToken,
|
|
162
|
+
isChecking: false,
|
|
163
|
+
missingScopes: [],
|
|
164
|
+
status: 'ready',
|
|
165
|
+
}
|
|
166
|
+
: {
|
|
167
|
+
accessToken,
|
|
168
|
+
isChecking: false,
|
|
169
|
+
missingScopes: missing,
|
|
170
|
+
status: 'needs-access',
|
|
171
|
+
};
|
|
172
|
+
setReadiness(result);
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
const result = {
|
|
177
|
+
error,
|
|
178
|
+
isChecking: false,
|
|
179
|
+
missingScopes: [],
|
|
180
|
+
status: 'error',
|
|
181
|
+
};
|
|
182
|
+
setReadiness(result);
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
}), [environmentId]);
|
|
186
|
+
const check = useCallback(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
187
|
+
setReadiness((prev) => (Object.assign(Object.assign({}, prev), { isChecking: true })));
|
|
188
|
+
return performCheck(user === null || user === void 0 ? void 0 : user.verifiedCredentials);
|
|
189
|
+
}), [performCheck, user]);
|
|
190
|
+
const requestAccess = useCallback(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
191
|
+
var _a;
|
|
192
|
+
setReadiness((prev) => (Object.assign(Object.assign({}, prev), { isChecking: true })));
|
|
193
|
+
try {
|
|
194
|
+
yield linkSocialAccount(ProviderEnum.Google, {
|
|
195
|
+
forcePopup: true,
|
|
196
|
+
showWidgetAfterConnection: false,
|
|
197
|
+
});
|
|
198
|
+
const updatedUser = yield refresh();
|
|
199
|
+
return performCheck((_a = updatedUser === null || updatedUser === void 0 ? void 0 : updatedUser.verifiedCredentials) !== null && _a !== void 0 ? _a : user === null || user === void 0 ? void 0 : user.verifiedCredentials);
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
const result = {
|
|
203
|
+
error,
|
|
204
|
+
isChecking: false,
|
|
205
|
+
missingScopes: [],
|
|
206
|
+
status: 'error',
|
|
207
|
+
};
|
|
208
|
+
setReadiness(result);
|
|
209
|
+
return result;
|
|
210
|
+
}
|
|
211
|
+
}), [linkSocialAccount, performCheck, refresh, user]);
|
|
212
|
+
const reset = useCallback(() => setReadiness(IDLE), []);
|
|
213
|
+
return Object.assign(Object.assign({}, readiness), { check,
|
|
214
|
+
requestAccess,
|
|
215
|
+
reset });
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
export { useGoogleDriveBackupReadiness };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Stable phrase the waas-sdk includes in Google Drive 401 / scope-insufficient
|
|
8
|
+
* upload failure messages (see dynamic-waas-sdk
|
|
9
|
+
* packages/browser/src/backup/providers/googleDrive.ts mapGoogleDriveUploadError).
|
|
10
|
+
*
|
|
11
|
+
* We match by `.includes(...)` rather than `.startsWith(...)` because the
|
|
12
|
+
* iframe message-handler fallback path
|
|
13
|
+
* (browser-wallet-client/src/services/iframeMessageHandler.ts:
|
|
14
|
+
* handleIframeMessageResponseError) rewraps the original error as
|
|
15
|
+
* `new Error(String(response.error))`, which prefixes the message with
|
|
16
|
+
* "Error: " and drops the original `isRetryable` field. The phrase remains
|
|
17
|
+
* unique to the auth/scope case across all reasons mapGoogleDriveUploadError
|
|
18
|
+
* produces (rate-limit, storage-full, 5xx, and network all use distinct
|
|
19
|
+
* prefixes), so message-only matching is sufficient to discriminate.
|
|
20
|
+
*
|
|
21
|
+
* TODO: switch to a stable `error.code` once dynamic-waas-sdk attaches one
|
|
22
|
+
* (see DYNT-754 follow-up).
|
|
23
|
+
*/
|
|
24
|
+
const ACCESS_DENIED_PHRASE = 'Google Drive access denied';
|
|
25
|
+
/**
|
|
26
|
+
* Type-guard for Google Drive backup failures caused by missing/insufficient
|
|
27
|
+
* OAuth scopes (or otherwise denied access). Recover by re-prompting consent
|
|
28
|
+
* via `linkSocialAccount(ProviderEnum.Google, { forcePopup: true })` and
|
|
29
|
+
* retrying the backup.
|
|
30
|
+
*/
|
|
31
|
+
const isInsufficientGoogleDriveScopesError = (err) => err instanceof Error && err.message.includes(ACCESS_DENIED_PHRASE);
|
|
32
|
+
|
|
33
|
+
exports.isInsufficientGoogleDriveScopesError = isInsufficientGoogleDriveScopesError;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type GoogleDriveBackupAccessError = Error;
|
|
2
|
+
/**
|
|
3
|
+
* Type-guard for Google Drive backup failures caused by missing/insufficient
|
|
4
|
+
* OAuth scopes (or otherwise denied access). Recover by re-prompting consent
|
|
5
|
+
* via `linkSocialAccount(ProviderEnum.Google, { forcePopup: true })` and
|
|
6
|
+
* retrying the backup.
|
|
7
|
+
*/
|
|
8
|
+
export declare const isInsufficientGoogleDriveScopesError: (err: unknown) => err is Error;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
/**
|
|
3
|
+
* Stable phrase the waas-sdk includes in Google Drive 401 / scope-insufficient
|
|
4
|
+
* upload failure messages (see dynamic-waas-sdk
|
|
5
|
+
* packages/browser/src/backup/providers/googleDrive.ts mapGoogleDriveUploadError).
|
|
6
|
+
*
|
|
7
|
+
* We match by `.includes(...)` rather than `.startsWith(...)` because the
|
|
8
|
+
* iframe message-handler fallback path
|
|
9
|
+
* (browser-wallet-client/src/services/iframeMessageHandler.ts:
|
|
10
|
+
* handleIframeMessageResponseError) rewraps the original error as
|
|
11
|
+
* `new Error(String(response.error))`, which prefixes the message with
|
|
12
|
+
* "Error: " and drops the original `isRetryable` field. The phrase remains
|
|
13
|
+
* unique to the auth/scope case across all reasons mapGoogleDriveUploadError
|
|
14
|
+
* produces (rate-limit, storage-full, 5xx, and network all use distinct
|
|
15
|
+
* prefixes), so message-only matching is sufficient to discriminate.
|
|
16
|
+
*
|
|
17
|
+
* TODO: switch to a stable `error.code` once dynamic-waas-sdk attaches one
|
|
18
|
+
* (see DYNT-754 follow-up).
|
|
19
|
+
*/
|
|
20
|
+
const ACCESS_DENIED_PHRASE = 'Google Drive access denied';
|
|
21
|
+
/**
|
|
22
|
+
* Type-guard for Google Drive backup failures caused by missing/insufficient
|
|
23
|
+
* OAuth scopes (or otherwise denied access). Recover by re-prompting consent
|
|
24
|
+
* via `linkSocialAccount(ProviderEnum.Google, { forcePopup: true })` and
|
|
25
|
+
* retrying the backup.
|
|
26
|
+
*/
|
|
27
|
+
const isInsufficientGoogleDriveScopesError = (err) => err instanceof Error && err.message.includes(ACCESS_DENIED_PHRASE);
|
|
28
|
+
|
|
29
|
+
export { isInsufficientGoogleDriveScopesError };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* OAuth scopes that must be granted on the user's Google account in order
|
|
8
|
+
* for the keyshare backup upload to Google Drive to succeed.
|
|
9
|
+
*
|
|
10
|
+
* Exported so host apps can pre-flight check / display these in their own
|
|
11
|
+
* UX if they want to. Kept as a `readonly` tuple to discourage mutation.
|
|
12
|
+
*/
|
|
13
|
+
const GOOGLE_DRIVE_BACKUP_REQUIRED_SCOPES = [
|
|
14
|
+
'https://www.googleapis.com/auth/drive.appdata',
|
|
15
|
+
'https://www.googleapis.com/auth/drive.file',
|
|
16
|
+
];
|
|
17
|
+
const findMissingGoogleDriveBackupScopes = (grantedScopes) => GOOGLE_DRIVE_BACKUP_REQUIRED_SCOPES.filter((required) => !grantedScopes.includes(required));
|
|
18
|
+
const hasAllGoogleDriveBackupScopes = (grantedScopes) => findMissingGoogleDriveBackupScopes(grantedScopes).length === 0;
|
|
19
|
+
|
|
20
|
+
exports.GOOGLE_DRIVE_BACKUP_REQUIRED_SCOPES = GOOGLE_DRIVE_BACKUP_REQUIRED_SCOPES;
|
|
21
|
+
exports.findMissingGoogleDriveBackupScopes = findMissingGoogleDriveBackupScopes;
|
|
22
|
+
exports.hasAllGoogleDriveBackupScopes = hasAllGoogleDriveBackupScopes;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth scopes that must be granted on the user's Google account in order
|
|
3
|
+
* for the keyshare backup upload to Google Drive to succeed.
|
|
4
|
+
*
|
|
5
|
+
* Exported so host apps can pre-flight check / display these in their own
|
|
6
|
+
* UX if they want to. Kept as a `readonly` tuple to discourage mutation.
|
|
7
|
+
*/
|
|
8
|
+
export declare const GOOGLE_DRIVE_BACKUP_REQUIRED_SCOPES: readonly ["https://www.googleapis.com/auth/drive.appdata", "https://www.googleapis.com/auth/drive.file"];
|
|
9
|
+
export declare const findMissingGoogleDriveBackupScopes: (grantedScopes: readonly string[]) => string[];
|
|
10
|
+
export declare const hasAllGoogleDriveBackupScopes: (grantedScopes: readonly string[]) => boolean;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
/**
|
|
3
|
+
* OAuth scopes that must be granted on the user's Google account in order
|
|
4
|
+
* for the keyshare backup upload to Google Drive to succeed.
|
|
5
|
+
*
|
|
6
|
+
* Exported so host apps can pre-flight check / display these in their own
|
|
7
|
+
* UX if they want to. Kept as a `readonly` tuple to discourage mutation.
|
|
8
|
+
*/
|
|
9
|
+
const GOOGLE_DRIVE_BACKUP_REQUIRED_SCOPES = [
|
|
10
|
+
'https://www.googleapis.com/auth/drive.appdata',
|
|
11
|
+
'https://www.googleapis.com/auth/drive.file',
|
|
12
|
+
];
|
|
13
|
+
const findMissingGoogleDriveBackupScopes = (grantedScopes) => GOOGLE_DRIVE_BACKUP_REQUIRED_SCOPES.filter((required) => !grantedScopes.includes(required));
|
|
14
|
+
const hasAllGoogleDriveBackupScopes = (grantedScopes) => findMissingGoogleDriveBackupScopes(grantedScopes).length === 0;
|
|
15
|
+
|
|
16
|
+
export { GOOGLE_DRIVE_BACKUP_REQUIRED_SCOPES, findMissingGoogleDriveBackupScopes, hasAllGoogleDriveBackupScopes };
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export { useWalletBackup, useBackupWallets, useDownloadKeyShares, isWalletBackedUp, type WalletToBackup, type WalletWithBackupStatus, type WalletBackupStatus, type WalletOperationState, } from './useWalletBackup';
|
|
2
2
|
export { CloudBackupProvider } from './types';
|
|
3
|
+
export { GOOGLE_DRIVE_BACKUP_REQUIRED_SCOPES, findMissingGoogleDriveBackupScopes, hasAllGoogleDriveBackupScopes, } from './googleDriveScopes';
|
|
4
|
+
export { isInsufficientGoogleDriveScopesError, type GoogleDriveBackupAccessError, } from './googleDriveBackupErrors';
|
|
@@ -148,6 +148,8 @@ const useWalletBackup = () => {
|
|
|
148
148
|
isProcessing: false,
|
|
149
149
|
totalWallets: 0,
|
|
150
150
|
});
|
|
151
|
+
const [lastBackupError, setLastBackupError] = React.useState(undefined);
|
|
152
|
+
const clearBackupError = React.useCallback(() => setLastBackupError(undefined), []);
|
|
151
153
|
const timeoutRef = React.useRef(null);
|
|
152
154
|
React.useEffect(() => () => {
|
|
153
155
|
if (timeoutRef.current) {
|
|
@@ -234,6 +236,7 @@ const useWalletBackup = () => {
|
|
|
234
236
|
}
|
|
235
237
|
}), [getWaasWalletConnector]);
|
|
236
238
|
const backupWallet = React.useCallback((walletToBackup_1, ...args_1) => _tslib.__awaiter(void 0, [walletToBackup_1, ...args_1], void 0, function* (walletToBackup, provider = types.CloudBackupProvider.GoogleDrive, displayContainer) {
|
|
239
|
+
setLastBackupError(undefined);
|
|
237
240
|
try {
|
|
238
241
|
const waasConnector = getWaasWalletConnector(walletToBackup.chain);
|
|
239
242
|
if (!waasConnector) {
|
|
@@ -259,6 +262,7 @@ const useWalletBackup = () => {
|
|
|
259
262
|
address: walletToBackup.address,
|
|
260
263
|
error,
|
|
261
264
|
});
|
|
265
|
+
setLastBackupError(error);
|
|
262
266
|
return false;
|
|
263
267
|
}
|
|
264
268
|
}), [getWaasWalletConnector]);
|
|
@@ -319,6 +323,7 @@ const useWalletBackup = () => {
|
|
|
319
323
|
return true;
|
|
320
324
|
}, [isGoogleLinked]);
|
|
321
325
|
const backupToCloudProvider = React.useCallback((options, walletToBackup) => _tslib.__awaiter(void 0, void 0, void 0, function* () {
|
|
326
|
+
setLastBackupError(undefined);
|
|
322
327
|
try {
|
|
323
328
|
const { provider, password } = options;
|
|
324
329
|
const waasConnector = getWaasWalletConnector(walletToBackup.chain);
|
|
@@ -344,6 +349,7 @@ const useWalletBackup = () => {
|
|
|
344
349
|
}
|
|
345
350
|
catch (error) {
|
|
346
351
|
logger.logger.warn('Error backing up to cloud provider', { error, options });
|
|
352
|
+
setLastBackupError(error);
|
|
347
353
|
return false;
|
|
348
354
|
}
|
|
349
355
|
}), [getWaasWalletConnector]);
|
|
@@ -355,6 +361,7 @@ const useWalletBackup = () => {
|
|
|
355
361
|
backupToCloudProvider,
|
|
356
362
|
backupWallet,
|
|
357
363
|
checkICloudAuth,
|
|
364
|
+
clearBackupError,
|
|
358
365
|
ensureGoogleLinked,
|
|
359
366
|
ensureProviderLinked,
|
|
360
367
|
getSupportedProviders,
|
|
@@ -364,6 +371,7 @@ const useWalletBackup = () => {
|
|
|
364
371
|
initBackupProcess,
|
|
365
372
|
isGoogleLinked,
|
|
366
373
|
isProviderLinked,
|
|
374
|
+
lastBackupError,
|
|
367
375
|
showICloudAuth,
|
|
368
376
|
startBackup,
|
|
369
377
|
};
|
|
@@ -24,6 +24,7 @@ export declare const useWalletBackup: () => {
|
|
|
24
24
|
readonly backupToCloudProvider: (options: CloudProviderBackupOptions, walletToBackup: WalletToBackup) => Promise<boolean>;
|
|
25
25
|
readonly backupWallet: (walletToBackup: WalletToBackup, provider?: CloudBackupProvider, displayContainer?: HTMLElement) => Promise<boolean>;
|
|
26
26
|
readonly checkICloudAuth: (walletChain: ChainEnum) => Promise<boolean>;
|
|
27
|
+
readonly clearBackupError: () => void;
|
|
27
28
|
readonly ensureGoogleLinked: () => Promise<boolean>;
|
|
28
29
|
readonly ensureProviderLinked: (provider: CloudBackupProvider) => Promise<boolean>;
|
|
29
30
|
readonly getSupportedProviders: () => import("./cloudProviders").CloudProviderConfigWithIcon[];
|
|
@@ -33,6 +34,7 @@ export declare const useWalletBackup: () => {
|
|
|
33
34
|
readonly initBackupProcess: () => void;
|
|
34
35
|
readonly isGoogleLinked: boolean;
|
|
35
36
|
readonly isProviderLinked: (provider: CloudBackupProvider) => boolean;
|
|
37
|
+
readonly lastBackupError: unknown;
|
|
36
38
|
readonly showICloudAuth: (displayContainer: HTMLElement, walletChain: ChainEnum) => Promise<boolean>;
|
|
37
39
|
readonly startBackup: (onComplete?: () => void, fromIndex?: number, provider?: CloudBackupProvider, displayContainer?: HTMLElement) => Promise<void>;
|
|
38
40
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
import { __awaiter } from '../../../../../_virtual/_tslib.js';
|
|
3
|
-
import { useMemo, useState, useRef, useEffect
|
|
3
|
+
import { useMemo, useState, useCallback, useRef, useEffect } from 'react';
|
|
4
4
|
import { WaasBackupOptionsEnum, ProviderEnum } from '@dynamic-labs/sdk-api-core';
|
|
5
5
|
import '@dynamic-labs-sdk/client/core';
|
|
6
6
|
import 'eventemitter3';
|
|
@@ -144,6 +144,8 @@ const useWalletBackup = () => {
|
|
|
144
144
|
isProcessing: false,
|
|
145
145
|
totalWallets: 0,
|
|
146
146
|
});
|
|
147
|
+
const [lastBackupError, setLastBackupError] = useState(undefined);
|
|
148
|
+
const clearBackupError = useCallback(() => setLastBackupError(undefined), []);
|
|
147
149
|
const timeoutRef = useRef(null);
|
|
148
150
|
useEffect(() => () => {
|
|
149
151
|
if (timeoutRef.current) {
|
|
@@ -230,6 +232,7 @@ const useWalletBackup = () => {
|
|
|
230
232
|
}
|
|
231
233
|
}), [getWaasWalletConnector]);
|
|
232
234
|
const backupWallet = useCallback((walletToBackup_1, ...args_1) => __awaiter(void 0, [walletToBackup_1, ...args_1], void 0, function* (walletToBackup, provider = CloudBackupProvider.GoogleDrive, displayContainer) {
|
|
235
|
+
setLastBackupError(undefined);
|
|
233
236
|
try {
|
|
234
237
|
const waasConnector = getWaasWalletConnector(walletToBackup.chain);
|
|
235
238
|
if (!waasConnector) {
|
|
@@ -255,6 +258,7 @@ const useWalletBackup = () => {
|
|
|
255
258
|
address: walletToBackup.address,
|
|
256
259
|
error,
|
|
257
260
|
});
|
|
261
|
+
setLastBackupError(error);
|
|
258
262
|
return false;
|
|
259
263
|
}
|
|
260
264
|
}), [getWaasWalletConnector]);
|
|
@@ -315,6 +319,7 @@ const useWalletBackup = () => {
|
|
|
315
319
|
return true;
|
|
316
320
|
}, [isGoogleLinked]);
|
|
317
321
|
const backupToCloudProvider = useCallback((options, walletToBackup) => __awaiter(void 0, void 0, void 0, function* () {
|
|
322
|
+
setLastBackupError(undefined);
|
|
318
323
|
try {
|
|
319
324
|
const { provider, password } = options;
|
|
320
325
|
const waasConnector = getWaasWalletConnector(walletToBackup.chain);
|
|
@@ -340,6 +345,7 @@ const useWalletBackup = () => {
|
|
|
340
345
|
}
|
|
341
346
|
catch (error) {
|
|
342
347
|
logger.warn('Error backing up to cloud provider', { error, options });
|
|
348
|
+
setLastBackupError(error);
|
|
343
349
|
return false;
|
|
344
350
|
}
|
|
345
351
|
}), [getWaasWalletConnector]);
|
|
@@ -351,6 +357,7 @@ const useWalletBackup = () => {
|
|
|
351
357
|
backupToCloudProvider,
|
|
352
358
|
backupWallet,
|
|
353
359
|
checkICloudAuth,
|
|
360
|
+
clearBackupError,
|
|
354
361
|
ensureGoogleLinked,
|
|
355
362
|
ensureProviderLinked,
|
|
356
363
|
getSupportedProviders: getSupportedProviders$1,
|
|
@@ -360,6 +367,7 @@ const useWalletBackup = () => {
|
|
|
360
367
|
initBackupProcess,
|
|
361
368
|
isGoogleLinked,
|
|
362
369
|
isProviderLinked,
|
|
370
|
+
lastBackupError,
|
|
363
371
|
showICloudAuth,
|
|
364
372
|
startBackup,
|
|
365
373
|
};
|