diff --git a/backends/LNC/credentialStore.ts b/backends/LNC/credentialStore.ts index 1f792b845..a419a0dac 100644 --- a/backends/LNC/credentialStore.ts +++ b/backends/LNC/credentialStore.ts @@ -1,18 +1,18 @@ -import EncryptedStorage from 'react-native-encrypted-storage'; +import Storage from '../../storage'; import { CredentialStore } from '../../zeus_modules/@lightninglabs/lnc-rn'; import hashjs from 'hash.js'; -const STORAGE_KEY = 'lnc-rn'; +const LNC_STORAGE_KEY = 'lnc-rn'; const hash = (stringToHash: string) => hashjs.sha256().update(stringToHash).digest('hex'); /** - * A wrapper around `EncryptedStorage` used to store sensitive data required + * A wrapper around `Storage` used to store sensitive data required * by LNC to reconnect after the initial pairing process has been completed. */ export default class LncCredentialStore implements CredentialStore { - // the data to store in EncryptedStorage + // the data to store in Storage private persisted = { serverHost: '', localKey: '', @@ -44,28 +44,22 @@ export default class LncCredentialStore implements CredentialStore { private async _migrateServerHost() { try { - const baseKey = `${STORAGE_KEY}:${hash(this._pairingPhrase)}`; + const baseKey = `${LNC_STORAGE_KEY}:${hash(this._pairingPhrase)}`; const hostKey = `${baseKey}:host`; - const hasNewFormat = await EncryptedStorage.getItem(hostKey); + const hasNewFormat = await Storage.getItem(hostKey); // Only migrate if new format doesn't exist yet if (!hasNewFormat) { - const oldData = await EncryptedStorage.getItem(baseKey); + const oldData = await Storage.getItem(baseKey); if (oldData) { const parsed = JSON.parse(oldData); if (parsed.serverHost) { - await EncryptedStorage.setItem( - hostKey, - parsed.serverHost - ); + await Storage.setItem(hostKey, parsed.serverHost); // Remove serverHost from old format delete parsed.serverHost; - await EncryptedStorage.setItem( - baseKey, - JSON.stringify(parsed) - ); + await Storage.setItem(baseKey, parsed); } } } @@ -81,7 +75,8 @@ export default class LncCredentialStore implements CredentialStore { /** Stores the host:port of the Lightning Node Connect proxy server to connect to */ get serverHost() { - return this.persisted.serverHost; + // strip string of quotation marks thay may have been added during migrations + return this.persisted.serverHost.replace(/['"]+/g, ''); } /** Stores the host:port of the Lightning Node Connect proxy server to connect to */ @@ -136,9 +131,9 @@ export default class LncCredentialStore implements CredentialStore { /** Clears any persisted data in the store */ clear() { - const baseKey = `${STORAGE_KEY}:${hash(this._pairingPhrase)}`; - EncryptedStorage.removeItem(baseKey); - EncryptedStorage.removeItem(`${baseKey}:host`); + const baseKey = `${LNC_STORAGE_KEY}:${hash(this._pairingPhrase)}`; + Storage.removeItem(baseKey); + Storage.removeItem(`${baseKey}:host`); this.persisted = { serverHost: this.persisted.serverHost, localKey: '', @@ -150,15 +145,15 @@ export default class LncCredentialStore implements CredentialStore { this._pairingPhrase = ''; } - /** Loads persisted data from EncryptedStorage */ + /** Loads persisted data from Storage */ async load(pairingPhrase?: string) { // only load if pairingPhrase is set if (!pairingPhrase) return; try { - const baseKey = `${STORAGE_KEY}:${hash(pairingPhrase)}`; + const baseKey = `${LNC_STORAGE_KEY}:${hash(pairingPhrase)}`; const hostKey = `${baseKey}:host`; - const json = await EncryptedStorage.getItem(baseKey); - const serverHost = await EncryptedStorage.getItem(hostKey); + const json = await Storage.getItem(baseKey); + const serverHost = await Storage.getItem(hostKey); if (json) { this.persisted = JSON.parse(json); if (serverHost) { @@ -179,18 +174,18 @@ export default class LncCredentialStore implements CredentialStore { // private _saveServerHost() { - const hostKey = `${STORAGE_KEY}:${hash(this._pairingPhrase)}:host`; - EncryptedStorage.setItem(hostKey, this.persisted.serverHost); + const hostKey = `${LNC_STORAGE_KEY}:${hash(this._pairingPhrase)}:host`; + Storage.setItem(hostKey, this.persisted.serverHost); } - /** Saves persisted data to EncryptedStorage */ + /** Saves persisted data to Storage */ private _save() { // only save if localKey and remoteKey is set if (!this._localKey) return; if (!this._remoteKey) return; - const baseKey = `${STORAGE_KEY}:${hash(this._pairingPhrase)}`; - EncryptedStorage.setItem(baseKey, JSON.stringify(this.persisted)); + const baseKey = `${LNC_STORAGE_KEY}:${hash(this._pairingPhrase)}`; + Storage.setItem(baseKey, this.persisted); } } -export { STORAGE_KEY, hash }; +export { LNC_STORAGE_KEY, hash }; diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 8a495a0b6..55a6c3ece 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1258,8 +1258,27 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNKeychain (8.1.1): + - RNKeychain (9.2.2): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Codegen - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga - RNReactNativeHapticFeedback (2.2.0): - React-Core - RNReanimated (3.12.0): @@ -1312,7 +1331,7 @@ PODS: - RNVectorIcons (7.1.0): - React - SocketRocket (0.7.0) - - SwiftProtobuf (1.26.0) + - SwiftProtobuf (1.28.2) - TcpSockets (4.0.0): - React - VisionCamera (4.3.2): @@ -1687,7 +1706,7 @@ SPEC CHECKSUMS: RNDeviceInfo: 900bd20e1fd3bfd894e7384cc4a83880c0341bd3 RNFS: 89de7d7f4c0f6bafa05343c578f61118c8282ed8 RNGestureHandler: 326e35460fb6c8c64a435d5d739bea90d7ed4e49 - RNKeychain: 3a8b12f0fdd0b967811a48d9e83d582fc2e483eb + RNKeychain: c339dd4a9f13baac6cf6b617bfad3a0eab5df8b1 RNReactNativeHapticFeedback: a6fb5b7a981683bf58af43e3fb827d4b7ed87f83 RNReanimated: f1a8e928e0a5e8698c354c226d8d5f80629f8658 RNScreens: a2d8a2555b4653d7a19706eb172f855657ac30d7 @@ -1695,7 +1714,7 @@ SPEC CHECKSUMS: RNSVG: e77adf5edb2302f0f10dd03a09e92bb9420d914e RNVectorIcons: 7159ba77d44e14808dcc50fbc03eb5666b1034fc SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d - SwiftProtobuf: 5e8349171e7c2f88f5b9e683cb3cb79d1dc780b3 + SwiftProtobuf: 4dbaffec76a39a8dc5da23b40af1a5dc01a4c02d TcpSockets: c5f030d4dbc758c4c7acb00496dc92c412b02cd2 VisionCamera: 6ad4b86176f6fea45e90e380d3d530bc5e1abff9 Yoga: ae3c32c514802d30f687a04a6a35b348506d411f diff --git a/package.json b/package.json index 62d6fe798..7181ca277 100644 --- a/package.json +++ b/package.json @@ -105,7 +105,7 @@ "react-native-haptic-feedback": "2.2.0", "react-native-hce": "0.1.2", "react-native-image-picker": "5.0.1", - "react-native-keychain": "8.1.1", + "react-native-keychain": "9.2.2", "react-native-level-fs": "3.0.1", "react-native-linear-gradient": "2.6.2", "react-native-nfc-manager": "3.14.14", diff --git a/storage/index.ts b/storage/index.ts new file mode 100644 index 000000000..605c600c1 --- /dev/null +++ b/storage/index.ts @@ -0,0 +1,28 @@ +import * as Keychain from 'react-native-keychain'; + +class Storage { + getItem = async (key: string) => { + const response: any = await Keychain.getInternetCredentials(key); + if (response.password) { + return response.password; + } + return false; + }; + + setItem = async (key: string, value: any) => { + const response = await Keychain.setInternetCredentials( + key, + key, + JSON.stringify(value) + ); + return response; + }; + + removeItem = async (key: string) => { + const response = await Keychain.resetInternetCredentials(key); + return response; + }; +} + +const storage = new Storage(); +export default storage; diff --git a/stores/ActivityStore.ts b/stores/ActivityStore.ts index 03a83c1f5..fc21ab641 100644 --- a/stores/ActivityStore.ts +++ b/stores/ActivityStore.ts @@ -1,5 +1,4 @@ import { action, observable } from 'mobx'; -import EncryptedStorage from 'react-native-encrypted-storage'; // LN import Payment from './../models/Payment'; @@ -15,6 +14,8 @@ import TransactionsStore from './TransactionsStore'; import BackendUtils from './../utils/BackendUtils'; import ActivityFilterUtils from '../utils/ActivityFilterUtils'; +import Storage from '../storage'; + const STORAGE_KEY = 'zeus-activity-filters'; export interface Filter { @@ -78,10 +79,7 @@ export default class ActivityStore { @action public resetFilters = async () => { this.filters = DEFAULT_FILTERS; - await EncryptedStorage.setItem( - STORAGE_KEY, - JSON.stringify(this.filters) - ); + await Storage.setItem(STORAGE_KEY, this.filters); this.setFilters(this.filters); }; @@ -101,10 +99,7 @@ export default class ActivityStore { startDate: undefined, endDate: undefined }; - await EncryptedStorage.setItem( - STORAGE_KEY, - JSON.stringify(this.filters) - ); + await Storage.setItem(STORAGE_KEY, this.filters); }; @action @@ -192,7 +187,7 @@ export default class ActivityStore { public async getFilters() { this.loading = true; try { - const filters = await EncryptedStorage.getItem(STORAGE_KEY); + const filters = await Storage.getItem(STORAGE_KEY); if (filters) { this.filters = JSON.parse(filters, (key, value) => (key === 'startDate' || key === 'endDate') && value @@ -224,7 +219,7 @@ export default class ActivityStore { activity.determineFormattedRemainingTimeUntilExpiry(locale); } }); - await EncryptedStorage.setItem(STORAGE_KEY, JSON.stringify(filters)); + await Storage.setItem(STORAGE_KEY, filters); this.loading = false; }; diff --git a/stores/ChannelBackupStore.ts b/stores/ChannelBackupStore.ts index 8f2ae72a9..052d834b7 100644 --- a/stores/ChannelBackupStore.ts +++ b/stores/ChannelBackupStore.ts @@ -1,6 +1,5 @@ import { action, observable } from 'mobx'; import ReactNativeBlobUtil from 'react-native-blob-util'; -import EncryptedStorage from 'react-native-encrypted-storage'; import * as CryptoJS from 'crypto-js'; import NodeInfoStore from './NodeInfoStore'; @@ -18,8 +17,16 @@ import { LndMobileEventEmitter } from '../utils/LndMobileUtils'; import Base64Utils from '../utils/Base64Utils'; import { errorToUserFriendly } from '../utils/ErrorUtils'; +import Storage from '../storage'; + const BACKUPS_HOST = 'https://backups.lnolymp.us'; +export const LEGACY_LAST_CHANNEL_BACKUP_STATUS = 'LAST_CHANNEL_BACKUP_STATUS'; +export const LEGACY_LAST_CHANNEL_BACKUP_TIME = 'LAST_CHANNEL_BACKUP_TIME'; + +export const LAST_CHANNEL_BACKUP_STATUS = 'zeus-last-channel-backup-status'; +export const LAST_CHANNEL_BACKUP_TIME = 'zeus-last-channel-backup-time'; + export default class ChannelBackupStore { @observable public channelEventsSubscription: any; @observable public backups: Array = []; @@ -44,11 +51,8 @@ export default class ChannelBackupStore { }; logBackupStatus = async (status: string) => { - await EncryptedStorage.setItem('LAST_CHANNEL_BACKUP_STATUS', status); - await EncryptedStorage.setItem( - 'LAST_CHANNEL_BACKUP_TIME', - `${new Date()}` - ); + await Storage.setItem('LAST_CHANNEL_BACKUP_STATUS', status); + await Storage.setItem('LAST_CHANNEL_BACKUP_TIME', `${new Date()}`); }; @action @@ -317,10 +321,8 @@ export default class ChannelBackupStore { public initSubscribeChannelEvents = async () => { // Check if latest channel backup status is success // or if it's over three days ago and trigger backup - const status = await EncryptedStorage.getItem( - 'LAST_CHANNEL_BACKUP_STATUS' - ); - const time = await EncryptedStorage.getItem('LAST_CHANNEL_BACKUP_TIME'); + const status = await Storage.getItem(LAST_CHANNEL_BACKUP_STATUS); + const time = await Storage.getItem(LAST_CHANNEL_BACKUP_TIME); if (status && status === 'ERROR') this.backupChannels(); if (time) { const ONE_HOUR = 60 * 60 * 1000; /* ms */ diff --git a/stores/ContactStore.ts b/stores/ContactStore.ts index a683d8ade..fbae598d8 100644 --- a/stores/ContactStore.ts +++ b/stores/ContactStore.ts @@ -1,7 +1,10 @@ import { action, observable } from 'mobx'; import { v4 as uuidv4 } from 'uuid'; import Contact from '../models/Contact'; -import EncryptedStorage from 'react-native-encrypted-storage'; +import Storage from '../storage'; + +export const LEGACY_CONTACTS_KEY = 'zeus-contacts'; +export const MODERN_CONTACTS_KEY = 'zeus-contacts-v2'; export default class ContactStore { @observable public loading: boolean = true; @@ -13,8 +16,8 @@ export default class ContactStore { try { this.loading = true; console.log('LOADING CONTACTS.....'); - const contactsString = await EncryptedStorage.getItem( - 'zeus-contacts' + const contactsString: any = await Storage.getItem( + MODERN_CONTACTS_KEY ); if (contactsString) { const allContacts: Contact[] = JSON.parse(contactsString); @@ -39,8 +42,8 @@ export default class ContactStore { try { const compactedContactDetails = this.compactContactArrays(contactDetails); - const contactsString = await EncryptedStorage.getItem( - 'zeus-contacts' + const contactsString: any = await Storage.getItem( + MODERN_CONTACTS_KEY ); const existingContacts: Contact[] = contactsString ? JSON.parse(contactsString) @@ -57,10 +60,7 @@ export default class ContactStore { updatedContacts.sort((a, b) => a.name.localeCompare(b.name)); // Save the updated contacts to encrypted storage - await EncryptedStorage.setItem( - 'zeus-contacts', - JSON.stringify(updatedContacts) - ); + await Storage.setItem(MODERN_CONTACTS_KEY, updatedContacts); console.log('Contact updated successfully!', updatedContacts); @@ -80,10 +80,7 @@ export default class ContactStore { ); // Save the updated contacts to encrypted storage - await EncryptedStorage.setItem( - 'zeus-contacts', - JSON.stringify(updatedContacts) - ); + await Storage.setItem(MODERN_CONTACTS_KEY, updatedContacts); console.log('Contact saved successfully!'); @@ -111,8 +108,8 @@ export default class ContactStore { public deleteContact = async (navigation: any) => { if (this.prefillContact) { try { - const contactsString = await EncryptedStorage.getItem( - 'zeus-contacts' + const contactsString: any = await Storage.getItem( + MODERN_CONTACTS_KEY ); const existingContacts: Contact[] = contactsString ? JSON.parse(contactsString) @@ -123,10 +120,7 @@ export default class ContactStore { contact.contactId !== this.prefillContact.contactId ); - await EncryptedStorage.setItem( - 'zeus-contacts', - JSON.stringify(updatedContacts) - ); + await Storage.setItem(MODERN_CONTACTS_KEY, updatedContacts); console.log('Contact deleted successfully!'); diff --git a/stores/InventoryStore.ts b/stores/InventoryStore.ts index 43710824f..1c9f11e77 100644 --- a/stores/InventoryStore.ts +++ b/stores/InventoryStore.ts @@ -1,10 +1,13 @@ import { action, observable } from 'mobx'; import Product from '../models/Product'; import ProductCategory from '../models/ProductCategory'; -import EncryptedStorage from 'react-native-encrypted-storage'; +import Storage from '../storage'; -const CATEGORY_KEY = 'zeus-product-categories'; -const PRODUCT_KEY = 'zeus-products'; +export const LEGACY_CATEGORY_KEY = 'zeus-product-categories'; +export const LEGACY_PRODUCT_KEY = 'zeus-products'; + +export const CATEGORY_KEY = 'zeus-product-categories-v2'; +export const PRODUCT_KEY = 'zeus-products-v2'; export default class InventoryStore { @observable categories: Array = []; @@ -16,12 +19,12 @@ export default class InventoryStore { this.loading = true; try { // Retrieve the categories - const categories = await EncryptedStorage.getItem(CATEGORY_KEY); + const categories = await Storage.getItem(CATEGORY_KEY); if (categories) { this.categories = JSON.parse(categories) || []; } // Retrieve the products - const products = await EncryptedStorage.getItem(PRODUCT_KEY); + const products = await Storage.getItem(PRODUCT_KEY); if (products) { this.products = JSON.parse(products) || []; } @@ -40,7 +43,7 @@ export default class InventoryStore { @action public async setCategories(categories: string) { this.loading = true; - await EncryptedStorage.setItem(CATEGORY_KEY, categories); + await Storage.setItem(CATEGORY_KEY, categories); this.loading = false; return categories; } @@ -90,7 +93,7 @@ export default class InventoryStore { @action public async setProducts(products: string) { this.loading = true; - await EncryptedStorage.setItem(PRODUCT_KEY, products); + await Storage.setItem(PRODUCT_KEY, products); this.loading = false; return products; } diff --git a/stores/LSPStore.ts b/stores/LSPStore.ts index 93664726e..f88cd1f34 100644 --- a/stores/LSPStore.ts +++ b/stores/LSPStore.ts @@ -20,6 +20,9 @@ import { LndMobileEventEmitter } from '../utils/LndMobileUtils'; import { localeString } from '../utils/LocaleUtils'; import { errorToUserFriendly } from '../utils/ErrorUtils'; +export const LEGACY_LSPS1_ORDERS_KEY = 'orderResponses'; +export const LSPS1_ORDERS_KEY = 'zeus-lsps1-orders'; + export default class LSPStore { @observable public info: any = {}; @observable public zeroConfFee: number | undefined; diff --git a/stores/LightningAddressStore.ts b/stores/LightningAddressStore.ts index 1882c302a..620da9185 100644 --- a/stores/LightningAddressStore.ts +++ b/stores/LightningAddressStore.ts @@ -1,7 +1,6 @@ import { Platform } from 'react-native'; import { action, observable } from 'mobx'; import ReactNativeBlobUtil from 'react-native-blob-util'; -import EncryptedStorage from 'react-native-encrypted-storage'; import { Notifications } from 'react-native-notifications'; import BigNumber from 'bignumber.js'; @@ -25,12 +24,17 @@ import Base64Utils from '../utils/Base64Utils'; import { sleep } from '../utils/SleepUtils'; import { localeString } from '../utils/LocaleUtils'; +import Storage from '../storage'; + const LNURL_HOST = 'https://zeuspay.com/api'; const LNURL_SOCKET_HOST = 'https://zeuspay.com'; const LNURL_SOCKET_PATH = '/stream'; -const ADDRESS_ACTIVATED_STRING = 'olympus-lightning-address'; -const HASHES_STORAGE_STRING = 'olympus-lightning-address-hashes'; +export const LEGACY_ADDRESS_ACTIVATED_STRING = 'olympus-lightning-address'; +export const LEGACY_HASHES_STORAGE_STRING = 'olympus-lightning-address-hashes'; + +export const ADDRESS_ACTIVATED_STRING = 'zeuspay-lightning-address'; +export const HASHES_STORAGE_STRING = 'zeuspay-lightning-address-hashes'; export default class LightningAddressStore { @observable public lightningAddress: string; @@ -65,20 +69,14 @@ export default class LightningAddressStore { @action public deleteAndGenerateNewPreimages = async () => { this.loading = true; - await EncryptedStorage.setItem( - HASHES_STORAGE_STRING, - JSON.stringify('') - ); + await Storage.setItem(HASHES_STORAGE_STRING, ''); this.generatePreimages(true); }; @action public DEV_deleteLocalHashes = async () => { this.loading = true; - await EncryptedStorage.setItem( - HASHES_STORAGE_STRING, - JSON.stringify('') - ); + await Storage.setItem(HASHES_STORAGE_STRING, ''); await this.status(); this.loading = false; }; @@ -86,7 +84,7 @@ export default class LightningAddressStore { @action public getPreimageMap = async () => { this.loading = true; - const map = await EncryptedStorage.getItem(HASHES_STORAGE_STRING); + const map = await Storage.getItem(HASHES_STORAGE_STRING); if (map) { this.preimageMap = JSON.parse(map); @@ -100,7 +98,7 @@ export default class LightningAddressStore { @action public getLightningAddressActivated = async () => { this.loading = true; - const lightningAddressActivated = await EncryptedStorage.getItem( + const lightningAddressActivated = await Storage.getItem( ADDRESS_ACTIVATED_STRING ); @@ -114,7 +112,7 @@ export default class LightningAddressStore { }; setLightningAddress = async (handle: string, domain: string) => { - await EncryptedStorage.setItem(ADDRESS_ACTIVATED_STRING, 'true'); + await Storage.setItem(ADDRESS_ACTIVATED_STRING, true); this.lightningAddressActivated = true; this.lightningAddressHandle = handle; this.lightningAddressDomain = domain; @@ -123,16 +121,13 @@ export default class LightningAddressStore { deleteHash = async (hash: string) => { const hashesString = - (await EncryptedStorage.getItem(HASHES_STORAGE_STRING)) || '{}'; + (await Storage.getItem(HASHES_STORAGE_STRING)) || '{}'; const oldHashes = JSON.parse(hashesString); delete oldHashes[hash]; const newHashes = oldHashes; - return await EncryptedStorage.setItem( - HASHES_STORAGE_STRING, - JSON.stringify(newHashes) - ); + return await Storage.setItem(HASHES_STORAGE_STRING, newHashes); }; @action @@ -172,9 +167,7 @@ export default class LightningAddressStore { } } - const hashesString = await EncryptedStorage.getItem( - HASHES_STORAGE_STRING - ); + const hashesString = await Storage.getItem(HASHES_STORAGE_STRING); let newHashes; if (hashesString) { @@ -189,10 +182,7 @@ export default class LightningAddressStore { }; } - await EncryptedStorage.setItem( - HASHES_STORAGE_STRING, - JSON.stringify(newHashes) - ); + await Storage.setItem(HASHES_STORAGE_STRING, newHashes); return new Promise((resolve, reject) => { ReactNativeBlobUtil.fetch( @@ -931,7 +921,7 @@ export default class LightningAddressStore { @action public updatePushCredentials = async () => { const DEVICE_TOKEN_KEY = 'zeus-notification-device-token'; - const token = await EncryptedStorage.getItem(DEVICE_TOKEN_KEY); + const token = await Storage.getItem(DEVICE_TOKEN_KEY); // only push update if the device token has changed if (this.deviceToken && (!token || this.deviceToken !== token)) { @@ -939,10 +929,7 @@ export default class LightningAddressStore { device_token: this.deviceToken, device_platform: Platform.OS }).then(async () => { - await EncryptedStorage.setItem( - DEVICE_TOKEN_KEY, - this.deviceToken - ); + await Storage.setItem(DEVICE_TOKEN_KEY, this.deviceToken); }); } }; diff --git a/stores/LnurlPayStore.ts b/stores/LnurlPayStore.ts index 79345621d..741af2e91 100644 --- a/stores/LnurlPayStore.ts +++ b/stores/LnurlPayStore.ts @@ -1,6 +1,5 @@ import { action } from 'mobx'; import { LNURLPaySuccessAction } from 'js-lnurl'; -import EncryptedStorage from 'react-native-encrypted-storage'; import { schnorr } from '@noble/curves/secp256k1'; import { hexToBytes } from '@noble/hashes/utils'; import hashjs from 'hash.js'; @@ -16,6 +15,8 @@ import { relayInit } from 'nostr-tools'; +import Storage from '../storage'; + import SettingsStore from './SettingsStore'; import NodeInfoStore from './NodeInfoStore'; @@ -26,7 +27,7 @@ export interface LnurlPayTransaction { metadata_hash: string; successAction: LNURLPaySuccessAction; time: number; - metadata?: Metadata; // only after an independent load from EncryptedStorage. + metadata?: Metadata; // only after an independent load from Storage. } interface Metadata { @@ -71,12 +72,10 @@ export default class LnurlPayStore { @action public load = async (paymentHash: string): Promise => { - let lnurlpaytx: any = await EncryptedStorage.getItem( - 'lnurlpay:' + paymentHash - ); + let lnurlpaytx: any = await Storage.getItem('lnurlpay:' + paymentHash); if (lnurlpaytx) { lnurlpaytx = JSON.parse(lnurlpaytx); - const metadata: any = await EncryptedStorage.getItem( + const metadata: any = await Storage.getItem( 'lnurlpay:' + lnurlpaytx.metadata_hash ); if (metadata) { @@ -118,14 +117,8 @@ export default class LnurlPayStore { last_stored: now }; - await EncryptedStorage.setItem( - 'lnurlpay:' + paymentHash, - JSON.stringify(transactionData) - ); - await EncryptedStorage.setItem( - 'lnurlpay:' + descriptionHash, - JSON.stringify(metadataEntry) - ); + await Storage.setItem('lnurlpay:' + paymentHash, transactionData); + await Storage.setItem('lnurlpay:' + descriptionHash, metadataEntry); this.paymentHash = paymentHash; this.successAction = successAction; diff --git a/stores/NotesStore.ts b/stores/NotesStore.ts index f88236050..35239da99 100644 --- a/stores/NotesStore.ts +++ b/stores/NotesStore.ts @@ -1,7 +1,8 @@ import { action, observable } from 'mobx'; -import EncryptedStorage from 'react-native-encrypted-storage'; +import Storage from '../storage'; -const NOTES_KEY = 'note-Keys'; +export const LEGACY_NOTES_KEY = 'note-Keys'; +export const MODERN_NOTES_KEY = 'zeus-notes-v2'; export default class NotesStore { @observable public noteKeys: string[] = []; @@ -37,13 +38,13 @@ export default class NotesStore { public async loadNoteKeys() { console.log('Loading notes...'); try { - const storedKeys = await EncryptedStorage.getItem(NOTES_KEY); + const storedKeys: any = await Storage.getItem(MODERN_NOTES_KEY); if (storedKeys) { this.noteKeys = JSON.parse(storedKeys); // Load all notes await Promise.all( this.noteKeys.map(async (key) => { - const note = await EncryptedStorage.getItem(key); + const note: any = await Storage.getItem(key); if (note) { this.notes[key] = note; } @@ -60,10 +61,7 @@ export default class NotesStore { writeNoteKeysToLocalStorage = async () => { try { - await EncryptedStorage.setItem( - NOTES_KEY, - JSON.stringify(this.noteKeys) - ); + await Storage.setItem(MODERN_NOTES_KEY, this.noteKeys); } catch (error) { console.error('Error saving to encrypted storage'); } diff --git a/stores/PosStore.ts b/stores/PosStore.ts index e149f0431..cda001317 100644 --- a/stores/PosStore.ts +++ b/stores/PosStore.ts @@ -1,5 +1,4 @@ import { action, observable } from 'mobx'; -import EncryptedStorage from 'react-native-encrypted-storage'; import ReactNativeBlobUtil from 'react-native-blob-util'; import BigNumber from 'bignumber.js'; import { v4 as uuidv4 } from 'uuid'; @@ -11,6 +10,8 @@ import Order from '../models/Order'; import { SATS_PER_BTC } from '../utils/UnitsUtils'; +import Storage from '../storage'; + export interface orderPaymentInfo { orderId: string; orderTotal: string; @@ -22,8 +23,11 @@ export interface orderPaymentInfo { preimage?: string; } -const POS_HIDDEN_KEY = 'pos-hidden'; -const POS_STANDALONE_KEY = 'pos-standalone'; +export const LEGACY_POS_HIDDEN_KEY = 'pos-hidden'; +export const LEGACY_POS_STANDALONE_KEY = 'pos-standalone'; + +export const POS_HIDDEN_KEY = 'zeus-pos-hidden'; +export const POS_STANDALONE_KEY = 'zeus-pos-standalone'; export default class PosStore { @observable public currentOrder: Order | null = null; @@ -51,13 +55,10 @@ export default class PosStore { @action public hideOrder = async (orderId: string) => { - const hiddenOrdersItem = await EncryptedStorage.getItem(POS_HIDDEN_KEY); + const hiddenOrdersItem = await Storage.getItem(POS_HIDDEN_KEY); const hiddenOrders = JSON.parse(hiddenOrdersItem || '[]'); hiddenOrders.push(orderId); - await EncryptedStorage.setItem( - POS_HIDDEN_KEY, - JSON.stringify(hiddenOrders) - ); + await Storage.setItem(POS_HIDDEN_KEY, hiddenOrders); }; @action @@ -85,19 +86,16 @@ export default class PosStore { tx, preimage }: orderPaymentInfo) => - EncryptedStorage.setItem( - `pos-${orderId}`, - JSON.stringify({ - orderId, - orderTotal, - orderTip, - exchangeRate, - rate, - type, - tx, - preimage - }) - ); + Storage.setItem(`pos-${orderId}`, { + orderId, + orderTotal, + orderTip, + exchangeRate, + rate, + type, + tx, + preimage + }); @action public clearCurrentOrder = () => (this.currentOrder = null); @@ -235,9 +233,9 @@ export default class PosStore { this.openOrders.push(updateOrder); } - await EncryptedStorage.setItem( + await Storage.setItem( POS_STANDALONE_KEY, - JSON.stringify(this.openOrders.concat(this.paidOrders)) + this.openOrders.concat(this.paidOrders) ); this.clearCurrentOrder(); @@ -265,10 +263,10 @@ export default class PosStore { this.loading = true; this.error = false; - const soOrders = await EncryptedStorage.getItem(POS_STANDALONE_KEY); + const soOrders = await Storage.getItem(POS_STANDALONE_KEY); // fetch hidden orders - orders customers couldn't pay - const hiddenOrdersItem = await EncryptedStorage.getItem(POS_HIDDEN_KEY); + const hiddenOrdersItem = await Storage.getItem(POS_HIDDEN_KEY); const hiddenOrders = JSON.parse(hiddenOrdersItem || '[]'); const orders = JSON.parse(soOrders || '[]').map( @@ -277,9 +275,7 @@ export default class PosStore { const enrichedOrders = await Promise.all( orders.map(async (order: any) => { - const payment = await EncryptedStorage.getItem( - `pos-${order.id}` - ); + const payment = await Storage.getItem(`pos-${order.id}`); // mark order if hidden if (hiddenOrders.includes(order.id)) { order.hidden = true; @@ -356,14 +352,14 @@ export default class PosStore { }); // fetch hidden orders - orders customers couldn't pay - const hiddenOrdersItem = await EncryptedStorage.getItem( + const hiddenOrdersItem = await Storage.getItem( POS_HIDDEN_KEY ); const hiddenOrders = JSON.parse(hiddenOrdersItem || '[]'); const enrichedOrders = await Promise.all( orders.map(async (order: any) => { - const payment = await EncryptedStorage.getItem( + const payment = await Storage.getItem( `pos-${order.id}` ); // mark order if hidden @@ -478,14 +474,14 @@ export default class PosStore { 'orderId, totalSats, tipSats, rateFull, rateNumerical, type, tx\n'; // fetch hidden orders - orders customers couldn't pay - const hiddenOrdersItem = await EncryptedStorage.getItem( + const hiddenOrdersItem = await Storage.getItem( POS_HIDDEN_KEY ); const hiddenOrders = JSON.parse(hiddenOrdersItem || '[]'); const enrichedOrders = await Promise.all( orders.map(async (order: any) => { - const payment = await EncryptedStorage.getItem( + const payment = await Storage.getItem( `pos-${order.id}` ); let tip; @@ -548,7 +544,7 @@ export default class PosStore { public getOrderPaymentById = async ( orderId: string ): Promise => { - const payment = await EncryptedStorage.getItem(`pos-${orderId}`); + const payment = await Storage.getItem(`pos-${orderId}`); if (payment) { return JSON.parse(payment); diff --git a/stores/SettingsStore.ts b/stores/SettingsStore.ts index d37bd7663..4210b51b3 100644 --- a/stores/SettingsStore.ts +++ b/stores/SettingsStore.ts @@ -5,12 +5,21 @@ import EncryptedStorage from 'react-native-encrypted-storage'; import isEqual from 'lodash/isEqual'; import BackendUtils from '../utils/BackendUtils'; +import { getSupportedBiometryType } from '../utils/BiometricUtils'; import { localeString } from '../utils/LocaleUtils'; +import MigrationsUtils from '../utils/MigrationUtils'; import { doTorRequest, RequestMethod } from '../utils/TorUtils'; -import { getSupportedBiometryType } from '../utils/BiometricUtils'; + +import Storage from '../storage'; // lndhub -import LoginRequest from './../models/LoginRequest'; +import LoginRequest from '../models/LoginRequest'; + +const LEGACY_STORAGE_KEY = 'zeus-settings'; +export const STORAGE_KEY = 'zeus-settings-v2'; + +export const LEGACY_CURRENCY_CODES_KEY = 'currency-codes'; +export const CURRENCY_CODES_KEY = 'zeus-currency-codes'; export interface Node { host?: string; @@ -329,7 +338,7 @@ export const LOCALE_KEYS = [ ]; // this mapping is only for migration and does not need to be updated when new languages are added -const localeMigrationMapping: { [oldLocale: string]: string } = { +export const localeMigrationMapping: { [oldLocale: string]: string } = { English: 'en', Español: 'es', Português: 'pt', @@ -1090,9 +1099,7 @@ export const DEFAULT_NEUTRINO_PEERS_TESTNET = [ 'testnet.blixtwallet.com' ]; -const STORAGE_KEY = 'zeus-settings'; - -const DEFAULT_SLIDE_TO_PAY_THRESHOLD = 10000; +export const DEFAULT_SLIDE_TO_PAY_THRESHOLD = 10000; export default class SettingsStore { @observable settings: Settings = { @@ -1388,254 +1395,60 @@ export default class SettingsStore { public async getSettings(silentUpdate: boolean = false) { if (!silentUpdate) this.loading = true; try { - // Retrieve the settings - const settings = await EncryptedStorage.getItem(STORAGE_KEY); - if (settings) { - const newSettings = JSON.parse(settings) as Settings; - if (!newSettings.fiatRatesSource) { - newSettings.fiatRatesSource = DEFAULT_FIAT_RATES_SOURCE; - } - - // migrate fiat settings from older versions - if (!newSettings.fiat || newSettings.fiat === 'Disabled') { - newSettings.fiat = DEFAULT_FIAT; - newSettings.fiatEnabled = false; - } else if (newSettings.fiatEnabled == null) { - newSettings.fiatEnabled = true; - } - - // set default LSPs if not defined - if (newSettings.enableLSP === undefined) { - newSettings.enableLSP = true; - } - if (!newSettings.lspMainnet) { - newSettings.lspMainnet = DEFAULT_LSP_MAINNET; - } - if (!newSettings.lspTestnet) { - newSettings.lspTestnet = DEFAULT_LSP_TESTNET; - } - - // default Lightning Address settings - if (!newSettings.lightningAddress) { - newSettings.lightningAddress = { - enabled: false, - automaticallyAccept: true, - automaticallyAcceptAttestationLevel: 2, - automaticallyRequestOlympusChannels: false, // deprecated - routeHints: false, - allowComments: true, - nostrPrivateKey: '', - nostrRelays: DEFAULT_NOSTR_RELAYS, - notifications: 0 - }; - } - - // migrate locale to ISO 639-1 - if ( - newSettings.locale != null && - localeMigrationMapping[newSettings.locale] - ) { - newSettings.locale = - localeMigrationMapping[newSettings.locale]; - } - - const MOD_KEY = 'lsp-taproot-mod'; - const mod = await EncryptedStorage.getItem(MOD_KEY); - if (!mod) { - newSettings.requestSimpleTaproot = true; - this.setSettings(JSON.stringify(newSettings)); - await EncryptedStorage.setItem(MOD_KEY, 'true'); - } - - const MOD_KEY2 = 'lsp-preview-mod'; - const mod2 = await EncryptedStorage.getItem(MOD_KEY2); - if (!mod2) { - if ( - newSettings?.lspMainnet === - 'https://lsp-preview.lnolymp.us' - ) { - newSettings.lspMainnet = DEFAULT_LSP_MAINNET; - } - if ( - newSettings?.lspTestnet === - 'https://testnet-lsp.lnolymp.us' - ) { - newSettings.lspTestnet = DEFAULT_LSP_TESTNET; - } - this.setSettings(JSON.stringify(newSettings)); - await EncryptedStorage.setItem(MOD_KEY2, 'true'); - } - - const MOD_KEY3 = 'neutrino-peers-mod1'; - const mod3 = await EncryptedStorage.getItem(MOD_KEY3); - if (!mod3) { - const neutrinoPeersMainnetOld = [ - 'btcd1.lnolymp.us', - 'btcd2.lnolymp.us', - 'btcd-mainnet.lightning.computer', - 'node.eldamar.icu', - 'noad.sathoarder.com' - ]; - if ( - JSON.stringify(newSettings?.neutrinoPeersMainnet) === - JSON.stringify(neutrinoPeersMainnetOld) - ) { - newSettings.neutrinoPeersMainnet = - DEFAULT_NEUTRINO_PEERS_MAINNET; - } - this.setSettings(JSON.stringify(newSettings)); - await EncryptedStorage.setItem(MOD_KEY3, 'true'); - } - - const MOD_KEY4 = 'lsps1-hosts1'; - const mod4 = await EncryptedStorage.getItem(MOD_KEY4); - if (!mod4) { - if (!newSettings?.lsps1HostMainnet) { - newSettings.lsps1HostMainnet = - DEFAULT_LSPS1_HOST_MAINNET; - } - if (!newSettings?.lsps1HostTestnet) { - newSettings.lsps1HostTestnet = - DEFAULT_LSPS1_HOST_TESTNET; - } - if (!newSettings?.lsps1PubkeyMainnet) { - newSettings.lsps1PubkeyMainnet = - DEFAULT_LSPS1_PUBKEY_MAINNET; - } - if (!newSettings?.lsps1PubkeyTestnet) { - newSettings.lsps1PubkeyTestnet = - DEFAULT_LSPS1_PUBKEY_TESTNET; - } - if (!newSettings?.lsps1RestMainnet) { - newSettings.lsps1RestMainnet = - DEFAULT_LSPS1_REST_MAINNET; - } - if (!newSettings?.lsps1RestTestnet) { - newSettings.lsps1RestTestnet = - DEFAULT_LSPS1_REST_TESTNET; - } - - if (!newSettings?.lsps1Token) { - newSettings.lsps1Token = ''; - } - - if (!newSettings?.lsps1ShowPurchaseButton) { - newSettings.lsps1ShowPurchaseButton = true; - } - - this.setSettings(JSON.stringify(newSettings)); - await EncryptedStorage.setItem(MOD_KEY4, 'true'); - } - - const MOD_KEY5 = 'millisat_amounts'; - const mod5 = await EncryptedStorage.getItem(MOD_KEY5); - if (!mod5) { - if (!newSettings?.display.showMillisatoshiAmounts) { - newSettings.display.showMillisatoshiAmounts = true; - } - - this.setSettings(JSON.stringify(newSettings)); - await EncryptedStorage.setItem(MOD_KEY5, 'true'); - } - - const MOD_KEY6 = 'egs-host'; - const mod6 = await EncryptedStorage.getItem(MOD_KEY6); - if (!mod6) { - if (!newSettings?.speedloader) { - newSettings.speedloader = DEFAULT_SPEEDLOADER; - newSettings.customSpeedloader = ''; - } - - this.setSettings(JSON.stringify(newSettings)); - await EncryptedStorage.setItem(MOD_KEY6, 'true'); - } - - // switch off bimodal pathfinding while bug exists - // https://github.com/lightningnetwork/lnd/issues/9085 - const MOD_KEY7 = 'bimodal-bug-9085'; - const mod7 = await EncryptedStorage.getItem(MOD_KEY7); - if (!mod7) { - if (newSettings?.bimodalPathfinding) { - newSettings.bimodalPathfinding = false; - } + const modernSettings: any = await Storage.getItem(STORAGE_KEY); - this.setSettings(JSON.stringify(newSettings)); - await EncryptedStorage.setItem(MOD_KEY7, 'true'); - } + if (modernSettings) { + console.log('attempting to load modern settings'); + this.settings = JSON.parse(modernSettings); + } else { + console.log('attempting to load legacy settings'); + + // Retrieve the settings + const settings = await EncryptedStorage.getItem( + LEGACY_STORAGE_KEY + ); + if (settings) { + const newSettings = + await MigrationsUtils.legacySettingsMigrations( + settings + ); - const MOD_KEY8 = 'nostr-relays-2024'; - const mod8 = await EncryptedStorage.getItem(MOD_KEY8); - if (!mod8) { - if ( - JSON.stringify( - newSettings?.lightningAddress?.nostrRelays - ) === JSON.stringify(DEFAULT_NOSTR_RELAYS_2023) - ) { - newSettings.lightningAddress.nostrRelays = - DEFAULT_NOSTR_RELAYS; + if (!isEqual(this.settings, newSettings)) { + this.settings = newSettings; } - this.setSettings(JSON.stringify(newSettings)); - await EncryptedStorage.setItem(MOD_KEY8, 'true'); - } - - // migrate old POS squareEnabled setting to posEnabled - if (newSettings?.pos?.squareEnabled) { - newSettings.pos.posEnabled = PosEnabled.Square; - newSettings.pos.squareEnabled = false; - } - - if (!newSettings.neutrinoPeersMainnet) { - newSettings.neutrinoPeersMainnet = - DEFAULT_NEUTRINO_PEERS_MAINNET; - } - if (!newSettings.neutrinoPeersTestnet) { - newSettings.neutrinoPeersTestnet = - DEFAULT_NEUTRINO_PEERS_TESTNET; - } - - if (newSettings.payments == null) { - newSettings.payments = { - slideToPayThreshold: DEFAULT_SLIDE_TO_PAY_THRESHOLD - }; - } else if (newSettings.payments.slideToPayThreshold == null) { - newSettings.payments.slideToPayThreshold = - DEFAULT_SLIDE_TO_PAY_THRESHOLD; - } - - if (!isEqual(this.settings, newSettings)) { - this.settings = newSettings; + await MigrationsUtils.storageMigrationV2(newSettings); + } else { + console.log('No legacy settings stored'); } + } - const node: any = - newSettings.nodes?.length && - newSettings.nodes[newSettings.selectedNode || 0]; - if (node) { - this.host = node.host; - this.port = node.port; - this.url = node.url; - this.username = node.username; - this.password = node.password; - this.lndhubUrl = node.lndhubUrl; - this.macaroonHex = node.macaroonHex; - this.rune = node.rune; - this.accessKey = node.accessKey; - this.dismissCustodialWarning = node.dismissCustodialWarning; - this.implementation = node.implementation || 'lnd'; - this.certVerification = node.certVerification || false; - this.enableTor = node.enableTor; - // LNC - this.pairingPhrase = node.pairingPhrase; - this.mailboxServer = node.mailboxServer; - this.customMailboxServer = node.customMailboxServer; - // Embeded lnd - this.seedPhrase = node.seedPhrase; - this.walletPassword = node.walletPassword; - this.adminMacaroon = node.adminMacaroon; - this.embeddedLndNetwork = node.embeddedLndNetwork; - } - } else { - console.log('No settings stored'); + const node: any = + this.settings?.nodes?.length && + this.settings?.nodes[this.settings.selectedNode || 0]; + if (node) { + this.host = node.host; + this.port = node.port; + this.url = node.url; + this.username = node.username; + this.password = node.password; + this.lndhubUrl = node.lndhubUrl; + this.macaroonHex = node.macaroonHex; + this.rune = node.rune; + this.accessKey = node.accessKey; + this.dismissCustodialWarning = node.dismissCustodialWarning; + this.implementation = node.implementation || 'lnd'; + this.certVerification = node.certVerification || false; + this.enableTor = node.enableTor; + // LNC + this.pairingPhrase = node.pairingPhrase; + this.mailboxServer = node.mailboxServer; + this.customMailboxServer = node.customMailboxServer; + // Embeded lnd + this.seedPhrase = node.seedPhrase; + this.walletPassword = node.walletPassword; + this.adminMacaroon = node.adminMacaroon; + this.embeddedLndNetwork = node.embeddedLndNetwork; } } catch (error) { console.error('Could not load settings', error); @@ -1647,9 +1460,9 @@ export default class SettingsStore { } @action - public async setSettings(settings: string) { + public async setSettings(settings: any) { this.loading = true; - await EncryptedStorage.setItem(STORAGE_KEY, settings); + await Storage.setItem(STORAGE_KEY, settings); this.loading = false; return settings; } @@ -1662,7 +1475,7 @@ export default class SettingsStore { ...newSetting }; - await this.setSettings(JSON.stringify(newSettings)); + await this.setSettings(newSettings); // ensure we get the enhanced settings set const settings = await this.getSettings(true); return settings; @@ -1836,7 +1649,7 @@ export default class SettingsStore { public checkBiometricsStatus = async () => { const biometryType = await getSupportedBiometryType(); if (this.settings.supportedBiometryType !== biometryType) { - this.updateSettings({ + await this.updateSettings({ supportedBiometryType: biometryType }); } diff --git a/stores/UTXOsStore.ts b/stores/UTXOsStore.ts index 010fe5eb2..f97952686 100644 --- a/stores/UTXOsStore.ts +++ b/stores/UTXOsStore.ts @@ -1,5 +1,5 @@ import { action, observable } from 'mobx'; -import EncryptedStorage from 'react-native-encrypted-storage'; +import Storage from '../storage'; import SettingsStore from './SettingsStore'; @@ -10,6 +10,9 @@ import Utxo from '../models/Utxo'; import { walletrpc } from '../proto/lightning'; +export const LEGACY_HIDDEN_ACCOUNTS_KEY = 'hidden-accounts'; +export const HIDDEN_ACCOUNTS_KEY = 'zeus-hidden-accounts'; + export default class UTXOsStore { // utxos @observable public loading = false; @@ -94,17 +97,12 @@ export default class UTXOsStore { public hideAccount = async (name: string) => { let hiddenAccounts: Array = []; try { - const hiddenString = await EncryptedStorage.getItem( - 'hidden-accounts' - ); + const hiddenString = await Storage.getItem(HIDDEN_ACCOUNTS_KEY); hiddenAccounts = JSON.parse(hiddenString || '[]'); if (!hiddenAccounts.includes(name)) hiddenAccounts.push(name); - await EncryptedStorage.setItem( - 'hidden-accounts', - JSON.stringify(hiddenAccounts) - ); + await Storage.setItem(HIDDEN_ACCOUNTS_KEY, hiddenAccounts); this.listAccounts(); } catch (error) { @@ -115,19 +113,14 @@ export default class UTXOsStore { public unhideAccount = async (name: string) => { let hiddenAccounts: Array = []; try { - const hiddenString = await EncryptedStorage.getItem( - 'hidden-accounts' - ); + const hiddenString = await Storage.getItem(HIDDEN_ACCOUNTS_KEY); hiddenAccounts = JSON.parse(hiddenString || '[]'); if (hiddenAccounts.includes(name)) { hiddenAccounts = hiddenAccounts.filter((item) => item !== name); } - await EncryptedStorage.setItem( - 'hidden-accounts', - JSON.stringify(hiddenAccounts) - ); + await Storage.setItem(HIDDEN_ACCOUNTS_KEY, hiddenAccounts); this.listAccounts(); } catch (error) { @@ -139,9 +132,7 @@ export default class UTXOsStore { public listAccounts = async (data?: any) => { let hiddenAccounts: Array = []; try { - const hiddenString = await EncryptedStorage.getItem( - 'hidden-accounts' - ); + const hiddenString = await Storage.getItem(HIDDEN_ACCOUNTS_KEY); if (hiddenString) { hiddenAccounts = JSON.parse(hiddenString); } diff --git a/stores/UnitsStore.ts b/stores/UnitsStore.ts index 5cfca5ec6..f52942703 100644 --- a/stores/UnitsStore.ts +++ b/stores/UnitsStore.ts @@ -1,5 +1,5 @@ import { action, observable } from 'mobx'; -import EncryptedStorage from 'react-native-encrypted-storage'; +import Storage from '../storage'; import SettingsStore from './SettingsStore'; import FiatStore from './FiatStore'; @@ -13,7 +13,8 @@ import FeeUtils from '../utils/FeeUtils'; type Units = 'sats' | 'BTC' | 'fiat'; -const UNIT_KEY = 'zeus-units'; +export const LEGACY_UNIT_KEY = 'zeus-units'; +export const UNIT_KEY = 'zeus-units-v2'; interface ValueDisplayProps { amount: string; @@ -38,14 +39,14 @@ export default class UnitsStore { } getUnits = async () => { - const units = await EncryptedStorage.getItem(UNIT_KEY); + const units = await Storage.getItem(UNIT_KEY); if (units) this.units = units; }; @action public changeUnits = async () => { this.units = this.getNextUnit(); - await EncryptedStorage.setItem(UNIT_KEY, this.units); + await Storage.setItem(UNIT_KEY, this.units); }; public getNextUnit = () => { diff --git a/utils/MigrationUtils.test.ts b/utils/MigrationUtils.test.ts new file mode 100644 index 000000000..aa5aa63b6 --- /dev/null +++ b/utils/MigrationUtils.test.ts @@ -0,0 +1,230 @@ +jest.mock('react-native-blob-util', () => ({})); +jest.mock('react-native-encrypted-storage', () => ({ + getItem: jest.fn(), + setItem: jest.fn() +})); +jest.mock('../stores/Stores', () => ({ + settingsStore: { + setSettings: jest.fn() + } +})); +jest.mock('../stores/ChannelBackupStore', () => ({})); +jest.mock('../stores/LightningAddressStore', () => ({})); +jest.mock('../stores/LSPStore', () => ({})); +jest.mock('../utils/BackendUtils', () => ({})); + +jest.mock('../stores/SettingsStore', () => ({ + DEFAULT_FIAT_RATES_SOURCE: 'Zeus', + DEFAULT_FIAT: 'USD', + DEFAULT_LSP_MAINNET: 'https://0conf.lnolymp.us', + DEFAULT_LSP_TESTNET: 'https://testnet-0conf.lnolymp.us', + DEFAULT_NOSTR_RELAYS: [ + 'wss://relay.damus.io', + 'wss://nostr.land', + 'wss://nostr.wine', + 'wss://nos.lol', + 'wss://relay.snort.social' + ], + DEFAULT_NEUTRINO_PEERS_MAINNET: [ + 'btcd1.lnolymp.us', + 'btcd2.lnolymp.us', + 'btcd-mainnet.lightning.computer', + 'node.eldamar.icu', + 'noad.sathoarder.com' + ], + DEFAULT_NEUTRINO_PEERS_TESTNET: [ + 'testnet.lnolymp.us', + 'btcd-testnet.lightning.computer', + 'testnet.blixtwallet.com' + ], + DEFAULT_LSPS1_HOST_MAINNET: '45.79.192.236:9735', + DEFAULT_LSPS1_HOST_TESTNET: '139.144.22.237:9735', + DEFAULT_LSPS1_PUBKEY_MAINNET: + '031b301307574bbe9b9ac7b79cbe1700e31e544513eae0b5d7497483083f99e581', + DEFAULT_LSPS1_PUBKEY_TESTNET: + '03e84a109cd70e57864274932fc87c5e6434c59ebb8e6e7d28532219ba38f7f6df', + DEFAULT_LSPS1_REST_MAINNET: 'https://lsps1.lnolymp.us', + DEFAULT_LSPS1_REST_TESTNET: 'https://testnet-lsps1.lnolymp.us', + DEFAULT_SPEEDLOADER: 'https://egs.lnze.us/', + DEFAULT_NOSTR_RELAYS_2023: [ + 'wss://nostr.mutinywallet.com', + 'wss://relay.damus.io', + 'wss://nostr.lnproxy.org' + ], + DEFAULT_SLIDE_TO_PAY_THRESHOLD: 10000, + STORAGE_KEY: 'zeus-settings-v2', + LEGACY_CURRENCY_CODES_KEY: 'currency-codes', + CURRENCY_CODES_KEY: 'zeus-currency-codes', + PosEnabled: { + Disabled: 'disabled', + Square: 'square', + Standalone: 'standalone' + } +})); + +import MigrationUtils from './MigrationUtils'; + +describe('MigrationUtils', () => { + const defaultSettings = { + customSpeedloader: '', + display: { + showMillisatoshiAmounts: true + }, + enableLSP: true, + fiat: 'USD', + fiatEnabled: false, + fiatRatesSource: 'Zeus', + lightningAddress: { + allowComments: true, + automaticallyAccept: true, + automaticallyAcceptAttestationLevel: 2, + automaticallyRequestOlympusChannels: false, + enabled: false, + nostrPrivateKey: '', + nostrRelays: [ + 'wss://relay.damus.io', + 'wss://nostr.land', + 'wss://nostr.wine', + 'wss://nos.lol', + 'wss://relay.snort.social' + ], + notifications: 0, + routeHints: false + }, + lspMainnet: 'https://0conf.lnolymp.us', + lspTestnet: 'https://testnet-0conf.lnolymp.us', + lsps1HostMainnet: '45.79.192.236:9735', + lsps1HostTestnet: '139.144.22.237:9735', + lsps1PubkeyMainnet: + '031b301307574bbe9b9ac7b79cbe1700e31e544513eae0b5d7497483083f99e581', + lsps1PubkeyTestnet: + '03e84a109cd70e57864274932fc87c5e6434c59ebb8e6e7d28532219ba38f7f6df', + lsps1RestMainnet: 'https://lsps1.lnolymp.us', + lsps1RestTestnet: 'https://testnet-lsps1.lnolymp.us', + lsps1ShowPurchaseButton: true, + lsps1Token: '', + neutrinoPeersMainnet: [ + 'btcd1.lnolymp.us', + 'btcd2.lnolymp.us', + 'btcd-mainnet.lightning.computer', + 'node.eldamar.icu', + 'noad.sathoarder.com' + ], + neutrinoPeersTestnet: [ + 'testnet.lnolymp.us', + 'btcd-testnet.lightning.computer', + 'testnet.blixtwallet.com' + ], + payments: { + slideToPayThreshold: 10000 + }, + requestSimpleTaproot: true, + speedloader: 'https://egs.lnze.us/' + }; + + describe('MigrationUtils', () => { + it('handles empty settings', async () => { + await expect( + MigrationUtils.legacySettingsMigrations('{}') + ).resolves.toEqual({ + ...defaultSettings + }); + }); + it('handles mod1', async () => { + await expect( + MigrationUtils.legacySettingsMigrations( + JSON.stringify({ + requestSimpleTaproot: false, + fiatRatesSource: 'Yadio' + }) + ) + ).resolves.toEqual({ + ...defaultSettings, + fiatRatesSource: 'Yadio' + }); + }); + it('handles mod2', async () => { + await expect( + MigrationUtils.legacySettingsMigrations( + JSON.stringify({ + lspMainnet: 'https://lsp-preview.lnolymp.us', + lspTestnet: 'https://testnet-lsp.lnolymp.us' + }) + ) + ).resolves.toEqual({ + ...defaultSettings + }); + }); + it('handles mod3', async () => { + await expect( + MigrationUtils.legacySettingsMigrations( + JSON.stringify({ + neutrinoPeersMainnet: [ + 'btcd1.lnolymp.us', + 'btcd2.lnolymp.us', + 'btcd-mainnet.lightning.computer', + 'node.eldamar.icu', + 'noad.sathoarder.com' + ] + }) + ) + ).resolves.toEqual({ + ...defaultSettings + }); + }); + it('handles mod7', async () => { + await expect( + MigrationUtils.legacySettingsMigrations( + JSON.stringify({ + bimodalPathfinding: true + }) + ) + ).resolves.toEqual({ + ...defaultSettings, + bimodalPathfinding: false + }); + }); + it('handles mod8', async () => { + await expect( + MigrationUtils.legacySettingsMigrations( + JSON.stringify({ + lightningAddress: { + nostrRelays: [ + 'wss://nostr.mutinywallet.com', + 'wss://relay.damus.io', + 'wss://nostr.lnproxy.org' + ], + allowComments: true, + automaticallyAccept: true, + automaticallyAcceptAttestationLevel: 2, + automaticallyRequestOlympusChannels: false, + enabled: false, + nostrPrivateKey: '', + notifications: 0, + routeHints: false + } + }) + ) + ).resolves.toEqual({ + ...defaultSettings + }); + }); + it('migrates old POS squareEnabled setting to posEnabled', async () => { + await expect( + MigrationUtils.legacySettingsMigrations( + JSON.stringify({ + pos: { + squareEnabled: true + } + }) + ) + ).resolves.toEqual({ + ...defaultSettings, + pos: { + posEnabled: 'square', + squareEnabled: false + } + }); + }); + }); +}); diff --git a/utils/MigrationUtils.ts b/utils/MigrationUtils.ts new file mode 100644 index 000000000..6e6bd34b0 --- /dev/null +++ b/utils/MigrationUtils.ts @@ -0,0 +1,705 @@ +import stores from '../stores/Stores'; +import { + Settings, + DEFAULT_FIAT_RATES_SOURCE, + DEFAULT_FIAT, + DEFAULT_LSP_MAINNET, + DEFAULT_LSP_TESTNET, + DEFAULT_NOSTR_RELAYS, + localeMigrationMapping, + DEFAULT_NEUTRINO_PEERS_MAINNET, + DEFAULT_NEUTRINO_PEERS_TESTNET, + DEFAULT_LSPS1_HOST_MAINNET, + DEFAULT_LSPS1_HOST_TESTNET, + DEFAULT_LSPS1_PUBKEY_MAINNET, + DEFAULT_LSPS1_PUBKEY_TESTNET, + DEFAULT_LSPS1_REST_MAINNET, + DEFAULT_LSPS1_REST_TESTNET, + DEFAULT_SPEEDLOADER, + DEFAULT_NOSTR_RELAYS_2023, + PosEnabled, + DEFAULT_SLIDE_TO_PAY_THRESHOLD, + STORAGE_KEY, + LEGACY_CURRENCY_CODES_KEY, + CURRENCY_CODES_KEY +} from '../stores/SettingsStore'; + +import { LEGACY_NOTES_KEY, MODERN_NOTES_KEY } from '../stores/NotesStore'; +import { + LEGACY_CONTACTS_KEY, + MODERN_CONTACTS_KEY +} from '../stores/ContactStore'; +import { + LEGACY_LAST_CHANNEL_BACKUP_STATUS, + LEGACY_LAST_CHANNEL_BACKUP_TIME, + LAST_CHANNEL_BACKUP_STATUS, + LAST_CHANNEL_BACKUP_TIME +} from '../stores/ChannelBackupStore'; +import { + LEGACY_ADDRESS_ACTIVATED_STRING, + LEGACY_HASHES_STORAGE_STRING, + ADDRESS_ACTIVATED_STRING, + HASHES_STORAGE_STRING +} from '../stores/LightningAddressStore'; +import { + LEGACY_POS_HIDDEN_KEY, + LEGACY_POS_STANDALONE_KEY, + POS_HIDDEN_KEY, + POS_STANDALONE_KEY +} from '../stores/PosStore'; +import { + LEGACY_CATEGORY_KEY, + LEGACY_PRODUCT_KEY, + CATEGORY_KEY, + PRODUCT_KEY +} from '../stores/InventoryStore'; +import { LEGACY_UNIT_KEY, UNIT_KEY } from '../stores/UnitsStore'; +import { + LEGACY_HIDDEN_ACCOUNTS_KEY, + HIDDEN_ACCOUNTS_KEY +} from '../stores/UTXOsStore'; + +import { LEGACY_LSPS1_ORDERS_KEY, LSPS1_ORDERS_KEY } from '../stores/LSPStore'; + +import { LNC_STORAGE_KEY, hash } from '../backends/LNC/credentialStore'; + +import EncryptedStorage from 'react-native-encrypted-storage'; +import Storage from '../storage'; + +class MigrationsUtils { + public async legacySettingsMigrations(settings: string) { + const newSettings = JSON.parse(settings) as Settings; + if (!newSettings.fiatRatesSource) { + newSettings.fiatRatesSource = DEFAULT_FIAT_RATES_SOURCE; + } + + // migrate fiat settings from older versions + if (!newSettings.fiat || newSettings.fiat === 'Disabled') { + newSettings.fiat = DEFAULT_FIAT; + newSettings.fiatEnabled = false; + } else if (newSettings.fiatEnabled == null) { + newSettings.fiatEnabled = true; + } + + // set default LSPs if not defined + if (newSettings.enableLSP === undefined) { + newSettings.enableLSP = true; + } + if (!newSettings.lspMainnet) { + newSettings.lspMainnet = DEFAULT_LSP_MAINNET; + } + if (!newSettings.lspTestnet) { + newSettings.lspTestnet = DEFAULT_LSP_TESTNET; + } + + // default Lightning Address settings + if (!newSettings.lightningAddress) { + newSettings.lightningAddress = { + enabled: false, + automaticallyAccept: true, + automaticallyAcceptAttestationLevel: 2, + automaticallyRequestOlympusChannels: false, // deprecated + routeHints: false, + allowComments: true, + nostrPrivateKey: '', + nostrRelays: DEFAULT_NOSTR_RELAYS, + notifications: 0 + }; + } + + // migrate locale to ISO 639-1 + if ( + newSettings.locale != null && + localeMigrationMapping[newSettings.locale] + ) { + newSettings.locale = localeMigrationMapping[newSettings.locale]; + } + + const MOD_KEY = 'lsp-taproot-mod'; + const mod = await EncryptedStorage.getItem(MOD_KEY); + if (!mod) { + newSettings.requestSimpleTaproot = true; + stores.settingsStore.setSettings(JSON.stringify(newSettings)); + await EncryptedStorage.setItem(MOD_KEY, 'true'); + } + + const MOD_KEY2 = 'lsp-preview-mod'; + const mod2 = await EncryptedStorage.getItem(MOD_KEY2); + if (!mod2) { + if (newSettings?.lspMainnet === 'https://lsp-preview.lnolymp.us') { + newSettings.lspMainnet = DEFAULT_LSP_MAINNET; + } + if (newSettings?.lspTestnet === 'https://testnet-lsp.lnolymp.us') { + newSettings.lspTestnet = DEFAULT_LSP_TESTNET; + } + stores.settingsStore.setSettings(JSON.stringify(newSettings)); + await EncryptedStorage.setItem(MOD_KEY2, 'true'); + } + + const MOD_KEY3 = 'neutrino-peers-mod1'; + const mod3 = await EncryptedStorage.getItem(MOD_KEY3); + if (!mod3) { + const neutrinoPeersMainnetOld = [ + 'btcd1.lnolymp.us', + 'btcd2.lnolymp.us', + 'btcd-mainnet.lightning.computer', + 'node.eldamar.icu', + 'noad.sathoarder.com' + ]; + if ( + JSON.stringify(newSettings?.neutrinoPeersMainnet) === + JSON.stringify(neutrinoPeersMainnetOld) + ) { + newSettings.neutrinoPeersMainnet = + DEFAULT_NEUTRINO_PEERS_MAINNET; + } + stores.settingsStore.setSettings(JSON.stringify(newSettings)); + await EncryptedStorage.setItem(MOD_KEY3, 'true'); + } + + const MOD_KEY4 = 'lsps1-hosts1'; + const mod4 = await EncryptedStorage.getItem(MOD_KEY4); + if (!mod4) { + if (!newSettings?.lsps1HostMainnet) { + newSettings.lsps1HostMainnet = DEFAULT_LSPS1_HOST_MAINNET; + } + if (!newSettings?.lsps1HostTestnet) { + newSettings.lsps1HostTestnet = DEFAULT_LSPS1_HOST_TESTNET; + } + if (!newSettings?.lsps1PubkeyMainnet) { + newSettings.lsps1PubkeyMainnet = DEFAULT_LSPS1_PUBKEY_MAINNET; + } + if (!newSettings?.lsps1PubkeyTestnet) { + newSettings.lsps1PubkeyTestnet = DEFAULT_LSPS1_PUBKEY_TESTNET; + } + if (!newSettings?.lsps1RestMainnet) { + newSettings.lsps1RestMainnet = DEFAULT_LSPS1_REST_MAINNET; + } + if (!newSettings?.lsps1RestTestnet) { + newSettings.lsps1RestTestnet = DEFAULT_LSPS1_REST_TESTNET; + } + + if (!newSettings?.lsps1Token) { + newSettings.lsps1Token = ''; + } + + if (!newSettings?.lsps1ShowPurchaseButton) { + newSettings.lsps1ShowPurchaseButton = true; + } + + stores.settingsStore.setSettings(JSON.stringify(newSettings)); + await EncryptedStorage.setItem(MOD_KEY4, 'true'); + } + + const MOD_KEY5 = 'millisat_amounts'; + const mod5 = await EncryptedStorage.getItem(MOD_KEY5); + if (!mod5) { + if (!newSettings?.display?.showMillisatoshiAmounts) { + if (!newSettings.display) { + newSettings.display = { + showMillisatoshiAmounts: true + }; + } else { + newSettings.display.showMillisatoshiAmounts = true; + } + } + + stores.settingsStore.setSettings(JSON.stringify(newSettings)); + await EncryptedStorage.setItem(MOD_KEY5, 'true'); + } + + const MOD_KEY6 = 'egs-host'; + const mod6 = await EncryptedStorage.getItem(MOD_KEY6); + if (!mod6) { + if (!newSettings?.speedloader) { + newSettings.speedloader = DEFAULT_SPEEDLOADER; + newSettings.customSpeedloader = ''; + } + + stores.settingsStore.setSettings(JSON.stringify(newSettings)); + await EncryptedStorage.setItem(MOD_KEY6, 'true'); + } + + // switch off bimodal pathfinding while bug exists + // https://github.com/lightningnetwork/lnd/issues/9085 + const MOD_KEY7 = 'bimodal-bug-9085'; + const mod7 = await EncryptedStorage.getItem(MOD_KEY7); + if (!mod7) { + if (newSettings?.bimodalPathfinding) { + newSettings.bimodalPathfinding = false; + } + + stores.settingsStore.setSettings(JSON.stringify(newSettings)); + await EncryptedStorage.setItem(MOD_KEY7, 'true'); + } + + const MOD_KEY8 = 'nostr-relays-2024'; + const mod8 = await EncryptedStorage.getItem(MOD_KEY8); + if (!mod8) { + if ( + JSON.stringify(newSettings?.lightningAddress?.nostrRelays) === + JSON.stringify(DEFAULT_NOSTR_RELAYS_2023) + ) { + newSettings.lightningAddress.nostrRelays = DEFAULT_NOSTR_RELAYS; + } + + stores.settingsStore.setSettings(JSON.stringify(newSettings)); + await EncryptedStorage.setItem(MOD_KEY8, 'true'); + } + + // migrate old POS squareEnabled setting to posEnabled + if (newSettings?.pos?.squareEnabled) { + newSettings.pos.posEnabled = PosEnabled.Square; + newSettings.pos.squareEnabled = false; + } + + if (!newSettings.neutrinoPeersMainnet) { + newSettings.neutrinoPeersMainnet = DEFAULT_NEUTRINO_PEERS_MAINNET; + } + if (!newSettings.neutrinoPeersTestnet) { + newSettings.neutrinoPeersTestnet = DEFAULT_NEUTRINO_PEERS_TESTNET; + } + + if (newSettings.payments == null) { + newSettings.payments = { + slideToPayThreshold: DEFAULT_SLIDE_TO_PAY_THRESHOLD + }; + } else if (newSettings.payments.slideToPayThreshold == null) { + newSettings.payments.slideToPayThreshold = + DEFAULT_SLIDE_TO_PAY_THRESHOLD; + } + + return newSettings; + } + + public async storageMigrationV2(settings: any) { + const migrationTasks = []; + + // Settings migration + console.log('Attemping settings migration'); + const settingsMigration = Storage.setItem(STORAGE_KEY, settings).then( + (writeSuccess) => { + console.log('Settings migration status', writeSuccess); + return writeSuccess; + } + ); + migrationTasks.push(settingsMigration); + + // Contacts migration + const contactsMigration = (async () => { + try { + const contacts = await EncryptedStorage.getItem( + LEGACY_CONTACTS_KEY + ); + if (contacts) { + console.log('Attemping contacts migration'); + const writeSuccess = await Storage.setItem( + MODERN_CONTACTS_KEY, + contacts + ); + console.log('Contacts migration status', writeSuccess); + return writeSuccess; + } + } catch (error) { + console.error( + 'Error loading contacts from encrypted storage', + error + ); + return false; + } + })(); + migrationTasks.push(contactsMigration); + + // Notes migration + const notesMigration = (async () => { + try { + const storedKeys = await EncryptedStorage.getItem( + LEGACY_NOTES_KEY + ); + if (storedKeys) { + const noteKeys = JSON.parse(storedKeys); + console.log('Attemping notes migration'); + const writeSuccess = await Storage.setItem( + MODERN_NOTES_KEY, + noteKeys + ); + console.log('Notes keys migration status', writeSuccess); + + // Load all legacy notes + const notesPromises = noteKeys.map(async (key: string) => { + const note = await EncryptedStorage.getItem(key); + if (note) { + const writeSuccess = await Storage.setItem( + key, + note + ); + console.log( + `Notes keys migration status: ${key}`, + writeSuccess + ); + return writeSuccess; + } + }); + + const noteResults = await Promise.all(notesPromises); + return ( + writeSuccess && + noteResults.every((result) => result !== false) + ); + } + } catch (error) { + console.error( + 'Error loading note keys from encrypted storage', + error + ); + return false; + } + })(); + migrationTasks.push(notesMigration); + + // Lightning address migration + const lightningAddressMigration = (async () => { + try { + let activatedSuccess: any = true; + let hashesSuccess: any = true; + + const activated = await EncryptedStorage.getItem( + LEGACY_ADDRESS_ACTIVATED_STRING + ); + if (activated) { + console.log( + 'Attemping lightning address activated migration' + ); + activatedSuccess = await Storage.setItem( + ADDRESS_ACTIVATED_STRING, + activated + ); + console.log( + 'Lightning address activated migration status', + activatedSuccess + ); + } + + const hashes = await EncryptedStorage.getItem( + LEGACY_HASHES_STORAGE_STRING + ); + if (hashes) { + console.log('Attemping lightning address hashes migration'); + hashesSuccess = await Storage.setItem( + HASHES_STORAGE_STRING, + hashes + ); + console.log( + 'Lightning address hashes migration status', + hashesSuccess + ); + } + + return activatedSuccess && hashesSuccess; + } catch (error) { + console.error( + 'Error loading lightning address data from encrypted storage', + error + ); + return false; + } + })(); + migrationTasks.push(lightningAddressMigration); + + // Backup status migration + const backupStatusMigration = (async () => { + try { + let statusSuccess: any = true; + let timeSuccess: any = true; + + const status = await EncryptedStorage.getItem( + LEGACY_LAST_CHANNEL_BACKUP_STATUS + ); + if (status) { + console.log('Attemping backup status migration'); + statusSuccess = await Storage.setItem( + LAST_CHANNEL_BACKUP_STATUS, + status + ); + console.log( + 'Backup status migration status', + statusSuccess + ); + } + + const time = await EncryptedStorage.getItem( + LEGACY_LAST_CHANNEL_BACKUP_TIME + ); + if (time) { + console.log('Attemping backup time migration'); + timeSuccess = await Storage.setItem( + LAST_CHANNEL_BACKUP_TIME, + time + ); + console.log('Backup time migration status', timeSuccess); + } + + return statusSuccess && timeSuccess; + } catch (error) { + console.error( + 'Error loading backup status from encrypted storage', + error + ); + return false; + } + })(); + migrationTasks.push(backupStatusMigration); + + // POS migration + const posMigration = (async () => { + try { + let hiddenKeySuccess: any = true; + let standaloneKeySuccess: any = true; + let categoriesSuccess: any = true; + let productsSuccess: any = true; + + const posHiddenKey = await EncryptedStorage.getItem( + LEGACY_POS_HIDDEN_KEY + ); + if (posHiddenKey) { + console.log('Attemping POS hidden key migration'); + hiddenKeySuccess = await Storage.setItem( + POS_HIDDEN_KEY, + posHiddenKey + ); + console.log( + 'POS hidden key migration status', + hiddenKeySuccess + ); + } + + const posStandaloneKey = await EncryptedStorage.getItem( + LEGACY_POS_STANDALONE_KEY + ); + if (posStandaloneKey) { + console.log('Attemping POS standalone key migration'); + standaloneKeySuccess = await Storage.setItem( + POS_STANDALONE_KEY, + posStandaloneKey + ); + console.log( + 'POS standalone key migration status', + standaloneKeySuccess + ); + } + + const categories = await EncryptedStorage.getItem( + LEGACY_CATEGORY_KEY + ); + if (categories) { + console.log('Attemping POS categories migration'); + categoriesSuccess = await Storage.setItem( + CATEGORY_KEY, + categories + ); + console.log( + 'POS categories migration status', + categoriesSuccess + ); + } + + const products = await EncryptedStorage.getItem( + LEGACY_PRODUCT_KEY + ); + if (products) { + console.log('Attemping POS products migration'); + productsSuccess = await Storage.setItem( + PRODUCT_KEY, + products + ); + console.log( + 'POS products migration status', + productsSuccess + ); + } + + return ( + hiddenKeySuccess && + standaloneKeySuccess && + categoriesSuccess && + productsSuccess + ); + } catch (error) { + console.error( + 'Error loading POS data from encrypted storage', + error + ); + return false; + } + })(); + migrationTasks.push(posMigration); + + // Units migration + const unitsMigration = (async () => { + try { + const units = await EncryptedStorage.getItem(LEGACY_UNIT_KEY); + if (units) { + console.log('Attemping units migration'); + const writeSuccess = await Storage.setItem(UNIT_KEY, units); + console.log('Units migration status', writeSuccess); + return writeSuccess; + } + } catch (error) { + console.error( + 'Error loading units data from encrypted storage', + error + ); + return false; + } + })(); + migrationTasks.push(unitsMigration); + + // Hidden accounts migration + const hiddenAccountsMigration = (async () => { + try { + const accounts = await EncryptedStorage.getItem( + LEGACY_HIDDEN_ACCOUNTS_KEY + ); + if (accounts) { + console.log('Attemping hidden accounts migration'); + const writeSuccess = await Storage.setItem( + HIDDEN_ACCOUNTS_KEY, + accounts + ); + console.log( + 'Hidden accounts migration status', + writeSuccess + ); + return writeSuccess; + } + } catch (error) { + console.error( + 'Error loading hidden accounts from encrypted storage', + error + ); + return false; + } + })(); + migrationTasks.push(hiddenAccountsMigration); + + // Currency codes migration + const currencyCodesMigration = (async () => { + try { + const currencyCodes = await EncryptedStorage.getItem( + LEGACY_CURRENCY_CODES_KEY + ); + if (currencyCodes) { + console.log('Attemping currency codes migration'); + const writeSuccess = await Storage.setItem( + CURRENCY_CODES_KEY, + currencyCodes + ); + console.log( + 'Currency codes migration status', + writeSuccess + ); + return writeSuccess; + } + } catch (error) { + console.error( + 'Error loading currency codes from encrypted storage', + error + ); + return false; + } + })(); + migrationTasks.push(currencyCodesMigration); + + // LSPS1 orders migration + const lsps1OrdersMigration = (async () => { + try { + const lsps1orders = await EncryptedStorage.getItem( + LEGACY_LSPS1_ORDERS_KEY + ); + if (lsps1orders) { + console.log('Attemping LSPS1 orders migration'); + const writeSuccess = await Storage.setItem( + LSPS1_ORDERS_KEY, + lsps1orders + ); + console.log('LSPS1 orders migration status', writeSuccess); + return writeSuccess; + } + } catch (error) { + console.error( + 'Error loading LSPS1 orders from encrypted storage', + error + ); + return false; + } + })(); + migrationTasks.push(lsps1OrdersMigration); + + // LNC migrations + const lncMigration = (async () => { + try { + const migrationPromises = + settings?.nodes.map(async (node: any) => { + if (node.implementation === 'lightning-node-connect') { + const baseKey = `${LNC_STORAGE_KEY}:${hash( + node.pairingPhrase + )}`; + const hostKey = `${baseKey}:host`; + + const baseKeyData: string = + (await EncryptedStorage.getItem(baseKey)) || + '{}'; + + console.log( + 'Attemping LNC base key migration', + baseKey + ); + const writeSuccess1 = await Storage.setItem( + baseKey, + JSON.parse(baseKeyData) + ); + console.log( + 'LNC base key migration status', + writeSuccess1 + ); + + const hostKeyData = await EncryptedStorage.getItem( + hostKey + ); + + console.log( + 'Attemping LNC host key migration', + baseKey + ); + const writeSuccess2 = await Storage.setItem( + hostKey, + hostKeyData + ); + console.log( + 'LNC host key migration status', + writeSuccess2 + ); + + return writeSuccess1 && writeSuccess2; + } + }) || []; + + const results = await Promise.all(migrationPromises); + return results.every((result) => result !== false); + } catch (error) { + console.error( + 'Error loading LNC data from encrypted storage', + error + ); + return false; + } + })(); + migrationTasks.push(lncMigration); + + const results = await Promise.all(migrationTasks); + return results.every((result) => result === true); + } +} + +const migrationsUtils = new MigrationsUtils(); +export default migrationsUtils; diff --git a/utils/handleAnything.test.ts b/utils/handleAnything.test.ts index e02b0ba73..e8edfa0ec 100644 --- a/utils/handleAnything.test.ts +++ b/utils/handleAnything.test.ts @@ -1,3 +1,7 @@ +jest.mock('../stores/ChannelBackupStore', () => ({})); +jest.mock('../stores/LSPStore', () => ({})); +jest.mock('react-native-notifications', () => ({})); + import stores from '../stores/Stores'; import handleAnything from './handleAnything'; diff --git a/views/AddNotes.tsx b/views/AddNotes.tsx index ae705f5a7..fbac281d8 100644 --- a/views/AddNotes.tsx +++ b/views/AddNotes.tsx @@ -1,6 +1,5 @@ import * as React from 'react'; import { TouchableOpacity, View } from 'react-native'; -import EncryptedStorage from 'react-native-encrypted-storage'; import { inject, observer } from 'mobx-react'; import { Route } from '@react-navigation/native'; import { StackNavigationProp } from '@react-navigation/stack'; @@ -12,6 +11,8 @@ import Button from '../components/Button'; import { localeString } from '../utils/LocaleUtils'; import { themeColor } from '../utils/ThemeUtils'; +import Storage from '../storage'; + import NotesStore from '../stores/NotesStore'; import TextInput from '../components/TextInput'; @@ -46,7 +47,7 @@ export default class AddNotes extends React.Component< } async componentDidMount() { const { noteKey } = this.state; - const storedNotes = await EncryptedStorage.getItem(noteKey!); + const storedNotes = await Storage.getItem(noteKey!); if (storedNotes) { this.setState({ notes: storedNotes, isNoteStored: true }); } @@ -60,7 +61,7 @@ export default class AddNotes extends React.Component< const saveNote = async () => { if (noteKey) { - EncryptedStorage.setItem(noteKey, notes || ''); + Storage.setItem(noteKey, notes || ''); await storeNoteKeys(noteKey, notes || ''); } diff --git a/views/ContactDetails.tsx b/views/ContactDetails.tsx index b1d66816b..8b68dc090 100644 --- a/views/ContactDetails.tsx +++ b/views/ContactDetails.tsx @@ -8,7 +8,6 @@ import { ScrollView } from 'react-native'; import { inject, observer } from 'mobx-react'; -import EncryptedStorage from 'react-native-encrypted-storage'; import { Route } from '@react-navigation/native'; import { StackNavigationProp } from '@react-navigation/stack'; @@ -18,7 +17,7 @@ import LoadingIndicator from '../components/LoadingIndicator'; import Header from '../components/Header'; import { Row } from '../components/layout/Row'; -import ContactStore from '../stores/ContactStore'; +import ContactStore, { MODERN_CONTACTS_KEY } from '../stores/ContactStore'; import LightningBolt from '../assets/images/SVG/Lightning Bolt.svg'; import BitcoinIcon from '../assets/images/SVG/BitcoinIcon.svg'; @@ -32,6 +31,8 @@ import { themeColor } from '../utils/ThemeUtils'; import LinkingUtils from '../utils/LinkingUtils'; import { localeString } from '../utils/LocaleUtils'; +import Storage from '../storage'; + import Contact from '../models/Contact'; interface ContactDetailsProps { @@ -101,8 +102,8 @@ export default class ContactDetails extends React.Component< try { const { contactId, nostrContact, isNostrContact } = this.props.route.params ?? {}; - const contactsString = await EncryptedStorage.getItem( - 'zeus-contacts' + const contactsString: any = await Storage.getItem( + MODERN_CONTACTS_KEY ); if (contactsString && contactId) { @@ -145,8 +146,8 @@ export default class ContactDetails extends React.Component< saveUpdatedContact = async (updatedContact: Contact) => { const { ContactStore } = this.props; try { - const contactsString = await EncryptedStorage.getItem( - 'zeus-contacts' + const contactsString: any = await Storage.getItem( + MODERN_CONTACTS_KEY ); if (contactsString) { @@ -162,9 +163,9 @@ export default class ContactDetails extends React.Component< existingContacts[contactIndex] = updatedContact; // Save the updated contacts back to storage - await EncryptedStorage.setItem( - 'zeus-contacts', - JSON.stringify(existingContacts) + await Storage.setItem( + MODERN_CONTACTS_KEY, + existingContacts ); console.log('Contact updated successfully!'); @@ -179,7 +180,7 @@ export default class ContactDetails extends React.Component< importToContacts = async () => { const { contact } = this.state; - const contactsString = await EncryptedStorage.getItem('zeus-contacts'); + const contactsString: any = await Storage.getItem(MODERN_CONTACTS_KEY); const existingContacts: Contact[] = contactsString ? JSON.parse(contactsString) @@ -189,10 +190,7 @@ export default class ContactDetails extends React.Component< a.name.localeCompare(b.name) ); - await EncryptedStorage.setItem( - 'zeus-contacts', - JSON.stringify(updatedContacts) - ); + await Storage.setItem(MODERN_CONTACTS_KEY, updatedContacts); console.log('Contact imported successfully!'); this.props.navigation.popTo('Contacts'); diff --git a/views/NostrContacts.tsx b/views/NostrContacts.tsx index da9d4db58..909ab34f5 100644 --- a/views/NostrContacts.tsx +++ b/views/NostrContacts.tsx @@ -11,7 +11,6 @@ import { } from 'react-native'; import { inject, observer } from 'mobx-react'; import { CheckBox, Icon } from 'react-native-elements'; -import EncryptedStorage from 'react-native-encrypted-storage'; // @ts-ignore:next-line import { relayInit, nip05, nip19 } from 'nostr-tools'; import { StackNavigationProp } from '@react-navigation/stack'; @@ -29,8 +28,10 @@ import ContactUtils from '../utils/ContactUtils'; import { localeString } from '../utils/LocaleUtils'; import { themeColor } from '../utils/ThemeUtils'; +import Storage from '../storage'; + import { DEFAULT_NOSTR_RELAYS } from '../stores/SettingsStore'; -import ContactStore from '../stores/ContactStore'; +import ContactStore, { MODERN_CONTACTS_KEY } from '../stores/ContactStore'; import SelectOff from '../assets/images/SVG/Select Off.svg'; import SelectOn from '../assets/images/SVG/Select On.svg'; @@ -407,8 +408,8 @@ export default class NostrContacts extends React.Component< ); // Retrieve existing contacts from Encrypted storage - const contactsString = await EncryptedStorage.getItem( - 'zeus-contacts' + const contactsString: any = await Storage.getItem( + MODERN_CONTACTS_KEY ); const existingContacts: any = contactsString ? JSON.parse(contactsString) @@ -421,10 +422,7 @@ export default class NostrContacts extends React.Component< ].sort((a, b) => a.name.localeCompare(b.name)); // Save the updated contacts to encrypted storage - await EncryptedStorage.setItem( - 'zeus-contacts', - JSON.stringify(updatedContacts) - ); + await Storage.setItem(MODERN_CONTACTS_KEY, updatedContacts); console.log('Contacts imported successfully!'); diff --git a/views/SendingLightning.tsx b/views/SendingLightning.tsx index 5cb56e861..5d37c91d7 100644 --- a/views/SendingLightning.tsx +++ b/views/SendingLightning.tsx @@ -1,5 +1,4 @@ import * as React from 'react'; -import EncryptedStorage from 'react-native-encrypted-storage'; import { BackHandler, Dimensions, @@ -27,6 +26,8 @@ import PaymentsStore from '../stores/PaymentsStore'; import { localeString } from '../utils/LocaleUtils'; import { themeColor } from '../utils/ThemeUtils'; +import Storage from '../storage'; + import Clock from '../assets/images/SVG/Clock.svg'; import ErrorIcon from '../assets/images/SVG/ErrorIcon.svg'; import Wordmark from '../assets/images/SVG/wordmark-black.svg'; @@ -68,7 +69,7 @@ export default class SendingLightning extends React.Component< navigation.addListener('focus', () => { const noteKey: string = TransactionsStore.noteKey; if (!noteKey) return; - EncryptedStorage.getItem(noteKey) + Storage.getItem(noteKey) .then((storedNotes) => { this.setState({ storedNotes: storedNotes || '' }); }) diff --git a/views/SendingOnChain.tsx b/views/SendingOnChain.tsx index 1f5742dcd..8b7050670 100644 --- a/views/SendingOnChain.tsx +++ b/views/SendingOnChain.tsx @@ -1,5 +1,4 @@ import * as React from 'react'; -import EncryptedStorage from 'react-native-encrypted-storage'; import { Dimensions, StyleSheet, Text, View } from 'react-native'; import { inject, observer } from 'mobx-react'; import { StackNavigationProp } from '@react-navigation/stack'; @@ -14,6 +13,8 @@ import { localeString } from '../utils/LocaleUtils'; import { themeColor } from '../utils/ThemeUtils'; import UrlUtils from '../utils/UrlUtils'; +import Storage from '../storage'; + import NodeInfoStore from '../stores/NodeInfoStore'; import TransactionsStore from '../stores/TransactionsStore'; @@ -39,7 +40,7 @@ export default class SendingOnChain extends React.Component< const { TransactionsStore, navigation } = this.props; navigation.addListener('focus', () => { if (!TransactionsStore.txid) return; - EncryptedStorage.getItem('note-' + TransactionsStore.txid) + Storage.getItem('note-' + TransactionsStore.txid) .then((storedNotes) => { this.setState({ storedNotes }); }) diff --git a/views/Settings/Contacts.tsx b/views/Settings/Contacts.tsx index f7f40c009..787267688 100644 --- a/views/Settings/Contacts.tsx +++ b/views/Settings/Contacts.tsx @@ -9,7 +9,6 @@ import { } from 'react-native'; import { inject, observer } from 'mobx-react'; import { SearchBar, Divider } from 'react-native-elements'; -import EncryptedStorage from 'react-native-encrypted-storage'; import { Route } from '@react-navigation/native'; import { StackNavigationProp } from '@react-navigation/stack'; @@ -21,9 +20,11 @@ import Header from '../../components/Header'; import { localeString } from '../../utils/LocaleUtils'; import { themeColor } from '../../utils/ThemeUtils'; +import Storage from '../../storage'; + import Contact from '../../models/Contact'; -import ContactStore from '../../stores/ContactStore'; +import ContactStore, { MODERN_CONTACTS_KEY } from '../../stores/ContactStore'; import Add from '../../assets/images/SVG/Add.svg'; import NostrichIcon from '../../assets/images/SVG/Nostrich.svg'; @@ -429,9 +430,9 @@ export default class Contacts extends React.Component< deletionAwaitingConfirmation: true }); } else { - await EncryptedStorage.setItem( - 'zeus-contacts', - JSON.stringify([]) + await Storage.setItem( + MODERN_CONTACTS_KEY, + [] ); this.setState({ deletionAwaitingConfirmation: false diff --git a/views/Settings/CurrencyConverter.tsx b/views/Settings/CurrencyConverter.tsx index f380b5860..1d885c8a4 100644 --- a/views/Settings/CurrencyConverter.tsx +++ b/views/Settings/CurrencyConverter.tsx @@ -13,7 +13,6 @@ import { observer, inject } from 'mobx-react'; import Svg, { Text } from 'react-native-svg'; import DragList, { DragListRenderItemInfo } from 'react-native-draglist'; import { Icon } from 'react-native-elements'; -import EncryptedStorage from 'react-native-encrypted-storage'; import isEmpty from 'lodash/isEmpty'; import { Route } from '@react-navigation/native'; import { StackNavigationProp } from '@react-navigation/stack'; @@ -29,8 +28,10 @@ import { themeColor } from '../../utils/ThemeUtils'; import { localeString } from '../../utils/LocaleUtils'; import { numberWithCommas } from '../../utils/UnitsUtils'; +import Storage from '../../storage'; + import FiatStore from '../../stores/FiatStore'; -import { CURRENCY_KEYS } from '../../stores/SettingsStore'; +import { CURRENCY_KEYS, CURRENCY_CODES_KEY } from '../../stores/SettingsStore'; import Add from '../../assets/images/SVG/Add.svg'; import Edit from '../../assets/images/SVG/Pen.svg'; @@ -106,9 +107,7 @@ export default class CurrencyConverter extends React.Component< try { const { inputValues } = this.state; - const inputValuesString = await EncryptedStorage.getItem( - 'currency-codes' - ); + const inputValuesString = await Storage.getItem(CURRENCY_CODES_KEY); const existingInputValues = inputValuesString ? JSON.parse(inputValuesString) : {}; @@ -121,10 +120,7 @@ export default class CurrencyConverter extends React.Component< } // Save updated inputValues to storage - await EncryptedStorage.setItem( - 'currency-codes', - JSON.stringify(existingInputValues) - ); + await Storage.setItem(CURRENCY_CODES_KEY, existingInputValues); // Update the state with the updated inputValues this.setState({ inputValues: existingInputValues }); @@ -135,10 +131,7 @@ export default class CurrencyConverter extends React.Component< saveInputValues = async () => { try { - await EncryptedStorage.setItem( - 'currency-codes', - JSON.stringify(this.state.inputValues) - ); + await Storage.setItem(CURRENCY_CODES_KEY, this.state.inputValues); } catch (error) { console.error('Error saving input values:', error); } @@ -146,9 +139,7 @@ export default class CurrencyConverter extends React.Component< retrieveInputValues = async () => { try { - const inputValuesString = await EncryptedStorage.getItem( - 'currency-codes' - ); + const inputValuesString = await Storage.getItem(CURRENCY_CODES_KEY); if (inputValuesString) { const inputValues = JSON.parse(inputValuesString); this.setState({ inputValues }); @@ -306,9 +297,7 @@ export default class CurrencyConverter extends React.Component< handleDeleteCurrency = async (currency: string) => { try { // Retrieve inputValues from storage - const inputValuesString = await EncryptedStorage.getItem( - 'currency-codes' - ); + const inputValuesString = await Storage.getItem(CURRENCY_CODES_KEY); const existingInputValues = inputValuesString ? JSON.parse(inputValuesString) : {}; @@ -320,10 +309,7 @@ export default class CurrencyConverter extends React.Component< delete updatedInputValues[currency]; // Save updated inputValues to storage - await EncryptedStorage.setItem( - 'currency-codes', - JSON.stringify(updatedInputValues) - ); + await Storage.setItem(CURRENCY_CODES_KEY, updatedInputValues); // Update the component state with the updated inputValues this.setState({ inputValues: updatedInputValues }); @@ -353,10 +339,7 @@ export default class CurrencyConverter extends React.Component< // Update state with reordered inputValues try { - await EncryptedStorage.setItem( - 'currency-codes', - JSON.stringify(reorderedValues) - ); + await Storage.setItem(CURRENCY_CODES_KEY, reorderedValues); } catch (error) { console.error('Error saving input values:', error); } diff --git a/views/Settings/EmbeddedNode/DisasterRecovery.tsx b/views/Settings/EmbeddedNode/DisasterRecovery.tsx index e00b62102..991f298ad 100644 --- a/views/Settings/EmbeddedNode/DisasterRecovery.tsx +++ b/views/Settings/EmbeddedNode/DisasterRecovery.tsx @@ -3,7 +3,6 @@ import { ScrollView, Text, View } from 'react-native'; import { ListItem, Divider } from 'react-native-elements'; import { inject, observer } from 'mobx-react'; import Clipboard from '@react-native-clipboard/clipboard'; -import EncryptedStorage from 'react-native-encrypted-storage'; import { StackNavigationProp } from '@react-navigation/stack'; import Button from '../../../components/Button'; @@ -12,14 +11,20 @@ import Header from '../../../components/Header'; import Switch from '../../../components/Switch'; import KeyValue from '../../../components/KeyValue'; -import SettingsStore from '../../../stores/SettingsStore'; import stores from '../../../stores/Stores'; +import SettingsStore from '../../../stores/SettingsStore'; +import { + LAST_CHANNEL_BACKUP_STATUS, + LAST_CHANNEL_BACKUP_TIME +} from '../../../stores/ChannelBackupStore'; import Base64Utils from '../../../utils/Base64Utils'; import { localeString } from '../../../utils/LocaleUtils'; import { restartNeeded } from '../../../utils/RestartUtils'; import { themeColor } from '../../../utils/ThemeUtils'; +import Storage from '../../../storage'; + import { exportAllChannelBackups } from '../../../lndmobile/channel'; interface DisasterRecoveryProps { @@ -71,10 +76,9 @@ export default class DisasterRecovery extends React.Component< getLastBackupStatus = async () => { const lastDisasterRecoveryBackupStatus = - (await EncryptedStorage.getItem('LAST_CHANNEL_BACKUP_STATUS')) || - ''; + (await Storage.getItem(LAST_CHANNEL_BACKUP_STATUS)) || ''; const lastDisasterRecoveryBackupTime = - (await EncryptedStorage.getItem('LAST_CHANNEL_BACKUP_TIME')) || ''; + (await Storage.getItem(LAST_CHANNEL_BACKUP_TIME)) || ''; this.setState({ lastDisasterRecoveryBackupStatus, diff --git a/views/Settings/LSPS1/Order.tsx b/views/Settings/LSPS1/Order.tsx index afd576d26..4a249a435 100644 --- a/views/Settings/LSPS1/Order.tsx +++ b/views/Settings/LSPS1/Order.tsx @@ -1,6 +1,5 @@ import React from 'react'; import { View, ScrollView } from 'react-native'; -import EncryptedStorage from 'react-native-encrypted-storage'; import { inject, observer } from 'mobx-react'; import { Route } from '@react-navigation/native'; import { StackNavigationProp } from '@react-navigation/stack'; @@ -14,7 +13,9 @@ import { themeColor } from '../../../utils/ThemeUtils'; import BackendUtils from '../../../utils/BackendUtils'; import { localeString } from '../../../utils/LocaleUtils'; -import LSPStore from '../../../stores/LSPStore'; +import Storage from '../../../storage'; + +import LSPStore, { LSPS1_ORDERS_KEY } from '../../../stores/LSPStore'; import SettingsStore from '../../../stores/SettingsStore'; import InvoicesStore from '../../../stores/InvoicesStore'; import NodeInfoStore from '../../../stores/NodeInfoStore'; @@ -54,7 +55,7 @@ export default class Orders extends React.Component { const orderShouldUpdate = route.params?.orderShouldUpdate; console.log('Looking for order in storage...'); - EncryptedStorage.getItem('orderResponses') + Storage.getItem(LSPS1_ORDERS_KEY) .then((responseArrayString) => { if (responseArrayString) { const responseArray = JSON.parse(responseArrayString); @@ -132,7 +133,7 @@ export default class Orders extends React.Component { updateOrderInStorage(order: Order) { console.log('Updating order in encrypted storage...'); - EncryptedStorage.getItem('orderResponses') + Storage.getItem(LSPS1_ORDERS_KEY) .then((responseArrayString) => { if (responseArrayString) { let responseArray = JSON.parse(responseArrayString); @@ -156,12 +157,13 @@ export default class Orders extends React.Component { responseArray[index] = JSON.stringify(oldOrder); // Save the updated order array back to encrypted storage - EncryptedStorage.setItem( - 'orderResponses', - JSON.stringify(responseArray) - ).then(() => { - console.log('Order updated in encrypted storage!'); - }); + Storage.setItem(LSPS1_ORDERS_KEY, responseArray).then( + () => { + console.log( + 'Order updated in encrypted storage!' + ); + } + ); } else { console.log('Order not found in encrypted storage.'); } diff --git a/views/Settings/LSPS1/OrdersPane.tsx b/views/Settings/LSPS1/OrdersPane.tsx index f4df3501c..ab9ff064b 100644 --- a/views/Settings/LSPS1/OrdersPane.tsx +++ b/views/Settings/LSPS1/OrdersPane.tsx @@ -1,5 +1,4 @@ import * as React from 'react'; -import EncryptedStorage from 'react-native-encrypted-storage'; import moment from 'moment'; import { inject, observer } from 'mobx-react'; import { StackNavigationProp } from '@react-navigation/stack'; @@ -14,7 +13,9 @@ import { themeColor } from '../../../utils/ThemeUtils'; import { localeString } from '../../../utils/LocaleUtils'; import BackendUtils from '../../../utils/BackendUtils'; -import LSPStore from '../../../stores/LSPStore'; +import Storage from '../../../storage'; + +import LSPStore, { LSPS1_ORDERS_KEY } from '../../../stores/LSPStore'; import NodeInfoStore from '../../../stores/NodeInfoStore'; import { WarningMessage } from '../../../components/SuccessErrorMessage'; @@ -85,8 +86,8 @@ export default class OrdersPane extends React.Component< navigation.addListener('focus', async () => { try { // Retrieve saved responses from encrypted storage - const responseArrayString = await EncryptedStorage.getItem( - 'orderResponses' + const responseArrayString = await Storage.getItem( + LSPS1_ORDERS_KEY ); if (responseArrayString) { const responseArray = JSON.parse(responseArrayString); diff --git a/views/Settings/LSPS1/index.tsx b/views/Settings/LSPS1/index.tsx index 1716d6960..be16403c3 100644 --- a/views/Settings/LSPS1/index.tsx +++ b/views/Settings/LSPS1/index.tsx @@ -9,7 +9,6 @@ import { TouchableOpacity } from 'react-native'; import { ButtonGroup, Icon } from 'react-native-elements'; -import EncryptedStorage from 'react-native-encrypted-storage'; import Slider from '@react-native-community/slider'; import { StackNavigationProp } from '@react-navigation/stack'; import { v4 as uuidv4 } from 'uuid'; @@ -37,7 +36,9 @@ import { themeColor } from '../../../utils/ThemeUtils'; import { localeString } from '../../../utils/LocaleUtils'; import { numberWithCommas } from '../../../utils/UnitsUtils'; -import LSPStore from '../../../stores/LSPStore'; +import Storage from '../../../storage'; + +import LSPStore, { LSPS1_ORDERS_KEY } from '../../../stores/LSPStore'; import InvoicesStore from '../../../stores/InvoicesStore'; import ChannelsStore from '../../../stores/ChannelsStore'; import SettingsStore from '../../../stores/SettingsStore'; @@ -1302,9 +1303,7 @@ export default class LSPS1 extends React.Component { const orderId = result.order_id; // Retrieve existing responses from encrypted storage or initialize an empty array - EncryptedStorage.getItem( - 'orderResponses' - ) + Storage.getItem(LSPS1_ORDERS_KEY) .then((responseArrayString) => { let responseArray = []; if (responseArrayString) { @@ -1374,11 +1373,9 @@ export default class LSPS1 extends React.Component { ); // Save the updated array back to encrypted storage - EncryptedStorage.setItem( - 'orderResponses', - JSON.stringify( - responseArray - ) + Storage.setItem( + LSPS1_ORDERS_KEY, + responseArray ) .then(() => { console.log( diff --git a/views/Settings/Seed.tsx b/views/Settings/Seed.tsx index de49fe012..d3534333c 100644 --- a/views/Settings/Seed.tsx +++ b/views/Settings/Seed.tsx @@ -8,7 +8,6 @@ import { View } from 'react-native'; import { inject, observer } from 'mobx-react'; -import EncryptedStorage from 'react-native-encrypted-storage'; import { StackNavigationProp } from '@react-navigation/stack'; import { Row } from '../../components/layout/Row'; @@ -24,6 +23,8 @@ import SettingsStore from '../../stores/SettingsStore'; import { themeColor } from '../../utils/ThemeUtils'; import { localeString } from '../../utils/LocaleUtils'; +import Storage from '../../storage'; + import Skull from '../../assets/images/SVG/Skull.svg'; import QR from '../../assets/images/SVG/QR.svg'; @@ -331,9 +332,9 @@ export default class Seed extends React.PureComponent { >