Skip to content

Commit 43db02d

Browse files
Merge pull request #6890 from KeeperCommunity/feat/recoveryKeyBackup
Forgot Passcode | Force Recovery Key Backup
2 parents 6b381c2 + 1e94238 commit 43db02d

File tree

20 files changed

+338
-86
lines changed

20 files changed

+338
-86
lines changed

android/app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ android {
9898
applicationId "io.hexawallet.keeper"
9999
minSdkVersion rootProject.ext.minSdkVersion
100100
targetSdkVersion rootProject.ext.targetSdkVersion
101-
versionCode 583
101+
versionCode 584
102102
versionName "2.5.8"
103103
missingDimensionStrategy 'react-native-camera', 'general'
104104
missingDimensionStrategy 'store', 'play'

ios/hexa_keeper.xcodeproj/project.pbxproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -748,7 +748,7 @@
748748
CODE_SIGN_ENTITLEMENTS = hexa_keeper/hexa_keeper.entitlements;
749749
CODE_SIGN_IDENTITY = "Apple Development";
750750
CODE_SIGN_STYLE = Automatic;
751-
CURRENT_PROJECT_VERSION = 583;
751+
CURRENT_PROJECT_VERSION = 584;
752752
DEVELOPMENT_TEAM = Y5TCB759QL;
753753
ENABLE_BITCODE = NO;
754754
HEADER_SEARCH_PATHS = (
@@ -875,7 +875,7 @@
875875
CODE_SIGN_ENTITLEMENTS = hexa_keeper/hexa_keeper.entitlements;
876876
CODE_SIGN_IDENTITY = "Apple Development";
877877
CODE_SIGN_STYLE = Automatic;
878-
CURRENT_PROJECT_VERSION = 583;
878+
CURRENT_PROJECT_VERSION = 584;
879879
DEVELOPMENT_TEAM = Y5TCB759QL;
880880
HEADER_SEARCH_PATHS = (
881881
"$(inherited)",
@@ -1144,7 +1144,7 @@
11441144
CODE_SIGN_IDENTITY = "Apple Development";
11451145
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
11461146
CODE_SIGN_STYLE = Manual;
1147-
CURRENT_PROJECT_VERSION = 583;
1147+
CURRENT_PROJECT_VERSION = 584;
11481148
DEVELOPMENT_TEAM = Y5TCB759QL;
11491149
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = Y5TCB759QL;
11501150
ENABLE_BITCODE = NO;
@@ -1275,7 +1275,7 @@
12751275
CODE_SIGN_ENTITLEMENTS = hexa_keeper_dev.entitlements;
12761276
CODE_SIGN_IDENTITY = "Apple Distribution";
12771277
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
1278-
CURRENT_PROJECT_VERSION = 583;
1278+
CURRENT_PROJECT_VERSION = 584;
12791279
DEVELOPMENT_TEAM = Y5TCB759QL;
12801280
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = Y5TCB759QL;
12811281
HEADER_SEARCH_PATHS = (

ios/hexa_keeper/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
</dict>
3737
</array>
3838
<key>CFBundleVersion</key>
39-
<string>583</string>
39+
<string>584</string>
4040
<key>LSApplicationQueriesSchemes</key>
4141
<array>
4242
<string>itms-apps</string>

ios/hexa_keeperTests/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@
1919
<key>CFBundleSignature</key>
2020
<string>????</string>
2121
<key>CFBundleVersion</key>
22-
<string>583</string>
22+
<string>584</string>
2323
</dict>
2424
</plist>

ios/hexa_keeper_dev-Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
</dict>
3737
</array>
3838
<key>CFBundleVersion</key>
39-
<string>583</string>
39+
<string>584</string>
4040
<key>LSApplicationQueriesSchemes</key>
4141
<array>
4242
<string>itms-apps</string>

src/components/Backup/BackupHealthCheckList.tsx

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@ import {
1212
seedBackedConfirmed,
1313
validateServerBackup,
1414
} from 'src/store/sagaActions/bhr';
15-
import { setBackupAllFailure, setBackupAllSuccess, setSeedConfirmed } from 'src/store/reducers/bhr';
15+
import {
16+
setAutomaticCloudBackup,
17+
setBackupAllFailure,
18+
setBackupAllSuccess,
19+
setSeedConfirmed,
20+
} from 'src/store/reducers/bhr';
1621
import { CommonActions, useIsFocused, useNavigation } from '@react-navigation/native';
1722
import { useQuery } from '@realm/react';
1823
import HealthCheck from 'src/assets/images/healthcheck_light.svg';
@@ -28,14 +33,15 @@ import Text from '../KeeperText';
2833
import { hp } from 'src/constants/responsive';
2934
import ThemedSvg from '../ThemedSvg.tsx/ThemedSvg';
3035
import ConfirmSeedWord from '../SeedWordBackup/ConfirmSeedWord';
36+
import { setRecoveryKeyBackedUp } from 'src/store/reducers/account';
3137

3238
const ContentType = {
3339
verifying: 'verifying',
3440
verificationFailed: 'verificationFailed',
3541
mismatch: 'mismatch',
3642
healthCheckSuccessful: 'healthCheckSuccessful',
3743
};
38-
function Content({ contentType }: { contentType: string }) {
44+
function Content({ contentType, asbEnabled }: { contentType: string; asbEnabled: boolean }) {
3945
const { BackupWallet } = useContext(LocalizationContext).translations;
4046
const illustrations = {
4147
[ContentType.verifying]: (
@@ -57,6 +63,7 @@ function Content({ contentType }: { contentType: string }) {
5763
<Box>
5864
<Box alignItems="center">{illustrations[contentType]}</Box>
5965
{descriptions[contentType] && <Text>{descriptions[contentType]}</Text>}
66+
{asbEnabled && <Text>{BackupWallet.assistedServerBackupEnabled}</Text>}
6067
</Box>
6168
);
6269
}
@@ -67,7 +74,7 @@ function BackupHealthCheckList({ isUaiFlow }) {
6774
const { translations } = useContext(LocalizationContext);
6875
const { BackupWallet, vault: vaultText, common } = translations;
6976
const dispatch = useAppDispatch();
70-
const { primaryMnemonic, backup } = useQuery(RealmSchema.KeeperApp).map(
77+
const { primaryMnemonic, backup, id } = useQuery(RealmSchema.KeeperApp).map(
7178
getJSONFromRealmObject
7279
)[0];
7380
const {
@@ -87,17 +94,33 @@ function BackupHealthCheckList({ isUaiFlow }) {
8794
const history: BackupHistoryItem[] = useMemo(() => data.sorted('date', true), [data]);
8895
const { isOnL2Above } = usePlan();
8996
const isFocused = useIsFocused();
97+
const [asbEnabled, setAsbEnabled] = useState(false);
9098

9199
const onPressConfirm = () => {
92100
setShowConfirmSeedModal(true);
93101
};
94102

103+
104+
useEffect(() => {
105+
if (backupAllSuccess || backupAllFailure) {
106+
if (!automaticCloudBackup) setAsbEnabled(true);
107+
dispatch(setBackupAllSuccess(false));
108+
dispatch(setBackupAllFailure(false));
109+
dispatch(setAutomaticCloudBackup(true));
110+
setHealthCheckModal(true);
111+
}
112+
}, [backupAllSuccess, backupAllFailure]);
113+
95114
useEffect(() => {
96115
if (seedConfirmed) {
97116
setShowConfirmSeedModal(false);
98-
setTimeout(() => {
99-
setHealthCheckModal(true);
100-
}, 100);
117+
dispatch(setRecoveryKeyBackedUp({ appId: id as string, status: true }));
118+
if (!automaticCloudBackup) dispatch(backupAllSignersAndVaults());
119+
else {
120+
setTimeout(() => {
121+
setHealthCheckModal(true);
122+
}, 100);
123+
}
101124
}
102125
return () => {
103126
dispatch(setSeedConfirmed(false));
@@ -284,7 +307,9 @@ function BackupHealthCheckList({ isUaiFlow }) {
284307
buttonCallback={() => {
285308
navigtaion.navigate('Home');
286309
}}
287-
Content={() => <Content contentType={ContentType.healthCheckSuccessful} />}
310+
Content={() => (
311+
<Content contentType={ContentType.healthCheckSuccessful} asbEnabled={asbEnabled} />
312+
)}
288313
closeOnOverlayClick={true}
289314
/>
290315
<ActivityIndicatorView visible={backupAllLoading} />

src/context/Localization/language/en.json

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@
198198
"Createpasscode": "Create a passcode",
199199
"Confirmyourpasscode": "Confirm your passcode",
200200
"CreateApp": "Create a fresh app or recover an exisiting one",
201+
"CreateAppOnly": "Create a fresh app",
201202
"passcode": "passcode",
202203
"Incorrect": "Incorrect Passcode, Try Again!",
203204
"newKeeperAppDesc": "Create a new Keeper app",
@@ -222,7 +223,11 @@
222223
"checkConnection": "Please check your internet connection and try again. If you continue offline, some features may not be available.",
223224
"incorrectPassword": "Incorrect Password",
224225
"youEnteredIncorrectPasscode": "You have entered an incorrect passcode. Please, try again. If you don’t remember your passcode, you will have to recover your wallet through the recovery flow",
225-
"checkinternetConnection": "Please check your internet connection and try again."
226+
"checkinternetConnection": "Please check your internet connection and try again.",
227+
"forgotPasscode": "Forgot passcode?",
228+
"resetPasscodeTitle": "Reset Passcode",
229+
"resetPasscodeDec1": "For your security, passcode resets are verified with your recovery key.",
230+
"resetPasscodeDec2": "Please have your recovery key ready before continuing."
226231
},
227232
"home": {
228233
"wallet": "Wallet",
@@ -243,7 +248,10 @@
243248
"incommingAndOutgoing": "All incoming and outgoing transactions",
244249
"viewAll": " View All",
245250
"securityTip": "Security Tip",
246-
"securityTipDesc": "Recreate the multisig on more coordinators. Receive a small amount and send a part of it. Check the balances are appropriately reflected across all the coordinators after each step."
251+
"securityTipDesc": "Recreate the multisig on more coordinators. Receive a small amount and send a part of it. Check the balances are appropriately reflected across all the coordinators after each step.",
252+
"backupModalTitle": "Set Up Wallet Recovery",
253+
"backupModalSubTitle": "To ensure you can always recover your wallet, please secure your Recovery Key and enable Assisted Server Backup.",
254+
"backupModalDesc": "This ensure that your encrypted data is backed up and can be decrypted by your Recovery key"
247255
},
248256
"transactions": {
249257
"Fees": "Fees",
@@ -1138,7 +1146,8 @@
11381146
"backupFailedModalDesc": "There might be a temporary issue with the server or your connection. Please try again later.",
11391147
"mismatchModalTitle": "Data Mismatch Detected",
11401148
"mismatchModalSubTitle": "Your local data doesn’t match the server backup.",
1141-
"mismatchModalDesc": "Some recent changes may not be backed up yet. To avoid data loss, we recommend initiating a backup now."
1149+
"mismatchModalDesc": "Some recent changes may not be backed up yet. To avoid data loss, we recommend initiating a backup now.",
1150+
"assistedServerBackupEnabled": "Assisted server backup is now enabled.\nThis will backup your encrypted data on a community-run Keeper server"
11421151
},
11431152
"importWallet": {
11441153
"addDetails": "Add details",

src/context/Localization/language/es.json

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@
198198
"Createpasscode": "Create a passcode",
199199
"Confirmyourpasscode": "Confirm your passcode",
200200
"CreateApp": "Create a fresh app or recover an exisiting one",
201+
"CreateAppOnly": "Create a fresh app",
201202
"passcode": "passcode",
202203
"Incorrect": "Incorrect Passcode, Try Again!",
203204
"newKeeperAppDesc": "Create a new Keeper app",
@@ -222,7 +223,11 @@
222223
"checkConnection": "Please check your internet connection and try again. If you continue offline, some features may not be available.",
223224
"incorrectPassword": "Incorrect Password",
224225
"youEnteredIncorrectPasscode": "You have entered an incorrect passcode. Please, try again. If you don’t remember your passcode, you will have to recover your wallet through the recovery flow",
225-
"checkinternetConnection": "Please check your internet connection and try again."
226+
"checkinternetConnection": "Please check your internet connection and try again.",
227+
"forgotPasscode": "Forgot passcode?",
228+
"resetPasscodeTitle": "Reset Passcode",
229+
"resetPasscodeDec1": "For your security, passcode resets are verified with your recovery key.",
230+
"resetPasscodeDec2": "Please have your recovery key ready before continuing."
226231
},
227232
"home": {
228233
"wallet": "Wallet",
@@ -243,7 +248,10 @@
243248
"incommingAndOutgoing": "All incoming and outgoing transactions",
244249
"viewAll": " View All",
245250
"securityTip": "Security Tip",
246-
"securityTipDesc": "Recreate the multisig on more coordinators. Receive a small amount and send a part of it. Check the balances are appropriately reflected across all the coordinators after each step."
251+
"securityTipDesc": "Recreate the multisig on more coordinators. Receive a small amount and send a part of it. Check the balances are appropriately reflected across all the coordinators after each step.",
252+
"backupModalTitle": "Set Up Wallet Recovery",
253+
"backupModalSubTitle": "To ensure you can always recover your wallet, please secure your Recovery Key and enable Assisted Server Backup.",
254+
"backupModalDesc": "This ensure that your encrypted data is backed up and can be decrypted by your Recovery key"
247255
},
248256
"transactions": {
249257
"Fees": "Fees",
@@ -1138,7 +1146,8 @@
11381146
"backupFailedModalDesc": "There might be a temporary issue with the server or your connection. Please try again later.",
11391147
"mismatchModalTitle": "Data Mismatch Detected",
11401148
"mismatchModalSubTitle": "Your local data doesn’t match the server backup.",
1141-
"mismatchModalDesc": "Some recent changes may not be backed up yet. To avoid data loss, we recommend initiating a backup now."
1149+
"mismatchModalDesc": "Some recent changes may not be backed up yet. To avoid data loss, we recommend initiating a backup now.",
1150+
"assistedServerBackupEnabled": "Assisted server backup is now enabled.\nThis will backup your encrypted data on a community-run Keeper server"
11421151
},
11431152
"importWallet": {
11441153
"addDetails": "Add details",

src/screens/Home/HomeScreen.tsx

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,17 @@ import MenuFooter from 'src/components/MenuFooter';
1717
import HomeWallet from './components/Wallet/HomeWallet';
1818
import ManageKeys from './components/Keys/ManageKeys';
1919
import KeeperSettings from './components/Settings/keeperSettings';
20-
import { useNavigation } from '@react-navigation/native';
20+
import { CommonActions, useFocusEffect, useNavigation } from '@react-navigation/native';
2121
import TickIcon from 'src/assets/images/icon_tick.svg';
2222
import ThemedSvg from 'src/components/ThemedSvg.tsx/ThemedSvg';
2323
import ThemedColor from 'src/components/ThemedColor/ThemedColor';
2424
import BuyBtc from './components/buyBtc/BuyBtc';
2525
import ConciergeComponent from './components/ConciergeComponent';
26+
import KeeperModal from 'src/components/KeeperModal';
27+
import Text from 'src/components/KeeperText';
28+
import { useQuery } from '@realm/react';
29+
import { RealmSchema } from 'src/storage/realm/enum';
30+
import dbManager from 'src/storage/realm/dbManager';
2631

2732
function NewHomeScreen({ route }) {
2833
const { colorMode } = useColorMode();
@@ -31,6 +36,7 @@ function NewHomeScreen({ route }) {
3136
const { addedSigner, selectedOption: selectedOptionFromRoute } = route.params || {};
3237
const { wallets } = useWallets({ getAll: true });
3338
const [electrumErrorVisible, setElectrumErrorVisible] = useState(false);
39+
const [backupModalVisible, setBackupModalVisible] = useState(true);
3440
const home_header_circle_background = ThemedColor({ name: 'home_header_circle_background' });
3541

3642
const { relayWalletUpdate, relayWalletError, realyWalletErrorMessage, homeToastMessage } =
@@ -41,13 +47,34 @@ function NewHomeScreen({ route }) {
4147
const [selectedOption, setSelectedOption] = useState(
4248
selectedOptionFromRoute || walletText.homeWallets
4349
);
50+
const backupHistory = useQuery(RealmSchema.BackupHistory);
51+
const { recoveryKeyBackedUpByAppId } = useAppSelector((state) => state.account);
52+
const { id } = dbManager.getObjectByIndex(RealmSchema.KeeperApp) as any;
53+
const shouldShowBackupModal = !(recoveryKeyBackedUpByAppId?.[id] ?? false);
4454

4555
useEffect(() => {
4656
if (selectedOptionFromRoute && selectedOptionFromRoute !== selectedOption) {
4757
setSelectedOption(selectedOptionFromRoute);
4858
}
4959
}, [selectedOptionFromRoute]);
5060

61+
useEffect(() => {
62+
if (shouldShowBackupModal) {
63+
setBackupModalVisible(true);
64+
}
65+
}, [shouldShowBackupModal]);
66+
67+
useFocusEffect(
68+
React.useCallback(() => {
69+
if (shouldShowBackupModal && selectedOption !== walletText.more) {
70+
const timer = setTimeout(() => {
71+
setBackupModalVisible(true);
72+
}, 100);
73+
return () => clearTimeout(timer);
74+
}
75+
}, [shouldShowBackupModal, selectedOption, walletText.more])
76+
);
77+
5178
const getContent = () => {
5279
switch (selectedOption) {
5380
case walletText.homeWallets:
@@ -159,6 +186,16 @@ function NewHomeScreen({ route }) {
159186
}
160187
}, [homeToastMessage]);
161188

189+
const BackupModalContent = () => {
190+
return (
191+
<Box style={{ gap: hp(10) }}>
192+
<Text color={`${colorMode}.primaryText`} style={{ fontSize: 14, letterSpacing: 0.13 }}>
193+
{homeTranslation.backupModalDesc}
194+
</Text>
195+
</Box>
196+
);
197+
};
198+
162199
return (
163200
<Box backgroundColor={`${colorMode}.primaryBackground`} style={styles.container}>
164201
<InititalAppController
@@ -175,6 +212,39 @@ function NewHomeScreen({ route }) {
175212
navigation.navigate('Home', { selectedOption: option });
176213
}}
177214
/>
215+
<KeeperModal
216+
visible={shouldShowBackupModal && backupModalVisible}
217+
close={() => {}}
218+
title={homeTranslation.backupModalTitle}
219+
subTitle={homeTranslation.backupModalSubTitle}
220+
modalBackground={`${colorMode}.modalWhiteBackground`}
221+
textColor={`${colorMode}.textGreen`}
222+
subTitleColor={`${colorMode}.modalSubtitleBlack`}
223+
buttonBackground={`${colorMode}.pantoneGreen`}
224+
showCloseIcon={false}
225+
buttonText={'Backup Recovery Key'}
226+
buttonCallback={() => {
227+
setBackupModalVisible(false);
228+
setTimeout(() => {
229+
if (backupHistory.length === 0) {
230+
navigation.dispatch(
231+
CommonActions.navigate('Home', {
232+
selectedOption: 'More',
233+
isUaiFlow: true,
234+
})
235+
);
236+
} else {
237+
navigation.dispatch(
238+
CommonActions.navigate('WalletBackHistory', {
239+
isUaiFlow: true,
240+
})
241+
);
242+
}
243+
}, 300);
244+
}}
245+
buttonTextColor={`${colorMode}.buttonText`}
246+
Content={BackupModalContent}
247+
/>
178248
</Box>
179249
);
180250
}

src/screens/Home/components/Settings/Component/SettingModal.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { CommonActions, useNavigation } from '@react-navigation/native';
22
import { useQuery } from '@realm/react';
33
import { Box, useColorMode } from 'native-base';
44
import React, { useContext, useEffect, useState } from 'react';
5+
import { Platform } from 'react-native';
56
import KeeperModal from 'src/components/KeeperModal';
67
import PasscodeVerifyModal from 'src/components/Modal/PasscodeVerify';
78
import { wp } from 'src/constants/responsive';
@@ -26,7 +27,10 @@ const SettingModal = ({ isUaiFlow, confirmPass, setConfirmPass }) => {
2627
useEffect(() => {
2728
if (confirmPass || isUaiFlow) {
2829
navigation.setParams({ isUaiFlow: false });
29-
setConfirmPassVisible(true);
30+
const delay = Platform.OS === 'ios' ? 400 : 0;
31+
setTimeout(() => {
32+
setConfirmPassVisible(true);
33+
}, delay);
3034
}
3135
}, [confirmPass, isUaiFlow]);
3236

0 commit comments

Comments
 (0)