mirror of
https://github.com/minibits-cash/minibits_wallet.git
synced 2026-08-11 17:07:44 +00:00
Finalize payment request details, improve offline mode UX
This commit is contained in:
@@ -487,11 +487,12 @@
|
||||
},
|
||||
"tranDetailScreen": {
|
||||
"amount": "Amount",
|
||||
"paymentId": "Payment ID",
|
||||
"balanceAfter": "Balance after this transaction",
|
||||
"claim": "Claim",
|
||||
"createdAt": "Created at",
|
||||
"expiresAt": "Expires at",
|
||||
"id": "ID",
|
||||
"id": "Transaction ID",
|
||||
"invoice": "Lightning invoice to pay",
|
||||
"isOffline": "Redeem online",
|
||||
"lightningFee": "Lightning network fee",
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useStores } from "../models"
|
||||
import { MintBalanceSelector } from "./Mints/MintBalanceSelector"
|
||||
import { CurrencyAmount } from "./Wallet/CurrencyAmount"
|
||||
import EventEmitter from '../utils/eventEmitter'
|
||||
import { getCurrency, MintUnit, CurrencyCode } from "../services/wallet/currency"
|
||||
import { getCurrency, MintUnit, CurrencyCode, convertToFromSats } from "../services/wallet/currency"
|
||||
import { round, toNumber } from "../utils/number"
|
||||
import { translate } from "../i18n"
|
||||
import AppError, { Err } from "../utils/AppError"
|
||||
@@ -25,7 +25,6 @@ import {
|
||||
Text,
|
||||
} from "../components"
|
||||
import useIsInternetReachable from "../utils/useIsInternetReachable"
|
||||
import { PaymentRequest} from "@cashu/cashu-ts"
|
||||
import {HANDLE_RECEIVED_EVENT_TASK, log, TransactionTaskResult, WalletTask } from "../services"
|
||||
import { QRCodeBlock } from "./Wallet/QRCode"
|
||||
import { TranItem } from "./TranDetailScreen"
|
||||
@@ -33,6 +32,7 @@ import { Transaction, TransactionStatus } from "../models/Transaction"
|
||||
import { CASHU_PAYMENT_REQUEST_TASK } from "../services/wallet/cashuPaymentRequestTask"
|
||||
import { ResultModalInfo } from "./Wallet/ResultModalInfo"
|
||||
import { MintHeader } from "./Mints/MintHeader"
|
||||
import { verticalScale } from "@gocodingnow/rn-size-matters"
|
||||
|
||||
type Props = StaticScreenProps<{
|
||||
unit: MintUnit,
|
||||
@@ -327,32 +327,30 @@ const headerBg = useThemeColor("header")
|
||||
const placeholderTextColor = useThemeColor("textDim")
|
||||
const amountInputColor = useThemeColor("amountInput")
|
||||
const inputText = useThemeColor("text")
|
||||
const convertedAmountColor = useThemeColor("headerSubTitle")
|
||||
const convertedAmountColor = useThemeColor('headerSubTitle')
|
||||
|
||||
const getConvertedAmount = () => {
|
||||
if (!walletStore.exchangeRate) return undefined
|
||||
const precision = getCurrency(unit).precision
|
||||
return (
|
||||
round(toNumber(amountToRequest) * precision, 0) &&
|
||||
walletStore.exchangeRate &&
|
||||
walletStore.exchangeRate[getCurrency(unit).code]
|
||||
? round(
|
||||
(toNumber(amountToRequest) * precision * walletStore.exchangeRate[userSettingsStore.exchangeCurrency]) /
|
||||
walletStore.exchangeRate[getCurrency(unit).code],
|
||||
2,
|
||||
const getConvertedAmount = function () {
|
||||
if (!walletStore.exchangeRate) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const precision = getCurrency(unit).precision
|
||||
return convertToFromSats(
|
||||
round(toNumber(amountToRequest) * precision, 0) || 0,
|
||||
getCurrency(unit).code,
|
||||
walletStore.exchangeRate
|
||||
)
|
||||
: undefined
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const isConvertedAmountVisible = function () {
|
||||
return (
|
||||
walletStore.exchangeRate &&
|
||||
(userSettingsStore.exchangeCurrency === getCurrency(unit).code ||
|
||||
unit === 'sat') &&
|
||||
getConvertedAmount() !== undefined
|
||||
)
|
||||
}
|
||||
|
||||
const isConvertedAmountVisible = () => {
|
||||
return (
|
||||
walletStore.exchangeRate &&
|
||||
(userSettingsStore.exchangeCurrency === getCurrency(unit).code ||
|
||||
unit === "sat") &&
|
||||
getConvertedAmount() !== undefined
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Screen preset="fixed" contentContainerStyle={$screen}>
|
||||
@@ -380,26 +378,15 @@ return (
|
||||
selectTextOnFocus={true}
|
||||
returnKeyType={"done"}
|
||||
/>
|
||||
{isConvertedAmountVisible() && (
|
||||
<CurrencyAmount
|
||||
amount={getConvertedAmount() ?? 0}
|
||||
currencyCode={
|
||||
unit === "sat"
|
||||
? userSettingsStore.exchangeCurrency
|
||||
: CurrencyCode.SAT
|
||||
}
|
||||
symbolStyle={{
|
||||
color: convertedAmountColor,
|
||||
marginTop: spacing.tiny,
|
||||
fontSize: 10,
|
||||
}}
|
||||
amountStyle={{
|
||||
color: convertedAmountColor,
|
||||
lineHeight: spacing.small,
|
||||
}}
|
||||
size="small"
|
||||
containerStyle={{ justifyContent: "center" }}
|
||||
/>
|
||||
{isConvertedAmountVisible() && (
|
||||
<CurrencyAmount
|
||||
amount={getConvertedAmount() ?? 0}
|
||||
currencyCode={unit === 'sat' ? userSettingsStore.exchangeCurrency : CurrencyCode.SAT}
|
||||
symbolStyle={{color: convertedAmountColor, marginTop: spacing.tiny, fontSize: verticalScale(10)}}
|
||||
amountStyle={{color: convertedAmountColor, lineHeight: spacing.small}}
|
||||
size='small'
|
||||
containerStyle={{justifyContent: 'center'}}
|
||||
/>
|
||||
)}
|
||||
<Text
|
||||
size="xs"
|
||||
|
||||
+39
-18
@@ -419,7 +419,7 @@ const pubkeyInputRef = useRef<TextInput>(null) // Initialize pubkeyInputRef
|
||||
|
||||
// Offline send
|
||||
useEffect(() => {
|
||||
if(isInternetReachable) return
|
||||
//if(isInternetReachable) return
|
||||
log.trace('[Offline send]')
|
||||
|
||||
// if offline we set all non-zero mint balances as available to allow ecash selection
|
||||
@@ -436,7 +436,7 @@ const pubkeyInputRef = useRef<TextInput>(null) // Initialize pubkeyInputRef
|
||||
setAvailableMintBalances(availableBalances)
|
||||
setMintBalanceToSendFrom(availableBalances[0])
|
||||
setIsMintSelectorVisible(true)
|
||||
}, [isInternetReachable])
|
||||
}, [])
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
@@ -1427,6 +1427,7 @@ const SelectProofsBlock = observer(function (props: {
|
||||
|
||||
const {proofsStore} = useStores()
|
||||
const hintColor = useThemeColor('textDim')
|
||||
const statusColor = useThemeColor('header')
|
||||
|
||||
|
||||
const onCancel = function () {
|
||||
@@ -1436,13 +1437,48 @@ const SelectProofsBlock = observer(function (props: {
|
||||
|
||||
return (
|
||||
<View style={$bottomModal}>
|
||||
<View
|
||||
style={[
|
||||
{
|
||||
alignSelf: 'center',
|
||||
marginTop: spacing.tiny,
|
||||
paddingHorizontal: spacing.tiny,
|
||||
borderRadius: spacing.tiny,
|
||||
backgroundColor: colors.palette.primary200,
|
||||
},
|
||||
]}>
|
||||
<Text
|
||||
text={'OFFLINE MODE'}
|
||||
style={[
|
||||
{
|
||||
color: statusColor,
|
||||
fontSize: 10,
|
||||
fontFamily: typography.primary?.light,
|
||||
padding: 0,
|
||||
lineHeight: 16,
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
<Text text='Select ecash to send' style={{marginTop: spacing.large}}/>
|
||||
<Text
|
||||
text='You can only send exact ecash denominations while you are offline.'
|
||||
style={{color: hintColor, paddingHorizontal: spacing.small, textAlign: 'center'}}
|
||||
size='xs'
|
||||
/>
|
||||
<CurrencyAmount
|
||||
amount={CashuUtils.getProofsAmount(props.selectedProofs)}
|
||||
mintUnit={props.unit}
|
||||
size='extraLarge'
|
||||
containerStyle={{marginTop: spacing.large, marginBottom: spacing.small, alignItems: 'center'}}
|
||||
/>
|
||||
<View style={{maxHeight: spacing.screenHeight * 0.4}}>
|
||||
<View style={{
|
||||
maxHeight: spacing.screenHeight * 0.45,
|
||||
borderWidth: 1,
|
||||
borderColor: hintColor,
|
||||
borderRadius: spacing.medium,
|
||||
marginTop: spacing.small
|
||||
}}>
|
||||
<FlatList<Proof>
|
||||
data={proofsStore.getByMint(props.mintBalanceToSendFrom.mintUrl, {isPending: false, unit: props.unit})}
|
||||
renderItem={({ item }) => {
|
||||
@@ -1464,21 +1500,6 @@ const SelectProofsBlock = observer(function (props: {
|
||||
/>
|
||||
</View>
|
||||
<View style={[$bottomContainer, {marginTop: spacing.extraLarge}]}>
|
||||
<View style={[$buttonContainer, {marginBottom: spacing.medium}]}>
|
||||
<CurrencyAmount
|
||||
amount={CashuUtils.getProofsAmount(props.selectedProofs)}
|
||||
mintUnit={props.unit}
|
||||
|
||||
size='large'
|
||||
/>
|
||||
<Button
|
||||
preset={'tertiary'}
|
||||
onPress={() => props.toggleIsLockedToPubkey()}
|
||||
LeftAccessory={() => <Icon icon={props.isLockedToPubkey ? 'faLock' : 'faLock'}/>}
|
||||
text={props.isLockedToPubkey ? 'Locked' : 'Lock'}
|
||||
style={{minWidth: 80}}
|
||||
/>
|
||||
</View>
|
||||
<View style={[$buttonContainer]}>
|
||||
<Button
|
||||
text="Create token"
|
||||
|
||||
+110
-124
@@ -4,6 +4,7 @@ import {
|
||||
View,
|
||||
TextStyle,
|
||||
TextInput,
|
||||
TouchableOpacity,
|
||||
} from 'react-native'
|
||||
import {colors, spacing, useThemeColor} from '../theme'
|
||||
import {log} from '../services/logService'
|
||||
@@ -32,6 +33,13 @@ export const TokenReceiveScreen = function TokenReceiveScreen({ route }: Props)
|
||||
const tokenInputRef = useRef<TextInput>(null)
|
||||
const {mintsStore} = useStores()
|
||||
|
||||
// New: controls visibility of token input
|
||||
const [showTokenInput, setShowTokenInput] = useState(false)
|
||||
const [encodedToken, setEncodedToken] = useState<string | undefined>(undefined)
|
||||
const [unit, setUnit] = useState<MintUnit>('sat')
|
||||
const [mint, setMint] = useState<Mint | undefined>(undefined)
|
||||
const [error, setError] = useState<AppError | undefined>()
|
||||
|
||||
async function autoPaste(setter: (text: string) => void, sideEffect: () => void) {
|
||||
const clipboard = (await Clipboard.getString()).trim();
|
||||
if (clipboard.length === 0) return
|
||||
@@ -40,6 +48,8 @@ export const TokenReceiveScreen = function TokenReceiveScreen({ route }: Props)
|
||||
const resultFromClipboard = IncomingParser.findAndExtract(clipboard, IncomingDataType.CASHU)
|
||||
setter(resultFromClipboard.encoded)
|
||||
sideEffect()
|
||||
// Show input if autopaste sets encodedToken
|
||||
setShowTokenInput(true)
|
||||
} catch (e: any) {
|
||||
return
|
||||
}
|
||||
@@ -70,12 +80,12 @@ export const TokenReceiveScreen = function TokenReceiveScreen({ route }: Props)
|
||||
return () => {}
|
||||
}, [])
|
||||
|
||||
|
||||
const [encodedToken, setEncodedToken] = useState<string | undefined>(undefined)
|
||||
const [unit, setUnit] = useState<MintUnit>('sat')
|
||||
const [mint, setMint] = useState<Mint | undefined>(undefined)
|
||||
const [error, setError] = useState<AppError | undefined>()
|
||||
|
||||
// If encodedToken is set (by autopaste), show input
|
||||
useEffect(() => {
|
||||
if (encodedToken && encodedToken.length > 0) {
|
||||
setShowTokenInput(true)
|
||||
}
|
||||
}, [encodedToken])
|
||||
|
||||
const onPaste = async function() {
|
||||
const clipboard = await Clipboard.getString()
|
||||
@@ -86,9 +96,9 @@ export const TokenReceiveScreen = function TokenReceiveScreen({ route }: Props)
|
||||
|
||||
setEncodedToken(clipboard)
|
||||
tokenInputRef.current?.blur()
|
||||
setShowTokenInput(true)
|
||||
}
|
||||
|
||||
|
||||
const gotoScan = async function () {
|
||||
tokenInputRef.current?.blur()
|
||||
//@ts-ignore
|
||||
@@ -98,7 +108,6 @@ export const TokenReceiveScreen = function TokenReceiveScreen({ route }: Props)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const gotoCashuPaymentRequest = async function () {
|
||||
//@ts-ignore
|
||||
navigation.navigate('CashuPaymentRequest', {
|
||||
@@ -107,18 +116,6 @@ export const TokenReceiveScreen = function TokenReceiveScreen({ route }: Props)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/* const gotoContacts = function () {
|
||||
//@ts-ignore
|
||||
navigation.navigate('ContactsNavigator', {
|
||||
screen: 'Contacts',
|
||||
params: {
|
||||
paymentOption: SendOption.LNURL_ADDRESS
|
||||
}
|
||||
})
|
||||
} */
|
||||
|
||||
|
||||
const onConfirm = async function() {
|
||||
if(!encodedToken) {
|
||||
setError({name: Err.VALIDATION_ERROR, message: translate("missingEcashTokenToReceiveError")})
|
||||
@@ -135,7 +132,6 @@ export const TokenReceiveScreen = function TokenReceiveScreen({ route }: Props)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const gotoTopup = async function () {
|
||||
//@ts-ignore
|
||||
navigation.navigate('Topup', {
|
||||
@@ -144,7 +140,6 @@ export const TokenReceiveScreen = function TokenReceiveScreen({ route }: Props)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const handleError = function(e: AppError): void {
|
||||
setError(e)
|
||||
}
|
||||
@@ -155,7 +150,8 @@ export const TokenReceiveScreen = function TokenReceiveScreen({ route }: Props)
|
||||
const inputText = useThemeColor('text')
|
||||
const contactIcon = useThemeColor('button')
|
||||
const headerBg = useThemeColor('header')
|
||||
const headerTitle = useThemeColor('headerTitle')
|
||||
const headerTitle = useThemeColor('headerTitle')
|
||||
const mainButtonColor = useThemeColor('card')
|
||||
|
||||
return (
|
||||
<Screen preset="fixed" contentContainerStyle={$screen}>
|
||||
@@ -171,105 +167,72 @@ export const TokenReceiveScreen = function TokenReceiveScreen({ route }: Props)
|
||||
/>
|
||||
</View>
|
||||
<View style={$contentContainer}>
|
||||
<Card
|
||||
HeadingComponent={
|
||||
<ListItem
|
||||
leftIcon='faMoneyBill1'
|
||||
leftIconColor={colors.palette.iconViolet300}
|
||||
tx="common.pasteEcashToken"
|
||||
bottomSeparator={true}
|
||||
/* RightComponent={
|
||||
<Button
|
||||
preset='tertiary'
|
||||
LeftAccessory={() => <Icon color={contactIcon} containerStyle={{paddingVertical: 0}} icon='faAddressBook' />}
|
||||
onPress={gotoContacts}
|
||||
text='Contacts'
|
||||
textStyle={{fontSize: 12, color: contactIcon}}
|
||||
/>
|
||||
}*/
|
||||
/>
|
||||
}
|
||||
<Card
|
||||
ContentComponent={
|
||||
<>
|
||||
<Text
|
||||
size='xs'
|
||||
style={{color: hintText, padding: spacing.extraSmall}}
|
||||
tx="pasteEcashTokenDesc"
|
||||
<ListItem
|
||||
leftIcon='faMoneyBill1'
|
||||
tx="common.pasteEcashToken"
|
||||
bottomSeparator={showTokenInput}
|
||||
onPress={() => setShowTokenInput((v) => !v)}
|
||||
/>
|
||||
<View style={{alignItems: 'center', marginTop: spacing.small}}>
|
||||
<TextInput
|
||||
ref={tokenInputRef}
|
||||
onChangeText={data => setEncodedToken(data)}
|
||||
value={encodedToken}
|
||||
autoCapitalize='none'
|
||||
keyboardType='default'
|
||||
maxLength={5000}
|
||||
numberOfLines={3}
|
||||
multiline={true}
|
||||
selectTextOnFocus={true}
|
||||
style={[$addressInput, {backgroundColor: inputBg, color: inputText}]}
|
||||
/>
|
||||
</View>
|
||||
{!!encodedToken && encodedToken?.length > 1 ? (
|
||||
<View style={$buttonContainer}>
|
||||
<Button
|
||||
preset='default'
|
||||
tx='common.confirm'
|
||||
onPress={onConfirm}
|
||||
style={{marginLeft: spacing.small}}
|
||||
LeftAccessory={() => <Icon icon='faCheckCircle' color='white'/>}
|
||||
{/* Token Input */}
|
||||
{showTokenInput && (
|
||||
<View style={{paddingVertical: spacing.extraSmall}}>
|
||||
<Text
|
||||
size='xs'
|
||||
style={{color: hintText, padding: spacing.extraSmall}}
|
||||
tx="pasteEcashTokenDesc"
|
||||
/>
|
||||
<View style={{alignItems: 'center', marginTop: spacing.small}}>
|
||||
<TextInput
|
||||
ref={tokenInputRef}
|
||||
onChangeText={data => setEncodedToken(data)}
|
||||
value={encodedToken}
|
||||
autoCapitalize='none'
|
||||
keyboardType='default'
|
||||
maxLength={5000}
|
||||
numberOfLines={3}
|
||||
multiline={true}
|
||||
selectTextOnFocus={true}
|
||||
style={[$addressInput, {backgroundColor: inputBg, color: inputText}]}
|
||||
/>
|
||||
</View>
|
||||
{!!encodedToken && encodedToken?.length > 1 ? (
|
||||
<View style={[$buttonContainer, {marginTop: spacing.small}]}>
|
||||
<Button
|
||||
preset='default'
|
||||
tx='common.confirm'
|
||||
onPress={onConfirm}
|
||||
// style={{marginLeft: spacing.small}}
|
||||
LeftAccessory={() => <Icon icon='faCheckCircle' color='white'/>}
|
||||
/>
|
||||
</View>
|
||||
) : (
|
||||
<View style={[$buttonContainer, {marginTop: spacing.small}]}>
|
||||
<Button
|
||||
preset='secondary'
|
||||
tx={'common.paste'}
|
||||
onPress={onPaste}
|
||||
LeftAccessory={() => (
|
||||
<Icon icon='faPaste'/>
|
||||
)}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
) : (
|
||||
<View style={$buttonContainer}>
|
||||
<Button
|
||||
preset='secondary'
|
||||
tx={'common.paste'}
|
||||
onPress={onPaste}
|
||||
LeftAccessory={() => (
|
||||
<Icon icon='faPaste'/>
|
||||
)}
|
||||
/>
|
||||
<Button
|
||||
preset='secondary'
|
||||
tx='common.scan'
|
||||
onPress={gotoScan}
|
||||
style={{marginLeft: spacing.small}}
|
||||
LeftAccessory={() => {
|
||||
return(
|
||||
<SvgXml
|
||||
width={spacing.medium}
|
||||
height={spacing.medium}
|
||||
xml={ScanIcon}
|
||||
fill={scanIcon}
|
||||
style={{marginHorizontal: spacing.extraSmall}}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
)}
|
||||
{/* Create Payment Request */}
|
||||
<ListItem
|
||||
leftIcon='faQrcode'
|
||||
tx="common.createCashuPaymentRequest"
|
||||
onPress={gotoCashuPaymentRequest}
|
||||
topSeparator={true}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<Card
|
||||
style={{marginTop: spacing.medium}}
|
||||
ContentComponent={
|
||||
<ListItem
|
||||
leftIcon='faQrcode'
|
||||
leftIconColor={colors.palette.iconYellow300}
|
||||
tx="common.createCashuPaymentRequest"
|
||||
RightComponent={
|
||||
<Button
|
||||
preset='secondary'
|
||||
//LeftAccessory={() => <Icon color={contactIcon} containerStyle={{paddingVertical: 0}} icon='faAddressBook' />}
|
||||
onPress={gotoCashuPaymentRequest}
|
||||
tx='common.create'
|
||||
// textStyle={{fontSize: 12, color: contactIcon}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
}
|
||||
style={$card}
|
||||
//style={{marginBottom: spacing.medium}}
|
||||
/>
|
||||
<Button
|
||||
tx="tokenReceiveScreen.topupWithLightning"
|
||||
@@ -291,8 +254,27 @@ export const TokenReceiveScreen = function TokenReceiveScreen({ route }: Props)
|
||||
marginTop: spacing.medium
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
</View>
|
||||
<View style={$bottomContainer}>
|
||||
<View style={$buttonContainer}>
|
||||
<Button
|
||||
preset='tertiary'
|
||||
LeftAccessory={() => (
|
||||
<SvgXml
|
||||
width={spacing.medium}
|
||||
height={spacing.medium}
|
||||
xml={ScanIcon}
|
||||
fill={scanIcon}
|
||||
style={{marginHorizontal: spacing.extraSmall}}
|
||||
/>
|
||||
)}
|
||||
onPress={gotoScan}
|
||||
style={{backgroundColor: mainButtonColor}}
|
||||
text='Scan'
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
{error && <ErrorModal error={error} />}
|
||||
</Screen>
|
||||
)
|
||||
@@ -303,10 +285,9 @@ const $screen: ViewStyle = {
|
||||
}
|
||||
|
||||
const $contentContainer: ViewStyle = {
|
||||
// flex: 1,
|
||||
marginTop: -spacing.extraLarge * 2,
|
||||
padding: spacing.extraSmall,
|
||||
// alignItems: 'center',
|
||||
flex: 1
|
||||
}
|
||||
|
||||
const $headerContainer: TextStyle = {
|
||||
@@ -316,12 +297,11 @@ const $headerContainer: TextStyle = {
|
||||
}
|
||||
|
||||
const $buttonContainer: ViewStyle = {
|
||||
marginTop: spacing.large,
|
||||
//marginTop: spacing.large,
|
||||
flexDirection: 'row',
|
||||
alignSelf: 'center',
|
||||
}
|
||||
|
||||
|
||||
const $addressInput: TextStyle = {
|
||||
textAlignVertical: 'top' ,
|
||||
borderRadius: spacing.extraSmall,
|
||||
@@ -330,15 +310,21 @@ const $addressInput: TextStyle = {
|
||||
height: verticalScale(70),
|
||||
}
|
||||
|
||||
|
||||
const $card: ViewStyle = {
|
||||
marginBottom: 0,
|
||||
}
|
||||
|
||||
const $bottomContainer: ViewStyle = {
|
||||
position: 'absolute',
|
||||
/*position: 'absolute',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
flex: 1,
|
||||
justifyContent: 'flex-end',
|
||||
marginBottom: spacing.medium,
|
||||
alignSelf: 'stretch',
|
||||
marginBottom: spacing.medium,*/
|
||||
alignSelf: 'center',
|
||||
// opacity: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -232,6 +232,7 @@ export const TranDetailScreen = observer(function TranDetailScreen({ route }: Pr
|
||||
const colorScheme = useColorScheme()
|
||||
const headerTitle = useThemeColor('headerTitle')
|
||||
const inputText = useThemeColor('text')
|
||||
const statusColor = useThemeColor('header')
|
||||
|
||||
return (
|
||||
<Screen contentContainerStyle={$screen} preset="fixed">
|
||||
@@ -253,7 +254,32 @@ export const TranDetailScreen = observer(function TranDetailScreen({ route }: Pr
|
||||
preset="heading"
|
||||
text={getFormattedAmount()}
|
||||
style={[$tranAmount, {color: headerTitle}]}
|
||||
/>
|
||||
/>
|
||||
{transaction.status !== TransactionStatus.COMPLETED && (
|
||||
<View
|
||||
style={[
|
||||
{
|
||||
alignSelf: 'center',
|
||||
marginTop: spacing.tiny,
|
||||
paddingHorizontal: spacing.tiny,
|
||||
borderRadius: spacing.tiny,
|
||||
backgroundColor: colors.palette.primary200,
|
||||
},
|
||||
]}>
|
||||
<Text
|
||||
text={transaction.status as string}
|
||||
style={[
|
||||
{
|
||||
color: statusColor,
|
||||
fontSize: 10,
|
||||
fontFamily: typography.primary?.light,
|
||||
padding: 0,
|
||||
lineHeight: 16,
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
<ScrollView style={$contentContainer}>
|
||||
<Card
|
||||
@@ -405,7 +431,8 @@ export const TranDetailScreen = observer(function TranDetailScreen({ route }: Pr
|
||||
].includes(transaction.status)
|
||||
) && (
|
||||
<Card
|
||||
label='Token tracking'
|
||||
label='Token tracking'
|
||||
style={$dataCard}
|
||||
ContentComponent={
|
||||
<>
|
||||
<ListItem
|
||||
@@ -722,10 +749,12 @@ const ReceiveInfoBlock = function (props: {
|
||||
isCurrency={true}
|
||||
isFirst={true}
|
||||
/>
|
||||
{transaction.memo && (
|
||||
<TranItem
|
||||
label="tranDetailScreen.memoFromSender"
|
||||
value={transaction.memo as string}
|
||||
/>
|
||||
)}
|
||||
{transaction.sentFrom && (
|
||||
<>
|
||||
{profilePicture ? (
|
||||
@@ -823,6 +852,12 @@ const ReceiveInfoBlock = function (props: {
|
||||
label="tranDetailScreen.createdAt"
|
||||
value={(transaction.createdAt as Date).toLocaleString()}
|
||||
/>
|
||||
{transaction.paymentId && (
|
||||
<TranItem
|
||||
label="tranDetailScreen.paymentId"
|
||||
value={transaction.paymentId as string}
|
||||
/>
|
||||
)}
|
||||
<TranItem label="tranDetailScreen.id" value={`${transaction.id}`} />
|
||||
</>
|
||||
}
|
||||
@@ -1037,6 +1072,12 @@ const ReceiveOfflineInfoBlock = function (props: {
|
||||
label="tranDetailScreen.createdAt"
|
||||
value={(transaction.createdAt as Date).toLocaleString()}
|
||||
/>
|
||||
{transaction.paymentId && (
|
||||
<TranItem
|
||||
label="tranDetailScreen.paymentId"
|
||||
value={transaction.paymentId as string}
|
||||
/>
|
||||
)}
|
||||
<TranItem label="tranDetailScreen.id" value={`${transaction.id}`} />
|
||||
</>
|
||||
}
|
||||
@@ -1503,7 +1544,13 @@ const TopupInfoBlock = function (props: {
|
||||
label="tranDetailScreen.expiresAt"
|
||||
value={(new Date(paymentRequest.expiresAt!)).toLocaleString()}
|
||||
/>
|
||||
)}
|
||||
)}
|
||||
{transaction.paymentId && (
|
||||
<TranItem
|
||||
label="tranDetailScreen.paymentId"
|
||||
value={transaction.paymentId as string}
|
||||
/>
|
||||
)}
|
||||
<TranItem label="tranDetailScreen.id" value={`${transaction.id}`} />
|
||||
</>
|
||||
}
|
||||
@@ -1748,6 +1795,12 @@ const TransferInfoBlock = function (props: {
|
||||
label="tranDetailScreen.createdAt"
|
||||
value={(transaction.createdAt as Date).toLocaleString()}
|
||||
/>
|
||||
{transaction.paymentId && (
|
||||
<TranItem
|
||||
label="tranDetailScreen.paymentId"
|
||||
value={transaction.paymentId as string}
|
||||
/>
|
||||
)}
|
||||
<TranItem label="tranDetailScreen.id" value={`${transaction.id}`} />
|
||||
</>
|
||||
}
|
||||
@@ -2003,7 +2056,7 @@ const $contentContainer: TextStyle = {
|
||||
const $tranAmount: TextStyle = {
|
||||
fontSize: verticalScale(48),
|
||||
lineHeight: verticalScale(48),
|
||||
marginLeft: -20,
|
||||
//marginLeft: -20,
|
||||
}
|
||||
|
||||
const $actionCard: ViewStyle = {
|
||||
|
||||
@@ -62,6 +62,14 @@ export const TransactionListItem = observer(function (props: TransactionListProp
|
||||
} else {
|
||||
return tx.memo ? tx.memo : translate('transactionCommon.youReceived')
|
||||
}
|
||||
case TransactionType.RECEIVE_BY_PAYMENT_REQUEST:
|
||||
if (tx.sentFrom) {
|
||||
if (!tx.memo || tx.memo.includes('Sent from Minibits')) {
|
||||
return translate('transactionCommon.from', {sender: getProfileName(tx.sentFrom)})
|
||||
}
|
||||
} else {
|
||||
return tx.memo ? tx.memo : translate('transactionCommon.youReceived')
|
||||
}
|
||||
case TransactionType.RECEIVE_OFFLINE:
|
||||
if (tx.sentFrom) {
|
||||
if (!tx.memo || tx.memo.includes('Sent from Minibits')) {
|
||||
|
||||
@@ -57,7 +57,13 @@ const extractEncodedLightningInvoice = function (maybeInvoice: string) {
|
||||
return encodedInvoice
|
||||
}
|
||||
|
||||
if (maybeInvoice && maybeInvoice.toLowerCase().startsWith('bitcoin:' || 'http')) {
|
||||
if (
|
||||
maybeInvoice &&
|
||||
(
|
||||
maybeInvoice.toLowerCase().startsWith('bitcoin:') ||
|
||||
maybeInvoice.toLowerCase().startsWith('http')
|
||||
)
|
||||
) {
|
||||
const url = new URL(maybeInvoice.toLowerCase())
|
||||
// Use URLSearchParams to get the value of the "lightning" parameter
|
||||
encodedInvoice = url.searchParams.get("lightning") as string
|
||||
|
||||
@@ -78,7 +78,7 @@ export const cashuPaymentRequestTask = async function (
|
||||
},
|
||||
] as PaymentRequestTransport[]
|
||||
|
||||
const cashuPrId = QuickCrypto.randomBytes(16).toString("hex")
|
||||
const cashuPrId = QuickCrypto.randomBytes(4).toString("hex")
|
||||
const cashuPaymentRequest = new CashuPaymentRequest(
|
||||
transport,
|
||||
cashuPrId,
|
||||
|
||||
@@ -11,6 +11,7 @@ import { WalletUtils } from './utils'
|
||||
import {isBefore} from 'date-fns'
|
||||
import { MintUnit, formatCurrency, getCurrency } from './currency'
|
||||
import { NostrEvent } from '../nostrService'
|
||||
import { LightningUtils } from '../lightning/lightningUtils'
|
||||
|
||||
const {
|
||||
transactionsStore,
|
||||
@@ -77,6 +78,8 @@ export const transferTask = async function (
|
||||
// store tx in db and in the model
|
||||
transaction = await transactionsStore.addTransaction(newTransaction)
|
||||
const transactionId = transaction.id
|
||||
const paymentHash = LightningUtils.getInvoiceData(LightningUtils.decodeInvoice(encodedInvoice)).payment_hash
|
||||
transaction.setPaymentId(paymentHash)
|
||||
|
||||
if (amountToTransfer + meltQuote.fee_reserve > mintBalanceToTransferFrom.balances[unit]!) {
|
||||
throw new AppError(
|
||||
@@ -263,7 +266,7 @@ export const transferTask = async function (
|
||||
)
|
||||
|
||||
const balanceAfter = proofsStore.getUnitBalance(unit)?.unitBalance!
|
||||
transaction.setBalanceAfter(balanceAfter)
|
||||
transaction.setBalanceAfter(balanceAfter)
|
||||
|
||||
return {
|
||||
taskFunction: TRANSFER_TASK,
|
||||
|
||||
Reference in New Issue
Block a user