From 143e78c6a5c9d768c1b7931804f571dda7ce353c Mon Sep 17 00:00:00 2001 From: minibits-cash Date: Wed, 1 May 2024 21:32:38 +0200 Subject: [PATCH] Topup and Send rewrite, fix balances --- patches/@cashu+cashu-ts+1.0.0-rc.3.patch | 35 +- src/components/Button.tsx | 6 +- src/components/MintIcon.tsx | 1 + src/components/NutIcon.ts | 28 - src/components/index.ts | 2 +- src/models/ContactsStore.ts | 10 +- src/models/PaymentRequest.ts | 8 +- src/models/ProofsStore.ts | 40 +- src/models/TransactionsStore.ts | 40 +- src/navigation/TabsNavigator.tsx | 34 +- src/screens/BackupScreen.tsx | 6 +- src/screens/ContactDetailScreen.tsx | 14 +- src/screens/Contacts/PrivateContacts.tsx | 42 +- src/screens/Contacts/PublicContacts.tsx | 52 +- src/screens/LightningPayScreen.tsx | 64 ++- src/screens/Mints/MintBalanceSelector.tsx | 129 +++++ src/screens/Mints/MintHeader.tsx | 51 ++ src/screens/Mints/MintListItem.tsx | 15 +- src/screens/ReceiveOptionsScreen.tsx | 6 +- src/screens/SendScreen.tsx | 423 +++++++-------- src/screens/TopupScreen.tsx | 494 ++++++++---------- src/screens/TranDetailScreen.tsx | 26 +- .../Transactions/TransactionListItem.tsx | 20 +- src/screens/Wallet/CurrencyAmount.tsx | 9 +- src/screens/WalletScreen.tsx | 227 +++++--- src/services/cashuMintClient.ts | 51 +- src/services/incomingParser.ts | 21 +- src/services/wallet/sendTask.ts | 17 +- src/services/wallet/topupTask.ts | 11 +- src/services/walletService.ts | 73 ++- src/theme/colors.ts | 5 +- 31 files changed, 1138 insertions(+), 822 deletions(-) create mode 100644 src/components/MintIcon.tsx delete mode 100644 src/components/NutIcon.ts create mode 100644 src/screens/Mints/MintBalanceSelector.tsx create mode 100644 src/screens/Mints/MintHeader.tsx diff --git a/patches/@cashu+cashu-ts+1.0.0-rc.3.patch b/patches/@cashu+cashu-ts+1.0.0-rc.3.patch index 70a11895..836456e6 100644 --- a/patches/@cashu+cashu-ts+1.0.0-rc.3.patch +++ b/patches/@cashu+cashu-ts+1.0.0-rc.3.patch @@ -13,7 +13,7 @@ index f2809bd..48176f1 100644 "type": "git", "url": "git+https://github.com/cashubtc/cashu-ts" diff --git a/node_modules/@cashu/cashu-ts/src/CashuMint.ts b/node_modules/@cashu/cashu-ts/src/CashuMint.ts -index 1491934..7a88aea 100644 +index 1491934..7852bd1 100644 --- a/node_modules/@cashu/cashu-ts/src/CashuMint.ts +++ b/node_modules/@cashu/cashu-ts/src/CashuMint.ts @@ -17,9 +17,9 @@ import type { @@ -29,6 +29,39 @@ index 1491934..7a88aea 100644 /** * Class represents Cashu Mint API. This class contains Lower level functions that are implemented by CashuWallet. +@@ -81,6 +81,32 @@ class CashuMint { + async mintQuote(mintQuotePayload: MintQuotePayload): Promise { + return CashuMint.mintQuote(this._mintUrl, mintQuotePayload, this._customRequest); + } ++ /** ++ * Retrieve if a mint quote has been paid ++ * @param mintUrl ++ * @param quote Quote ID. ++ * @param customRequest ++ * @returns MintQuoteResponse with paid boolean ++ */ ++ public static async mintQuotePaid( ++ mintUrl: string, ++ quote: string, ++ customRequest?: typeof request ++ ): Promise { ++ const requestInstance = customRequest || request; ++ return requestInstance({ ++ endpoint: joinUrls(mintUrl, '/v1/mint/quote/bolt11', quote) ++ }); ++ } ++ ++ /** ++ * Retrieve if a mint quote has been paid ++ * @param quote Quote ID. ++ * @returns MintQuoteResponse with paid boolean ++ */ ++ async mintQuotePaid(quote: string): Promise { ++ return CashuMint.mintQuotePaid(this._mintUrl, quote, this._customRequest); ++ } + /** + * Requests the mint to perform token minting after the LN invoice has been paid + * @param mintUrl diff --git a/node_modules/@cashu/cashu-ts/src/CashuWallet.ts b/node_modules/@cashu/cashu-ts/src/CashuWallet.ts index 6d3a0b4..02ddb59 100644 --- a/node_modules/@cashu/cashu-ts/src/CashuWallet.ts diff --git a/src/components/Button.tsx b/src/components/Button.tsx index 0dd48c35..0b1f6642 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -101,6 +101,7 @@ export function Button(props: ButtonProps) { { backgroundColor: defaultBg }, preset === "secondary" && { backgroundColor: secondaryBg }, preset === "tertiary" && { backgroundColor: tertiaryBg }, + !!LeftAccessory && {paddingLeft: spacing.tiny}, $viewStyleOverride, !!pressed && [$pressedViewPresets[preset], $pressedViewStyleOverride], ] @@ -141,8 +142,9 @@ const $baseViewStyle: ViewStyle = { justifyContent: "center", alignItems: "center", flexDirection: "row", - paddingVertical: moderateVerticalScale(spacing.small), - paddingHorizontal: spacing.small, + paddingVertical: spacing.small, + paddingLeft: spacing.small, + paddingRight: spacing.small, overflow: "hidden", } diff --git a/src/components/MintIcon.tsx b/src/components/MintIcon.tsx new file mode 100644 index 00000000..86105121 --- /dev/null +++ b/src/components/MintIcon.tsx @@ -0,0 +1 @@ +export const MintIcon = ` ` \ No newline at end of file diff --git a/src/components/NutIcon.ts b/src/components/NutIcon.ts deleted file mode 100644 index 8fb55ca0..00000000 --- a/src/components/NutIcon.ts +++ /dev/null @@ -1,28 +0,0 @@ -export const NutIcon = ` - - - - - - - - - - - - - -` diff --git a/src/components/index.ts b/src/components/index.ts index b3ee9e95..5156d82e 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -17,5 +17,5 @@ export * from "./ScanIcon" export * from "./BtcIcon" export * from "./UsdIcon" export * from "./EurIcon" -export * from "./NutIcon" +export * from "./MintIcon" export * from "./HeaderBg" diff --git a/src/models/ContactsStore.ts b/src/models/ContactsStore.ts index 49f467cf..dfb47203 100644 --- a/src/models/ContactsStore.ts +++ b/src/models/ContactsStore.ts @@ -6,6 +6,7 @@ import { destroy, isStateTreeNode, detach, + getSnapshot, } from 'mobx-state-tree' import {withSetPropAction} from './helpers/withSetPropAction' import { @@ -23,7 +24,8 @@ import { MINIBITS_NIP05_DOMAIN } from '@env' contacts: types.array(ContactModel), publicPubkey: types.maybe(types.string), lastPendingReceivedCheck: types.maybe(types.number), // UNIX timestamp - receivedEventIds: types.optional(types.array(types.string), []) + receivedEventIds: types.optional(types.array(types.string), []), + selectedContact: types.maybe(types.frozen()) }) .actions(withSetPropAction) .views(self => ({ @@ -99,6 +101,12 @@ import { MINIBITS_NIP05_DOMAIN } from '@env' log.debug('[removeContact]', 'Contact removed from MintsStore') } }, + selectContact(contact: Contact) { + self.selectedContact = getSnapshot(contact) + }, + unselectContact() { + self.selectedContact = undefined + }, removeAllContacts() { self.contacts.clear() log.debug('[removeAllContacts]', 'Removed all Contacts from ContactsStore') diff --git a/src/models/PaymentRequest.ts b/src/models/PaymentRequest.ts index e3d02e6f..8f541448 100644 --- a/src/models/PaymentRequest.ts +++ b/src/models/PaymentRequest.ts @@ -31,9 +31,9 @@ export const PaymentRequestModel = types status: types.frozen(), mint: types.maybe(types.string), mintQuote: types.maybe(types.string), - mintUnit: types.frozen(), // TODO migration to set default unit for existing PRs + mintUnit: types.maybe(types.frozen()), // TODO migration to set default unit for existing PRs mintMethod: types.optional(types.frozen(), MintMethod.BOLT11), - amountToTopup: types.number, + amountToTopup: types.maybe(types.number), encodedInvoice: types.string, invoicedAmount: types.number, invoicedUnit: types.optional(types.frozen(), 'sat'), @@ -58,10 +58,10 @@ export type PaymentRequest = { type: PaymentRequestType status: PaymentRequestStatus mint?: string - mintUnit: MintUnit + mintUnit?: MintUnit mintQuote?: string mintMethod?: MintMethod - amountToTopup: number + amountToTopup?: number encodedInvoice: string invoicedAmount: number description?: string diff --git a/src/models/ProofsStore.ts b/src/models/ProofsStore.ts index 52661096..3b842dc4 100644 --- a/src/models/ProofsStore.ts +++ b/src/models/ProofsStore.ts @@ -51,6 +51,7 @@ export const ProofsStoreModel = types ): Proof[] | undefined { const proofs = options.isPending ? self.pendingProofs : self.proofs + if (options.unit) { return proofs.filter(proof => proof.mintUrl === mintUrl && proof.unit === options.unit) } @@ -188,7 +189,7 @@ export const ProofsStoreModel = types return self.pendingProofs }, })) - .views(self => ({ // move to MintsStore? + .views(self => ({ getBalances() { const mintBalancesMap: Map = new Map() const unitBalancesMap: Map = new Map() @@ -200,15 +201,22 @@ export const ProofsStoreModel = types // make sure balances are defined even if we have no proofs for (const mint of mints) { const {mintUrl, units} = mint - const zeroBalances = Object.fromEntries(units!.map(unit => [unit, 0])) as { [key in MintUnit]: number }; - mintBalancesMap.set(mintUrl, { mintUrl, balances: zeroBalances}) - mintPendingBalancesMap.set(mintUrl, { mintUrl, balances: zeroBalances}) + const zeroBalances = Object.fromEntries(units!.map(unit => [unit, 0])) as { [key in MintUnit]: number }; + + // make distinct copies of zeroBalances so that balances are not summed by reference!!! + mintBalancesMap.set(mintUrl, { mintUrl, balances: JSON.parse(JSON.stringify(zeroBalances))}) + mintPendingBalancesMap.set(mintUrl, { mintUrl, balances: JSON.parse(JSON.stringify(zeroBalances))}) for (const unit of mint.units!) { unitBalancesMap.set(unit, 0) } } + log.trace('[getBalances] zero balances', [...mintBalancesMap.entries()]) + log.trace('[getBalances] zero pendingBalances', [...mintPendingBalancesMap.entries()]) + + log.trace('[getBalances] proofs count', self.proofs.length) + for (const proof of self.proofs) { const { mintUrl, unit, amount } = proof @@ -225,16 +233,20 @@ export const ProofsStoreModel = types const mintBalances: MintBalance[] = Array.from(mintBalancesMap.values()) + log.trace('[getBalances] filled balances', [...mintBalancesMap.entries()]) + + log.trace('[getBalances] zero pendingBalances', [...mintPendingBalancesMap.entries()]) + // Convert map to array of UnitBalance objects const unitBalances: UnitBalance[] = Array.from(unitBalancesMap.entries()).map(([unit, unitBalance]) => ({ unitBalance, unit - })) - - + })) for (const proof of self.pendingProofs) { const { mintUrl, unit, amount } = proof + + log.trace('adding pending proof amount', proof.amount) // Make sure to not cause madness from orphaned proofs if it would happen if (!mintPendingBalancesMap.has(mintUrl)) { @@ -242,10 +254,12 @@ export const ProofsStoreModel = types } // Update balance for the unit - const mintBalance = mintPendingBalancesMap.get(mintUrl)! - mintBalance.balances[unit] = (mintBalance.balances[unit] || 0) + amount + const mintPendingBalance = mintPendingBalancesMap.get(mintUrl)! + mintPendingBalance.balances[unit] = (mintPendingBalance.balances[unit] || 0) + amount unitPendingBalancesMap.set(unit, (unitPendingBalancesMap.get(unit) || 0) + amount) } + + log.trace('[getBalances] pendingBalances', [...mintPendingBalancesMap.entries()]) const mintPendingBalances: MintBalance[] = Array.from(mintPendingBalancesMap.values()) @@ -324,6 +338,14 @@ export const ProofsStoreModel = types log.debug('[getMintBalanceWithMaxBalance]', maxBalance) return maxBalance }, + getUnitBalance: (unit: MintUnit) => { + const balances = self.getBalances().unitBalances + + const unitBalance = balances + .find((balance: UnitBalance) => balance.unit === unit) + + return unitBalance + }, getProofsToSend: (amount: number, proofs: Proof[]) => { let proofsAmount = 0 const proofSubset = proofs.filter(proof => { diff --git a/src/models/TransactionsStore.ts b/src/models/TransactionsStore.ts index bcc023d9..a4a111d0 100644 --- a/src/models/TransactionsStore.ts +++ b/src/models/TransactionsStore.ts @@ -18,10 +18,15 @@ import {Database, MintClient} from '../services' import {log} from '../services/logService' import { getRootStore } from './helpers/getRootStore' import { formatDistance } from 'date-fns' +import { MintUnit } from '../services/wallet/currency' +import { Mint } from './Mint' +import { ReceiveOption, SendOption } from '../screens' +import { Contact } from './Contact' export const maxTransactionsInModel = 10 export const maxTransactionsByMint = 10 export const maxTransactionsByHostname = 3 +export const maxTransactionsByUnit = 3 export type GroupedByTimeAgo = { [timeAgo: string]: Transaction[]; @@ -30,6 +35,10 @@ export type GroupedByTimeAgo = { export const TransactionsStoreModel = types .model('TransactionsStore', { transactions: types.array(TransactionModel), + /*tranUnit: types.maybe(types.frozen()), + tranMint: types.maybe(types.frozen()), + tranOption: types.maybe(types.frozen()), + tranContact: types.maybe(types.frozen())*/ }) .actions(withSetPropAction) .views(self => ({ @@ -53,7 +62,7 @@ export const TransactionsStoreModel = types return this.all.filter(t => t.status === TransactionStatus.PENDING) }, get groupedByTimeAgo() { - return this.all.reduce((groups: GroupedByTimeAgo, transaction: Transaction) => { + return this.all.reduce((groups: GroupedByTimeAgo, transaction: Transaction) => { const timeAgo = formatDistance(transaction.createdAt as Date, new Date(), {addSuffix: true}) if (!groups[timeAgo]) { groups[timeAgo] = [] @@ -63,7 +72,7 @@ export const TransactionsStoreModel = types }, {}) }, get groupedPendingByTimeAgo() { - return this.pending.reduce((groups: GroupedByTimeAgo, transaction: Transaction) => { + return this.pending.reduce((groups: GroupedByTimeAgo, transaction: Transaction) => { const timeAgo = formatDistance(transaction.createdAt as Date, new Date(), {addSuffix: true}) if (!groups[timeAgo]) { groups[timeAgo] = [] @@ -79,6 +88,9 @@ export const TransactionsStoreModel = types recentByHostname(mintHostname: string) { return this.all.filter(t => getHostname(t.mint as string) === mintHostname).slice(0, maxTransactionsByHostname) }, + recentByUnit(unit: MintUnit) { + return this.all.filter(t => t.unit === unit).slice(0, maxTransactionsByUnit) + }, recentByHostnameGroupedByTimeAgo(mintHostname: string) { const recentByHostname = this.recentByHostname(mintHostname) @@ -328,6 +340,30 @@ export const TransactionsStoreModel = types self.transactions.clear() log.debug('[removeAllTransactions]', 'Removed all transactions from TransactionsStore') }, + /* setTranUnit(unit: MintUnit) { + self.tranUnit = unit + }, + setTranMint(mint: Mint) { + self.tranMint = mint + }, + setTranOption(option: ReceiveOption | SendOption) { + self.tranOption = option + }, + setTranContact(contact: Contact) { + self.tranContact = contact + }, + unsetTranUnit() { + self.tranUnit = undefined + }, + unsetTranMint(mint: Mint) { + self.tranMint = undefined + }, + unsetTranOption(option: ReceiveOption | SendOption) { + self.tranOption = undefined + }, + unsetTranContact(contact: Contact) { + self.tranContact = undefined + },*/ })) const getHostname = function (mintUrl: string) { diff --git a/src/navigation/TabsNavigator.tsx b/src/navigation/TabsNavigator.tsx index aa1f164a..535a5a58 100644 --- a/src/navigation/TabsNavigator.tsx +++ b/src/navigation/TabsNavigator.tsx @@ -120,33 +120,37 @@ export function TabsNavigator() { export type WalletStackParamList = { Wallet: {scannedMintUrl? : string, returnWithNavigationReset?: boolean} - ReceiveOptions: undefined + ReceiveOptions: {unit: MintUnit} Receive: {encodedToken? : string} - SendOptions: undefined - Send: {contact?: Contact, relays?: string[], paymentOption?: SendOption} + SendOptions: {unit: MintUnit} + Send: { + unit: MintUnit, + paymentOption?: SendOption, + contact?: Contact, + mintUrl?: string, + } Scan: undefined LightningPay: { - mintUrl?: string, - unit?: MintUnit + unit: MintUnit + mintUrl?: string, } TranDetail: {id: number} TranHistory: undefined PaymentRequests: undefined Transfer: { - encodedInvoice?: string, + unit: MintUnit, + encodedInvoice?: string, paymentRequest?: PaymentRequest, lnurlParams?: LNURLPayParams & {address?: string}, paymentOption?: SendOption, - mintUrl?: string, - unit?: MintUnit + mintUrl?: string, } - Topup: { - contact?: Contact, - relays?: string[], - paymentOption?: ReceiveOption, + Topup: { + unit: MintUnit, + paymentOption?: ReceiveOption, + contact?: Contact, lnurlParams?: LNURLWithdrawParams, - mintUrl?: string, - unit?: MintUnit + mintUrl?: string, } ContactsNavigator: {screen: string, params: any} SettingsNavigator: {screen: string, params: any} @@ -192,7 +196,7 @@ export type ContactsStackParamList = { WalletName: undefined RandomName: {navigation: any} OwnName: {navigation: any} - ContactDetail: {contact: Contact, relays: string[]} + ContactDetail: {contact: Contact} OwnKeys: undefined WalletNavigator: {screen: string, params: any} SettingsNavigator: {screen: string} diff --git a/src/screens/BackupScreen.tsx b/src/screens/BackupScreen.tsx index a4eb9203..b5d1cc40 100644 --- a/src/screens/BackupScreen.tsx +++ b/src/screens/BackupScreen.tsx @@ -40,7 +40,7 @@ export const BackupScreen: FC> = observer(fun const [info, setInfo] = useState('') const [isBackupModalVisible, setIsBackupModalVisible] = useState(false) - const [isHandleSpentFromSpendavleSentToQueue, setIsHandleSpentFromSpendavleSentToQueue] = useState(false) + const [isHandleSpentFromSpendableSentToQueue, setIsHandleSpentFromSpendableSentToQueue] = useState(false) const [backupResultMessage, setBackupResultMessage] = useState() const [totalSpentCount, setTotalSpentCount] = useState(0) const [totalSpentAmount, setTotalSpentAmount] = useState(0) @@ -72,7 +72,7 @@ export const BackupScreen: FC> = observer(fun return () => { EventEmitter.off('ev__handleSpentByMintTask_result', handleSpentByMintTaskResult) } - }, [isHandleSpentFromSpendavleSentToQueue]) + }, [isHandleSpentFromSpendableSentToQueue]) const toggleBackupSwitch = () => { try { @@ -124,7 +124,7 @@ export const BackupScreen: FC> = observer(fun const checkSpent = async function () { setIsLoading(true) - setIsHandleSpentFromSpendavleSentToQueue(true) + setIsHandleSpentFromSpendableSentToQueue(true) WalletTask.handleSpentFromSpendable() } diff --git a/src/screens/ContactDetailScreen.tsx b/src/screens/ContactDetailScreen.tsx index f5741e27..f7de2fae 100644 --- a/src/screens/ContactDetailScreen.tsx +++ b/src/screens/ContactDetailScreen.tsx @@ -21,7 +21,7 @@ interface ContactDetailScreenProps extends ContactsStackScreenProps<'ContactDeta export const ContactDetailScreen: FC = observer( function ContactScreen({route, navigation}) { - const {contact, relays} = route.params + const {contact} = route.params const {contactsStore} = useStores() const noteInputRef = useRef(null) @@ -50,24 +50,24 @@ export const ContactDetailScreen: FC = observer( const gotoTopup = () => { + navigation.navigate('WalletNavigator', { screen: 'Topup', params: { - paymentOption: ReceiveOption.SEND_PAYMENT_REQUEST, - contact, - relays + paymentOption: ReceiveOption.SEND_PAYMENT_REQUEST, + contact }, }) } const gotoSend = () => { + navigation.navigate('WalletNavigator', { screen: 'Send', params: { - paymentOption: SendOption.SEND_TOKEN, - contact, - relays + paymentOption: SendOption.SEND_TOKEN, + contact }, }) } diff --git a/src/screens/Contacts/PrivateContacts.tsx b/src/screens/Contacts/PrivateContacts.tsx index dac486f9..a807cb59 100644 --- a/src/screens/Contacts/PrivateContacts.tsx +++ b/src/screens/Contacts/PrivateContacts.tsx @@ -17,12 +17,13 @@ import { ReceiveOption } from '../ReceiveOptionsScreen' import { SendOption } from '../SendOptionsScreen' import { infoMessage, warningMessage } from '../../utils/utils' import { IncomingDataType, IncomingParser } from '../../services/incomingParser' +import { RouteProp } from '@react-navigation/native' export const PrivateContacts = observer(function (props: { - navigation: StackNavigationProp, - paymentOption: ReceiveOption | SendOption | undefined} + navigation: StackNavigationProp, + paymentOption: ReceiveOption | SendOption | undefined}, ) { const {contactsStore, relaysStore} = useStores() const {navigation} = props @@ -39,15 +40,15 @@ export const PrivateContacts = observer(function (props: { const { paymentOption } = props if (paymentOption && paymentOption === ReceiveOption.SEND_PAYMENT_REQUEST) { - infoMessage('Select contact to send your payment request to.') + // infoMessage('Select contact to send your payment request to.') } if (paymentOption && paymentOption === SendOption.SEND_TOKEN) { - infoMessage('Select contact to send your ecash to.') + // infoMessage('Select contact to send your ecash to.') } if (paymentOption && paymentOption === SendOption.LNURL_ADDRESS) { - infoMessage('Select contact to send Lightning payment to.') + // infoMessage('Select contact to send Lightning payment to.') } }, []) @@ -160,28 +161,22 @@ export const PrivateContacts = observer(function (props: { log.trace('paymentOption', {paymentOption}, 'gotoContactDetail') - const relays = relaysStore.allUrls - if(paymentOption && paymentOption === ReceiveOption.SEND_PAYMENT_REQUEST) { // Topup tx contact selection setIsLoading(true) if(contact.nip05) { await NostrClient.verifyNip05(contact.nip05 as string, contact.pubkey) // throws } - + + // contactsStore.selectContact(contact) navigation.navigate('WalletNavigator', { screen: 'Topup', params: { - paymentOption, - contact, - relays // TODO remove, switch to relaysStore - }, - }) - - //reset - navigation.setParams({ - paymentOption: undefined, + paymentOption, + contact + }, }) + setIsLoading(false) return } @@ -198,15 +193,10 @@ export const PrivateContacts = observer(function (props: { screen: 'Send', params: { paymentOption, - contact, - relays // TODO remove, switch to relaysStore + contact }, }) - //reset - navigation.setParams({ - paymentOption: undefined, - }) setIsLoading(false) return } @@ -231,10 +221,8 @@ export const PrivateContacts = observer(function (props: { return } - navigation.navigate('ContactDetail', { - contact, - relays // TODO remove, switch to relaysStore - }) + navigation.navigate('ContactDetail', {contact}) + } catch (e: any) { // reset so that invalid contact can be deleted navigation.setParams({ diff --git a/src/screens/Contacts/PublicContacts.tsx b/src/screens/Contacts/PublicContacts.tsx index 427f706f..1d1f35c0 100644 --- a/src/screens/Contacts/PublicContacts.tsx +++ b/src/screens/Contacts/PublicContacts.tsx @@ -17,6 +17,7 @@ import { ReceiveOption } from '../ReceiveOptionsScreen' import { useSafeAreaInsetsStyle } from '../../utils/useSafeAreaInsetsStyle' import { useSafeAreaInsets } from 'react-native-safe-area-context' import { IncomingDataType, IncomingParser } from '../../services/incomingParser' +import { RouteProp } from '@react-navigation/native' // const defaultPublicNpub = 'npub14n7frsyufzqsxlvkx8vje22cjah3pcwnnyqncxkuj2243jvt9kmqsdgs52' @@ -341,44 +342,38 @@ export const PublicContacts = observer(function (props: { const gotoContactDetail = async function (contact: Contact) { const {paymentOption} = props - contact.type = ContactType.PUBLIC // ??? - - const relays = relaysStore.allPublicUrls + contact.type = ContactType.PUBLIC - if(paymentOption && paymentOption === ReceiveOption.SEND_PAYMENT_REQUEST) { - navigation.navigate('WalletNavigator', { - screen: 'Topup', - params: { - paymentOption, - contact, - relays // TODO remove, switch to relaysStore - }, - }) - - //reset - navigation.setParams({ - paymentOption: undefined, - }) - + + if(paymentOption && paymentOption === ReceiveOption.SEND_PAYMENT_REQUEST) { // Topup tx contact selection + /*if(contact.nip05) { // test ifworks and makes sense + await NostrClient.verifyNip05(contact.nip05 as string, contact.pubkey) // throws + }*/ + + if(paymentOption === ReceiveOption.SEND_PAYMENT_REQUEST) { + navigation.navigate('WalletNavigator', { + screen: 'Topup', + params: { + paymentOption, + contact + }, + }) + } + return } - if(paymentOption && paymentOption === SendOption.SEND_TOKEN) { + if(paymentOption && paymentOption === SendOption.SEND_TOKEN) { + navigation.navigate('WalletNavigator', { screen: 'Send', params: { paymentOption, - contact, - relays // TODO remove, switch to relaysStore + contact }, }) - - //reset - navigation.setParams({ - paymentOption: undefined, - }) - + return } @@ -410,8 +405,7 @@ export const PublicContacts = observer(function (props: { log.trace('[gotoContactDetail]', contact) navigation.navigate('ContactDetail', { - contact, - relays // TODO remove, switch to relaysStore + contact }) } diff --git a/src/screens/LightningPayScreen.tsx b/src/screens/LightningPayScreen.tsx index ebc57dae..d1f3beb8 100644 --- a/src/screens/LightningPayScreen.tsx +++ b/src/screens/LightningPayScreen.tsx @@ -11,22 +11,25 @@ import {useHeader} from '../utils/useHeader' import {log} from '../services/logService' import { IncomingDataType, IncomingParser } from '../services/incomingParser' import AppError, { Err } from '../utils/AppError' -import { Button, Card, ErrorModal, Icon, ListItem, ScanIcon, Screen, Text } from '../components' +import { Button, Card, ErrorModal, Header, Icon, ListItem, ScanIcon, Screen, Text } from '../components' import { LnurlUtils } from '../services/lnurl/lnurlUtils' import { infoMessage } from '../utils/utils' import Clipboard from '@react-native-clipboard/clipboard' import { SvgXml } from 'react-native-svg' import { SendOption } from './SendOptionsScreen' +import { CurrencyCode, MintUnit } from '../services/wallet/currency' +import { CurrencySign } from './Wallet/CurrencySign' +import { useStores } from '../models' +import { setMinutes } from 'date-fns' +import { Mint } from '../models/Mint' +import { CurrencyAmount } from './Wallet/CurrencyAmount' +import { MintHeader } from './Mints/MintHeader' export const LightningPayScreen: FC> = function LightningPayScreen(_props) { - const {navigation} = _props - useHeader({ - leftIcon: 'faArrowLeft', - onLeftPress: () => navigation.goBack(), - }) - - const lightningInputRef = useRef(null) + const {navigation, route} = _props + const lightningInputRef = useRef(null) + const {mintsStore} = useStores() useEffect(() => { const focus = () => { @@ -40,10 +43,28 @@ export const LightningPayScreen: FC> = fu } }, []) + useEffect(() => { + const setUnitAndMint = () => { + if(route.params && route.params.unit && route.params.mintUrl) { + const mint = mintsStore.findByUrl(route.params.mintUrl) + const unit = route.params.unit + + setMint(mint) + setUnit(unit) + } + } + + setUnitAndMint() + + return () => {} + }, []) + const [prevRouteName, setPrevRouteName] = useState('') - const [lightningData, setLightningData] = useState(undefined) + const [lightningData, setLightningData] = useState(undefined) + const [unit, setUnit] = useState(undefined) + const [mint, setMint] = useState(undefined) const [error, setError] = useState() @@ -80,7 +101,7 @@ export const LightningPayScreen: FC> = fu try { const invoiceResult = IncomingParser.findAndExtract(lightningData as string, IncomingDataType.INVOICE) - return IncomingParser.navigateWithIncomingData(invoiceResult, navigation) + return IncomingParser.navigateWithIncomingData(invoiceResult, navigation, mint && unit && {mintUrl: mint.mintUrl, unit}) } catch (e: any) { const maybeLnurlAddress = LnurlUtils.findEncodedLnurlAddress(lightningData as string) @@ -93,8 +114,8 @@ export const LightningPayScreen: FC> = fu if(validAddress) { await IncomingParser.navigateWithIncomingData({ type: IncomingDataType.LNURL_ADDRESS, - encoded: validAddress - }, navigation) + encoded: validAddress, + }, navigation, mint && unit && {mintUrl: mint.mintUrl, unit}) } return } catch (e3: any) { @@ -114,7 +135,7 @@ export const LightningPayScreen: FC> = fu await IncomingParser.navigateWithIncomingData({ type: IncomingDataType.LNURL, encoded: encodedLnurl - }, navigation) + }, navigation, mint && unit && {mintUrl: mint.mintUrl, unit}) } return } catch (e2: any) { @@ -136,11 +157,20 @@ export const LightningPayScreen: FC> = fu } const hintText = useThemeColor('textDim') + const scanIcon = useThemeColor('text') const inputBg = useThemeColor('background') + const contactIcon = useThemeColor('button') const headerBg = useThemeColor('header') + + return ( + > = fu RightComponent={