From 1f7bd1707a532cb6abac447aaede19d5f88f5a39 Mon Sep 17 00:00:00 2001 From: Max Voloshinskii Date: Tue, 12 Mar 2024 22:26:04 +0300 Subject: [PATCH 1/6] fix(mobile): fix migration using biometry --- packages/mobile/src/hooks/useMigration.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/mobile/src/hooks/useMigration.ts b/packages/mobile/src/hooks/useMigration.ts index 855721763..e91e2f4a8 100644 --- a/packages/mobile/src/hooks/useMigration.ts +++ b/packages/mobile/src/hooks/useMigration.ts @@ -22,12 +22,8 @@ export const useMigration = () => { if (isNewSecurityFlow) { const keychainService = await AsyncStorage.getItem('keychainService'); - if (!keychainService) { - throw new Error(); - } - jsonstr = await SecureStore.getItemAsync('biometry_' + keychainItemName, { - keychainService, + keychainService: keychainService || 'TKProtected', }); } else { jsonstr = await EncryptedStorage.getItem(keychainItemName); From a6744efb14f7239452f9e47457bb3be1499b8ef5 Mon Sep 17 00:00:00 2001 From: Max Voloshinskii Date: Tue, 12 Mar 2024 22:26:56 +0300 Subject: [PATCH 2/6] bump(mobile): 4.0.1 --- packages/mobile/android/app/build.gradle | 2 +- packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index ef0441fb9..f6584c79e 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -92,7 +92,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 433 - versionName "4.0.0" + versionName "4.0.1" missingDimensionStrategy 'react-native-camera', 'general' missingDimensionStrategy 'store', 'play' } diff --git a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj index 3742e1b05..05e38f21d 100644 --- a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj +++ b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj @@ -1294,7 +1294,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 4.0.0; + MARKETING_VERSION = 4.0.1; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -1328,7 +1328,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 4.0.0; + MARKETING_VERSION = 4.0.1; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", From 36b246b8086e2756e8bdd24ef8d18e78c1056c08 Mon Sep 17 00:00:00 2001 From: Max Voloshinskii Date: Tue, 12 Mar 2024 23:21:10 +0300 Subject: [PATCH 3/6] fix(mobile): Move isMigrated flag to separated storage to avoid race condition --- .../src/navigation/MainStack/MainStack.tsx | 2 +- packages/mobile/src/wallet/Tonkeeper.ts | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/mobile/src/navigation/MainStack/MainStack.tsx b/packages/mobile/src/navigation/MainStack/MainStack.tsx index 1f8300cbe..c2a9f5106 100644 --- a/packages/mobile/src/navigation/MainStack/MainStack.tsx +++ b/packages/mobile/src/navigation/MainStack/MainStack.tsx @@ -63,7 +63,7 @@ export const MainStack: FC = () => { const showLockScreen = tk.lockEnabled && !isUnlocked && hasWallet && !attachedScreen.pathname; - const isMigrated = useExternalState(tk.walletsStore, (state) => state.isMigrated); + const isMigrated = useExternalState(tk.migrationStore, (state) => state.isMigrated); const root = useMemo(() => { if (hasWallet) { diff --git a/packages/mobile/src/wallet/Tonkeeper.ts b/packages/mobile/src/wallet/Tonkeeper.ts index 1fe5a8834..80e8cc508 100644 --- a/packages/mobile/src/wallet/Tonkeeper.ts +++ b/packages/mobile/src/wallet/Tonkeeper.ts @@ -45,6 +45,9 @@ export interface WalletsStoreState { selectedIdentifier: string; biometryEnabled: boolean; lockEnabled: boolean; +} + +export interface MigrationState { isMigrated: boolean; } @@ -77,6 +80,9 @@ export class Tonkeeper { selectedIdentifier: '', biometryEnabled: false, lockEnabled: true, + }); + + public migrationStore = new State({ isMigrated: false, }); @@ -122,12 +128,18 @@ export class Tonkeeper { await Promise.all([ this.walletsStore.rehydrate(), this.tonPrice.rehydrate(), + this.migrationStore.rehydrate(), this.biometry.detectTypes(), ]); + // @ts-ignore moved to migrationStore, may be set on some clients. So we need to migrate it + if (this.walletsStore.data.isMigrated) { + this.setMigrated(); + } + this.tonPrice.load(); - if (!this.walletsStore.data.isMigrated) { + if (!this.migrationStore.data.isMigrated) { this.migrationData = await this.getMigrationData(); } @@ -496,7 +508,7 @@ export class Tonkeeper { public setMigrated() { console.log('migrated'); - this.walletsStore.set({ isMigrated: true }); + this.migrationStore.set({ isMigrated: true }); } public saveLastBackupTimestampAll(identifiers: string[], dismissSetup = false) { From 89319b1ffe1a454d4bf774e4991cee8c72d6d873 Mon Sep 17 00:00:00 2001 From: Max Voloshinskii Date: Tue, 12 Mar 2024 23:30:54 +0300 Subject: [PATCH 4/6] fix(mobile): add setAsync method --- packages/@core-js/src/utils/State.ts | 9 +++++++++ packages/mobile/src/wallet/Tonkeeper.ts | 24 ++++++++++++++---------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/packages/@core-js/src/utils/State.ts b/packages/@core-js/src/utils/State.ts index b0c43ef23..a1a40c589 100644 --- a/packages/@core-js/src/utils/State.ts +++ b/packages/@core-js/src/utils/State.ts @@ -98,6 +98,15 @@ export class State { this.storeIfNeeded(); }; + public setAsync = async ( + updater: Partial | ((data: TData) => Partial), + ) => { + const newData = typeof updater === 'function' ? updater(this.data) : updater; + this.data = { ...this.data, ...newData }; + this.emit(); + await this.storeIfNeeded(); + }; + private emit() { this.subscribers.forEach((subscriber) => subscriber(this.data)); } diff --git a/packages/mobile/src/wallet/Tonkeeper.ts b/packages/mobile/src/wallet/Tonkeeper.ts index 80e8cc508..8750a5981 100644 --- a/packages/mobile/src/wallet/Tonkeeper.ts +++ b/packages/mobile/src/wallet/Tonkeeper.ts @@ -249,7 +249,9 @@ export class Tonkeeper { return indexA - indexB; }); - this.walletsStore.set(({ wallets }) => ({ wallets: [...wallets, ...sortedWallets] })); + await this.walletsStore.setAsync(({ wallets }) => ({ + wallets: [...wallets, ...sortedWallets], + })); const walletsInstances = await Promise.all( sortedWallets.map((wallet) => this.createWalletInstance(wallet)), ); @@ -346,7 +348,9 @@ export class Tonkeeper { version, }; - this.walletsStore.set(({ wallets }) => ({ wallets: [...wallets, config] })); + await this.walletsStore.setAsync(({ wallets }) => ({ + wallets: [...wallets, config], + })); const wallet = await this.createWalletInstance(config); this.setWallet(wallet); @@ -360,7 +364,7 @@ export class Tonkeeper { Array.from(this.wallets.keys()).find((item) => item !== identifier) ?? '', ) ?? null; - this.walletsStore.set(({ wallets }) => ({ + await this.walletsStore.setAsync(({ wallets }) => ({ wallets: wallets.filter((w) => w.identifier !== identifier), selectedIdentifier: nextWallet?.identifier ?? '', })); @@ -370,7 +374,7 @@ export class Tonkeeper { this.wallets.delete(identifier); if (this.wallets.size === 0) { - this.walletsStore.set({ biometryEnabled: false }); + await this.walletsStore.setAsync({ biometryEnabled: false }); this.vault.destroy(); } @@ -381,7 +385,7 @@ export class Tonkeeper { } public async removeAllWallets() { - this.walletsStore.set({ + await this.walletsStore.setAsync({ wallets: [], selectedIdentifier: '', biometryEnabled: false, @@ -453,7 +457,7 @@ export class Tonkeeper { }, ); - this.walletsStore.set({ wallets: updatedWallets }); + await this.walletsStore.setAsync({ wallets: updatedWallets }); identifiers.forEach((identifier) => { const currentConfig = updatedWallets.find( @@ -524,7 +528,7 @@ export class Tonkeeper { public async enableBiometry(passcode: string) { await this.vault.setupBiometry(passcode); - this.walletsStore.set({ biometryEnabled: true }); + await this.walletsStore.setAsync({ biometryEnabled: true }); } public async disableBiometry() { @@ -532,14 +536,14 @@ export class Tonkeeper { await this.vault.removeBiometry(); } catch {} - this.walletsStore.set({ biometryEnabled: false }); + await this.walletsStore.setAsync({ biometryEnabled: false }); } public async enableLock() { - this.walletsStore.set({ lockEnabled: true }); + await this.walletsStore.setAsync({ lockEnabled: true }); } public async disableLock() { - this.walletsStore.set({ lockEnabled: false }); + await this.walletsStore.setAsync({ lockEnabled: false }); } } From 7cd020136577921f28907b27e4b682c760f99839 Mon Sep 17 00:00:00 2001 From: Max Voloshinskii Date: Wed, 13 Mar 2024 01:28:36 +0300 Subject: [PATCH 5/6] add persist options for migrationStore --- packages/mobile/src/wallet/Tonkeeper.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/mobile/src/wallet/Tonkeeper.ts b/packages/mobile/src/wallet/Tonkeeper.ts index 8750a5981..65c7e28a4 100644 --- a/packages/mobile/src/wallet/Tonkeeper.ts +++ b/packages/mobile/src/wallet/Tonkeeper.ts @@ -105,6 +105,11 @@ export class Tonkeeper { storage: this.storage, key: 'walletsStore', }); + + this.migrationStore.persist({ + storage: this.storage, + key: 'migrationStore', + }); } public get wallet() { From ba62e2237e8b400690057bee73d6a8aa2d5ea722 Mon Sep 17 00:00:00 2001 From: Andrey Sorokin Date: Wed, 13 Mar 2024 14:40:32 +0000 Subject: [PATCH 6/6] fix(mobile): fix seed phrase on small displays --- .../BackupPhraseScreen/BackupPhraseScreen.tsx | 77 +++++++++---------- 1 file changed, 37 insertions(+), 40 deletions(-) diff --git a/packages/mobile/src/screens/BackupPhraseScreen/BackupPhraseScreen.tsx b/packages/mobile/src/screens/BackupPhraseScreen/BackupPhraseScreen.tsx index 0b03553ec..9b56fb282 100644 --- a/packages/mobile/src/screens/BackupPhraseScreen/BackupPhraseScreen.tsx +++ b/packages/mobile/src/screens/BackupPhraseScreen/BackupPhraseScreen.tsx @@ -41,43 +41,45 @@ export const BackupPhraseScreen = memo(() => { return ( - - - {t('recovery_phrase.title')} - - - - {t('recovery_phrase.caption')} - - + + + + {t('recovery_phrase.title')} + + + + {t('recovery_phrase.caption')} + + - - - - {leftColumn.map((word, index) => ( - - - {index + 1}. - - {word} - - ))} - - - {rightColumn.map((word, index) => ( - - - {index + 1 + 12}. - - {word} - - ))} + + + + {leftColumn.map((word, index) => ( + + + {index + 1}. + + {word} + + ))} + + + {rightColumn.map((word, index) => ( + + + {index + 1 + 12}. + + {word} + + ))} + - - - + + + {lastBackupAt !== null && !params.isBackupAgain ? (