Reload wallet if backgrounded (#290)

* creating centralized location to handle stale background state

* adding global background threashold

* creating auth context

* disconnecting from liquid node during force auth

* reset did get to homepage variable

* fix crash if globalContactsInformation.myProfile is not set

* resetting custody account settings

* clearing keys information on reset

* remove roostock swap listener on reset

* resetting webview context on stale auth

* adding auth reset to spark context

* only load user info once per session

* only open tables once per session

* making sure to recheck login status to fix issue with initial load

* adding large heap flag

* fixing sql table open bug

* making sure didLoadUserSettings runs on app open

* fixing unqiueName being null bug

* fixing race condition causing switch to naive spark version

* misc changes
This commit is contained in:
Blake Kaufman
2025-12-05 09:35:51 -05:00
committed by GitHub
parent 01a0553421
commit b1a58e23b2
31 changed files with 766 additions and 347 deletions
+72 -49
View File
@@ -114,6 +114,7 @@ import { RootstockSwapProvider } from './context-store/rootstockSwapContext';
import { SparkConnectionManager } from './context-store/sparkConnection';
import { GlobalNostrWalletConnectProvider } from './context-store/NWC';
import { GlobalServerTimeProvider } from './context-store/serverTime';
import { AuthStatusProvider } from './context-store/authContext';
import { ActiveCustodyAccountProvider } from './context-store/activeAccount';
import { GiftProvider } from './context-store/giftContext';
// import { LRC20EventProvider } from './context-store/lrc20Listener';
@@ -138,51 +139,53 @@ function App(): JSX.Element {
<GlobalThemeProvider>
<DropdownProvider>
<AppStatusProvider>
<KeysContextProvider>
<GlobalContactsList>
<GlobalContextProvider>
<ActiveCustodyAccountProvider>
<WebViewProvider>
{/* <GlobaleCashVariables> */}
<SparkWalletProvider>
<GLobalNodeContextProider>
{/* <GlobalConbinedTxContextProvider> */}
<GlobalAppDataProvider>
<POSTransactionsProvider>
<PushNotificationProvider>
<LiquidEventProvider>
<RootstockSwapProvider>
{/* <LRC20EventProvider> */}
<GlobalNostrWalletConnectProvider>
{/* <LightningEventProvider> */}
<ImageCacheProvider>
<GlobalServerTimeProvider>
<GiftProvider>
{/* <Suspense
<AuthStatusProvider>
<KeysContextProvider>
<GlobalContactsList>
<GlobalContextProvider>
<ActiveCustodyAccountProvider>
<WebViewProvider>
{/* <GlobaleCashVariables> */}
<SparkWalletProvider>
<GLobalNodeContextProider>
{/* <GlobalConbinedTxContextProvider> */}
<GlobalAppDataProvider>
<POSTransactionsProvider>
<PushNotificationProvider>
<LiquidEventProvider>
<RootstockSwapProvider>
{/* <LRC20EventProvider> */}
<GlobalNostrWalletConnectProvider>
{/* <LightningEventProvider> */}
<ImageCacheProvider>
<GlobalServerTimeProvider>
<GiftProvider>
{/* <Suspense
fallback={<FullLoadingScreen text={'Loading Page'} />}> */}
<ResetStack />
</GiftProvider>
{/* </Suspense> */}
</GlobalServerTimeProvider>
</ImageCacheProvider>
{/* </LightningEventProvider> */}
</GlobalNostrWalletConnectProvider>
{/* </LRC20EventProvider> */}
</RootstockSwapProvider>
</LiquidEventProvider>
</PushNotificationProvider>
</POSTransactionsProvider>
</GlobalAppDataProvider>
{/* <BreezTest /> */}
{/* </GlobalConbinedTxContextProvider> */}
</GLobalNodeContextProider>
</SparkWalletProvider>
{/* </GlobaleCashVariables> */}
</WebViewProvider>
</ActiveCustodyAccountProvider>
</GlobalContextProvider>
</GlobalContactsList>
</KeysContextProvider>
<ResetStack />
</GiftProvider>
{/* </Suspense> */}
</GlobalServerTimeProvider>
</ImageCacheProvider>
{/* </LightningEventProvider> */}
</GlobalNostrWalletConnectProvider>
{/* </LRC20EventProvider> */}
</RootstockSwapProvider>
</LiquidEventProvider>
</PushNotificationProvider>
</POSTransactionsProvider>
</GlobalAppDataProvider>
{/* <BreezTest /> */}
{/* </GlobalConbinedTxContextProvider> */}
</GLobalNodeContextProider>
</SparkWalletProvider>
{/* </GlobaleCashVariables> */}
</WebViewProvider>
</ActiveCustodyAccountProvider>
</GlobalContextProvider>
</GlobalContactsList>
</KeysContextProvider>
</AuthStatusProvider>
</AppStatusProvider>
</DropdownProvider>
</GlobalThemeProvider>
@@ -442,7 +445,6 @@ function ResetStack(): JSX.Element | null {
await runSecureStoreMigrationV2();
const [
initialURL,
// registerBackground,
loginModeType,
pin,
mnemonic,
@@ -450,7 +452,6 @@ function ResetStack(): JSX.Element | null {
userSelectedLanguage,
] = await Promise.all([
getInitialURL(),
// registerBackgroundNotificationTask(),
retrieveData(LOGIN_SECURITY_MODE_TYPE_KEY),
retrieveData('pinHash'),
retrieveData('encryptedMnemonic'),
@@ -492,10 +493,32 @@ function ResetStack(): JSX.Element | null {
}
if (appState === 'background') return;
if (didInitializeSettings.current) return;
didInitializeSettings.current = true;
initWallet();
if (!didInitializeSettings.current) {
didInitializeSettings.current = true;
initWallet();
} else {
// Re-check login status when app becomes active again
async function recheckLoginStatus() {
const [pin, mnemonic, securitySettings] = await Promise.all([
retrieveData('pinHash'),
retrieveData('encryptedMnemonic'),
getLocalStorageItem(LOGIN_SECUITY_MODE_KEY),
]);
const storedSettings = JSON.parse(securitySettings);
setInitSettings(prev => {
return {
...prev,
isLoggedIn: !!pin.value && !!mnemonic.value,
hasSecurityEnabled: storedSettings?.isSecurityEnabled ?? true,
};
});
}
recheckLoginStatus();
}
}, [appState]);
const handleAnimationFinish = () => {
+17 -11
View File
@@ -94,9 +94,9 @@ android {
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 60
versionName "0.7.2"
ndk {
abiFilters 'arm64-v8a', 'x86_64', 'x86', 'armeabi-v7a'// Exclude riscv64
}
// ndk {
// abiFilters 'arm64-v8a', 'x86_64', 'x86', 'armeabi-v7a'// Exclude riscv64
// }
}
signingConfigs {
@@ -113,14 +113,14 @@ android {
keyPassword System.getenv("MY_KEY_PASSWORD")
}
}
// splits {
// abi {
// reset()
// enable true
// universalApk true
// include "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
// }
// }
splits {
abi {
reset()
enable true
universalApk true
include "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
}
}
buildTypes {
debug {
signingConfig signingConfigs.debug
@@ -149,6 +149,12 @@ android {
}
}
packagingOptions {
jniLibs {
useLegacyPackaging true
}
}
}
dependencies {
+2 -1
View File
@@ -28,7 +28,8 @@
android:allowBackup="false"
android:theme="@style/AppTheme"
android:usesCleartextTraffic="${usesCleartextTraffic}"
android:supportsRtl="true">
android:supportsRtl="true"
android:largeHeap="true">
<!-- Firebase notification settings -->
@@ -194,7 +194,7 @@ export default function AddContactsHalfModal({
newContact: {
name: searchInput.split('@')[0],
bio: '',
uniqueName: null,
uniqueName: '',
isFavorite: false,
transactions: [],
unlookedTransactions: 0,
@@ -43,6 +43,7 @@ import {
import { useTranslation } from 'react-i18next';
import { useGlobalInsets } from '../../../../../context-store/insetsProvider';
import { TAB_ITEM_HEIGHT } from '../../../../../navigation/tabs';
import { formatDisplayName } from './utils/formatListDisplayName';
export default function ContactsPage({ navigation }) {
const { contactsPrivateKey, publicKey } = useKeysContext();
@@ -66,7 +67,7 @@ export default function ContactsPage({ navigation }) {
const tabsNavigate = navigation.navigate;
const navigate = useNavigation();
const myProfile = globalContactsInformation.myProfile;
const didEditProfile = myProfile.didEditProfile;
const didEditProfile = myProfile?.didEditProfile;
// Use custom hooks for processed data
const contactInfoList = useProcessedContacts(
@@ -524,11 +525,7 @@ const PinnedContactElement = memo(
fontSize: SIZES.small,
textAlign: 'center',
}}
content={
contact.name?.length
? contact.name.trim()
: contact.uniqueName.trim()
}
content={formatDisplayName(contact)}
/>
</View>
</View>
@@ -587,9 +584,6 @@ const ContactElement = memo(
navigateToExpandedContact(contact);
}, [contact, navigateToExpandedContact]);
const displayName = contact.name?.length
? contact.name
: contact.uniqueName;
const formattedDate = lastUpdated
? createFormattedDate(
lastUpdated - serverTimeOffset,
@@ -618,7 +612,7 @@ const ContactElement = memo(
CustomEllipsizeMode="tail"
CustomNumberOfLines={1}
styles={{ flex: 1, marginRight: 5 }}
content={displayName}
content={formatDisplayName(contact)}
/>
{hasUnlookedTransaction && <View style={notificationStyle} />}
<ThemeText
@@ -286,12 +286,26 @@ function InnerContent({
selectedAddedContact?.receiveAddress || '';
const [isSaving, setIsSaving] = useState(false);
const [inputs, setInputs] = useState({
name: '',
bio: '',
uniquename: '',
receiveAddress: '',
});
const [inputs, setInputs] = useState(() => ({
name: isFirstTimeEditing
? isEditingMyProfile
? myContactName || ''
: selectedAddedContactName || ''
: '',
bio: isFirstTimeEditing
? isEditingMyProfile
? myContactBio || ''
: selectedAddedContactBio || ''
: '',
uniquename: isFirstTimeEditing
? isEditingMyProfile
? myContactUniqueName || ''
: selectedAddedContactUniqueName || ''
: '',
receiveAddress: selectedAddedContactReceiveAddress || '',
}));
// Remove the entire useEffect
const [tempImage, setTempImage] = useState({
uri: null,
comparison: null,
@@ -310,42 +324,6 @@ function InnerContent({
});
}
useEffect(() => {
changeInputText(
isFirstTimeEditing
? isEditingMyProfile
? myContactName || ''
: selectedAddedContactName || ''
: '',
'name',
);
changeInputText(
isFirstTimeEditing
? isEditingMyProfile
? myContactBio || ''
: selectedAddedContactBio || ''
: '',
'bio',
);
changeInputText(
isFirstTimeEditing
? isEditingMyProfile
? myContactUniqueName || ''
: selectedAddedContactUniqueName || ''
: '',
'uniquename',
);
changeInputText(selectedAddedContactReceiveAddress || '', 'receiveAddress');
}, [
isEditingMyProfile,
myContactName,
myContactBio,
myContactUniqueName,
selectedAddedContactName,
selectedAddedContactBio,
selectedAddedContactUniqueName,
]);
const myProfileImage = cache[myContact?.uuid];
const selectedAddedContactImage = cache[selectedAddedContact?.uuid];
const hasImage = tempImage.shouldDelete
@@ -612,6 +590,8 @@ function InnerContent({
? inputs.uniquename || myContact.uniqueName
: inputs.uniquename;
console.log(selectedAddedContact, 'tt', isEditingMyProfile);
if (isEditingMyProfile) {
if (
myContact?.bio === inputs.bio &&
@@ -661,6 +641,7 @@ function InnerContent({
navigate.goBack();
}
} else {
console.log(selectedAddedContact, 'testing');
if (fromInitialAdd) {
let tempContact = JSON.parse(JSON.stringify(selectedAddedContact));
tempContact.name = inputs.name.trim();
@@ -693,7 +674,7 @@ function InnerContent({
} else return addedContact;
});
} else newAddedContacts.push(tempContact);
console.log(tempContact, newAddedContacts);
toggleGlobalContactsInformation(
{
myProfile: {
@@ -733,6 +714,7 @@ function InnerContent({
) {
contact['receiveAddress'] = inputs.receiveAddress.trim();
}
console.log(contact, newAddedContacts);
toggleGlobalContactsInformation(
{
@@ -30,6 +30,7 @@ import useHandleBackPressNew from '../../../../../hooks/useHandleBackPressNew';
import { useImageCache } from '../../../../../../context-store/imageCache';
import ContactProfileImage from '../internalComponents/profileImage';
import { useGlobalInsets } from '../../../../../../context-store/insetsProvider';
import { formatDisplayName } from '../utils/formatListDisplayName';
export default function ChooseContactHalfModal() {
const { theme, darkModeType } = useGlobalThemeContext();
@@ -74,7 +75,9 @@ export default function ChooseContactHalfModal() {
return (
contact.name.toLowerCase().startsWith(inputText.toLowerCase()) ||
(!contact.isLNURL &&
contact.uniqueName.toLowerCase().startsWith(inputText.toLowerCase()))
contact.uniqueName
?.toLowerCase()
?.startsWith(inputText.toLowerCase()))
);
});
}, [sortedContacts, inputText, cache]);
@@ -119,9 +122,7 @@ export default function ChooseContactHalfModal() {
<ThemeText
CustomEllipsizeMode={'tail'}
CustomNumberOfLines={1}
content={
!!contact.name.length ? contact.name : contact.uniqueName
}
content={formatDisplayName(contact)}
/>
<ThemeText
CustomEllipsizeMode={'tail'}
@@ -23,7 +23,7 @@ export default async function getDeepLinkUser({
const newContact = {
name: user.contacts.myProfile.name || '',
bio: user.contacts.myProfile.bio || '',
uniqueName: user.contacts.myProfile.uniqueName,
uniqueName: user.contacts.myProfile.uniqueName || '',
isFavorite: false,
unlookedTransactions: 0,
uuid: user.contacts.myProfile.uuid,
@@ -0,0 +1,17 @@
export function formatDisplayName(contact) {
try {
if (contact.name?.length) {
return contact.name?.trim();
} else if (contact.uniqueName?.length) {
return contact.uniqueName?.trim();
} else if (contact.isLNURL) {
const [prefix, suffix] = contact.receiveAddress.split('@');
console.log(prefix, suffix);
return prefix;
}
return '';
} catch (err) {
console.log('error formatting display name', err);
return '';
}
}
+3
View File
@@ -103,6 +103,8 @@ const IS_DONTATION_PAYMENT_BUFFER = 10_000;
const SKELETON_ANIMATION_SPEED = 1200;
const TOKEN_TICKER_MAX_LENGTH = 10;
const BACKGROUND_THRESHOLD_MS = 3 * 60 * 1000;
const SCREEN_DIMENSIONS = Dimensions.get('screen');
export {
@@ -178,4 +180,5 @@ export {
GIFT_DEEPLINK_REGEX,
GIFT_DERIVE_PATH_CUTOFF,
CONTACT_UNIVERSAL_LINK_REGEX,
BACKGROUND_THRESHOLD_MS,
};
+28 -5
View File
@@ -1,18 +1,36 @@
import {openDatabaseAsync} from 'expo-sqlite';
import { openDatabaseAsync } from 'expo-sqlite';
export const ROOTSTOCK_DB_NAME = 'ROOTSTOCK_SWAPS';
export const ROOTSTOCK_TABLE_NAME = 'saved_rootstock_swaps';
let sqlLiteDB;
let isInitialized = false;
let initPromise = null;
if (!sqlLiteDB) {
async function openDBConnection() {
sqlLiteDB = await openDatabaseAsync(`${ROOTSTOCK_DB_NAME}.db`);
async function openDBConnection() {
if (!initPromise) {
initPromise = (async () => {
sqlLiteDB = await openDatabaseAsync(`${ROOTSTOCK_DB_NAME}.db`);
isInitialized = true;
return sqlLiteDB;
})();
}
openDBConnection();
return initPromise;
}
export const isRoostockDatabaseOpen = () => {
return isInitialized;
};
export const ensureRootstockDatabaseReady = async () => {
if (!isInitialized) {
await openDBConnection();
}
return sqlLiteDB;
};
export async function initRootstockSwapDB() {
try {
await ensureRootstockDatabaseReady();
await sqlLiteDB.execAsync(`PRAGMA journal_mode = WAL;
CREATE TABLE IF NOT EXISTS ${ROOTSTOCK_TABLE_NAME} (
id TEXT PRIMARY KEY NOT NULL,
@@ -30,6 +48,7 @@ export async function initRootstockSwapDB() {
// Save swap as JSON string
export async function saveSwap(id, type, data) {
try {
await ensureRootstockDatabaseReady();
await sqlLiteDB.runAsync(
`INSERT OR REPLACE INTO ${ROOTSTOCK_TABLE_NAME} (id, type, data) VALUES (?, ?, ?)`,
[id, type, JSON.stringify(data)],
@@ -42,6 +61,7 @@ export async function saveSwap(id, type, data) {
// Update swap with new details properties
export async function updateSwap(id, newDetails) {
try {
await ensureRootstockDatabaseReady();
// First, get the existing swap data
const existingSwap = await sqlLiteDB.getFirstAsync(
`SELECT data FROM ${ROOTSTOCK_TABLE_NAME} WHERE id = ?`,
@@ -78,6 +98,7 @@ export async function updateSwap(id, newDetails) {
// Load all swaps
export async function loadSwaps() {
try {
await ensureRootstockDatabaseReady();
const result = await sqlLiteDB.getAllAsync(
`SELECT * FROM ${ROOTSTOCK_TABLE_NAME}`,
);
@@ -94,6 +115,7 @@ export async function loadSwaps() {
// Load single swap by id
export async function getSwapById(id) {
try {
await ensureRootstockDatabaseReady();
const result = await sqlLiteDB.getAllAsync(
`SELECT * FROM ${ROOTSTOCK_TABLE_NAME} WHERE id = ?`,
[id],
@@ -114,6 +136,7 @@ export async function getSwapById(id) {
*/
export async function deleteSwapById(id) {
try {
await ensureRootstockDatabaseReady();
await sqlLiteDB.runAsync(
`DELETE FROM ${ROOTSTOCK_TABLE_NAME} WHERE id = ?`,
[id],
+3 -13
View File
@@ -3,30 +3,20 @@ import {
defaultConfig,
LiquidNetwork,
} from '@breeztech/react-native-breez-sdk-liquid';
import {getOrCreateDirectory} from './connectToNode';
import {crashlyticsLogReport} from './crashlyticsLogs';
import { getOrCreateDirectory } from './connectToNode';
import { crashlyticsLogReport } from './crashlyticsLogs';
const logHandler = logEntry => {
if (logEntry.level != 'TRACE') {
console.log(`[${logEntry.level}]: ${logEntry.line}`);
}
};
let didConnect = false;
export default async function connectToLiquidNode(accountMnemoinc) {
crashlyticsLogReport('Starting connect to liquid function');
// Create the default config
// setLogger(logHandler);
if (didConnect) {
console.log('RUNNING IN DID CONNECT');
return {
isConnected: true,
reason: null,
};
}
didConnect = true;
try {
crashlyticsLogReport('Getting config and mnemoinc');
// Create the default config, providing your Breez API key
@@ -53,7 +43,7 @@ export default async function connectToLiquidNode(accountMnemoinc) {
// console.log(`Working directory: ${config.workingDir}`);
// config.workingDir = "path to writable directory"
crashlyticsLogReport('Running connect request');
await connect({mnemonic, config});
await connect({ mnemonic, config });
// addEventListener(breezLiquidEvent);
return {
+27 -6
View File
@@ -1,22 +1,39 @@
// giftCardStorage.js
import {openDatabaseAsync} from 'expo-sqlite';
import { openDatabaseAsync } from 'expo-sqlite';
import fetchBackend from '../../../db/handleBackend';
export const GIFT_CARDS_TABLE_NAME = 'giftCardsTable';
export const GIFT_CARD_UPDATE_EVENT_NAME = 'GIFT_CARD_UPDATED';
let giftCardDB;
let isInitialized = false;
let initPromise = null;
// Initialize database connection
if (!giftCardDB) {
async function openGiftCardDB() {
giftCardDB = await openDatabaseAsync('giftCards.db');
async function openGiftCardDB() {
if (!initPromise) {
initPromise = (async () => {
giftCardDB = await openDatabaseAsync('giftCards.db');
isInitialized = true;
return giftCardDB;
})();
}
openGiftCardDB();
return initPromise;
}
export const isGiftCardDatabaseOpen = () => {
return isInitialized;
};
export const ensureGiftCardDatabaseReady = async () => {
if (!isInitialized) {
await openGiftCardDB();
}
return giftCardDB;
};
export const initializeGiftCardDatabase = async () => {
try {
await ensureGiftCardDatabaseReady();
await giftCardDB.execAsync(`PRAGMA journal_mode = WAL;
CREATE TABLE IF NOT EXISTS ${GIFT_CARDS_TABLE_NAME} (
id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -35,6 +52,7 @@ export const initializeGiftCardDatabase = async () => {
export const getGiftCardData = async invoice => {
try {
await ensureGiftCardDatabaseReady();
const result = await giftCardDB.getFirstAsync(
`SELECT * FROM ${GIFT_CARDS_TABLE_NAME} WHERE invoice = ?;`,
[invoice],
@@ -57,6 +75,7 @@ export const getGiftCardData = async invoice => {
export const saveGiftCardData = async (invoice, giftCardData) => {
try {
await ensureGiftCardDatabaseReady();
const existingData = await giftCardDB.getFirstAsync(
`SELECT id FROM ${GIFT_CARDS_TABLE_NAME} WHERE invoice = ?;`,
[invoice],
@@ -156,6 +175,7 @@ export const fetchAndCacheGiftCardData = async (
export const getAllGiftCards = async () => {
try {
await ensureGiftCardDatabaseReady();
const result = await giftCardDB.getAllAsync(
`SELECT * FROM ${GIFT_CARDS_TABLE_NAME} ORDER BY lastUpdated DESC;`,
);
@@ -174,6 +194,7 @@ export const getAllGiftCards = async () => {
export const deleteGiftCardData = async invoice => {
try {
await ensureGiftCardDatabaseReady();
await giftCardDB.runAsync(
`DELETE FROM ${GIFT_CARDS_TABLE_NAME} WHERE invoice = ?;`,
[invoice],
+25 -7
View File
@@ -4,14 +4,34 @@ export const CACHED_GIFTS = 'SAVED_GIFTS';
let sqlLiteDB = null;
let isInitialized = false;
let initPromise = null;
const getDatabase = async () => {
try {
if (!sqlLiteDB) {
async function openDBConnection() {
if (!initPromise) {
initPromise = (async () => {
console.log('Opening database connection...');
sqlLiteDB = await SQLite.openDatabaseAsync(`${CACHED_GIFTS}.db`);
console.log('Database connection opened');
}
return sqlLiteDB;
})();
}
return initPromise;
}
export const isGiftDatabaseOpen = () => {
return isInitialized;
};
const ensureGiftDatabaseReady = async () => {
if (!sqlLiteDB) {
await openDBConnection();
}
return sqlLiteDB;
};
const getDatabase = async () => {
try {
await ensureGiftDatabaseReady();
// Initialize database if not already done
if (!isInitialized) {
@@ -29,9 +49,7 @@ export const initGiftDb = async () => {
try {
console.log('Initializing gift database...');
if (!sqlLiteDB) {
sqlLiteDB = await SQLite.openDatabaseAsync(`${CACHED_GIFTS}.db`);
}
await ensureGiftDatabaseReady();
await sqlLiteDB.execAsync(`
PRAGMA journal_mode = WAL;
+5 -22
View File
@@ -1,6 +1,4 @@
import { privateKeyFromSeedWords } from './nostrCompatability';
import { getPublicKey } from 'nostr-tools';
import { addDataToCollection, getDataFromCollection } from '../../db';
import { getDataFromCollection } from '../../db';
import { generateRandomContact } from './contacts';
import {
getCurrentDateFormatted,
@@ -13,39 +11,26 @@ import {
} from '../constants';
import { sendDataToDB } from '../../db/interactionManager';
import { firebaseAuth, initializeFirebase } from '../../db/initializeFirebase';
import {
fetchLocalStorageItems,
shouldLoadExploreData,
} from './initializeUserSettingsHelpers';
import { fetchLocalStorageItems } from './initializeUserSettingsHelpers';
import { crashlyticsLogReport } from './crashlyticsLogs';
import { getLocalStorageItem, setLocalStorageItem } from './localStorage';
import fetchBackend from '../../db/handleBackend';
import { setLocalStorageItem } from './localStorage';
import { getNWCData } from './nwc';
import { getNWCSparkIdentityPubKey, initializeNWCWallet } from './nwc/wallet';
export default async function initializeUserSettingsFromHistory({
accountMnemoinc,
setContactsPrivateKey,
setMasterInfoObject,
toggleGlobalContactsInformation,
toggleGlobalAppDataInformation,
toggleMasterInfoObject,
preloadedData,
setPreLoadedUserData,
privateKey,
publicKey,
}) {
try {
crashlyticsLogReport('Begining process of getting user settings');
let needsToUpdate = false;
let tempObject = {};
const mnemonic = accountMnemoinc;
if (!mnemonic) throw new Error('Unable to load user settings, no mnemonic');
const privateKey = await privateKeyFromSeedWords(mnemonic);
const publicKey = privateKey ? getPublicKey(privateKey) : null;
if (!privateKey || !publicKey) throw Error('Failed to retrieve keys');
const [
_,
@@ -110,8 +95,6 @@ export default async function initializeUserSettingsFromHistory({
if (blitzStoredData === null) throw Error('Failed to retrive');
blitzStoredData = blitzStoredData || {};
setContactsPrivateKey(privateKey);
const generatedUniqueName = blitzStoredData?.contacts?.uniqueName
? ''
: generateRandomContact();
+29 -5
View File
@@ -15,15 +15,34 @@ export const contactsSQLEventEmitter = new EventEmitter();
let sqlLiteDB;
let messageQueue = [];
let isProcessing = false;
let isInitialized = false;
let initPromise = null;
if (!sqlLiteDB) {
async function openDBConnection() {
sqlLiteDB = await openDatabaseAsync(`${CACHED_MESSAGES_KEY}.db`);
async function openDBConnection() {
if (!initPromise) {
initPromise = (async () => {
sqlLiteDB = await openDatabaseAsync(`${CACHED_MESSAGES_KEY}.db`);
isInitialized = true;
return sqlLiteDB;
})();
}
openDBConnection();
return initPromise;
}
export const isMessagesDatabaseOpen = () => {
return isInitialized;
};
export const ensureDatabaseReady = async () => {
if (!isInitialized) {
await openDBConnection();
}
return sqlLiteDB;
};
export const initializeDatabase = async () => {
try {
await ensureDatabaseReady();
await sqlLiteDB.execAsync(`PRAGMA journal_mode = WAL;
CREATE TABLE IF NOT EXISTS ${SQL_TABLE_NAME} (
id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -35,12 +54,14 @@ export const initializeDatabase = async () => {
console.log('didOPEN');
return true;
} catch (err) {
console.log(err);
console.log('error opening messages database', err);
return false;
}
};
export const getCachedMessages = async () => {
try {
await ensureDatabaseReady();
const result = await sqlLiteDB.getAllAsync(
`SELECT * FROM ${SQL_TABLE_NAME} ORDER BY timestamp ASC;`,
);
@@ -143,6 +164,7 @@ const addUnpaidContactTransactions = async ({ newMessagesList, myPubKey }) => {
const setCashedMessages = async ({ newMessagesList, myPubKey }) => {
const BATCH_SIZE = 25;
try {
await ensureDatabaseReady();
for (let i = 0; i < newMessagesList.length; i += BATCH_SIZE) {
const batch = newMessagesList.slice(i, i + BATCH_SIZE);
@@ -249,6 +271,7 @@ const setCashedMessages = async ({ newMessagesList, myPubKey }) => {
export const deleteCachedMessages = async contactPubKey => {
try {
await ensureDatabaseReady();
await sqlLiteDB.runAsync(
`DELETE FROM ${SQL_TABLE_NAME} WHERE contactPubKey = ?;`,
[contactPubKey],
@@ -269,6 +292,7 @@ export const deleteCachedMessages = async contactPubKey => {
};
export const deleteTable = async () => {
try {
await ensureDatabaseReady();
await sqlLiteDB.runAsync(`DROP TABLE IF EXISTS ${SQL_TABLE_NAME};`);
console.log(`Table ${SQL_TABLE_NAME} deleted successfully`);
} catch (error) {
+38 -13
View File
@@ -1,9 +1,9 @@
import {getLocalStorageItem, setLocalStorageItem} from '../localStorage';
import {getTwoWeeksAgoDate} from '../rotateAddressDateChecker';
import {decryptMessage} from '../messaging/encodingAndDecodingMessages';
import { getLocalStorageItem, setLocalStorageItem } from '../localStorage';
import { getTwoWeeksAgoDate } from '../rotateAddressDateChecker';
import { decryptMessage } from '../messaging/encodingAndDecodingMessages';
import EventEmitter from 'events';
import {handleEventEmitterPost} from '../handleEventEmitters';
import {openDatabaseAsync} from 'expo-sqlite';
import { handleEventEmitterPost } from '../handleEventEmitters';
import { openDatabaseAsync } from 'expo-sqlite';
export const POS_TRANSACTION_TABLE_NAME = 'POS_TRANSACTIONS';
export const POS_LAST_RECEIVED_TIME = 'LAST_RECEIVED_POS_EVENT';
@@ -14,16 +14,34 @@ export const pointOfSaleEventEmitter = new EventEmitter();
let sqlLiteDB;
let messageQueue = [];
let isProcessing = false;
let isInitialized = false;
let initPromise = null;
if (!sqlLiteDB) {
async function openDBConnection() {
sqlLiteDB = await openDatabaseAsync(`${POS_TRANSACTION_TABLE_NAME}.db`);
async function openDBConnection() {
if (!initPromise) {
initPromise = (async () => {
sqlLiteDB = await openDatabaseAsync(`${POS_TRANSACTION_TABLE_NAME}.db`);
isInitialized = true;
return sqlLiteDB;
})();
}
openDBConnection();
return initPromise;
}
export const isSavedPOSTxsDatabaseOpen = () => {
return isInitialized;
};
export const ensurePOSDatabaseReady = async () => {
if (!isInitialized) {
await openDBConnection();
}
return sqlLiteDB;
};
export const initializePOSTransactionsDatabase = async () => {
try {
await ensurePOSDatabaseReady();
await sqlLiteDB.execAsync('PRAGMA journal_mode = WAL;');
await sqlLiteDB.execAsync(`
@@ -61,7 +79,7 @@ export const initializePOSTransactionsDatabase = async () => {
);
return true;
} catch (err) {
console.log(err);
console.log('error opening pos txs database', err);
handleEventEmitterPost(
pointOfSaleEventEmitter,
DID_OPEN_TABLES_EVENT_NAME,
@@ -71,8 +89,10 @@ export const initializePOSTransactionsDatabase = async () => {
return false;
}
};
export const getSavedPOSTransactions = async () => {
try {
await ensurePOSDatabaseReady();
const result = await sqlLiteDB.getAllAsync(
`SELECT * FROM ${POS_TRANSACTION_TABLE_NAME} ORDER BY timestamp DESC;`,
);
@@ -108,7 +128,7 @@ const processQueue = async () => {
if (isProcessing) return;
isProcessing = true;
while (messageQueue.length > 0) {
const {transactionsList, privateKey} = messageQueue.shift();
const { transactionsList, privateKey } = messageQueue.shift();
try {
await setPOSTransactions({
transactionsList,
@@ -121,7 +141,7 @@ const processQueue = async () => {
isProcessing = false;
};
export const queuePOSTransactions = ({transactionsList, privateKey}) => {
export const queuePOSTransactions = ({ transactionsList, privateKey }) => {
messageQueue.push({
transactionsList,
privateKey,
@@ -131,8 +151,9 @@ export const queuePOSTransactions = ({transactionsList, privateKey}) => {
}
};
const setPOSTransactions = async ({transactionsList, privateKey}) => {
const setPOSTransactions = async ({ transactionsList, privateKey }) => {
try {
await ensurePOSDatabaseReady();
// Start a database transaction for better performance
await sqlLiteDB.execAsync('BEGIN TRANSACTION;');
@@ -190,6 +211,7 @@ export const bulkUpdateDidPay = async dbDateAddedArray => {
}
try {
await ensurePOSDatabaseReady();
// Generate placeholders for the query (?, ?, ?)
const placeholders = dbDateAddedArray.map(() => '?').join(', ');
@@ -219,6 +241,7 @@ export const updateDidPayForSingleTx = async (didPaySetting, dbDateAdded) => {
return;
}
try {
await ensurePOSDatabaseReady();
await sqlLiteDB.runAsync(
`UPDATE ${POS_TRANSACTION_TABLE_NAME}
SET didPay = ?
@@ -241,6 +264,7 @@ export const updateDidPayForSingleTx = async (didPaySetting, dbDateAdded) => {
export const deleteEmployee = async employeeName => {
try {
await ensurePOSDatabaseReady();
await sqlLiteDB.runAsync(
`DELETE FROM ${POS_TRANSACTION_TABLE_NAME} WHERE LOWER(serverName) = LOWER(?);`,
[employeeName],
@@ -262,6 +286,7 @@ export const deleteEmployee = async employeeName => {
export const deletePOSTransactionsTable = async () => {
try {
await ensurePOSDatabaseReady();
await sqlLiteDB.runAsync(
`DROP TABLE IF EXISTS ${POS_TRANSACTION_TABLE_NAME};`,
);
+44 -8
View File
@@ -1,28 +1,48 @@
import EventEmitter from 'events';
import { handleEventEmitterPost } from '../handleEventEmitters';
import { openDatabaseAsync } from 'expo-sqlite';
export const SPARK_TRANSACTIONS_DATABASE_NAME = 'SPARK_INFORMATION_DATABASE';
export const SPARK_TRANSACTIONS_TABLE_NAME = 'SPARK_TRANSACTIONS';
export const LIGHTNING_REQUEST_IDS_TABLE_NAME = 'LIGHTNING_REQUEST_IDS';
export const SPARK_REQUEST_IDS_TABLE_NAME = 'SPARK_REQUEST_IDS';
export const sparkTransactionsEventEmitter = new EventEmitter();
export const SPARK_TX_UPDATE_ENVENT_NAME = 'UPDATE_SPARK_STATE';
let bulkUpdateTransactionQueue = [];
let isProcessingBulkUpdate = false;
let sqlLiteDB;
let isInitialized = false;
let initPromise = null;
if (!sqlLiteDB) {
async function openDBConnection() {
sqlLiteDB = await openDatabaseAsync(
`${SPARK_TRANSACTIONS_DATABASE_NAME}.db`,
);
async function openDBConnection() {
if (!initPromise) {
initPromise = (async () => {
sqlLiteDB = await openDatabaseAsync(
`${SPARK_TRANSACTIONS_DATABASE_NAME}.db`,
);
isInitialized = true;
return sqlLiteDB;
})();
}
openDBConnection();
return initPromise;
}
export const isSparkTxDatabaseOpen = () => {
return isInitialized;
};
export const ensureSparkDatabaseReady = async () => {
if (!isInitialized) {
await openDBConnection();
}
return sqlLiteDB;
};
export const initializeSparkDatabase = async () => {
try {
await ensureSparkDatabaseReady();
// Payment status: pending, completed, failed
await sqlLiteDB.execAsync(`
PRAGMA journal_mode = WAL;
@@ -58,13 +78,13 @@ export const initializeSparkDatabase = async () => {
console.log('Opened spark transaction and contacts tables');
return true;
} catch (err) {
console.log('Database initialization failed:', err);
console.log('Spark Database initialization failed:', err);
return false;
}
};
export const getAllSparkTransactions = async (options = {}) => {
try {
await ensureSparkDatabaseReady();
const {
limit = null,
offset = null,
@@ -110,6 +130,7 @@ export const getAllSparkTransactions = async (options = {}) => {
export const getAllPendingSparkPayments = async accountId => {
try {
await ensureSparkDatabaseReady();
let query = `
SELECT *
FROM ${SPARK_TRANSACTIONS_TABLE_NAME}
@@ -132,6 +153,7 @@ export const getAllPendingSparkPayments = async accountId => {
export const getAllSparkContactInvoices = async () => {
try {
await ensureSparkDatabaseReady();
const result = await sqlLiteDB.getAllAsync(
`SELECT * FROM ${SPARK_REQUEST_IDS_TABLE_NAME}`,
);
@@ -148,6 +170,7 @@ export const addSingleUnpaidSparkTransaction = async tx => {
}
try {
await ensureSparkDatabaseReady();
await sqlLiteDB.runAsync(
`INSERT INTO ${SPARK_REQUEST_IDS_TABLE_NAME}
(sparkID, description, sendersPubkey, details)
@@ -176,6 +199,7 @@ export const addBulkUnpaidSparkContactTransactions = async transactions => {
}
try {
await ensureSparkDatabaseReady();
const placeholders = validTransactions.map(() => '(?, ?, ?, ?)').join(', ');
const values = validTransactions.flatMap(tx => [
@@ -208,6 +232,7 @@ export const addBulkUnpaidSparkContactTransactions = async transactions => {
export const deleteSparkContactTransaction = async sparkID => {
try {
await ensureSparkDatabaseReady();
await sqlLiteDB.runAsync(
`DELETE FROM ${SPARK_REQUEST_IDS_TABLE_NAME} WHERE sparkID = ?`,
sparkID,
@@ -222,6 +247,7 @@ export const deleteSparkContactTransaction = async sparkID => {
export const getAllUnpaidSparkLightningInvoices = async () => {
try {
await ensureSparkDatabaseReady();
const result = await sqlLiteDB.getAllAsync(
`SELECT * FROM ${LIGHTNING_REQUEST_IDS_TABLE_NAME}`,
);
@@ -237,6 +263,7 @@ export const addSingleUnpaidSparkLightningTransaction = async tx => {
}
try {
await ensureSparkDatabaseReady();
await sqlLiteDB.runAsync(
`INSERT INTO ${LIGHTNING_REQUEST_IDS_TABLE_NAME}
(sparkID, amount, expiration, description, shouldNavigate, details)
@@ -298,6 +325,7 @@ export const bulkUpdateSparkTransactions = async (transactions, ...data) => {
return addToBulkUpdateQueue(async () => {
try {
await ensureSparkDatabaseReady();
console.log('Running bulk updates', updateType);
console.log(transactions);
@@ -558,6 +586,7 @@ export const addSingleSparkTransaction = async tx => {
}
try {
await ensureSparkDatabaseReady();
const newDetails = tx.details;
await sqlLiteDB.runAsync(
`INSERT INTO ${SPARK_TRANSACTIONS_TABLE_NAME}
@@ -588,6 +617,7 @@ export const addSingleSparkTransaction = async tx => {
export const deleteSparkTransaction = async sparkID => {
try {
await ensureSparkDatabaseReady();
await sqlLiteDB.runAsync(
`DELETE FROM ${SPARK_TRANSACTIONS_TABLE_NAME} WHERE sparkID = ?`,
sparkID,
@@ -605,8 +635,10 @@ export const deleteSparkTransaction = async sparkID => {
return false;
}
};
export const deleteUnpaidSparkLightningTransaction = async sparkID => {
try {
await ensureSparkDatabaseReady();
await sqlLiteDB.runAsync(
`DELETE FROM ${LIGHTNING_REQUEST_IDS_TABLE_NAME} WHERE sparkID = ?`,
sparkID,
@@ -620,6 +652,7 @@ export const deleteUnpaidSparkLightningTransaction = async sparkID => {
export const deleteSparkTransactionTable = async () => {
try {
await ensureSparkDatabaseReady();
await sqlLiteDB.execAsync(
`DROP TABLE IF EXISTS ${SPARK_TRANSACTIONS_TABLE_NAME}`,
);
@@ -632,6 +665,7 @@ export const deleteSparkTransactionTable = async () => {
export const deleteSparkContactsTransactionsTable = async () => {
try {
await ensureSparkDatabaseReady();
await sqlLiteDB.execAsync(
`DROP TABLE IF EXISTS ${SPARK_REQUEST_IDS_TABLE_NAME}`,
);
@@ -644,6 +678,7 @@ export const deleteSparkContactsTransactionsTable = async () => {
export const deleteUnpaidSparkLightningTransactionTable = async () => {
try {
await ensureSparkDatabaseReady();
await sqlLiteDB.execAsync(
`DROP TABLE IF EXISTS ${LIGHTNING_REQUEST_IDS_TABLE_NAME}`,
);
@@ -656,6 +691,7 @@ export const deleteUnpaidSparkLightningTransactionTable = async () => {
export const cleanStalePendingSparkLightningTransactions = async () => {
try {
await ensureSparkDatabaseReady();
const now = new Date().getTime();
// Delete where status is 'INVOICE_CREATED' and expires_at_time is not null and in the past
await sqlLiteDB.runAsync(
+52 -34
View File
@@ -21,6 +21,8 @@ import { updateMascatWalkingAnimation } from '../../functions/lottieViewColorTra
import { crashlyticsLogReport } from '../../functions/crashlyticsLogs';
import { useSparkWallet } from '../../../context-store/sparkContext';
import { removeLocalStorageItem } from '../../functions/localStorage';
import { privateKeyFromSeedWords } from '../../functions/nostrCompatability';
import { getPublicKey } from 'nostr-tools';
// import { initializeSparkDatabase } from '../../functions/spark/transactions';
// import { getCachedSparkTransactions } from '../../functions/spark';
// import { getLocalStorageItem, setLocalStorageItem } from '../../functions';
@@ -39,7 +41,7 @@ export default function ConnectingToNodeLoadingScreen({
const navigate = useNavigation();
const {
toggleMasterInfoObject,
// masterInfoObject,
masterInfoObject,
setMasterInfoObject,
preloadedUserData,
setPreLoadedUserData,
@@ -106,46 +108,62 @@ export default function ConnectingToNodeLoadingScreen({
console.log('Process 1', new Date().getTime());
connectToSparkWallet();
// connectToLiquidNode(accountMnemoinc);
const [
// didOpen,
// giftCardTable,
// posTransactions,
// sparkTxs,
// rootstockSwaps,
didLoadUserSettings,
] = await Promise.all([
// initializeDatabase(),
// initializeGiftCardDatabase(),
// initializePOSTransactionsDatabase(),
// initializeSparkDatabase(),
// initRootstockSwapDB(),
initializeUserSettingsFromHistory({
accountMnemoinc,
setContactsPrivateKey: toggleContactsPrivateKey,
setMasterInfoObject,
toggleGlobalContactsInformation,
// toggleGLobalEcashInformation,
toggleGlobalAppDataInformation,
toggleMasterInfoObject,
preloadedData: preloadedUserData.data,
setPreLoadedUserData,
}),
]);
const privateKey = await privateKeyFromSeedWords(accountMnemoinc);
const publicKey = privateKey ? getPublicKey(privateKey) : null;
console.log('Process 2', new Date().getTime());
crashlyticsLogReport('Opened all SQL lite tables');
if (!didLoadUserSettings)
if (!privateKey || !publicKey)
throw new Error(
t('screens.inAccount.loadingScreen.userSettingsError'),
);
crashlyticsLogReport('Loaded users settings from firebase');
const hasSavedInfo = Object.keys(masterInfoObject || {}).length > 5; //arbitrary number but filters out onboarding items
if (!hasSavedInfo) {
// connectToLiquidNode(accountMnemoinc);
const [
// didOpen,
// giftCardTable,
// posTransactions,
// sparkTxs,
// rootstockSwaps,
didLoadUserSettings,
] = await Promise.all([
// initializeDatabase(),
// initializeGiftCardDatabase(),
// initializePOSTransactionsDatabase(),
// initializeSparkDatabase(),
// initRootstockSwapDB(),
initializeUserSettingsFromHistory({
setMasterInfoObject,
toggleGlobalContactsInformation,
// toggleGLobalEcashInformation,
toggleGlobalAppDataInformation,
toggleMasterInfoObject,
preloadedData: preloadedUserData.data,
setPreLoadedUserData,
privateKey,
publicKey,
}),
]);
console.log('Process 2', new Date().getTime());
crashlyticsLogReport('Opened all SQL lite tables');
if (!didLoadUserSettings)
throw new Error(
t('screens.inAccount.loadingScreen.userSettingsError'),
);
crashlyticsLogReport('Loaded users settings from firebase');
}
toggleContactsPrivateKey(privateKey);
console.log('Process 3', new Date().getTime());
const elapsedTime = Date.now() - startTime;
const remainingTime = Math.max(0, 1500 - elapsedTime);
const remainingTime = Math.max(
0,
(hasSavedInfo ? 500 : 1500) - elapsedTime,
);
if (remainingTime > 0) {
console.log(
@@ -165,7 +183,7 @@ export default function ConnectingToNodeLoadingScreen({
didRunConnectionRef.current = true;
requestAnimationFrame(startConnectProcess);
}, [preloadedUserData]);
}, [preloadedUserData, masterInfoObject]);
// useEffect(() => {
// if (
// Object.keys(masterInfoObject).length === 0 ||
+48 -30
View File
@@ -12,18 +12,33 @@ import {
applyErrorAnimationTheme,
updateBlitzAnimationData,
} from '../functions/lottieViewColorTransformer';
import { initializeDatabase } from '../functions/messaging/cachedMessages';
import { initializeGiftCardDatabase } from '../functions/contacts/giftCardStorage';
import { initializePOSTransactionsDatabase } from '../functions/pos';
import { initializeSparkDatabase } from '../functions/spark/transactions';
import { initRootstockSwapDB } from '../functions/boltz/rootstock/swapDb';
import {
initializeDatabase,
isMessagesDatabaseOpen,
} from '../functions/messaging/cachedMessages';
import {
initializeGiftCardDatabase,
isGiftCardDatabaseOpen,
} from '../functions/contacts/giftCardStorage';
import {
initializePOSTransactionsDatabase,
isSavedPOSTxsDatabaseOpen,
} from '../functions/pos';
import {
initializeSparkDatabase,
isSparkTxDatabaseOpen,
} from '../functions/spark/transactions';
import {
initRootstockSwapDB,
isRoostockDatabaseOpen,
} from '../functions/boltz/rootstock/swapDb';
import { GlobalThemeView, ThemeText } from '../functions/CustomElements';
import { t } from 'i18next';
import CustomButton from '../functions/CustomElements/button';
import { INSET_WINDOW_WIDTH } from '../constants/theme';
import openWebBrowser from '../functions/openWebBrowser';
import { useNavigation } from '@react-navigation/native';
import { initGiftDb } from '../functions/gift/giftsStorage';
import { initGiftDb, isGiftDatabaseOpen } from '../functions/gift/giftsStorage';
import * as ExpoSplashScreen from 'expo-splash-screen';
const BlitzAnimation = require('../assets/BlitzAnimation.json');
@@ -76,31 +91,34 @@ const SplashScreen = () => {
animationRef.current?.play();
}, 500);
const [
didOpen,
giftCardTable,
posTransactions,
sparkTxs,
rootstockSwaps,
giftsDb,
] = await Promise.all([
initializeDatabase(),
initializeGiftCardDatabase(),
initializePOSTransactionsDatabase(),
initializeSparkDatabase(),
initRootstockSwapDB(),
initGiftDb(),
]);
if (
!didOpen ||
!giftCardTable ||
!posTransactions ||
!sparkTxs ||
!rootstockSwaps ||
!giftsDb
)
throw new Error(t('screens.inAccount.loadingScreen.dbInitError'));
isMessagesDatabaseOpen() &&
isGiftCardDatabaseOpen() &&
isSavedPOSTxsDatabaseOpen() &&
isSparkTxDatabaseOpen() &&
isRoostockDatabaseOpen() &&
isGiftDatabaseOpen()
) {
console.log('Tables are already opened, blocking...');
} else {
// Initialize databases one at a time
const didOpen = await initializeDatabase();
const giftCardTable = await initializeGiftCardDatabase();
const posTransactions = await initializePOSTransactionsDatabase();
const sparkTxs = await initializeSparkDatabase();
const rootstockSwaps = await initRootstockSwapDB();
const giftsDb = await initGiftDb();
if (
!didOpen ||
!giftCardTable ||
!posTransactions ||
!sparkTxs ||
!rootstockSwaps ||
!giftsDb
)
throw new Error(t('screens.inAccount.loadingScreen.dbInitError'));
}
setTimeout(() => {
if (navigate) {
+32 -17
View File
@@ -14,25 +14,28 @@ import {
CUSTODY_ACCOUNTS_STORAGE_KEY,
NWC_SECURE_STORE_MNEMOINC,
} from '../app/constants';
import {useKeysContext} from './keys';
import { useKeysContext } from './keys';
import {
decryptMnemonic,
encryptMnemonic,
} from '../app/functions/handleMnemonic';
import {useGlobalContextProvider} from './context';
import { useGlobalContextProvider } from './context';
import { useAuthContext } from './authContext';
// Create a context for the WebView ref
const ActiveCustodyAccount = createContext(null);
export const ActiveCustodyAccountProvider = ({children}) => {
const {masterInfoObject} = useGlobalContextProvider();
export const ActiveCustodyAccountProvider = ({ children }) => {
const { masterInfoObject } = useGlobalContextProvider();
const { authResetkey } = useAuthContext();
const [custodyAccounts, setCustodyAccounts] = useState([]);
const [isUsingNostr, setIsUsingNostr] = useState(false);
const {accountMnemoinc} = useKeysContext();
const { accountMnemoinc } = useKeysContext();
const [nostrSeed, setNostrSeed] = useState('');
const hasSessionReset = useRef(false);
const selectedAltAccount = custodyAccounts.filter(item => item.isActive);
const didSelectAltAccount = !!selectedAltAccount.length;
const isInitialRender = useRef(true);
const enabledNWC = masterInfoObject.didViewNWCMessage;
useEffect(() => {
@@ -125,10 +128,10 @@ export const ActiveCustodyAccountProvider = ({children}) => {
JSON.stringify(encriptAccountsList(newAccounts)),
);
setCustodyAccounts(newAccounts);
return {didWork: true};
return { didWork: true };
} catch (err) {
console.log('Remove account error', err);
return {didWork: false, err: err.message};
return { didWork: false, err: err.message };
}
};
const createAccount = async accountInformation => {
@@ -143,10 +146,10 @@ export const ActiveCustodyAccountProvider = ({children}) => {
JSON.stringify(encriptAccountsList(savedAccountInformation)),
);
setCustodyAccounts(savedAccountInformation);
return {didWork: true};
return { didWork: true };
} catch (err) {
console.log('Create custody account error', err);
return {didWork: false, err: err.message};
return { didWork: false, err: err.message };
}
};
@@ -155,7 +158,7 @@ export const ActiveCustodyAccountProvider = ({children}) => {
let accountInformation = JSON.parse(JSON.stringify(custodyAccounts));
let newAccounts = accountInformation.map(accounts => {
if (account.uuid === accounts.uuid) {
return {...accounts, ...account};
return { ...accounts, ...account };
} else return accounts;
});
@@ -164,10 +167,10 @@ export const ActiveCustodyAccountProvider = ({children}) => {
JSON.stringify(encriptAccountsList(newAccounts)),
);
setCustodyAccounts(newAccounts);
return {didWork: true};
return { didWork: true };
} catch (err) {
console.log('Remove account error', err);
return {didWork: false, err: err.message};
return { didWork: false, err: err.message };
}
};
const updateAccountCacheOnly = async account => {
@@ -176,18 +179,29 @@ export const ActiveCustodyAccountProvider = ({children}) => {
let accountInformation = JSON.parse(JSON.stringify(custodyAccounts));
let newAccounts = accountInformation.map(accounts => {
if (account.uuid === accounts.uuid) {
return {...accounts, ...account};
} else return {...accounts, isActive: false};
return { ...accounts, ...account };
} else return { ...accounts, isActive: false };
});
setCustodyAccounts(newAccounts);
return {didWork: true};
return { didWork: true };
} catch (err) {
console.log('Remove account error', err);
return {didWork: false, err: err.message};
return { didWork: false, err: err.message };
}
};
useEffect(() => {
if (isInitialRender.current) {
isInitialRender.current = false;
return;
}
setNostrSeed('');
setIsUsingNostr(false);
setCustodyAccounts([]);
hasSessionReset.current = false;
}, [authResetkey]);
const currentWalletMnemoinc = useMemo(() => {
if (didSelectAltAccount) {
return selectedAltAccount[0].mnemoinc;
@@ -220,7 +234,8 @@ export const ActiveCustodyAccountProvider = ({children}) => {
toggleIsUsingNostr,
isUsingNostr,
nostrSeed,
}}>
}}
>
{children}
</ActiveCustodyAccount.Provider>
);
+25
View File
@@ -11,6 +11,7 @@ import { AppState, Dimensions, Platform } from 'react-native';
import { getBoltzSwapPairInformation } from '../app/functions/boltz/boltzSwapInfo';
import * as Network from 'expo-network';
import { navigationRef } from '../navigation/navigationService';
import { BACKGROUND_THRESHOLD_MS } from '../app/constants';
// Initiate context
const AppStatusManager = createContext(null);
@@ -31,6 +32,8 @@ const AppStatusProvider = ({ children }) => {
const [screenDimensions, setScreenDimensions] = useState(() =>
Dimensions.get('screen'),
);
const shouldResetStateRef = useRef(null);
const timeInBackgroundRef = useRef(0);
const hasInitializedNavListener = useRef(false);
const hasInitializedBoltzData = useRef(false);
@@ -56,6 +59,26 @@ const AppStatusProvider = ({ children }) => {
useEffect(() => {
const handleAppStateChange = nextAppState => {
console.log('App state changed to:', nextAppState);
if (nextAppState === 'background') {
shouldResetStateRef.current = false;
timeInBackgroundRef.current = Date.now();
} else if (nextAppState === 'active') {
const timeInBackground = timeInBackgroundRef.current
? Date.now() - timeInBackgroundRef.current
: 0;
if (timeInBackground > BACKGROUND_THRESHOLD_MS) {
setTimeout(() => {
toggleDidGetToHomepage(false);
}, 100);
shouldResetStateRef.current = true;
} else {
shouldResetStateRef.current = false;
}
timeInBackgroundRef.current = null;
}
setAppState(nextAppState);
};
@@ -187,6 +210,7 @@ const AppStatusProvider = ({ children }) => {
toggleDidGetToHomepage,
appState,
screenDimensions,
shouldResetStateRef,
}),
[
minMaxLiquidSwapAmounts,
@@ -196,6 +220,7 @@ const AppStatusProvider = ({ children }) => {
toggleDidGetToHomepage,
appState,
screenDimensions,
shouldResetStateRef,
],
);
+44
View File
@@ -0,0 +1,44 @@
import { createContext, useState, useContext, useEffect, useMemo } from 'react';
import { navigationRef } from '../navigation/navigationService';
import { useAppStatus } from './appStatus';
// Initiate context
const AuthStatusManager = createContext(null);
const AuthStatusProvider = ({ children }) => {
const { appState, shouldResetStateRef } = useAppStatus();
const [authResetkey, setAuthResetKey] = useState(0);
useEffect(() => {
if (appState === 'active' && shouldResetStateRef.current) {
setAuthResetKey(prev => prev + 1);
navigationRef.current.reset({
index: 0,
routes: [{ name: 'Splash' }],
});
}
}, [appState]);
const contextValue = useMemo(
() => ({
authResetkey,
}),
[authResetkey],
);
return (
<AuthStatusManager.Provider value={contextValue}>
{children}
</AuthStatusManager.Provider>
);
};
function useAuthContext() {
const context = useContext(AuthStatusManager);
if (!context) {
throw new Error('useAuthContext must be used within a AuthContextProvider');
}
return context;
}
export { AuthStatusManager, AuthStatusProvider, useAuthContext };
+1 -1
View File
@@ -237,7 +237,7 @@ export const GlobalContactsList = ({ children }) => {
bio: contact.bio,
unlookedTransactions: 0,
isLNURL: contact.isLNURL,
uniqueName: contact.uniqueName,
uniqueName: contact.uniqueName || '',
uuid: contact.uuid,
isAdded: true,
isFavorite: false,
+16 -3
View File
@@ -1,4 +1,4 @@
import {getPublicKey} from 'nostr-tools';
import { getPublicKey } from 'nostr-tools';
import {
createContext,
useState,
@@ -6,22 +6,35 @@ import {
useEffect,
useMemo,
useCallback,
useRef,
} from 'react';
import { useAuthContext } from './authContext';
// Initiate context
const KeysContextManager = createContext(null);
const KeysContextProvider = ({children}) => {
const KeysContextProvider = ({ children }) => {
const { authResetkey } = useAuthContext();
const [contactsPrivateKey, setContactsPrivateKey] = useState('');
const publicKey = useMemo(
() => (contactsPrivateKey ? getPublicKey(contactsPrivateKey) : null),
[contactsPrivateKey],
);
const [accountMnemoinc, setAccountMnemonic] = useState('');
const isInitialRender = useRef(true);
const toggleContactsPrivateKey = useCallback(newKey => {
setContactsPrivateKey(newKey);
}, []);
useEffect(() => {
if (isInitialRender.current) {
isInitialRender.current = false;
return;
}
setContactsPrivateKey('');
setAccountMnemonic('');
}, [authResetkey]);
const contextValue = useMemo(
() => ({
contactsPrivateKey,
@@ -54,4 +67,4 @@ function useKeysContext() {
return context;
}
export {KeysContextManager, KeysContextProvider, useKeysContext};
export { KeysContextManager, KeysContextProvider, useKeysContext };
+21
View File
@@ -10,9 +10,11 @@ import {
addEventListener,
removeEventListener,
SdkEventVariant,
disconnect,
} from '@breeztech/react-native-breez-sdk-liquid';
import startLiquidUpdateInterval from '../app/functions/liquidBackupUpdate';
import { useNodeContext } from './nodeContext';
import { useAuthContext } from './authContext';
const LiquidEventContext = createContext(null);
@@ -22,6 +24,7 @@ const REQUIRED_SYNC_COUNT = 2;
// Create a context for the WebView ref
export function LiquidEventProvider({ children }) {
const { authResetkey } = useAuthContext();
const { toggleLiquidNodeInformation, liquidNodeInformation } =
useNodeContext();
const initialLiquidRun = useRef(null);
@@ -31,6 +34,7 @@ export function LiquidEventProvider({ children }) {
const intervalId = useRef(null);
const debounceTimer = useRef(null);
const syncRunCounter = useRef(0);
const isInitialRender = useRef(true);
useEffect(() => {
if (!liquidNodeInformation.didConnectToNode) return;
@@ -39,6 +43,23 @@ export function LiquidEventProvider({ children }) {
startLiquidEventListener(6);
}, [liquidNodeInformation.didConnectToNode]);
useEffect(() => {
if (isInitialRender.current) {
isInitialRender.current = false;
return;
}
cleanup();
disconnect();
toggleLiquidNodeInformation({
didConnectToNode: null,
transactions: [],
userBalance: 0,
});
liquidEventRunCounter.current = 0;
numberOfLiquidEvents.current = DEFAULT_EVENT_LIMIT;
initialLiquidRun.current = null;
}, [authResetkey]);
const cleanup = useCallback(() => {
if (debounceTimer.current) {
clearTimeout(debounceTimer.current);
+11
View File
@@ -15,6 +15,7 @@ import connectToLiquidNode from '../app/functions/connectToLiquid';
import { useSparkWallet } from './sparkContext';
import { useGlobalContacts } from './globalContacts';
import liquidToSparkSwap from '../app/functions/spark/liquidToSparkSwap';
import { useAuthContext } from './authContext';
// Initiate context
const NodeContextManager = createContext(null);
@@ -30,6 +31,8 @@ const GLobalNodeContextProider = ({ children }) => {
transactions: [],
userBalance: 0,
});
const isInitialRender = useRef(true);
const { authResetkey } = useAuthContext();
const selectedCurrency = masterInfoObject.fiatCurrency;
const [fiatStats, setFiatStats] = useState({ coin: 'USD', value: 100_000 });
@@ -124,6 +127,14 @@ const GLobalNodeContextProider = ({ children }) => {
globalContactsInformation?.myProfile?.uniqueName,
]);
useEffect(() => {
if (isInitialRender.current) {
isInitialRender.current = false;
return;
}
didRunLiquidConnection.current = false;
}, [authResetkey]);
const contextValue = useMemo(
() => ({
liquidNodeInformation,
+6 -5
View File
@@ -6,8 +6,8 @@ import {
useMemo,
useCallback,
} from 'react';
import {useKeysContext} from './keys';
import {db} from '../db/initializeFirebase';
import { useKeysContext } from './keys';
import { db } from '../db/initializeFirebase';
import {
DID_OPEN_TABLES_EVENT_NAME,
getSavedPOSTransactions,
@@ -15,7 +15,7 @@ import {
POS_EVENT_UPDATE,
queuePOSTransactions,
} from '../app/functions/pos';
import {getTwoWeeksAgoDate} from '../app/functions/rotateAddressDateChecker';
import { getTwoWeeksAgoDate } from '../app/functions/rotateAddressDateChecker';
import {
collection,
getDocs,
@@ -28,8 +28,8 @@ import {
// Initiate context
const POSTransactionsContextManager = createContext(null);
const POSTransactionsProvider = ({children}) => {
const {publicKey, contactsPrivateKey} = useKeysContext();
const POSTransactionsProvider = ({ children }) => {
const { publicKey, contactsPrivateKey } = useKeysContext();
const [txList, setTxList] = useState([]);
const [didOpenTable, setDidOpenTable] = useState(false);
@@ -165,6 +165,7 @@ const POSTransactionsProvider = ({children}) => {
pointOfSaleEventEmitter.on(POS_EVENT_UPDATE, updateTxListFunction);
return () => {
pointOfSaleEventEmitter.removeAllListeners(POS_EVENT_UPDATE);
pointOfSaleEventEmitter.removeAllListeners(DID_OPEN_TABLES_EVENT_NAME);
};
}, []);
+13
View File
@@ -31,11 +31,13 @@ import { JsonRpcProvider, Wallet } from 'ethers';
import { getBoltzWsUrl } from '../app/functions/boltz/boltzEndpoitns';
import { useSparkWallet } from './sparkContext';
import { useWebView } from './webViewContext';
import { useAuthContext } from './authContext';
export const RootstockSwapContext = createContext();
export const RootstockSwapProvider = ({ children }) => {
const { sendWebViewRequest } = useWebView();
const { authResetkey } = useAuthContext();
const { sparkInformation } = useSparkWallet();
const { accountMnemoinc } = useKeysContext();
const { didGetToHomepage, minMaxLiquidSwapAmounts } = useAppStatus();
@@ -44,6 +46,7 @@ export const RootstockSwapProvider = ({ children }) => {
const [signer, setSigner] = useState(null);
const [pendingNavigation, setPendingNavigation] = useState(null);
const isInitialRender = useRef(true);
const wsRef = useRef(null);
const intervalRef = useRef(null);
const timeoutRef = useRef(null);
@@ -372,6 +375,16 @@ export const RootstockSwapProvider = ({ children }) => {
],
);
useEffect(() => {
if (isInitialRender.current) {
isInitialRender.current = false;
return;
}
cleanupRootstockListener();
didRunSignerCreation.current = false;
setSigner(null);
}, [authResetkey]);
const contextValue = useMemo(() => {
return {
provider,
+118 -28
View File
@@ -9,6 +9,7 @@ import {
} from 'react';
import {
claimnSparkStaticDepositAddress,
clearMnemonicCache,
findTransactionTxFromTxHistory,
getSparkBalance,
getSparkStaticBitcoinL1AddressQuote,
@@ -60,6 +61,7 @@ import {
useWebView,
} from './webViewContext';
import { useGlobalContextProvider } from './context';
import { useAuthContext } from './authContext';
export const isSendingPayingEventEmiiter = new EventEmitter();
export const SENDING_PAYMENT_EVENT_NAME = 'SENDING_PAYMENT_EVENT';
@@ -68,11 +70,12 @@ export const SENDING_PAYMENT_EVENT_NAME = 'SENDING_PAYMENT_EVENT';
const SparkWalletManager = createContext(null);
const SparkWalletProvider = ({ children }) => {
const { authResetkey } = useAuthContext();
const { masterInfoObject } = useGlobalContextProvider();
const { changeSparkConnectionState, sendWebViewRequest } = useWebView();
const { accountMnemoinc, contactsPrivateKey, publicKey } = useKeysContext();
const { currentWalletMnemoinc } = useActiveCustodyAccount();
const { didGetToHomepage, appState } = useAppStatus();
const { didGetToHomepage, appState, shouldResetStateRef } = useAppStatus();
// const { liquidNodeInformation } = useNodeContext();
const { toggleGlobalContactsInformation, globalContactsInformation } =
useGlobalContacts();
@@ -112,6 +115,8 @@ const SparkWalletProvider = ({ children }) => {
const balancePollingTimeoutRef = useRef(null);
const balancePollingAbortControllerRef = useRef(null);
const currentPollingMnemonicRef = useRef(null);
const isInitialRender = useRef(true);
const authResetKeyRef = useRef(authResetkey);
const showTokensInformation =
masterInfoObject.enabledBTKNTokens === null
? !!Object.keys(sparkInformation.tokens || {}).length
@@ -123,6 +128,10 @@ const SparkWalletProvider = ({ children }) => {
didRunNormalConnection || normalConnectionTimeout;
const currentMnemonicRef = useRef(currentWalletMnemoinc);
useEffect(() => {
authResetKeyRef.current = authResetkey;
}, [authResetkey]);
useEffect(() => {
sparkInfoRef.current = {
balance: sparkInformation.balance,
@@ -225,10 +234,14 @@ const SparkWalletProvider = ({ children }) => {
if (didInitializeSendingPaymentEvent.current) return;
didInitializeSendingPaymentEvent.current = true;
isSendingPayingEventEmiiter.addListener(
SENDING_PAYMENT_EVENT_NAME,
toggleIsSendingPayment,
);
if (
!isSendingPayingEventEmiiter.listenerCount(SENDING_PAYMENT_EVENT_NAME)
) {
isSendingPayingEventEmiiter.addListener(
SENDING_PAYMENT_EVENT_NAME,
toggleIsSendingPayment,
);
}
}, []);
// This is a function that handles incoming transactions and formats it to required format
@@ -683,14 +696,43 @@ const SparkWalletProvider = ({ children }) => {
console.log('BLOCKING TRYING TO SET INTERVAL AGAIN');
clearInterval(updatePendingPaymentsIntervalRef.current);
}
const capturedAuthKey = authResetKeyRef.current;
const capturedMnemonic = currentMnemonicRef.current;
updatePendingPaymentsIntervalRef.current = setInterval(async () => {
try {
if (AppState.currentState !== 'active') return;
if (capturedAuthKey !== authResetKeyRef.current) {
console.log('Auth key changed. Aborting interval.');
return;
}
if (capturedMnemonic !== currentMnemonicRef.current) {
console.log('Mnemonic changed. Aborting interval.');
return;
}
if (AppState.currentState !== 'active') {
console.log('App not active. Skipping interval.');
return;
}
await updateSparkTxStatus(
currentMnemonicRef.current,
sparkInfoRef.current.identityPubKey,
sendWebViewRequest,
);
if (
capturedAuthKey !== authResetKeyRef.current ||
capturedMnemonic !== currentMnemonicRef.current
) {
console.log(
'Context changed during updateSparkTxStatus. Aborting getLRC20Transactions.',
);
return;
}
await getLRC20Transactions({
ownerPublicKeys: [sparkInfoRef.current.identityPubKey],
sparkAddress: sparkInfoRef.current.sparkAddress,
@@ -713,28 +755,30 @@ const SparkWalletProvider = ({ children }) => {
isRunningAddListeners.current = false;
};
const removeListeners = async () => {
const removeListeners = async (onlyClearIntervals = false) => {
console.log('Removing spark listeners');
const runtime = await selectSparkRuntime(currentMnemonicRef.current);
if (!prevAccountMnemoincRef.current) {
prevAccountMnemoincRef.current = currentMnemonicRef.current;
return;
}
const hashedMnemoinc = sha256Hash(prevAccountMnemoincRef.current);
if (runtime === 'native') {
if (
prevAccountMnemoincRef.current &&
sparkWallet[hashedMnemoinc]?.listenerCount('transfer:claimed')
) {
sparkWallet[hashedMnemoinc]?.removeAllListeners('transfer:claimed');
if (!onlyClearIntervals) {
const runtime = await selectSparkRuntime(currentMnemonicRef.current);
if (!prevAccountMnemoincRef.current) {
prevAccountMnemoincRef.current = currentMnemonicRef.current;
return;
}
} else {
await sendWebViewRequestGlobal(OPERATION_TYPES.removeListeners, {
mnemonic: prevAccountMnemoincRef.current,
});
const hashedMnemoinc = sha256Hash(prevAccountMnemoincRef.current);
if (runtime === 'native') {
if (
prevAccountMnemoincRef.current &&
sparkWallet[hashedMnemoinc]?.listenerCount('transfer:claimed')
) {
sparkWallet[hashedMnemoinc]?.removeAllListeners('transfer:claimed');
}
} else {
await sendWebViewRequestGlobal(OPERATION_TYPES.removeListeners, {
mnemonic: prevAccountMnemoincRef.current,
});
}
prevAccountMnemoincRef.current = currentMnemonicRef.current;
}
prevAccountMnemoincRef.current = currentMnemonicRef.current;
// Clear debounce timeout when removing listeners
if (debounceTimeoutRef.current) {
@@ -761,8 +805,54 @@ const SparkWalletProvider = ({ children }) => {
currentPollingMnemonicRef.current = null;
};
// Add event listeners to listen for bitcoin and lightning or spark transfers when receiving only when screen is active
const resetSparkState = useCallback(async () => {
// Reset refs to initial values
await removeListeners(true);
clearMnemonicCache();
prevAccountMnemoincRef.current = null;
isRunningAddListeners.current = false;
depositAddressIntervalRef.current = null;
updatePendingPaymentsIntervalRef.current = null;
sparkInfoRef.current = {
balance: 0,
tokens: {},
identityPubKey: '',
sparkAddress: '',
};
handledTransfers.current = new Set();
prevListenerType.current = null;
prevAppState.current = 'active';
prevAccountId.current = null;
isSendingPaymentRef.current = false;
balancePollingTimeoutRef.current = null;
balancePollingAbortControllerRef.current = null;
currentPollingMnemonicRef.current = null;
// Reset state variables
setSparkConnectionError(null);
setSparkInformation({
balance: 0,
tokens: {},
transactions: [],
identityPubKey: '',
sparkAddress: '',
didConnect: null,
});
setPendingNavigation(null);
setDidRunNormalConnection(false);
setNormalConnectionTimeout(false);
}, []);
useEffect(() => {
if (isInitialRender.current) {
isInitialRender.current = false;
return;
}
resetSparkState();
}, [authResetkey]);
// Add event listeners to listen for bitcoin and lightning or spark transfers when receiving only when screen is active
useEffect(() => {
// Handle immediate background transitions synchronously(background events on android were not running)
if (prevAppState.current !== appState && appState === 'background') {
@@ -772,8 +862,7 @@ const SparkWalletProvider = ({ children }) => {
const timeoutId = setTimeout(async () => {
if (!didGetToHomepage) return;
if (!sparkInformation.didConnect) return;
if (!sparkInformation.identityPubKey) return;
if (!sparkInfoRef.current.identityPubKey) return;
const getListenerType = () => {
if (appState === 'active') return 'full';
@@ -820,6 +909,7 @@ const SparkWalletProvider = ({ children }) => {
console.log('l1Deposit check running....');
if (AppState.currentState !== 'active') return;
if (isSendingPaymentRef.current) return;
if (!currentMnemonicRef.current) return;
const allTxs = await getAllSparkTransactions({
accountId: sparkInfoRef.current.identityPubKey,
});
+33 -30
View File
@@ -26,6 +26,7 @@ import DeviceInfo, {
import { getLocalStorageItem, setLocalStorageItem } from '../app/functions';
import { useAppStatus } from './appStatus';
import { useActiveCustodyAccount } from './activeAccount';
import { useAuthContext } from './authContext';
export const OPERATION_TYPES = {
initWallet: 'initializeSparkWallet',
@@ -101,7 +102,6 @@ const WASM_ERRORS = [
let handshakeComplete = false;
let forceReactNativeUse = null;
let globalSendWebViewRequest = null;
const BACKGROUND_THRESHOLD_MS = 3 * 60 * 1000;
const WebViewContext = createContext(null);
@@ -150,6 +150,7 @@ export const getHandshakeComplete = () => {
};
export const WebViewProvider = ({ children }) => {
const { authResetkey } = useAuthContext();
const { currentWalletMnemoinc } = useActiveCustodyAccount();
const { appState, isConnectedToTheInternet, didGetToHomepage } =
useAppStatus();
@@ -171,7 +172,8 @@ export const WebViewProvider = ({ children }) => {
const prevConnectionStatus = useRef(isConnectedToTheInternet);
const internetConnectionRef = useRef(isConnectedToTheInternet);
const walletInitialized = useRef(false);
const backgroundTimeRef = useRef(null);
const isInitialRender = useRef(true);
const currentWalletMnemoincRef = useRef(currentWalletMnemoinc);
const didRunInit = useRef(null);
const didGetToHomepageRef = useRef(didGetToHomepage);
const [changeSparkConnectionState, setChangeSparkConnectionState] = useState({
@@ -179,6 +181,19 @@ export const WebViewProvider = ({ children }) => {
count: 0,
});
useEffect(() => {
currentWalletMnemoincRef.current = currentWalletMnemoinc;
}, [currentWalletMnemoinc]);
// reset webview when app is stale in background
useEffect(() => {
if (isInitialRender.current) {
isInitialRender.current = false;
return;
}
blockAndResetWebview();
}, [authResetkey]);
useEffect(() => {
didGetToHomepageRef.current = didGetToHomepage;
}, [didGetToHomepage]);
@@ -321,7 +336,6 @@ export const WebViewProvider = ({ children }) => {
if (appState === 'background') {
console.log('App going to background - clearing all timeouts');
backgroundTimeRef.current = Date.now();
// Clear everything to prevent timeout issues on background
Object.values(activeTimeoutsRef.current).forEach(t =>
clearTimeout(t.timeoutId),
@@ -333,10 +347,6 @@ export const WebViewProvider = ({ children }) => {
} else if (appState === 'active') {
console.log('App returned to foreground');
const timeInBackground = backgroundTimeRef.current
? Date.now() - backgroundTimeRef.current
: 0;
// Wait for internet connection before proceeding
if (!isConnectedToTheInternet) {
console.log('Waiting for internet connection before processing...');
@@ -360,10 +370,7 @@ export const WebViewProvider = ({ children }) => {
connectionChanged && isConnectedToTheInternet;
if (justBecameActive || connectionJustRestored) {
if (
timeInBackground > BACKGROUND_THRESHOLD_MS ||
(!nonceVerified.current && !isResetting.current)
) {
if (!nonceVerified.current && !isResetting.current) {
console.log('Background time exceeded threshold - reloading WebView');
blockAndResetWebview();
} else {
@@ -384,8 +391,6 @@ export const WebViewProvider = ({ children }) => {
}
}
}
backgroundTimeRef.current = null;
}
previousAppState.current = appState;
@@ -577,12 +582,7 @@ export const WebViewProvider = ({ children }) => {
forceReactNativeUse = true;
}
},
[
decryptMessage,
resetWebViewState,
currentWalletMnemoinc,
processQueuedRequests,
],
[decryptMessage, resetWebViewState, processQueuedRequests],
);
const sendWebViewRequestInternal = useCallback(
@@ -903,12 +903,12 @@ export const WebViewProvider = ({ children }) => {
if (didRunInit.current) return;
didRunInit.current = true;
const androidAPI = DeviceInfo.getApiLevelSync();
if (androidAPI == 33 || androidAPI == 34) {
console.warn(`Skipping handshake on Android API ${androidAPI}`);
forceReactNativeUse = true;
return;
}
// const androidAPI = DeviceInfo.getApiLevelSync();
// if (androidAPI == 33 || androidAPI == 34) {
// console.warn(`Skipping handshake on Android API ${androidAPI}`);
// forceReactNativeUse = true;
// return;
// }
const savedVariable = await getLocalStorageItem('FORCE_REACT_NATIVE');
@@ -955,15 +955,15 @@ export const WebViewProvider = ({ children }) => {
if (
(handshakeComplete &&
!walletInitialized.current &&
currentWalletMnemoinc) ||
(currentWalletMnemoinc && connectionJustRestored)
currentWalletMnemoincRef.current) ||
(currentWalletMnemoincRef.current && connectionJustRestored)
) {
console.log('Re-initializing wallet before processing queue');
try {
// No need to handle any state changes here, handled inside of the promise. But this might be where the stale connection state comes from. if a request is sent to the webview but not responded to the change to react-native woudnt have happpened before leaving everything in "not connected to spark".
const response = await sendWebViewRequestInternal(
OPERATION_TYPES.initWallet,
{ mnemonic: currentWalletMnemoinc },
{ mnemonic: currentWalletMnemoincRef.current },
true,
);
if (!response?.isConnected) throw new Error('Wallet init failed');
@@ -986,7 +986,10 @@ export const WebViewProvider = ({ children }) => {
`Processing ${queuedRequests.current.length} queued requests`,
);
if (queuedRequests.current.length === 0) {
if (
queuedRequests.current.length === 0 ||
!currentWalletMnemoincRef.current
) {
isResetting.current = false;
return;
}
@@ -1007,7 +1010,7 @@ export const WebViewProvider = ({ children }) => {
isResetting.current = false;
},
[currentWalletMnemoinc, sendWebViewRequestInternal],
[sendWebViewRequestInternal],
);
const getCustomUserAgent = useCallback(() => {