Add percent to send max (#142)
* updating imports * fixed infinate loop bug * added percent dropdown to send max
This commit is contained in:
@@ -17,5 +17,6 @@ module.exports = {
|
||||
Buffer: 'readonly',
|
||||
btoa: 'readonly', // both are native to Hermes now
|
||||
atob: 'readonly',
|
||||
TextDecoder: 'readonly',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -6,17 +6,8 @@
|
||||
*/
|
||||
|
||||
import {DefaultTheme, NavigationContainer} from '@react-navigation/native';
|
||||
import './pollyfills';
|
||||
import './i18n'; // for translation option
|
||||
import {createNativeStackNavigator} from '@react-navigation/native-stack';
|
||||
import React, {
|
||||
JSX,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import React, {JSX, useCallback, useEffect, useMemo, useState} from 'react';
|
||||
import {registerRootComponent} from 'expo';
|
||||
import {
|
||||
getLocalStorageItem,
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
import React, {useState, useMemo} from 'react';
|
||||
import {useNavigation} from '@react-navigation/native';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {InputTypeVariant} from '@breeztech/react-native-breez-sdk-liquid';
|
||||
|
||||
import {useGlobalContextProvider} from '../../../../../../context-store/context';
|
||||
import {useNodeContext} from '../../../../../../context-store/nodeContext';
|
||||
import {useAppStatus} from '../../../../../../context-store/appStatus';
|
||||
import {useActiveCustodyAccount} from '../../../../../../context-store/activeAccount';
|
||||
|
||||
import CustomButton from '../../../../../functions/CustomElements/button';
|
||||
import displayCorrectDenomination from '../../../../../functions/displayCorrectDenomination';
|
||||
|
||||
import {
|
||||
CENTER,
|
||||
SMALLEST_ONCHAIN_SPARK_SEND_AMOUNT,
|
||||
} from '../../../../../constants';
|
||||
import CustomButton from '../../../../../functions/CustomElements/button';
|
||||
import {useState} from 'react';
|
||||
import {useNodeContext} from '../../../../../../context-store/nodeContext';
|
||||
import {useAppStatus} from '../../../../../../context-store/appStatus';
|
||||
import displayCorrectDenomination from '../../../../../functions/displayCorrectDenomination';
|
||||
import {InputTypeVariant} from '@breeztech/react-native-breez-sdk-liquid';
|
||||
import {useActiveCustodyAccount} from '../../../../../../context-store/activeAccount';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
|
||||
export default function AcceptButtonSendPage({
|
||||
canSendPayment,
|
||||
@@ -33,141 +36,185 @@ export default function AcceptButtonSendPage({
|
||||
seletctedToken,
|
||||
isLRC20Payment,
|
||||
}) {
|
||||
const navigate = useNavigation();
|
||||
const {t} = useTranslation();
|
||||
const [isGeneratingInvoice, setIsGeneratingInvoice] = useState(false);
|
||||
|
||||
const {masterInfoObject} = useGlobalContextProvider();
|
||||
const {liquidNodeInformation, fiatStats} = useNodeContext();
|
||||
const {minMaxLiquidSwapAmounts} = useAppStatus();
|
||||
const {currentWalletMnemoinc} = useActiveCustodyAccount();
|
||||
const {t} = useTranslation();
|
||||
const [isGeneratingInvoice, setIsGeneratingInvoice] = useState(false);
|
||||
const navigate = useNavigation();
|
||||
return (
|
||||
<CustomButton
|
||||
buttonStyles={{
|
||||
opacity:
|
||||
canSendPayment &&
|
||||
!(
|
||||
isLiquidPayment &&
|
||||
(convertedSendAmount < minMaxLiquidSwapAmounts.min ||
|
||||
convertedSendAmount > minMaxLiquidSwapAmounts.max)
|
||||
) &&
|
||||
!(
|
||||
paymentInfo?.type === 'lnUrlPay' &&
|
||||
(convertedSendAmount < minLNURLSatAmount ||
|
||||
convertedSendAmount > maxLNURLSatAmount)
|
||||
) &&
|
||||
!(
|
||||
paymentInfo?.type === 'Bitcoin' &&
|
||||
convertedSendAmount < SMALLEST_ONCHAIN_SPARK_SEND_AMOUNT
|
||||
) &&
|
||||
!(isLRC20Payment && sparkInformation.balance < 10)
|
||||
? 1
|
||||
: 0.5,
|
||||
width: 'auto',
|
||||
...CENTER,
|
||||
}}
|
||||
useLoading={isGeneratingInvoice}
|
||||
actionFunction={handleEnterSendAmount}
|
||||
textContent={t('constants.accept')}
|
||||
/>
|
||||
);
|
||||
|
||||
async function handleEnterSendAmount() {
|
||||
const isLiquidAmountValid = useMemo(() => {
|
||||
if (!isLiquidPayment) return true;
|
||||
return (
|
||||
convertedSendAmount >= minMaxLiquidSwapAmounts.min &&
|
||||
convertedSendAmount <= minMaxLiquidSwapAmounts.max
|
||||
);
|
||||
}, [isLiquidPayment, convertedSendAmount, minMaxLiquidSwapAmounts]);
|
||||
|
||||
const isLNURLAmountValid = useMemo(() => {
|
||||
if (paymentInfo?.type !== 'lnUrlPay') return true;
|
||||
return (
|
||||
convertedSendAmount >= minLNURLSatAmount &&
|
||||
convertedSendAmount <= maxLNURLSatAmount
|
||||
);
|
||||
}, [
|
||||
paymentInfo?.type,
|
||||
convertedSendAmount,
|
||||
minLNURLSatAmount,
|
||||
maxLNURLSatAmount,
|
||||
]);
|
||||
|
||||
const isBitcoinAmountValid = useMemo(() => {
|
||||
if (paymentInfo?.type !== 'Bitcoin') return true;
|
||||
return convertedSendAmount >= SMALLEST_ONCHAIN_SPARK_SEND_AMOUNT;
|
||||
}, [paymentInfo?.type, convertedSendAmount]);
|
||||
|
||||
const isLRC20Valid = useMemo(() => {
|
||||
if (!isLRC20Payment) return true;
|
||||
return sparkInformation.balance >= 10;
|
||||
}, [isLRC20Payment, sparkInformation?.balance]);
|
||||
|
||||
const buttonOpacity = useMemo(() => {
|
||||
return canSendPayment &&
|
||||
isLiquidAmountValid &&
|
||||
isLNURLAmountValid &&
|
||||
isBitcoinAmountValid &&
|
||||
isLRC20Valid
|
||||
? 1
|
||||
: 0.5;
|
||||
}, [
|
||||
canSendPayment,
|
||||
isLiquidAmountValid,
|
||||
isLNURLAmountValid,
|
||||
isBitcoinAmountValid,
|
||||
isLRC20Valid,
|
||||
]);
|
||||
|
||||
const handleLiquidAmountError = () => {
|
||||
const isMinError = convertedSendAmount < minMaxLiquidSwapAmounts.min;
|
||||
const errorAmount = isMinError
|
||||
? minMaxLiquidSwapAmounts.min
|
||||
: minMaxLiquidSwapAmounts.max;
|
||||
|
||||
navigate.navigate('ErrorScreen', {
|
||||
errorMessage: t('wallet.sendPages.acceptButton.liquidError', {
|
||||
overFlowType: isMinError ? 'Minimum' : 'Maximum',
|
||||
amount: displayCorrectDenomination({
|
||||
amount: errorAmount,
|
||||
fiatStats,
|
||||
masterInfoObject,
|
||||
}),
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
const handleBitcoinAmountError = () => {
|
||||
navigate.navigate('ErrorScreen', {
|
||||
errorMessage: t('wallet.sendPages.acceptButton.onchainError', {
|
||||
amount: displayCorrectDenomination({
|
||||
amount: SMALLEST_ONCHAIN_SPARK_SEND_AMOUNT,
|
||||
fiatStats,
|
||||
masterInfoObject,
|
||||
}),
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
const handleLNURLPayError = () => {
|
||||
const isMinError = convertedSendAmount < minLNURLSatAmount;
|
||||
const errorAmount = isMinError ? minLNURLSatAmount : maxLNURLSatAmount;
|
||||
|
||||
navigate.navigate('ErrorScreen', {
|
||||
errorMessage: t('wallet.sendPages.acceptButton.lnurlPayError', {
|
||||
overFlowType: isMinError ? 'Minimum' : 'Maximum',
|
||||
amount: displayCorrectDenomination({
|
||||
amount: errorAmount,
|
||||
fiatStats,
|
||||
masterInfoObject,
|
||||
}),
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
const handleLRC20Error = () => {
|
||||
navigate.navigate('ErrorScreen', {
|
||||
errorMessage: t('wallet.sendPages.acceptButton.lrc20FeeError', {
|
||||
amount: displayCorrectDenomination({
|
||||
amount: 10,
|
||||
masterInfoObject,
|
||||
fiatStats,
|
||||
}),
|
||||
balance: displayCorrectDenomination({
|
||||
amount: sparkInformation.balance,
|
||||
masterInfoObject,
|
||||
fiatStats,
|
||||
}),
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
const handleInsufficientBalanceError = () => {
|
||||
navigate.navigate('ErrorScreen', {
|
||||
errorMessage: t('wallet.sendPages.acceptButton.balanceError'),
|
||||
});
|
||||
};
|
||||
|
||||
const handleNoSendAmountError = () => {
|
||||
navigate.navigate('ErrorScreen', {
|
||||
errorMessage: t('wallet.sendPages.acceptButton.noSendAmountError'),
|
||||
});
|
||||
};
|
||||
|
||||
const validatePaymentAmount = () => {
|
||||
if (!paymentInfo?.sendAmount) {
|
||||
navigate.navigate('ErrorScreen', {
|
||||
errorMessage: t('wallet.sendPages.acceptButton.noSendAmountError'),
|
||||
});
|
||||
return;
|
||||
handleNoSendAmountError();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
isLiquidPayment &&
|
||||
(convertedSendAmount < minMaxLiquidSwapAmounts.min ||
|
||||
convertedSendAmount > minMaxLiquidSwapAmounts.max)
|
||||
) {
|
||||
navigate.navigate('ErrorScreen', {
|
||||
errorMessage: t('wallet.sendPages.acceptButton.liquidError', {
|
||||
overFlowType:
|
||||
convertedSendAmount < minMaxLiquidSwapAmounts.min
|
||||
? 'Minimum'
|
||||
: 'Maximum',
|
||||
amount: displayCorrectDenomination({
|
||||
amount:
|
||||
convertedSendAmount < minMaxLiquidSwapAmounts.min
|
||||
? minMaxLiquidSwapAmounts.min
|
||||
: minMaxLiquidSwapAmounts.max,
|
||||
fiatStats,
|
||||
masterInfoObject,
|
||||
}),
|
||||
}),
|
||||
});
|
||||
return;
|
||||
if (!isLiquidAmountValid) {
|
||||
handleLiquidAmountError();
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
paymentInfo?.type === 'Bitcoin' &&
|
||||
convertedSendAmount < SMALLEST_ONCHAIN_SPARK_SEND_AMOUNT
|
||||
) {
|
||||
navigate.navigate('ErrorScreen', {
|
||||
errorMessage: t('wallet.sendPages.acceptButton.onchainError', {
|
||||
amount: displayCorrectDenomination({
|
||||
amount: SMALLEST_ONCHAIN_SPARK_SEND_AMOUNT,
|
||||
fiatStats,
|
||||
masterInfoObject,
|
||||
}),
|
||||
}),
|
||||
});
|
||||
return;
|
||||
|
||||
if (!isBitcoinAmountValid) {
|
||||
handleBitcoinAmountError();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
paymentInfo?.type === InputTypeVariant.LN_URL_PAY &&
|
||||
(convertedSendAmount < minLNURLSatAmount ||
|
||||
convertedSendAmount > maxLNURLSatAmount)
|
||||
!isLNURLAmountValid
|
||||
) {
|
||||
navigate.navigate('ErrorScreen', {
|
||||
errorMessage: t('wallet.sendPages.acceptButton.lnurlPayError', {
|
||||
overFlowType:
|
||||
convertedSendAmount < minLNURLSatAmount ? 'Minimum' : 'Maximum',
|
||||
amount: displayCorrectDenomination({
|
||||
amount:
|
||||
convertedSendAmount < minLNURLSatAmount
|
||||
? minLNURLSatAmount
|
||||
: maxLNURLSatAmount,
|
||||
fiatStats,
|
||||
masterInfoObject,
|
||||
}),
|
||||
}),
|
||||
});
|
||||
return;
|
||||
handleLNURLPayError();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isLRC20Payment && sparkInformation.balance < 10) {
|
||||
navigate.navigate('ErrorScreen', {
|
||||
errorMessage: t('wallet.sendPages.acceptButton.lrc20FeeError', {
|
||||
amount: displayCorrectDenomination({
|
||||
amount: 10,
|
||||
masterInfoObject,
|
||||
fiatStats,
|
||||
}),
|
||||
balance: displayCorrectDenomination({
|
||||
amount: sparkInformation.balance,
|
||||
masterInfoObject,
|
||||
fiatStats,
|
||||
}),
|
||||
}),
|
||||
});
|
||||
return;
|
||||
if (!isLRC20Valid) {
|
||||
handleLRC20Error();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!canSendPayment && !!paymentInfo?.sendAmount) {
|
||||
navigate.navigate('ErrorScreen', {
|
||||
errorMessage: t('wallet.sendPages.acceptButton.balanceError'),
|
||||
});
|
||||
handleInsufficientBalanceError();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const handleEnterSendAmount = async () => {
|
||||
if (!validatePaymentAmount()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsGeneratingInvoice(true);
|
||||
|
||||
try {
|
||||
await decodeSendAddress({
|
||||
fiatStats,
|
||||
btcAdress: btcAdress,
|
||||
btcAdress,
|
||||
goBackFunction: errorMessageNavigation,
|
||||
setPaymentInfo,
|
||||
liquidNodeInformation,
|
||||
@@ -191,9 +238,23 @@ export default function AcceptButtonSendPage({
|
||||
currentWalletMnemoinc,
|
||||
t,
|
||||
});
|
||||
} catch (error) {
|
||||
console.log('Accept button error:', error);
|
||||
} finally {
|
||||
setIsGeneratingInvoice(false);
|
||||
} catch (err) {
|
||||
console.log('accecpt button error', err);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<CustomButton
|
||||
buttonStyles={{
|
||||
opacity: buttonOpacity,
|
||||
width: 'auto',
|
||||
...CENTER,
|
||||
}}
|
||||
useLoading={isGeneratingInvoice}
|
||||
actionFunction={handleEnterSendAmount}
|
||||
textContent={t('constants.accept')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {StyleSheet, View} from 'react-native';
|
||||
import {CENTER, ICONS, SIZES} from '../../../../../constants';
|
||||
import {ICONS, SIZES} from '../../../../../constants';
|
||||
import FormattedSatText from '../../../../../functions/CustomElements/satTextDisplay';
|
||||
import ThemeImage from '../../../../../functions/CustomElements/themeImage';
|
||||
import {useSparkWallet} from '../../../../../../context-store/sparkContext';
|
||||
@@ -9,6 +9,12 @@ export default function NavbarBalance({seletctedToken, selectedLRC20Asset}) {
|
||||
const {sparkInformation} = useSparkWallet();
|
||||
|
||||
const balance = seletctedToken?.balance || sparkInformation.balance;
|
||||
|
||||
const formattedTokensBalance =
|
||||
selectedLRC20Asset !== 'Bitcoin'
|
||||
? formatTokensNumber(balance, seletctedToken?.tokenMetadata?.decimals)
|
||||
: balance;
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<ThemeImage
|
||||
@@ -22,11 +28,8 @@ export default function NavbarBalance({seletctedToken, selectedLRC20Asset}) {
|
||||
neverHideBalance={true}
|
||||
styles={styles.headerText}
|
||||
balance={
|
||||
selectedLRC20Asset !== 'Bitcoin'
|
||||
? formatTokensNumber(
|
||||
balance,
|
||||
seletctedToken?.tokenMetadata?.decimals,
|
||||
)
|
||||
selectedLRC20Asset !== 'Bitcoin' && formattedTokensBalance > 1
|
||||
? Number(formattedTokensBalance).toFixed(2)
|
||||
: balance
|
||||
}
|
||||
useCustomLabel={
|
||||
@@ -44,7 +47,6 @@ const styles = StyleSheet.create({
|
||||
container: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
...CENTER,
|
||||
flexGrow: 1,
|
||||
paddingHorizontal: 35,
|
||||
justifyContent: 'center',
|
||||
@@ -52,7 +54,7 @@ const styles = StyleSheet.create({
|
||||
|
||||
walletIcon: {marginRight: 5, width: 23, height: 23},
|
||||
headerText: {
|
||||
fontSize: SIZES.large,
|
||||
includeFontPadding: false,
|
||||
fontSize: SIZES.xLarge,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -116,8 +116,7 @@ export default function SelectLRC20Token({
|
||||
<CustomKeyboardAvoidingView
|
||||
useStandardWidth={true}
|
||||
useLocalPadding={true}
|
||||
isKeyboardActive={isKeyboardActive}
|
||||
style={styles.container}>
|
||||
isKeyboardActive={isKeyboardActive}>
|
||||
<CustomSettingsTopBar
|
||||
customBackFunction={goBackFunction}
|
||||
label={t('wallet.sendPages.selectLRC20Token.title')}
|
||||
@@ -170,7 +169,12 @@ const styles = StyleSheet.create({
|
||||
alignItems: 'center',
|
||||
...CENTER,
|
||||
},
|
||||
innerContainer: {flex: 1, width: INSET_WINDOW_WIDTH, ...CENTER},
|
||||
innerContainer: {
|
||||
flex: 1,
|
||||
width: INSET_WINDOW_WIDTH,
|
||||
...CENTER,
|
||||
marginTop: 10,
|
||||
},
|
||||
|
||||
titleText: {
|
||||
fontSize: SIZES.large,
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
import {useState} from 'react';
|
||||
import {useCallback, useState} from 'react';
|
||||
import {CENTER} from '../../../../../constants';
|
||||
import {SATSPERBITCOIN} from '../../../../../constants/math';
|
||||
import CustomButton from '../../../../../functions/CustomElements/button';
|
||||
import {crashlyticsLogReport} from '../../../../../functions/crashlyticsLogs';
|
||||
import {sparkPaymenWrapper} from '../../../../../functions/spark/payments';
|
||||
import {getLNAddressForLiquidPayment} from '../functions/payments';
|
||||
import {calculateBoltzFeeNew} from '../../../../../functions/boltz/boltzFeeNew';
|
||||
import {useActiveCustodyAccount} from '../../../../../../context-store/activeAccount';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import DropdownMenu from '../../../../../functions/CustomElements/dropdownMenu';
|
||||
import {StyleSheet, View} from 'react-native';
|
||||
|
||||
const MAX_SEND_OPTIONS = [
|
||||
{label: '25%', value: '25'},
|
||||
{label: '50%', value: '50'},
|
||||
{label: '75%', value: '75'},
|
||||
{label: '100%', value: '100'},
|
||||
];
|
||||
export default function SendMaxComponent({
|
||||
fiatStats,
|
||||
sparkInformation,
|
||||
@@ -18,91 +25,134 @@ export default function SendMaxComponent({
|
||||
paymentFee,
|
||||
paymentType,
|
||||
minMaxLiquidSwapAmounts,
|
||||
seletctedToken,
|
||||
selectedLRC20Asset,
|
||||
}) {
|
||||
const {t} = useTranslation();
|
||||
const [isGettingMax, setIsGettingMax] = useState(false);
|
||||
const {currentWalletMnemoinc} = useActiveCustodyAccount();
|
||||
return (
|
||||
<CustomButton
|
||||
buttonStyles={{
|
||||
width: 'auto',
|
||||
...CENTER,
|
||||
marginBottom: 10,
|
||||
}}
|
||||
useLoading={isGettingMax}
|
||||
actionFunction={sendMax}
|
||||
textContent={t('wallet.sendPages.sendMaxComponent.sendMax')}
|
||||
/>
|
||||
);
|
||||
async function sendMax() {
|
||||
try {
|
||||
crashlyticsLogReport('Starting send max process');
|
||||
setIsGettingMax(true);
|
||||
|
||||
if (paymentInfo.type === 'liquid') {
|
||||
const supportFee = masterInfoObject?.enabledDeveloperSupport.isEnabled
|
||||
? Math.ceil(
|
||||
Number(sparkInformation.balance) *
|
||||
const handleSelctProcesss = useCallback(
|
||||
async item => {
|
||||
try {
|
||||
if (isGettingMax) return;
|
||||
await new Promise(res => setTimeout(res, 250));
|
||||
const balance = seletctedToken?.balance || sparkInformation.balance;
|
||||
const selectedPercent = !item ? 100 : item.value;
|
||||
const sendingBalance = Math.round(balance * (selectedPercent / 100));
|
||||
console.log(selectedPercent, balance, sendingBalance);
|
||||
crashlyticsLogReport('Starting send max process');
|
||||
setIsGettingMax(true);
|
||||
|
||||
if (paymentInfo.type === 'liquid') {
|
||||
const supportFee =
|
||||
Math.ceil(
|
||||
Number(sendingBalance) *
|
||||
masterInfoObject?.enabledDeveloperSupport.baseFeePercent,
|
||||
) + Number(masterInfoObject?.enabledDeveloperSupport?.baseFee)
|
||||
: 0;
|
||||
const boltzFee = calculateBoltzFeeNew(
|
||||
Number(sparkInformation.balance),
|
||||
'ln-liquid',
|
||||
minMaxLiquidSwapAmounts.reverseSwapStats,
|
||||
);
|
||||
) + Number(masterInfoObject?.enabledDeveloperSupport?.baseFee);
|
||||
|
||||
setPaymentInfo(prev => ({
|
||||
...prev,
|
||||
sendAmount: String(
|
||||
Number(sparkInformation.balance) - (supportFee + boltzFee) * 1.5,
|
||||
),
|
||||
}));
|
||||
return;
|
||||
const boltzFee = calculateBoltzFeeNew(
|
||||
Number(sendingBalance),
|
||||
'ln-liquid',
|
||||
minMaxLiquidSwapAmounts.reverseSwapStats,
|
||||
);
|
||||
|
||||
setPaymentInfo(prev => ({
|
||||
...prev,
|
||||
sendAmount: String(
|
||||
Number(sendingBalance) - (supportFee + boltzFee) * 1.5,
|
||||
),
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedLRC20Asset !== 'Bitcoin') {
|
||||
setPaymentInfo(prev => ({
|
||||
...prev,
|
||||
sendAmount: String(sendingBalance),
|
||||
}));
|
||||
} else {
|
||||
let address = paymentInfo?.address;
|
||||
|
||||
if (paymentInfo.type === 'lnUrlPay') {
|
||||
const invoice = await getLNAddressForLiquidPayment(
|
||||
paymentInfo,
|
||||
Number(sendingBalance),
|
||||
);
|
||||
address = invoice;
|
||||
}
|
||||
|
||||
const feeResponse = await sparkPaymenWrapper({
|
||||
getFee: true,
|
||||
address: address,
|
||||
paymentType: paymentType.toLowerCase(),
|
||||
amountSats: Number(sendingBalance),
|
||||
masterInfoObject,
|
||||
seletctedToken: selectedLRC20Asset,
|
||||
mnemonic: currentWalletMnemoinc,
|
||||
});
|
||||
|
||||
if (!feeResponse.didWork) throw new Error(feeResponse.error);
|
||||
|
||||
const maxAmountSats =
|
||||
Number(sendingBalance) -
|
||||
(feeResponse.fee + feeResponse.supportFee) * 1.1;
|
||||
|
||||
const convertedMax =
|
||||
masterInfoObject.userBalanceDenomination != 'fiat'
|
||||
? Math.round(Number(maxAmountSats))
|
||||
: (
|
||||
Number(maxAmountSats) /
|
||||
Math.round(SATSPERBITCOIN / fiatStats?.value)
|
||||
).toFixed(3);
|
||||
setPaymentInfo(prev => ({
|
||||
...prev,
|
||||
sendAmount: String(convertedMax),
|
||||
feeQuote: feeResponse.feeQuote,
|
||||
paymentFee: feeResponse.fee,
|
||||
supportFee: feeResponse.supportFee,
|
||||
}));
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err, 'ERROR');
|
||||
} finally {
|
||||
setIsGettingMax(false);
|
||||
}
|
||||
let address = paymentInfo?.address;
|
||||
},
|
||||
[isGettingMax, seletctedToken, selectedLRC20Asset],
|
||||
);
|
||||
|
||||
if (paymentInfo.type === 'lnUrlPay') {
|
||||
const invoice = await getLNAddressForLiquidPayment(
|
||||
paymentInfo,
|
||||
Number(sparkInformation.balance),
|
||||
);
|
||||
address = invoice;
|
||||
}
|
||||
const feeResponse = await sparkPaymenWrapper({
|
||||
getFee: true,
|
||||
address: address,
|
||||
paymentType: paymentType.toLowerCase(),
|
||||
amountSats: Number(sparkInformation.balance),
|
||||
masterInfoObject,
|
||||
mnemonic: currentWalletMnemoinc,
|
||||
});
|
||||
|
||||
if (!feeResponse.didWork) throw new Error(feeResponse.error);
|
||||
|
||||
const maxAmountSats =
|
||||
Number(sparkInformation.balance) -
|
||||
(feeResponse.fee + feeResponse.supportFee) * 1.1;
|
||||
|
||||
const convertedMax =
|
||||
masterInfoObject.userBalanceDenomination != 'fiat'
|
||||
? Math.round(Number(maxAmountSats))
|
||||
: (
|
||||
Number(maxAmountSats) /
|
||||
Math.round(SATSPERBITCOIN / fiatStats?.value)
|
||||
).toFixed(3);
|
||||
setPaymentInfo(prev => ({
|
||||
...prev,
|
||||
sendAmount: String(convertedMax),
|
||||
feeQuote: feeResponse.feeQuote,
|
||||
paymentFee: feeResponse.fee,
|
||||
supportFee: feeResponse.supportFee,
|
||||
}));
|
||||
return;
|
||||
} catch (err) {
|
||||
console.log(err, 'ERROR');
|
||||
} finally {
|
||||
setIsGettingMax(false);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<View style={styles.dropdownMenuContainer}>
|
||||
<DropdownMenu
|
||||
selectedValue={
|
||||
selectedLRC20Asset === 'Bitcoin'
|
||||
? t('wallet.sendPages.sendMaxComponent.sendMax')
|
||||
: t('wallet.sendPages.sendMaxComponent.tokensMax')
|
||||
}
|
||||
onSelect={handleSelctProcesss}
|
||||
options={MAX_SEND_OPTIONS}
|
||||
showClearIcon={false}
|
||||
showVerticalArrows={false}
|
||||
customButtonStyles={styles.containerStyles}
|
||||
textStyles={styles.textStyles}
|
||||
useIsLoading={isGettingMax}
|
||||
disableDropdownPress={isGettingMax}
|
||||
customFunction={
|
||||
selectedLRC20Asset === 'Bitcoin' ? handleSelctProcesss : undefined
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
textStyles: {textAlign: 'center'},
|
||||
dropdownMenuContainer: {
|
||||
marginBottom: 10,
|
||||
},
|
||||
containerStyles: {
|
||||
flex: 0,
|
||||
...CENTER,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -139,6 +139,7 @@ export default function SendPaymentScreen(props) {
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
crashlyticsLogReport('Starting decode address');
|
||||
await decodeSendAddress({
|
||||
fiatStats,
|
||||
@@ -168,31 +169,10 @@ export default function SendPaymentScreen(props) {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
console.log(
|
||||
!Object.keys(paymentInfo).length,
|
||||
'|',
|
||||
!masterInfoObject[QUICK_PAY_STORAGE_KEY].isFastPayEnabled,
|
||||
'|',
|
||||
!canSendPayment,
|
||||
'|',
|
||||
// paymentInfo.type === InputTypeVariant.LN_URL_PAY,
|
||||
// '|',
|
||||
!(
|
||||
masterInfoObject[QUICK_PAY_STORAGE_KEY].fastPayThresholdSats >=
|
||||
convertedSendAmount
|
||||
),
|
||||
'|',
|
||||
// paymentInfo.type === 'liquid' && !paymentInfo.data.isBip21,
|
||||
'FAST PAY SETTINGS',
|
||||
masterInfoObject[QUICK_PAY_STORAGE_KEY].fastPayThresholdSats,
|
||||
convertedSendAmount,
|
||||
);
|
||||
|
||||
if (!Object.keys(paymentInfo).length) return;
|
||||
if (!masterInfoObject[QUICK_PAY_STORAGE_KEY].isFastPayEnabled) return;
|
||||
if (!canSendPayment) return;
|
||||
if (canEditPaymentAmount) return;
|
||||
// if (paymentInfo.type === InputTypeVariant.LN_URL_PAY) return;
|
||||
if (
|
||||
!(
|
||||
masterInfoObject[QUICK_PAY_STORAGE_KEY].fastPayThresholdSats >=
|
||||
@@ -200,7 +180,6 @@ export default function SendPaymentScreen(props) {
|
||||
)
|
||||
)
|
||||
return;
|
||||
// if (paymentInfo.type === 'liquid' && !paymentInfo.data.isBip21) return;
|
||||
|
||||
setTimeout(() => {
|
||||
sendPayment();
|
||||
@@ -241,6 +220,11 @@ export default function SendPaymentScreen(props) {
|
||||
);
|
||||
}
|
||||
|
||||
const clearSettings = () => {
|
||||
setPaymentInfo(prev => ({...prev, canEditPayment: true, sendAmount: ''}));
|
||||
setMasterTokenInfo({});
|
||||
};
|
||||
|
||||
return (
|
||||
<CustomKeyboardAvoidingView
|
||||
useLocalPadding={true}
|
||||
@@ -248,12 +232,12 @@ export default function SendPaymentScreen(props) {
|
||||
useStandardWidth={true}>
|
||||
<View style={styles.topBar}>
|
||||
<TouchableOpacity
|
||||
style={{position: 'absolute', zIndex: 99, left: 0}}
|
||||
style={styles.backArrow}
|
||||
onPress={
|
||||
enabledLRC20 &&
|
||||
Object.keys(seletctedToken).length &&
|
||||
paymentInfo.type === 'spark'
|
||||
? () => setMasterTokenInfo({})
|
||||
? clearSettings
|
||||
: goBackFunction
|
||||
}>
|
||||
<ThemeImage
|
||||
@@ -329,19 +313,18 @@ export default function SendPaymentScreen(props) {
|
||||
</ScrollView>
|
||||
{canEditPaymentAmount && (
|
||||
<>
|
||||
{(paymentInfo.type !== 'spark' ||
|
||||
(paymentInfo.type === 'spark' && !enabledLRC20)) && (
|
||||
<SendMaxComponent
|
||||
fiatStats={fiatStats}
|
||||
sparkInformation={sparkInformation}
|
||||
paymentInfo={paymentInfo}
|
||||
setPaymentInfo={setPaymentInfo}
|
||||
masterInfoObject={masterInfoObject}
|
||||
paymentFee={paymentFee}
|
||||
paymentType={paymentInfo?.paymentNetwork}
|
||||
minMaxLiquidSwapAmounts={minMaxLiquidSwapAmounts}
|
||||
/>
|
||||
)}
|
||||
<SendMaxComponent
|
||||
fiatStats={fiatStats}
|
||||
sparkInformation={sparkInformation}
|
||||
paymentInfo={paymentInfo}
|
||||
setPaymentInfo={setPaymentInfo}
|
||||
masterInfoObject={masterInfoObject}
|
||||
paymentFee={paymentFee}
|
||||
paymentType={paymentInfo?.paymentNetwork}
|
||||
minMaxLiquidSwapAmounts={minMaxLiquidSwapAmounts}
|
||||
selectedLRC20Asset={selectedLRC20Asset}
|
||||
seletctedToken={seletctedToken}
|
||||
/>
|
||||
|
||||
<CustomSearchInput
|
||||
onFocusFunction={() => setIsAmountFocused(false)}
|
||||
@@ -536,4 +519,5 @@ const styles = StyleSheet.create({
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
backArrow: {position: 'absolute', zIndex: 99, left: 0},
|
||||
});
|
||||
|
||||
@@ -14,6 +14,7 @@ import GetThemeColors from '../../hooks/themeColors';
|
||||
import {useGlobalThemeContext} from '../../../context-store/theme';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import CountryFlag from 'react-native-country-flag';
|
||||
import FullLoadingScreen from './loadingScreen';
|
||||
|
||||
const DropdownMenu = ({
|
||||
options,
|
||||
@@ -26,18 +27,20 @@ const DropdownMenu = ({
|
||||
customButtonStyles = {},
|
||||
dropdownItemCustomStyles = {},
|
||||
showFlag = false,
|
||||
useIsLoading = false,
|
||||
disableDropdownPress = false,
|
||||
customFunction,
|
||||
}) => {
|
||||
const {t} = useTranslation();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [buttonLayout, setButtonLayout] = useState(null);
|
||||
const [itemSelectorLayout, setItemSelectorLayout] = useState(null);
|
||||
|
||||
const dropdownRef = useRef(null);
|
||||
const {theme, darkModeType} = useGlobalThemeContext();
|
||||
const {backgroundOffset, backgroundColor} = GetThemeColors();
|
||||
const [dropdownHeight, setDropdownHeight] = useState(0);
|
||||
const placeholderText = placeholder || t('constants.selectOption');
|
||||
|
||||
const placeholderText = placeholder || t('constants.selctOption');
|
||||
const handleSelect = item => {
|
||||
onSelect(item);
|
||||
setIsOpen(false);
|
||||
@@ -46,6 +49,7 @@ const DropdownMenu = ({
|
||||
const handleLayout = event => {
|
||||
setButtonLayout(event.nativeEvent.layout);
|
||||
};
|
||||
|
||||
const handleItemSelectorHeight = event => {
|
||||
setItemSelectorLayout(event.nativeEvent.layout);
|
||||
};
|
||||
@@ -70,12 +74,19 @@ const DropdownMenu = ({
|
||||
};
|
||||
|
||||
const handleDropdownToggle = async () => {
|
||||
if (disableDropdownPress) return;
|
||||
|
||||
if (!isOpen) {
|
||||
// Recalculate position before opening
|
||||
await measureButtonPosition();
|
||||
}
|
||||
if (customFunction) {
|
||||
customFunction();
|
||||
return;
|
||||
}
|
||||
setIsOpen(!isOpen);
|
||||
};
|
||||
|
||||
// Calculate if dropdown should open upwards based on screen position
|
||||
const screenHeight = Dimensions.get('window').height;
|
||||
const isTooLow =
|
||||
@@ -86,16 +97,21 @@ const DropdownMenu = ({
|
||||
showFlag && options.find(item => item.value === selectedValue)?.flagCode;
|
||||
|
||||
return (
|
||||
<View style={styles.container} ref={dropdownRef} onLayout={handleLayout}>
|
||||
<View
|
||||
style={styles.container}
|
||||
// ref={dropdownRef}
|
||||
onLayout={handleLayout}>
|
||||
<View style={styles.selectorContainer}>
|
||||
<TouchableOpacity
|
||||
activeOpacity={disableDropdownPress ? 1 : 0.2}
|
||||
ref={dropdownRef}
|
||||
onLayout={handleItemSelectorHeight}
|
||||
style={{
|
||||
...styles.dropdownButton,
|
||||
backgroundColor: theme ? backgroundOffset : COLORS.darkModeText,
|
||||
...customButtonStyles,
|
||||
}}
|
||||
onPress={() => handleDropdownToggle()}>
|
||||
onPress={handleDropdownToggle}>
|
||||
{showFlag && flag && (
|
||||
<CountryFlag
|
||||
style={{padding: 0, marginRight: 5, backgroundColor: 'red'}}
|
||||
@@ -103,15 +119,22 @@ const DropdownMenu = ({
|
||||
size={15}
|
||||
/>
|
||||
)}
|
||||
<ThemeText
|
||||
styles={{
|
||||
includeFontPadding: false,
|
||||
flexShrink: 1,
|
||||
...textStyles,
|
||||
}}
|
||||
CustomNumberOfLines={1}
|
||||
content={selectedValue ? selectedValue : placeholderText}
|
||||
/>
|
||||
{useIsLoading ? (
|
||||
<FullLoadingScreen
|
||||
containerStyles={styles.loadingButton}
|
||||
showText={false}
|
||||
size="small"
|
||||
/>
|
||||
) : (
|
||||
<ThemeText
|
||||
styles={{
|
||||
...styles.defTextStyle,
|
||||
...textStyles,
|
||||
}}
|
||||
CustomNumberOfLines={1}
|
||||
content={selectedValue ? selectedValue : placeholderText}
|
||||
/>
|
||||
)}
|
||||
{showVerticalArrows && (
|
||||
<View style={styles.verticalArrowsContainer}>
|
||||
<ThemeImage
|
||||
@@ -141,7 +164,6 @@ const DropdownMenu = ({
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
|
||||
<Modal
|
||||
visible={isOpen}
|
||||
transparent={true}
|
||||
@@ -159,9 +181,9 @@ const DropdownMenu = ({
|
||||
buttonLayout && {
|
||||
top: isTooLow
|
||||
? buttonLayout.y - dropdownHeight - 5
|
||||
: buttonLayout.y + itemSelectorLayout?.height + 5,
|
||||
left: '7.3%',
|
||||
width: itemSelectorLayout?.width,
|
||||
: buttonLayout.y + buttonLayout.height + 5,
|
||||
left: buttonLayout.x,
|
||||
width: buttonLayout.width,
|
||||
},
|
||||
{
|
||||
backgroundColor: theme ? backgroundOffset : COLORS.darkModeText,
|
||||
@@ -181,7 +203,7 @@ const DropdownMenu = ({
|
||||
...dropdownItemCustomStyles,
|
||||
}}
|
||||
onPress={() => handleSelect(item)}>
|
||||
{showFlag && flag && (
|
||||
{showFlag && item.flagCode && (
|
||||
<CountryFlag
|
||||
style={{
|
||||
padding: 0,
|
||||
@@ -192,7 +214,10 @@ const DropdownMenu = ({
|
||||
size={15}
|
||||
/>
|
||||
)}
|
||||
<ThemeText content={t(item.label)} />
|
||||
<ThemeText
|
||||
styles={styles.defTextStyle}
|
||||
content={t(item.label)}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</ScrollView>
|
||||
@@ -263,6 +288,13 @@ const styles = StyleSheet.create({
|
||||
paddingHorizontal: 10,
|
||||
borderBottomWidth: 1,
|
||||
},
|
||||
defTextStyle: {
|
||||
includeFontPadding: false,
|
||||
flexShrink: 1,
|
||||
},
|
||||
loadingButton: {
|
||||
flex: 0,
|
||||
},
|
||||
});
|
||||
|
||||
export default DropdownMenu;
|
||||
|
||||
@@ -86,5 +86,6 @@ const styles = StyleSheet.create({
|
||||
fontFamily: FONT.Title_Regular,
|
||||
textAlign: 'center',
|
||||
...CENTER,
|
||||
includeFontPadding: false,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
import MaskedView from '@react-native-masked-view/masked-view';
|
||||
import * as React from 'react';
|
||||
import {
|
||||
Children,
|
||||
cloneElement,
|
||||
createContext,
|
||||
isValidElement,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import {
|
||||
Animated,
|
||||
Dimensions,
|
||||
@@ -22,11 +31,11 @@ const SkeletonTextPlaceholder = ({
|
||||
direction = 'right',
|
||||
shimmerWidth,
|
||||
}) => {
|
||||
const [layout, setLayout] = React.useState();
|
||||
const animatedValueRef = React.useRef(new Animated.Value(0));
|
||||
const [layout, setLayout] = useState();
|
||||
const animatedValueRef = useRef(new Animated.Value(0));
|
||||
const isAnimationReady = Boolean(speed && layout?.width && layout?.height);
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (!isAnimationReady) return;
|
||||
const loop = Animated.loop(
|
||||
Animated.timing(animatedValueRef.current, {
|
||||
@@ -40,7 +49,7 @@ const SkeletonTextPlaceholder = ({
|
||||
return () => loop.stop();
|
||||
}, [isAnimationReady, speed]);
|
||||
|
||||
const animatedGradientStyle = React.useMemo(() => {
|
||||
const animatedGradientStyle = useMemo(() => {
|
||||
const animationWidth = WINDOW_WIDTH + (shimmerWidth ?? 0);
|
||||
return {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
@@ -59,7 +68,7 @@ const SkeletonTextPlaceholder = ({
|
||||
};
|
||||
}, [direction, WINDOW_WIDTH, shimmerWidth]);
|
||||
|
||||
const transparentColor = React.useMemo(
|
||||
const transparentColor = useMemo(
|
||||
() => getTransparentColor(highlightColor.replace(/ /g, '')),
|
||||
[highlightColor],
|
||||
);
|
||||
@@ -101,7 +110,7 @@ const SkeletonTextPlaceholder = ({
|
||||
};
|
||||
|
||||
// Context to override text colors
|
||||
const TextColorContext = React.createContext();
|
||||
const TextColorContext = createContext();
|
||||
|
||||
const TextColorProvider = ({children, color}) => {
|
||||
return (
|
||||
@@ -113,7 +122,7 @@ const TextColorProvider = ({children, color}) => {
|
||||
|
||||
// Recursively transform elements to override text rendering
|
||||
const transformElementsForMask = (children, textColor) => {
|
||||
return React.Children.map(children, (child, index) => {
|
||||
return Children.map(children, (child, index) => {
|
||||
if (!child) return null;
|
||||
|
||||
if (typeof child === 'string') {
|
||||
@@ -124,7 +133,7 @@ const transformElementsForMask = (children, textColor) => {
|
||||
);
|
||||
}
|
||||
|
||||
if (React.isValidElement(child)) {
|
||||
if (isValidElement(child)) {
|
||||
const props = {key: index};
|
||||
|
||||
// If it's a Text component, override the color
|
||||
@@ -162,7 +171,7 @@ const transformElementsForMask = (children, textColor) => {
|
||||
);
|
||||
}
|
||||
|
||||
return React.cloneElement(child, props);
|
||||
return cloneElement(child, props);
|
||||
}
|
||||
|
||||
return child;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import MaskedView from '@react-native-masked-view/masked-view';
|
||||
import * as React from 'react';
|
||||
import {
|
||||
Animated,
|
||||
Dimensions,
|
||||
@@ -12,6 +11,7 @@ import {
|
||||
} from 'react-native';
|
||||
import {LinearGradient} from 'expo-linear-gradient';
|
||||
import {SKELETON_ANIMATION_SPEED} from '../../constants';
|
||||
import {Children, Fragment, useEffect, useMemo, useRef, useState} from 'react';
|
||||
|
||||
const WINDOW_WIDTH = Dimensions.get('window').width;
|
||||
|
||||
@@ -27,11 +27,11 @@ const SkeletonPlaceholder = ({
|
||||
borderRadius,
|
||||
shimmerWidth,
|
||||
}) => {
|
||||
const [layout, setLayout] = React.useState();
|
||||
const animatedValueRef = React.useRef(new Animated.Value(0));
|
||||
const [layout, setLayout] = useState();
|
||||
const animatedValueRef = useRef(new Animated.Value(0));
|
||||
const isAnimationReady = Boolean(speed && layout?.width && layout?.height);
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (!isAnimationReady) return;
|
||||
|
||||
const loop = Animated.loop(
|
||||
@@ -46,7 +46,7 @@ const SkeletonPlaceholder = ({
|
||||
return () => loop.stop();
|
||||
}, [isAnimationReady, speed]);
|
||||
|
||||
const animatedGradientStyle = React.useMemo(() => {
|
||||
const animatedGradientStyle = useMemo(() => {
|
||||
const animationWidth = WINDOW_WIDTH + (shimmerWidth ?? 0);
|
||||
return {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
@@ -65,7 +65,7 @@ const SkeletonPlaceholder = ({
|
||||
};
|
||||
}, [direction, WINDOW_WIDTH, shimmerWidth]);
|
||||
|
||||
const placeholders = React.useMemo(() => {
|
||||
const placeholders = useMemo(() => {
|
||||
if (!enabled) return null;
|
||||
|
||||
return (
|
||||
@@ -75,7 +75,7 @@ const SkeletonPlaceholder = ({
|
||||
);
|
||||
}, [backgroundColor, children, borderRadius, enabled]);
|
||||
|
||||
const transparentColor = React.useMemo(
|
||||
const transparentColor = useMemo(
|
||||
() => getTransparentColor(highlightColor.replace(/ /g, '')),
|
||||
[highlightColor],
|
||||
);
|
||||
@@ -130,10 +130,10 @@ const getItemStyle = ({children: _, style, ...styleFromProps}) => {
|
||||
const transformToPlaceholder = (rootElement, backgroundColor, radius) => {
|
||||
if (!rootElement) return null;
|
||||
|
||||
return React.Children.map(rootElement, (element, index) => {
|
||||
return Children.map(rootElement, (element, index) => {
|
||||
if (!element) return null;
|
||||
|
||||
if (element.type === React.Fragment)
|
||||
if (element.type === Fragment)
|
||||
return (
|
||||
<>
|
||||
{transformToPlaceholder(
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
import * as bip21 from 'bip21';
|
||||
import {crashlyticsLogReport} from './crashlyticsLogs';
|
||||
export function formatBip21Address({
|
||||
address = '',
|
||||
amount = 0,
|
||||
message = '',
|
||||
prefix = '',
|
||||
}) {
|
||||
try {
|
||||
crashlyticsLogReport('Formatting bip21 liquid address');
|
||||
return bip21.encode(
|
||||
address,
|
||||
{
|
||||
amount: amount,
|
||||
label: message,
|
||||
message: message,
|
||||
},
|
||||
prefix,
|
||||
);
|
||||
} catch (err) {
|
||||
console.log('format bip21 spark address error', err);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
export function decodeBip21Address(address, prefix) {
|
||||
try {
|
||||
crashlyticsLogReport('decoding bip21 spark');
|
||||
return bip21.decode(address, prefix);
|
||||
} catch (err) {
|
||||
console.log('format bip21 spark address error', err);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
// import {decode, encode} from 'bip21';
|
||||
// import {crashlyticsLogReport} from './crashlyticsLogs';
|
||||
// export function formatBip21Address({
|
||||
// address = '',
|
||||
// amount = 0,
|
||||
// message = '',
|
||||
// prefix = '',
|
||||
// }) {
|
||||
// try {
|
||||
// crashlyticsLogReport('Formatting bip21 liquid address');
|
||||
// return encode(
|
||||
// address,
|
||||
// {
|
||||
// amount: amount,
|
||||
// label: message,
|
||||
// message: message,
|
||||
// },
|
||||
// prefix,
|
||||
// );
|
||||
// } catch (err) {
|
||||
// console.log('format bip21 spark address error', err);
|
||||
// return '';
|
||||
// }
|
||||
// }
|
||||
// export function decodeBip21Address(address, prefix) {
|
||||
// try {
|
||||
// crashlyticsLogReport('decoding bip21 spark');
|
||||
// return decode(address, prefix);
|
||||
// } catch (err) {
|
||||
// console.log('format bip21 spark address error', err);
|
||||
// return '';
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as SQLite from 'expo-sqlite';
|
||||
import {openDatabaseAsync} from 'expo-sqlite';
|
||||
|
||||
export const ROOTSTOCK_DB_NAME = 'ROOTSTOCK_SWAPS';
|
||||
export const ROOTSTOCK_TABLE_NAME = 'saved_rootstock_swaps';
|
||||
@@ -6,7 +6,7 @@ let sqlLiteDB;
|
||||
|
||||
if (!sqlLiteDB) {
|
||||
async function openDBConnection() {
|
||||
sqlLiteDB = await SQLite.openDatabaseAsync(`${ROOTSTOCK_DB_NAME}.db`);
|
||||
sqlLiteDB = await openDatabaseAsync(`${ROOTSTOCK_DB_NAME}.db`);
|
||||
}
|
||||
openDBConnection();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import * as FileSystem from 'expo-file-system';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import {BLITZ_PROFILE_IMG_STORAGE_REF} from '../constants';
|
||||
import {getDownloadURL, getMetadata, ref} from '@react-native-firebase/storage';
|
||||
import {storage} from '../../db/initializeFirebase';
|
||||
import {
|
||||
cacheDirectory,
|
||||
downloadAsync,
|
||||
getInfoAsync,
|
||||
makeDirectoryAsync,
|
||||
} from 'expo-file-system';
|
||||
import {getLocalStorageItem, setLocalStorageItem} from './localStorage';
|
||||
|
||||
const FILE_DIR = FileSystem.cacheDirectory + 'profileImages/';
|
||||
const FILE_DIR = cacheDirectory + 'profileImages/';
|
||||
const CACHE_KEY = uuid => `${BLITZ_PROFILE_IMG_STORAGE_REF}/${uuid}`;
|
||||
|
||||
export async function getCachedProfileImage(uuid) {
|
||||
@@ -19,22 +24,23 @@ export async function getCachedProfileImage(uuid) {
|
||||
const updated = metadata.updated;
|
||||
|
||||
// Check for cached image info
|
||||
const cacheEntry = await AsyncStorage.getItem(key);
|
||||
const cacheEntry = await getLocalStorageItem(key);
|
||||
const parsed = cacheEntry ? JSON.parse(cacheEntry) : null;
|
||||
|
||||
if (parsed?.updated === updated) {
|
||||
const exists = await FileSystem.getInfoAsync(parsed.localUri);
|
||||
const exists = await getInfoAsync(parsed.localUri);
|
||||
if (exists.exists)
|
||||
return {localUri: parsed.localUri, updated: parsed?.updated};
|
||||
}
|
||||
|
||||
const url = await getDownloadURL(reference);
|
||||
await FileSystem.makeDirectoryAsync(FILE_DIR, {intermediates: true});
|
||||
|
||||
await makeDirectoryAsync(FILE_DIR, {intermediates: true});
|
||||
const localUri = `${FILE_DIR}${uuid}.jpg`;
|
||||
|
||||
await FileSystem.downloadAsync(url, localUri);
|
||||
await downloadAsync(url, localUri);
|
||||
const newEntry = {localUri, updated};
|
||||
await AsyncStorage.setItem(key, JSON.stringify(newEntry));
|
||||
await setLocalStorageItem(key, JSON.stringify(newEntry));
|
||||
|
||||
return {localUri, updated};
|
||||
} catch (e) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {getLocalStorageItem, setLocalStorageItem} from './localStorage';
|
||||
import * as FileSystem from 'expo-file-system';
|
||||
import {Platform} from 'react-native';
|
||||
import customUUID from './customUUID';
|
||||
import {getInfoAsync, makeDirectoryAsync} from 'expo-file-system';
|
||||
|
||||
export async function getOrCreateDirectory(uuidKey, workingDir) {
|
||||
try {
|
||||
@@ -16,11 +16,11 @@ export async function getOrCreateDirectory(uuidKey, workingDir) {
|
||||
const checkPath =
|
||||
Platform.OS === 'android' ? `file://${directoryPath}` : directoryPath;
|
||||
|
||||
const dirInfo = await FileSystem.getInfoAsync(checkPath);
|
||||
const dirInfo = await getInfoAsync(checkPath);
|
||||
console.log('Directory Info:', dirInfo);
|
||||
|
||||
if (!dirInfo.exists) {
|
||||
await FileSystem.makeDirectoryAsync(checkPath, {intermediates: true});
|
||||
await makeDirectoryAsync(checkPath, {intermediates: true});
|
||||
console.log(`Directory created: ${checkPath}`);
|
||||
await new Promise(resolve => setTimeout(resolve, 8000)); //adds buffer
|
||||
} else {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as Clipboard from 'expo-clipboard';
|
||||
import {setStringAsync} from 'expo-clipboard';
|
||||
|
||||
export default async function copyToClipboard(
|
||||
data,
|
||||
@@ -7,7 +7,7 @@ export default async function copyToClipboard(
|
||||
customText,
|
||||
) {
|
||||
try {
|
||||
await Clipboard.setStringAsync(data);
|
||||
await setStringAsync(data);
|
||||
if (page === 'ChatGPT') return;
|
||||
showToast({
|
||||
type: 'clipboard',
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import * as Clipboard from 'expo-clipboard';
|
||||
import {getStringAsync} from 'expo-clipboard';
|
||||
|
||||
export default async function getClipboardText() {
|
||||
try {
|
||||
const data = await Clipboard.getStringAsync();
|
||||
const data = await getStringAsync();
|
||||
if (!data || !data.trim().length)
|
||||
throw new Error('errormessages.clipboardContentError');
|
||||
|
||||
|
||||
@@ -61,3 +61,23 @@ export async function removeAllLocalData() {
|
||||
// read key error
|
||||
}
|
||||
}
|
||||
export async function getAllLocalKeys() {
|
||||
try {
|
||||
crashlyticsLogReport('Starting get all local storage keys');
|
||||
const keys = await AsyncStorage.getAllKeys();
|
||||
return keys;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
export async function getMultipleItems(itemsList) {
|
||||
try {
|
||||
crashlyticsLogReport('Starting get all local storage item function');
|
||||
const items = await AsyncStorage.multiGet(itemsList);
|
||||
return items;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {getLocalStorageItem, setLocalStorageItem} from '../localStorage';
|
||||
import * as SQLite from 'expo-sqlite';
|
||||
import {getTwoWeeksAgoDate} from '../rotateAddressDateChecker';
|
||||
import EventEmitter from 'events';
|
||||
import {handleEventEmitterPost} from '../handleEventEmitters';
|
||||
import {openDatabaseAsync} from 'expo-sqlite';
|
||||
export const CACHED_MESSAGES_KEY = 'CASHED_CONTACTS_MESSAGES';
|
||||
export const SQL_TABLE_NAME = 'messagesTable';
|
||||
export const LOCALSTORAGE_LAST_RECEIVED_TIME_KEY =
|
||||
@@ -16,7 +16,7 @@ let isProcessing = false;
|
||||
|
||||
if (!sqlLiteDB) {
|
||||
async function openDBConnection() {
|
||||
sqlLiteDB = await SQLite.openDatabaseAsync(`${CACHED_MESSAGES_KEY}.db`);
|
||||
sqlLiteDB = await openDatabaseAsync(`${CACHED_MESSAGES_KEY}.db`);
|
||||
}
|
||||
openDBConnection();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import * as Notifications from 'expo-notifications';
|
||||
import {
|
||||
scheduleNotificationAsync,
|
||||
setNotificationHandler,
|
||||
} from 'expo-notifications';
|
||||
|
||||
// Configure notification behavior
|
||||
Notifications.setNotificationHandler({
|
||||
setNotificationHandler({
|
||||
handleNotification: async () => ({
|
||||
shouldShowBanner: true,
|
||||
shouldPlaySound: true,
|
||||
@@ -17,7 +20,7 @@ Notifications.setNotificationHandler({
|
||||
*/
|
||||
export const pushInstantNotification = async (message, title = '') => {
|
||||
try {
|
||||
const notificationId = await Notifications.scheduleNotificationAsync({
|
||||
const notificationId = await scheduleNotificationAsync({
|
||||
content: {
|
||||
title: title,
|
||||
body: message,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as SQLite from 'expo-sqlite';
|
||||
import {openDatabaseAsync} from 'expo-sqlite';
|
||||
|
||||
// Database configuration
|
||||
const DB_NAME = 'nwc_invoices.db';
|
||||
@@ -13,7 +13,7 @@ class InvoiceDatabase {
|
||||
// Initialize database connection
|
||||
async initialize() {
|
||||
try {
|
||||
this.db = await SQLite.openDatabaseAsync(DB_NAME);
|
||||
this.db = await openDatabaseAsync(DB_NAME);
|
||||
await this.createTables();
|
||||
this.isInitialized = true;
|
||||
console.log('Invoice database initialized successfully');
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import * as SQLite from 'expo-sqlite';
|
||||
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';
|
||||
export const POS_TRANSACTION_TABLE_NAME = 'POS_TRANSACTIONS';
|
||||
|
||||
export const POS_LAST_RECEIVED_TIME = 'LAST_RECEIVED_POS_EVENT';
|
||||
@@ -17,9 +17,7 @@ let isProcessing = false;
|
||||
|
||||
if (!sqlLiteDB) {
|
||||
async function openDBConnection() {
|
||||
sqlLiteDB = await SQLite.openDatabaseAsync(
|
||||
`${POS_TRANSACTION_TABLE_NAME}.db`,
|
||||
);
|
||||
sqlLiteDB = await openDatabaseAsync(`${POS_TRANSACTION_TABLE_NAME}.db`);
|
||||
}
|
||||
openDBConnection();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import * as SecureStore from 'expo-secure-store';
|
||||
import {
|
||||
getLocalStorageItem,
|
||||
removeAllLocalData,
|
||||
@@ -7,19 +6,25 @@ import {
|
||||
import {crashlyticsLogReport} from './crashlyticsLogs';
|
||||
import {CUSTODY_ACCOUNTS_STORAGE_KEY} from '../constants';
|
||||
import {BIOMETRIC_KEY} from '../constants';
|
||||
import {
|
||||
AFTER_FIRST_UNLOCK,
|
||||
deleteItemAsync,
|
||||
getItemAsync,
|
||||
setItemAsync,
|
||||
} from 'expo-secure-store';
|
||||
const keychainService = '38WX44YTA6.com.blitzwallet.SharedKeychain';
|
||||
export const MIGRATION_FLAG = 'secureStoreMigrationComplete';
|
||||
export const SECURE_MIGRATION_V2_FLAG = 'secureStoreMigrationV2Complete';
|
||||
|
||||
const KEYCHAIN_OPTION = {
|
||||
keychainService: keychainService,
|
||||
keychainAccessible: SecureStore.AFTER_FIRST_UNLOCK,
|
||||
keychainAccessible: AFTER_FIRST_UNLOCK,
|
||||
};
|
||||
|
||||
async function storeData(key, value, options = {}) {
|
||||
try {
|
||||
crashlyticsLogReport('Starting store data to secure store function');
|
||||
await SecureStore.setItemAsync(key, value, {
|
||||
await setItemAsync(key, value, {
|
||||
...KEYCHAIN_OPTION,
|
||||
...options,
|
||||
});
|
||||
@@ -34,7 +39,7 @@ async function retrieveData(key, options = {}) {
|
||||
try {
|
||||
crashlyticsLogReport('Starting retrive data from secure store function');
|
||||
|
||||
const value = await SecureStore.getItemAsync(key, {
|
||||
const value = await getItemAsync(key, {
|
||||
...KEYCHAIN_OPTION,
|
||||
...options,
|
||||
});
|
||||
@@ -49,13 +54,11 @@ async function retrieveData(key, options = {}) {
|
||||
async function terminateAccount() {
|
||||
try {
|
||||
crashlyticsLogReport('Starting termiate data from secure store function');
|
||||
await SecureStore.deleteItemAsync('pinHash', KEYCHAIN_OPTION);
|
||||
await SecureStore.deleteItemAsync('encryptedMnemonic', KEYCHAIN_OPTION);
|
||||
await SecureStore.deleteItemAsync(BIOMETRIC_KEY, KEYCHAIN_OPTION);
|
||||
await SecureStore.deleteItemAsync(
|
||||
CUSTODY_ACCOUNTS_STORAGE_KEY,
|
||||
KEYCHAIN_OPTION,
|
||||
);
|
||||
|
||||
await deleteItemAsync('pinHash', KEYCHAIN_OPTION);
|
||||
await deleteItemAsync('encryptedMnemonic', KEYCHAIN_OPTION);
|
||||
await deleteItemAsync(BIOMETRIC_KEY, KEYCHAIN_OPTION);
|
||||
await deleteItemAsync(CUSTODY_ACCOUNTS_STORAGE_KEY, KEYCHAIN_OPTION);
|
||||
|
||||
const didRemove = await removeAllLocalData();
|
||||
if (!didRemove) throw Error('not able to remove local storage data');
|
||||
@@ -69,7 +72,7 @@ async function terminateAccount() {
|
||||
async function deleteItem(key) {
|
||||
try {
|
||||
crashlyticsLogReport('Starting delte item from secure store function');
|
||||
await SecureStore.deleteItemAsync(key, KEYCHAIN_OPTION);
|
||||
await deleteItemAsync(key, KEYCHAIN_OPTION);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
@@ -89,15 +92,15 @@ async function runPinAndMnemoicMigration() {
|
||||
crashlyticsLogReport('Running SecureStore migration');
|
||||
|
||||
const [oldPin, oldMnemonic] = await Promise.all([
|
||||
SecureStore.getItemAsync('pin'),
|
||||
SecureStore.getItemAsync('mnemonic'),
|
||||
getItemAsync('pin'),
|
||||
getItemAsync('mnemonic'),
|
||||
]);
|
||||
|
||||
if (oldPin || oldMnemonic) {
|
||||
if (oldPin) await storeData('pinHash', oldPin);
|
||||
if (oldMnemonic) await storeData('encryptedMnemonic', oldMnemonic);
|
||||
await SecureStore.deleteItemAsync('pin');
|
||||
await SecureStore.deleteItemAsync('mnemonic');
|
||||
await deleteItemAsync('pin');
|
||||
await deleteItemAsync('mnemonic');
|
||||
}
|
||||
|
||||
await setLocalStorageItem(MIGRATION_FLAG, 'true');
|
||||
@@ -119,8 +122,8 @@ async function runSecureStoreMigrationV2() {
|
||||
|
||||
// Get unencrypted PIN and mnemonic (possibly migrated from old V1 already)
|
||||
const [plainPin, plainMnemonic] = await Promise.all([
|
||||
SecureStore.getItemAsync('pin', KEYCHAIN_OPTION),
|
||||
SecureStore.getItemAsync('mnemonic', KEYCHAIN_OPTION),
|
||||
getItemAsync('pin', KEYCHAIN_OPTION),
|
||||
getItemAsync('mnemonic', KEYCHAIN_OPTION),
|
||||
]);
|
||||
|
||||
if (plainPin && plainMnemonic) {
|
||||
|
||||
@@ -46,7 +46,7 @@ export function handleRestoreFromText(seedString) {
|
||||
let maxIndex = seedString.length;
|
||||
let currentWord = '';
|
||||
|
||||
while (currentIndex <= maxIndex) {
|
||||
while (currentIndex <= maxIndex && wordArray.length < 13) {
|
||||
const letter = seedString[currentIndex];
|
||||
const isLetter = IS_LETTER_REGEX.test(letter);
|
||||
if (!isLetter) {
|
||||
@@ -62,6 +62,11 @@ export function handleRestoreFromText(seedString) {
|
||||
|
||||
if (!posibleOptins.length) {
|
||||
const lastPosibleOption = currentWord.slice(0, currentWord.length - 1);
|
||||
|
||||
if (!lastPosibleOption) {
|
||||
currentIndex += 1;
|
||||
continue;
|
||||
}
|
||||
wordArray.push(lastPosibleOption);
|
||||
currentWord = '';
|
||||
continue;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {decode, encode} from 'bip21';
|
||||
import {crashlyticsLogReport} from '../crashlyticsLogs';
|
||||
import * as bip21 from 'bip21';
|
||||
/**
|
||||
* Formats a Liquid 'spark' BIP21 payment URI from an address, amount, and optional message.
|
||||
*
|
||||
@@ -22,7 +22,7 @@ export function formatBip21SparkAddress({
|
||||
try {
|
||||
const formattedAmount = amountSat;
|
||||
crashlyticsLogReport('Formatting bip21 liquid address');
|
||||
const liquidBip21 = bip21.encode(
|
||||
const liquidBip21 = encode(
|
||||
address,
|
||||
{
|
||||
amount: formattedAmount,
|
||||
@@ -50,7 +50,8 @@ export function formatBip21SparkAddress({
|
||||
export function decodeBip21SparkAddress(address) {
|
||||
try {
|
||||
crashlyticsLogReport('decoding bip21 spark');
|
||||
return bip21.decode(address, 'spark');
|
||||
|
||||
return decode(address, 'spark');
|
||||
} catch (err) {
|
||||
console.log('format bip21 spark address error', err);
|
||||
return '';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as SQLite from 'expo-sqlite';
|
||||
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';
|
||||
@@ -13,7 +13,7 @@ let sqlLiteDB;
|
||||
|
||||
if (!sqlLiteDB) {
|
||||
async function openDBConnection() {
|
||||
sqlLiteDB = await SQLite.openDatabaseAsync(
|
||||
sqlLiteDB = await openDatabaseAsync(
|
||||
`${SPARK_TRANSACTIONS_DATABASE_NAME}.db`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import * as FileSystem from 'expo-file-system';
|
||||
import {Platform, Share} from 'react-native';
|
||||
import {crashlyticsLogReport} from './crashlyticsLogs';
|
||||
import {
|
||||
documentDirectory,
|
||||
EncodingType,
|
||||
StorageAccessFramework,
|
||||
writeAsStringAsync,
|
||||
} from 'expo-file-system';
|
||||
|
||||
export default async function writeAndShareFileToFilesystem(
|
||||
fileData,
|
||||
@@ -11,9 +16,10 @@ export default async function writeAndShareFileToFilesystem(
|
||||
|
||||
try {
|
||||
crashlyticsLogReport('Starting write to filesystem process');
|
||||
const fileUri = `${FileSystem.documentDirectory}${fileName}`;
|
||||
await FileSystem.writeAsStringAsync(fileUri, fileData, {
|
||||
encoding: FileSystem.EncodingType.UTF8,
|
||||
|
||||
const fileUri = `${documentDirectory}${fileName}`;
|
||||
await writeAsStringAsync(fileUri, fileData, {
|
||||
encoding: EncodingType.UTF8,
|
||||
});
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
@@ -26,19 +32,18 @@ export default async function writeAndShareFileToFilesystem(
|
||||
} else {
|
||||
try {
|
||||
const permissions =
|
||||
await FileSystem.StorageAccessFramework.requestDirectoryPermissionsAsync();
|
||||
await StorageAccessFramework.requestDirectoryPermissionsAsync();
|
||||
|
||||
if (permissions.granted) {
|
||||
const data =
|
||||
await FileSystem.StorageAccessFramework.readAsStringAsync(fileUri);
|
||||
const data = await StorageAccessFramework.readAsStringAsync(fileUri);
|
||||
|
||||
try {
|
||||
const uri = await FileSystem.StorageAccessFramework.createFileAsync(
|
||||
const uri = await StorageAccessFramework.createFileAsync(
|
||||
permissions.directoryUri,
|
||||
fileName,
|
||||
fileType,
|
||||
);
|
||||
await FileSystem.writeAsStringAsync(uri, data);
|
||||
await writeAsStringAsync(uri, data);
|
||||
return {success: true, error: null};
|
||||
} catch (err) {
|
||||
console.log('writting file to filesystem for android err', err);
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
import {useEffect, useState} from 'react';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import {BLITZ_PROFILE_IMG_STORAGE_REF} from '../constants';
|
||||
// import {useEffect, useState} from 'react';
|
||||
// import {BLITZ_PROFILE_IMG_STORAGE_REF} from '../constants';
|
||||
// import {getAllLocalKeys, getMultipleItems} from '../functions/localStorage';
|
||||
|
||||
export function useContactImage(uuid) {
|
||||
const [uri, setUri] = useState({});
|
||||
// export function useContactImage(uuid) {
|
||||
// const [uri, setUri] = useState({});
|
||||
|
||||
useEffect(() => {
|
||||
async function load() {
|
||||
const keys = await AsyncStorage.getAllKeys();
|
||||
const imgKeys = keys.filter(k =>
|
||||
k.startsWith(BLITZ_PROFILE_IMG_STORAGE_REF),
|
||||
);
|
||||
const stores = await AsyncStorage.multiGet(imgKeys);
|
||||
const initialCache = {};
|
||||
stores.forEach(([key, value]) => {
|
||||
if (value) {
|
||||
const uuid = key.replace(BLITZ_PROFILE_IMG_STORAGE_REF + '/', '');
|
||||
const parsed = JSON.parse(value);
|
||||
initialCache[uuid] = parsed;
|
||||
}
|
||||
});
|
||||
if (initialCache[uuid]?.localUri) {
|
||||
setUri(initialCache[uuid]);
|
||||
}
|
||||
}
|
||||
load();
|
||||
}, [uuid]);
|
||||
// useEffect(() => {
|
||||
// async function load() {
|
||||
// const keys = await getAllLocalKeys();
|
||||
// const imgKeys = keys.filter(k =>
|
||||
// k.startsWith(BLITZ_PROFILE_IMG_STORAGE_REF),
|
||||
// );
|
||||
// const stores = await getMultipleItems(imgKeys);
|
||||
// const initialCache = {};
|
||||
// stores.forEach(([key, value]) => {
|
||||
// if (value) {
|
||||
// const uuid = key.replace(BLITZ_PROFILE_IMG_STORAGE_REF + '/', '');
|
||||
// const parsed = JSON.parse(value);
|
||||
// initialCache[uuid] = parsed;
|
||||
// }
|
||||
// });
|
||||
// if (initialCache[uuid]?.localUri) {
|
||||
// setUri(initialCache[uuid]);
|
||||
// }
|
||||
// }
|
||||
// load();
|
||||
// }, [uuid]);
|
||||
|
||||
return uri;
|
||||
}
|
||||
// return uri;
|
||||
// }
|
||||
|
||||
@@ -56,7 +56,7 @@ export default function CreateAccountHome({navigation: {navigate}}) {
|
||||
/>
|
||||
|
||||
<ThemeText
|
||||
styles={{...styles.disclamer_text}}
|
||||
styles={styles.disclamer_text}
|
||||
content={t('createAccount.homePage.subtitle')}
|
||||
/>
|
||||
</GlobalThemeView>
|
||||
@@ -87,6 +87,6 @@ const styles = StyleSheet.create({
|
||||
disclamer_text: {
|
||||
marginTop: 'auto',
|
||||
fontSize: SIZES.small,
|
||||
marginBottom: 5,
|
||||
includeFontPadding: false,
|
||||
},
|
||||
});
|
||||
|
||||
+16
-10
@@ -7,15 +7,20 @@ import React, {
|
||||
useMemo,
|
||||
} from 'react';
|
||||
import {getDownloadURL, getMetadata, ref} from '@react-native-firebase/storage';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import * as FileSystem from 'expo-file-system';
|
||||
import {useGlobalContacts} from './globalContacts';
|
||||
import {useAppStatus} from './appStatus';
|
||||
import {BLITZ_PROFILE_IMG_STORAGE_REF} from '../app/constants';
|
||||
import {useGlobalContextProvider} from './context';
|
||||
import {getLocalStorageItem, setLocalStorageItem} from '../app/functions';
|
||||
import {storage} from '../db/initializeFirebase';
|
||||
const FILE_DIR = FileSystem.cacheDirectory + 'profile_images/';
|
||||
import {
|
||||
cacheDirectory,
|
||||
downloadAsync,
|
||||
getInfoAsync,
|
||||
makeDirectoryAsync,
|
||||
} from 'expo-file-system';
|
||||
import {getAllLocalKeys, getMultipleItems} from '../app/functions/localStorage';
|
||||
const FILE_DIR = cacheDirectory + 'profile_images/';
|
||||
const ImageCacheContext = createContext();
|
||||
|
||||
export function ImageCacheProvider({children}) {
|
||||
@@ -27,11 +32,11 @@ export function ImageCacheProvider({children}) {
|
||||
|
||||
const refreshCacheObject = async () => {
|
||||
try {
|
||||
const keys = await AsyncStorage.getAllKeys();
|
||||
const keys = await getAllLocalKeys();
|
||||
const imgKeys = keys.filter(k =>
|
||||
k.startsWith(BLITZ_PROFILE_IMG_STORAGE_REF),
|
||||
);
|
||||
const stores = await AsyncStorage.multiGet(imgKeys);
|
||||
const stores = await getMultipleItems(imgKeys);
|
||||
const initialCache = {};
|
||||
stores.forEach(([key, value]) => {
|
||||
if (value) {
|
||||
@@ -94,7 +99,7 @@ export function ImageCacheProvider({children}) {
|
||||
|
||||
const cached = cache[uuid];
|
||||
if (cached && cached.updated === updated) {
|
||||
const fileInfo = await FileSystem.getInfoAsync(cached.localUri);
|
||||
const fileInfo = await getInfoAsync(cached.localUri);
|
||||
if (fileInfo.exists) return;
|
||||
}
|
||||
|
||||
@@ -106,8 +111,8 @@ export function ImageCacheProvider({children}) {
|
||||
|
||||
const localUri = `${FILE_DIR}${uuid}.jpg`;
|
||||
|
||||
await FileSystem.makeDirectoryAsync(FILE_DIR, {intermediates: true});
|
||||
await FileSystem.downloadAsync(url, localUri);
|
||||
await makeDirectoryAsync(FILE_DIR, {intermediates: true});
|
||||
await downloadAsync(url, localUri);
|
||||
|
||||
const newCacheEntry = {
|
||||
uri: localUri,
|
||||
@@ -115,7 +120,7 @@ export function ImageCacheProvider({children}) {
|
||||
updated,
|
||||
};
|
||||
|
||||
await AsyncStorage.setItem(key, JSON.stringify(newCacheEntry));
|
||||
await setLocalStorageItem(key, JSON.stringify(newCacheEntry));
|
||||
setCache(prev => ({...prev, [uuid]: newCacheEntry}));
|
||||
|
||||
return newCacheEntry;
|
||||
@@ -134,7 +139,8 @@ export function ImageCacheProvider({children}) {
|
||||
localUri: null,
|
||||
updated: new Date().getTime(),
|
||||
};
|
||||
await AsyncStorage.setItem(key, JSON.stringify(newCacheEntry));
|
||||
|
||||
await setLocalStorageItem(key, JSON.stringify(newCacheEntry));
|
||||
setCache(prev => ({...prev, [uuid]: newCacheEntry}));
|
||||
return newCacheEntry;
|
||||
} catch (err) {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
import './pollyfills.js';
|
||||
import './disableFontScalling.js';
|
||||
import './i18n'; // for translation option
|
||||
import {AppRegistry} from 'react-native';
|
||||
import App from './App';
|
||||
import {name as appName} from './app.json';
|
||||
|
||||
@@ -841,7 +841,8 @@
|
||||
"noTokensFoundText": "No tokens found"
|
||||
},
|
||||
"sendMaxComponent": {
|
||||
"sendMax": "Send Max"
|
||||
"sendMax": "Send Max",
|
||||
"tokensMax": "Send %"
|
||||
},
|
||||
"acceptButton": {
|
||||
"noSendAmountError": "Please enter a send amount",
|
||||
|
||||
@@ -687,7 +687,8 @@
|
||||
"noTokensFoundText": "No se encontraron tokens"
|
||||
},
|
||||
"sendMaxComponent": {
|
||||
"sendMax": "Enviar máximo"
|
||||
"sendMax": "Enviar máximo",
|
||||
"tokensMax": "Enviar %"
|
||||
},
|
||||
"acceptButton": {
|
||||
"noSendAmountError": "Por favor ingresa una cantidad a enviar",
|
||||
|
||||
Reference in New Issue
Block a user