mirror of
https://github.com/minibits-cash/minibits_wallet.git
synced 2026-10-05 19:28:26 +00:00
Support mints behind Tor with Orbot VPN, Add memo to payments to lightning address, Many fixes and UI improvements
This commit is contained in:
@@ -110,7 +110,7 @@ export function BottomModal(props: ModalProps) {
|
||||
isVisible = true,
|
||||
onBackdropPress,
|
||||
onBackButtonPress,
|
||||
backdropOpacity = 0.25,
|
||||
backdropOpacity = 0.4,
|
||||
content,
|
||||
contentTx,
|
||||
contentTxOptions,
|
||||
|
||||
@@ -92,8 +92,17 @@ export function Button(props: ButtonProps) {
|
||||
|
||||
const textColor = useThemeColor("text")
|
||||
const defaultBg = useThemeColor("button")
|
||||
const secondaryBg = useThemeColor("background")
|
||||
const tertiaryBg = "transparent"
|
||||
const defaultBgPressed = useThemeColor("buttonPressed")
|
||||
const secondaryBg = useThemeColor("buttonSecondary")
|
||||
const secondaryBgPressed = useThemeColor("buttonSecondaryPressed")
|
||||
const tertiaryBg = useThemeColor("buttonTertiary")
|
||||
const tertiaryBgPressed = useThemeColor("buttonTertiaryPressed")
|
||||
|
||||
const $pressedViewPresets: Record<Presets, StyleProp<ViewStyle>> = {
|
||||
default: { backgroundColor: defaultBgPressed },
|
||||
secondary: { backgroundColor: secondaryBgPressed },
|
||||
tertiary: { backgroundColor: tertiaryBgPressed },
|
||||
}
|
||||
|
||||
function $viewStyle({ pressed }) {
|
||||
return [
|
||||
@@ -181,12 +190,6 @@ const $textPresets: Record<Presets, StyleProp<TextStyle>> = {
|
||||
tertiary: $baseTextStyle,
|
||||
}
|
||||
|
||||
const $pressedViewPresets: Record<Presets, StyleProp<ViewStyle>> = {
|
||||
default: { backgroundColor: colors.palette.success100 },
|
||||
secondary: { backgroundColor: colors.palette.neutral300 },
|
||||
tertiary: { backgroundColor: colors.palette.neutral200, opacity: 0.4 },
|
||||
}
|
||||
|
||||
const $pressedTextPresets: Record<Presets, StyleProp<TextStyle>> = {
|
||||
default: { opacity: 0.9 },
|
||||
secondary: { opacity: 0.9 },
|
||||
|
||||
@@ -24,6 +24,7 @@ export const InfoModal: FC<InfoModalProps> = function ({ message }) {
|
||||
}
|
||||
|
||||
const backgroundColor = useThemeColor('info')
|
||||
const iconColor = useThemeColor('textDim')
|
||||
|
||||
return (
|
||||
<BottomModal
|
||||
@@ -32,7 +33,7 @@ export const InfoModal: FC<InfoModalProps> = function ({ message }) {
|
||||
onBackButtonPress={() => onClose()}
|
||||
ContentComponent={
|
||||
<View style={{ padding: spacing.small, flexDirection: 'row', alignItems: 'center', marginRight: spacing.medium}}>
|
||||
<Icon icon="faInfoCircle" size={spacing.large} color="white" />
|
||||
<Icon icon="faInfoCircle" size={spacing.large} color={iconColor} />
|
||||
<Text style={{ marginHorizontal: spacing.extraSmall}}>{message}</Text>
|
||||
</View>
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ const $loading = (bg: ColorValue, shiftedUp = false) => ({
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: bg,
|
||||
opacity: 0.5,
|
||||
opacity: 0.25,
|
||||
zIndex: 9999,
|
||||
// for cards that go up in the header, the loading should cover them
|
||||
marginTop: shiftedUp ? -(spacing.extraLarge * 2 + spacing.small) : 0,
|
||||
|
||||
@@ -463,6 +463,7 @@
|
||||
"balanceAfter": "Balance after this transaction",
|
||||
"claim": "Claim",
|
||||
"createdAt": "Created at",
|
||||
"expiresAt": "Expires at",
|
||||
"id": "ID",
|
||||
"invoice": "Lightning invoice to pay",
|
||||
"isOffline": "Redeem online",
|
||||
|
||||
@@ -30,6 +30,7 @@ import QRCode from 'react-native-qrcode-svg'
|
||||
import { infoMessage } from '../../utils/utils'
|
||||
import Clipboard from '@react-native-clipboard/clipboard'
|
||||
import { translate } from '../../i18n'
|
||||
import { QRCodeBlock } from '../Wallet/QRCode'
|
||||
|
||||
|
||||
export const OutgoingRequests = observer(function (props: {
|
||||
@@ -66,35 +67,6 @@ export const OutgoingRequests = observer(function (props: {
|
||||
}
|
||||
|
||||
|
||||
const onShareToApp = async () => {
|
||||
try {
|
||||
const result = await Share.share({
|
||||
message: selectedRequest?.encodedInvoice as string,
|
||||
})
|
||||
|
||||
if (result.action === Share.sharedAction) {
|
||||
setTimeout(
|
||||
() => infoMessage(translate("lightningInvoiceSharedWaiting")),
|
||||
500,
|
||||
)
|
||||
} else if (result.action === Share.dismissedAction) {
|
||||
infoMessage(translate("share.cancelled"))
|
||||
}
|
||||
} catch (e: any) {
|
||||
handleError(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const onCopy = function () {
|
||||
try {
|
||||
Clipboard.setString(selectedRequest?.encodedInvoice as string)
|
||||
} catch (e: any) {
|
||||
setInfo(translate('common.copyFailParam', { param: e.message }))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const handleError = function (e: AppError): void {
|
||||
setIsLoading(false)
|
||||
setError(e)
|
||||
@@ -132,78 +104,33 @@ export const OutgoingRequests = observer(function (props: {
|
||||
contentStyle={{color: hintColor, padding: spacing.small}}
|
||||
style={$card}
|
||||
/>
|
||||
)}
|
||||
{isLoading && <Loading />}
|
||||
)}
|
||||
</View>
|
||||
<BottomModal
|
||||
isVisible={isQRModalVisible}
|
||||
ContentComponent={
|
||||
<ShareAsQRCodeBlock
|
||||
toggleQRModal={toggleQRModal}
|
||||
invoiceToPay={selectedRequest?.encodedInvoice as string}
|
||||
onShareToApp={onShareToApp}
|
||||
onCopy={onCopy}
|
||||
onError={handleError}
|
||||
/>
|
||||
<QRCodeBlock
|
||||
qrCodeData={selectedRequest?.encodedInvoice as string}
|
||||
title="Scan and pay to topup"
|
||||
size={270}
|
||||
/>
|
||||
}
|
||||
onBackButtonPress={toggleQRModal}
|
||||
onBackdropPress={toggleQRModal}
|
||||
/>
|
||||
{error && <ErrorModal error={error} />}
|
||||
{info && <InfoModal message={info} />}
|
||||
{isLoading && <Loading />}
|
||||
</Screen>
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
const ShareAsQRCodeBlock = observer(function (props: {
|
||||
toggleQRModal: any
|
||||
invoiceToPay: string
|
||||
onShareToApp: any
|
||||
onCopy: any
|
||||
onError: any
|
||||
}) {
|
||||
return (
|
||||
<View style={[$bottomModal, {marginHorizontal: spacing.small}]}>
|
||||
<Text tx="paymentRequestScreen.outgoing.scanAndPay" />
|
||||
<View style={$qrCodeContainer}>
|
||||
<QRCode
|
||||
size={270}
|
||||
value={props.invoiceToPay}
|
||||
onError={props.onError}
|
||||
/>
|
||||
</View>
|
||||
<View style={$buttonContainer}>
|
||||
<Button
|
||||
tx="common.share"
|
||||
onPress={props.onShareToApp}
|
||||
style={{marginRight: spacing.medium}}
|
||||
LeftAccessory={() => (
|
||||
<Icon
|
||||
icon="faShareFromSquare"
|
||||
color="white"
|
||||
size={spacing.medium}
|
||||
// containerStyle={{marginRight: spacing.small}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Button preset="secondary" tx="common.copy" onPress={props.onCopy} />
|
||||
<Button
|
||||
preset="tertiary"
|
||||
tx="common.close"
|
||||
onPress={props.toggleQRModal}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
})
|
||||
|
||||
const $screen: ViewStyle = {
|
||||
flex: 1
|
||||
}
|
||||
|
||||
|
||||
const $contentContainer: TextStyle = {
|
||||
padding: spacing.extraSmall,
|
||||
}
|
||||
|
||||
@@ -805,8 +805,6 @@ export const TopupScreen: FC<WalletStackScreenProps<'Topup'>> = observer(
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
{isLoading && <Loading />}
|
||||
{info && <InfoModal message={info} />}
|
||||
</View>
|
||||
<BottomModal
|
||||
isVisible={isNostrDMModalVisible ? true : false}
|
||||
@@ -910,6 +908,7 @@ export const TopupScreen: FC<WalletStackScreenProps<'Topup'>> = observer(
|
||||
/>
|
||||
{error && <ErrorModal error={error} />}
|
||||
{info && <InfoModal message={info} />}
|
||||
{isLoading && <Loading />}
|
||||
</Screen>
|
||||
)
|
||||
},
|
||||
|
||||
@@ -1115,7 +1115,7 @@ const TopupInfoBlock = function (props: {
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
useFocusEffect(useCallback(() => {
|
||||
const handlePendingTopupTaskResult = async (result: TransactionTaskResult) => {
|
||||
log.trace('[handlePendingTopupTaskResult] event handler triggered')
|
||||
setIsLoading(false)
|
||||
@@ -1147,19 +1147,12 @@ const TopupInfoBlock = function (props: {
|
||||
return () => {
|
||||
EventEmitter.off('ev__handlePendingTopupTask_result', handlePendingTopupTaskResult)
|
||||
}
|
||||
}, [isPendingTopupTaskSentToQueue])
|
||||
}, [isPendingTopupTaskSentToQueue]))
|
||||
|
||||
|
||||
const toggleResultModal = () =>
|
||||
setIsResultModalVisible(previousState => !previousState)
|
||||
|
||||
const copyInvoice = function () {
|
||||
try {
|
||||
Clipboard.setString(paymentRequest?.encodedInvoice as string)
|
||||
} catch (e: any) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const onRetryToHandlePendingTopup = async function () {
|
||||
if(!isInternetReachable || !paymentRequest) {
|
||||
@@ -1193,10 +1186,12 @@ const TopupInfoBlock = function (props: {
|
||||
isCurrency={true}
|
||||
isFirst={true}
|
||||
/>
|
||||
<TranItem
|
||||
label="receiverMemo"
|
||||
value={transaction.memo as string}
|
||||
/>
|
||||
{transaction.memo && transaction.memo.length > 0 && (
|
||||
<TranItem
|
||||
label="receiverMemo"
|
||||
value={transaction.memo as string}
|
||||
/>
|
||||
)}
|
||||
{transaction.sentFrom && (
|
||||
<TranItem
|
||||
label="tranDetailScreen.sentFrom"
|
||||
@@ -1234,11 +1229,17 @@ const TopupInfoBlock = function (props: {
|
||||
unit={transaction.unit}
|
||||
isCurrency={true}
|
||||
/>
|
||||
)}
|
||||
)}
|
||||
<TranItem
|
||||
label="tranDetailScreen.createdAt"
|
||||
value={(transaction.createdAt as Date).toLocaleString()}
|
||||
/>
|
||||
{paymentRequest && (
|
||||
<TranItem
|
||||
label="tranDetailScreen.expiresAt"
|
||||
value={(new Date(paymentRequest.expiresAt!)).toLocaleString()}
|
||||
/>
|
||||
)}
|
||||
<TranItem label="tranDetailScreen.id" value={`${transaction.id}`} />
|
||||
</>
|
||||
}
|
||||
|
||||
@@ -93,6 +93,7 @@ export const TransferScreen: FC<WalletStackScreenProps<'Transfer'>> = observer(
|
||||
const [info, setInfo] = useState('')
|
||||
const [error, setError] = useState<AppError | undefined>()
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [isNotEnoughFunds, setIsNotEnoughFunds] = useState(false)
|
||||
const [isPasteInvoiceModalVisible, setIsPasteInvoiceModalVisible] = useState(false)
|
||||
const [isInvoiceDonation, setIsInvoiceDonation] = useState(false)
|
||||
const [isTransferTaskSentToQueue, setIsTransferTaskSentToQueue] = useState(false)
|
||||
@@ -223,9 +224,7 @@ useFocusEffect(
|
||||
}
|
||||
}
|
||||
|
||||
const amountSats = roundUp(lnurlParams.minSendable / 1000, 0)
|
||||
|
||||
log.trace('[handleLnurlPay]', {lnurlParams})
|
||||
const amountSats = roundUp(lnurlParams.minSendable / 1000, 0)
|
||||
|
||||
setAmountToTransfer(`${numbro(amountSats).format({thousandSeparated: true, mantissa: 0})}`)
|
||||
setLnurlPayParams(lnurlParams)
|
||||
@@ -279,38 +278,38 @@ useFocusEffect(
|
||||
useEffect(() => {
|
||||
const getMeltQuote = async function () {
|
||||
try {
|
||||
log.trace('[getEstimatedFee]', {mintBalanceToTransferFrom})
|
||||
if (!mintBalanceToTransferFrom || !mintBalanceToTransferFrom.balances[unit] || !encodedInvoice) {
|
||||
log.trace('[getMeltQuote]', {mintBalanceToTransferFrom})
|
||||
if (!mintBalanceToTransferFrom || !encodedInvoice) {
|
||||
log.trace(
|
||||
'[getEstimatedFee]',
|
||||
'Not ready... exiting',
|
||||
mintBalanceToTransferFrom,
|
||||
mintBalanceToTransferFrom?.balances[unit],
|
||||
encodedInvoice,
|
||||
'[getMeltQuote]',
|
||||
'Not yet ready to request melt quote or melt quote already exists... exiting',
|
||||
{mintBalanceToTransferFrom,
|
||||
encodedInvoice}
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
setIsLoading(true)
|
||||
const meltQuote = await MintClient.getLightningMeltQuote(
|
||||
const quote = await MintClient.getLightningMeltQuote(
|
||||
mintBalanceToTransferFrom.mintUrl,
|
||||
unit,
|
||||
encodedInvoice,
|
||||
)
|
||||
|
||||
setIsLoading(false)
|
||||
setMeltQuote(meltQuote)
|
||||
setAmountToTransfer(`${numbro(meltQuote.amount / getCurrency(unit).precision).format({thousandSeparated: true, mantissa: getCurrency(unit).mantissa})}`)
|
||||
setMeltQuote(quote)
|
||||
setAmountToTransfer(`${numbro(quote.amount / getCurrency(unit).precision).format({thousandSeparated: true, mantissa: getCurrency(unit).mantissa})}`)
|
||||
|
||||
const totalAmount = meltQuote.amount + meltQuote.fee_reserve
|
||||
const totalAmount = quote.amount + quote.fee_reserve
|
||||
|
||||
let availableBalances = proofsStore.getMintBalancesWithEnoughBalance(totalAmount, unit)
|
||||
|
||||
if (availableBalances.length === 0) {
|
||||
infoMessage(translate("transferScreen.insufficientFunds", {
|
||||
setInfo(translate("transferScreen.insufficientFunds", {
|
||||
currency: getCurrency(unit).code,
|
||||
amount: amountToTransfer
|
||||
}))
|
||||
setIsNotEnoughFunds(true)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -434,7 +433,7 @@ const resetState = function () {
|
||||
const toggleResultModal = () => setIsResultModalVisible(previousState => !previousState)
|
||||
|
||||
const onMintBalanceSelect = function (balance: MintBalance) {
|
||||
setMintBalanceToTransferFrom(balance) // this triggers effect to get estimated fees
|
||||
setMintBalanceToTransferFrom(balance) // this triggers effect to get melt quote
|
||||
}
|
||||
|
||||
// Amount is editable only in case of LNURL Pay, while invoice is not yet retrieved
|
||||
@@ -444,7 +443,7 @@ const onRequestLnurlInvoice = async function () { // onAmountEndEditing
|
||||
const amount = round(toNumber(amountToTransfer) * precision, 0)
|
||||
|
||||
if (!amount || amount === 0) {
|
||||
infoMessage(translate('payCommon.amountZeroOrNegative'))
|
||||
setInfo(translate('payCommon.amountZeroOrNegative'))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -453,7 +452,7 @@ const onRequestLnurlInvoice = async function () { // onAmountEndEditing
|
||||
}
|
||||
|
||||
if (lnurlPayParams.minSendable && amount < lnurlPayParams.minSendable / 1000) {
|
||||
infoMessage(translate('payCommon.minimumWithdraw', {
|
||||
setInfo(translate('payCommon.minimumWithdraw', {
|
||||
amount: roundUp(lnurlPayParams.minSendable / 1000, 0),
|
||||
currency: CurrencyCode.SATS
|
||||
}))
|
||||
@@ -461,7 +460,7 @@ const onRequestLnurlInvoice = async function () { // onAmountEndEditing
|
||||
}
|
||||
|
||||
if (lnurlPayParams.maxSendable && amount > lnurlPayParams.maxSendable / 1000) {
|
||||
infoMessage(translate("payCommon.maximumPay", {
|
||||
setInfo(translate("payCommon.maximumPay", {
|
||||
amount: roundDown(lnurlPayParams.maxSendable / 1000, 0),
|
||||
currency: CurrencyCode.SATS
|
||||
}))
|
||||
@@ -469,7 +468,7 @@ const onRequestLnurlInvoice = async function () { // onAmountEndEditing
|
||||
}
|
||||
|
||||
if (lnurlPayParams.payerData) {
|
||||
infoMessage(translate("transferScreen.LUD18unsupported"))
|
||||
setInfo(translate("transferScreen.LUD18unsupported"))
|
||||
}
|
||||
|
||||
setAmountToTransfer(`${numbro(amountToTransfer).format({thousandSeparated: true, mantissa: getCurrency(unit).mantissa})}`)
|
||||
@@ -494,7 +493,7 @@ const ensureCommentNotTooLong = async function () {
|
||||
|
||||
|
||||
const onEncodedInvoice = async function (encoded: string, paymentRequestDesc: string = '') {
|
||||
log.trace("onEncodedInvoice")
|
||||
log.trace("[onEncodedInvoice] start")
|
||||
try {
|
||||
navigation.setParams({encodedInvoice: undefined})
|
||||
navigation.setParams({paymentRequest: undefined})
|
||||
@@ -506,7 +505,7 @@ const onEncodedInvoice = async function (encoded: string, paymentRequestDesc: st
|
||||
const expiresAt = addSeconds(new Date(timestamp as number * 1000), expiry as number)
|
||||
|
||||
if (!amount || amount === 0) {
|
||||
infoMessage(translate('payCommon.amountZeroOrNegative'))
|
||||
setInfo(translate('payCommon.amountZeroOrNegative'))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -534,11 +533,15 @@ const onEncodedInvoice = async function (encoded: string, paymentRequestDesc: st
|
||||
proofsStore.getMintBalance(mintUrl) :
|
||||
proofsStore.getMintBalancesWithUnit(unit)[0]
|
||||
|
||||
log.trace('[onEncodedInvoice]', {balanceToTransferFrom})
|
||||
|
||||
if (!balanceToTransferFrom) {
|
||||
infoMessage(translate("transferScreen.noMintWithBalance", { unit }))
|
||||
log.warn('Not enough alance')
|
||||
setInfo(translate("transferScreen.noMintWithBalance", { unit }))
|
||||
setIsNotEnoughFunds(true)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setMintBalanceToTransferFrom(balanceToTransferFrom)
|
||||
// continues in hook that handles other mint selection by user
|
||||
|
||||
@@ -701,6 +704,17 @@ const iconColor = useThemeColor('textDim')
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
{isNotEnoughFunds && transactionStatus !== TransactionStatus.COMPLETED && (
|
||||
<View style={$bottomContainer}>
|
||||
<View style={$buttonContainer}>
|
||||
<Button
|
||||
tx="common.close"
|
||||
onPress={onClose}
|
||||
preset="secondary"
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
{availableMintBalances.length > 0 &&
|
||||
transactionStatus !== TransactionStatus.COMPLETED && (
|
||||
<MintBalanceSelector
|
||||
@@ -758,9 +772,6 @@ const iconColor = useThemeColor('textDim')
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
{isLoading && <Loading />}
|
||||
{error && <ErrorModal error={error} />}
|
||||
{info && <InfoModal message={info} />}
|
||||
</View>
|
||||
<BottomModal
|
||||
isVisible={isResultModalVisible}
|
||||
@@ -856,6 +867,9 @@ const iconColor = useThemeColor('textDim')
|
||||
onBackButtonPress={toggleResultModal}
|
||||
onBackdropPress={toggleResultModal}
|
||||
/>
|
||||
{isLoading && <Loading />}
|
||||
{error && <ErrorModal error={error} />}
|
||||
{info && <InfoModal message={info} />}
|
||||
</Screen>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -594,6 +594,7 @@ export const WalletScreen: FC<WalletScreenProps> = observer(
|
||||
tx="walletScreen.sendEcash"
|
||||
subTx="walletScreen.sendEcashDesc"
|
||||
onPress={gotoSend}
|
||||
bottomSeparator={true}
|
||||
/>
|
||||
<ListItem
|
||||
leftIcon='faBolt'
|
||||
@@ -616,6 +617,7 @@ export const WalletScreen: FC<WalletScreenProps> = observer(
|
||||
tx="walletScreen.receiveEcash"
|
||||
subTx="walletScreen.receiveEcashDesc"
|
||||
onPress={gotoTokenReceive}
|
||||
bottomSeparator={true}
|
||||
/>
|
||||
<ListItem
|
||||
leftIcon='faBolt'
|
||||
@@ -784,9 +786,6 @@ const MintsByUnitListItem = observer(function (props: {
|
||||
<ListItem
|
||||
text={mint.shortname}
|
||||
subText={mint.hostname}
|
||||
// leftIcon={mint.status === MintStatus.OFFLINE ? 'faTriangleExclamation' : 'faMoneyBill1'}
|
||||
// leftIconInverse={true}
|
||||
// leftIconColor={mint.color}
|
||||
LeftComponent={
|
||||
<View
|
||||
style={{
|
||||
@@ -836,20 +835,6 @@ const MintsByUnitListItem = observer(function (props: {
|
||||
marginRight: spacing.small
|
||||
}}
|
||||
/>
|
||||
{/*<Button
|
||||
text={'Exchange'}
|
||||
LeftAccessory={() => (
|
||||
<Icon
|
||||
icon='faRotate'
|
||||
color={color}
|
||||
size={spacing.medium}
|
||||
/>
|
||||
)}
|
||||
textStyle={{fontSize: 14, color}}
|
||||
preset='tertiary'
|
||||
onPress={props.gotoMintInfo}
|
||||
style={{minHeight: moderateVerticalScale(40), paddingVertical: moderateVerticalScale(spacing.tiny)}}
|
||||
/>*/}
|
||||
<Button
|
||||
tx="walletScreen.pay"
|
||||
LeftAccessory={() => (
|
||||
@@ -868,24 +853,6 @@ const MintsByUnitListItem = observer(function (props: {
|
||||
marginRight: spacing.small
|
||||
}}
|
||||
/>
|
||||
{/*<Button
|
||||
text={'Send'}
|
||||
LeftAccessory={() => (
|
||||
<Icon
|
||||
icon='faMoneyBill1'
|
||||
color={color}
|
||||
size={spacing.medium}
|
||||
/>
|
||||
)}
|
||||
textStyle={{fontSize: 14, color}}
|
||||
preset='secondary'
|
||||
onPress={() => gotoSend(mintsByUnit.unit, mint.mintUrl)}
|
||||
style={{
|
||||
minHeight: moderateVerticalScale(40),
|
||||
paddingVertical: moderateVerticalScale(spacing.tiny),
|
||||
marginRight: spacing.tiny
|
||||
}}
|
||||
/>*/}
|
||||
<Button
|
||||
tx="wallerScreen.mintButton"
|
||||
LeftAccessory={() => (
|
||||
|
||||
@@ -763,7 +763,7 @@ const _handlePendingTopupTask = async function (params: {paymentRequest: Payment
|
||||
const amountPreferences = getDefaultAmountPreference(amount)
|
||||
const countOfInFlightProofs = CashuUtils.getAmountPreferencesCount(amountPreferences)
|
||||
|
||||
log.trace('[_handlePendingTopupTask]', 'paymentRequest', paymentHash)
|
||||
log.trace('[_handlePendingTopupTask]', 'paymentHash', paymentHash)
|
||||
log.trace('[_handlePendingTopupTask]', 'amountPreferences', amountPreferences)
|
||||
log.trace('[_handlePendingTopupTask]', 'countOfInFlightProofs', countOfInFlightProofs)
|
||||
|
||||
@@ -775,7 +775,30 @@ const _handlePendingTopupTask = async function (params: {paymentRequest: Payment
|
||||
}
|
||||
|
||||
if (isPaid !== true) {
|
||||
log.trace('[_handlePendingTopupTask] Quot not paid', {mintUrl: mint, mintQuote})
|
||||
log.trace('[_handlePendingTopupTask] Quote not paid', {mintUrl: mint, mintQuote})
|
||||
|
||||
if (isBefore(pr.expiresAt as Date, new Date())) {
|
||||
log.debug('[_handlePendingTopupTask]', `Invoice expired, removing: ${pr.paymentHash}`)
|
||||
|
||||
// expire related tx - but only if it has not been completed before this check
|
||||
if(transaction && transaction.status !== TransactionStatus.COMPLETED) {
|
||||
const transactionDataUpdate = {
|
||||
status: TransactionStatus.EXPIRED,
|
||||
message: 'Invoice expired',
|
||||
createdAt: new Date(),
|
||||
}
|
||||
|
||||
await transactionsStore.updateStatuses(
|
||||
[transactionId as number],
|
||||
TransactionStatus.EXPIRED,
|
||||
JSON.stringify(transactionDataUpdate),
|
||||
)
|
||||
}
|
||||
|
||||
stopPolling(`handlePendingTopupPoller-${paymentHash}`)
|
||||
paymentRequestsStore.removePaymentRequest(pr)
|
||||
}
|
||||
|
||||
return {
|
||||
taskFunction: '_handlePendingTopupTask',
|
||||
transaction,
|
||||
@@ -799,94 +822,20 @@ const _handlePendingTopupTask = async function (params: {paymentRequest: Payment
|
||||
const lockedProofsCounter = await mintInstance.getProofsCounterByUnit(unit)
|
||||
|
||||
let proofs: CashuProof[] = []
|
||||
|
||||
try {
|
||||
proofs = (await MintClient.mintProofs(
|
||||
mint as string,
|
||||
unit,
|
||||
amount,
|
||||
mintQuote as string,
|
||||
amountPreferences,
|
||||
lockedProofsCounter.inFlightFrom as number
|
||||
)) as CashuProof[]
|
||||
|
||||
// log.info('[_handlePendingTopupTask]', {proofs})
|
||||
|
||||
} catch (e: any) {
|
||||
// decrease so that unpaid invoices does not cause counter gaps from polling
|
||||
mintInstance.decreaseProofsCounter(lockedProofsCounter.keyset, countOfInFlightProofs)
|
||||
|
||||
// remove already expired invoices
|
||||
if (isBefore(pr.expiresAt as Date, new Date())) {
|
||||
log.debug('[_handlePendingTopupTask]', `Invoice expired, removing: ${pr.paymentHash}`)
|
||||
|
||||
// expire related tx - but only if it has not been completed before this check
|
||||
if(transaction && transaction.status !== TransactionStatus.COMPLETED) {
|
||||
const transactionDataUpdate = {
|
||||
status: TransactionStatus.EXPIRED,
|
||||
message: 'Mint returned error and invoice expired',
|
||||
error: {name: e.name, message: e.message, params: e.params},
|
||||
createdAt: new Date(),
|
||||
}
|
||||
|
||||
await transactionsStore.updateStatuses(
|
||||
[transactionId as number],
|
||||
TransactionStatus.EXPIRED,
|
||||
JSON.stringify(transactionDataUpdate),
|
||||
)
|
||||
}
|
||||
|
||||
stopPolling(`handlePendingTopupPoller-${paymentHash}`)
|
||||
paymentRequestsStore.removePaymentRequest(pr)
|
||||
}
|
||||
|
||||
// throw but keep polling
|
||||
throw e
|
||||
}
|
||||
|
||||
mintInstance.decreaseProofsCounter(lockedProofsCounter.keyset, countOfInFlightProofs)
|
||||
|
||||
// This runs if invoice has not yet been paid, w/o other error
|
||||
if (!proofs || proofs.length === 0) {
|
||||
log.trace('[_handlePendingTopupTask]', 'Invoice has not yet been paid')
|
||||
|
||||
const transaction = transactionsStore.findById(transactionId as number)
|
||||
let isExpired: boolean = false
|
||||
proofs = (await MintClient.mintProofs(
|
||||
mint as string,
|
||||
unit,
|
||||
amount,
|
||||
mintQuote as string,
|
||||
amountPreferences,
|
||||
lockedProofsCounter.inFlightFrom as number
|
||||
)) as CashuProof[]
|
||||
|
||||
// remove already expired invoices only after check that they have not been paid
|
||||
if (isBefore(pr.expiresAt as Date, new Date())) {
|
||||
log.debug('[_handlePendingTopupTask]', `Invoice expired, removing: ${paymentHash}`)
|
||||
|
||||
if(transaction && transaction.status !== TransactionStatus.COMPLETED) {
|
||||
const transactionDataUpdate = {
|
||||
status: TransactionStatus.EXPIRED,
|
||||
message: 'Invoice expired before being paid.',
|
||||
createdAt: new Date(),
|
||||
}
|
||||
|
||||
await transactionsStore.updateStatuses(
|
||||
[transactionId as number],
|
||||
TransactionStatus.EXPIRED,
|
||||
JSON.stringify(transactionDataUpdate),
|
||||
)
|
||||
}
|
||||
|
||||
stopPolling(`handlePendingTopupTaskPoller-${paymentHash}`)
|
||||
paymentRequestsStore.removePaymentRequest(pr)
|
||||
isExpired = true
|
||||
}
|
||||
// release lock and move on (keep polling)
|
||||
mintInstance.resetInFlight(transactionId as number )
|
||||
|
||||
return {
|
||||
taskFunction: '_handlePendingTopupTask',
|
||||
mintUrl: mint,
|
||||
unit,
|
||||
amount,
|
||||
paymentHash,
|
||||
transaction,
|
||||
message: isExpired ? 'Lightning invoice expired unpaid.' : 'Lightning invoice has not yet been paid.',
|
||||
} as TransactionTaskResult
|
||||
mintInstance.decreaseProofsCounter(lockedProofsCounter.keyset, countOfInFlightProofs)
|
||||
|
||||
if (!proofs || proofs.length === 0) {
|
||||
throw new AppError(Err.VALIDATION_ERROR, 'Mint did not return any proofs.')
|
||||
}
|
||||
|
||||
// we got proofs, accept to the wallet asap
|
||||
|
||||
+41
-1
@@ -105,6 +105,26 @@ export const colors = {
|
||||
* The default bg color of the primary button.
|
||||
*/
|
||||
button: palette.success300,
|
||||
/**
|
||||
* The default bg color of the primary button.
|
||||
*/
|
||||
buttonPressed: palette.success200,
|
||||
/**
|
||||
* The default bg color of the primary button.
|
||||
*/
|
||||
buttonSecondary: palette.neutral200,
|
||||
/**
|
||||
* The default bg color of the primary button.
|
||||
*/
|
||||
buttonSecondaryPressed: palette.neutral300,
|
||||
/**
|
||||
* The default bg color of the primary button.
|
||||
*/
|
||||
buttonTertiary: 'transparent',
|
||||
/**
|
||||
* The default bg color of the primary button.
|
||||
*/
|
||||
buttonTertiaryPressed: palette.neutral200,
|
||||
/**
|
||||
* The default color of the header and status bar.
|
||||
*/
|
||||
@@ -183,7 +203,27 @@ export const colors = {
|
||||
/**
|
||||
* The default bg color of the primary button.
|
||||
*/
|
||||
button: palette.success200,
|
||||
button: palette.success300,
|
||||
/**
|
||||
* The default bg color of the primary button.
|
||||
*/
|
||||
buttonPressed: palette.success200,
|
||||
/**
|
||||
* The default bg color of the primary button.
|
||||
*/
|
||||
buttonSecondary: palette.neutral700,
|
||||
/**
|
||||
* The default bg color of the primary button.
|
||||
*/
|
||||
buttonSecondaryPressed: palette.neutral600,
|
||||
/**
|
||||
* The default bg color of the primary button.
|
||||
*/
|
||||
buttonTertiary: 'transparent',
|
||||
/**
|
||||
* The default bg color of the primary button.
|
||||
*/
|
||||
buttonTertiaryPressed: palette.neutral700,
|
||||
/**
|
||||
* The default color of the header and status bar.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user