Merge pull request #4 from Permissionless-Software-Foundation/dh-buy-modal

feat(buy): Added buy modal to DEX taker wallet
This commit is contained in:
Chris Troutner
2025-03-12 08:10:06 -07:00
committed by GitHub
6 changed files with 161 additions and 71 deletions
+5
View File
@@ -36,6 +36,11 @@
cursor: pointer;
}
.buy-modal {
top: 30%!important;
width: auto!important;
}
/* Remove input number arrows Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
+3 -3
View File
@@ -73,16 +73,16 @@ function App (props) {
// Load the P2WDB libraries.
addToModal('Loading P2WDB Libraries', appData)
const {p2wdbRead, p2wdbWrite} = asyncLoad.getP2WDBLib({bchWallet: walletTemp})
const { p2wdbRead, p2wdbWrite } = asyncLoad.getP2WDBLib({ bchWallet: walletTemp })
// Load the DEX library.
addToModal('Loading DEX Library', appData)
const dexLib = asyncLoad.getDexLib({bchWallet: walletTemp, p2wdbRead, p2wdbWrite})
const dexLib = asyncLoad.getDexLib({ bchWallet: walletTemp, p2wdbRead, p2wdbWrite })
appData.setDexLib(dexLib)
// Load the Nostr library.
addToModal('Loading Nostr Library', appData)
const nostrLib = asyncLoad.getNostrLib({bchWallet: walletTemp})
const nostrLib = asyncLoad.getNostrLib({ bchWallet: walletTemp })
appData.setNostr(nostrLib)
// Update state
@@ -5,49 +5,86 @@
// Global npm libraries
import React, { useState } from 'react'
import { Button, Modal, Container, Row, Col } from 'react-bootstrap'
import { Button, Modal, Container, Row, Col, Spinner } from 'react-bootstrap'
function BuyButton (props) {
const { token, appData } = props
const [show, setShow] = useState(false)
const { token, appData, onSuccess } = props
console.log('props: ', props)
const [show, setShow] = useState(false) // show the modal
const [onFetch, setOnFetch] = useState(false) // show the spinner
const [error, setError] = useState(false) // show the error message
// const [eventId, setEventId] = useState(false) // show the event id
const [noteId, setNoteId] = useState(false) // show the note id
const [success, setSuccess] = useState(false) // show the success message
const [showConfirmation, setShowConfirmation] = useState(true) // show the confirmation view
const handleBuy = async () => {
console.log('handleBuy()')
console.log('token: ', token)
console.log('appData: ', appData)
try {
console.log('handleBuy()')
console.log('token: ', token)
console.log('appData: ', appData)
setShowConfirmation(false)
setOnFetch(true)
const targetOffer = token.nostrEventId
console.log('targetOffer: ', targetOffer)
/* // dev-test success view
setShowConfirmation(false)
setSuccess(true)
setNoteId('note12986q83gre76vl9dldpnnhej7y67h76xzw0tx0hm4mq6uradr6lskch7v9')
setOnFetch(false)
return
*/
const targetOffer = token.nostrEventId
console.log('targetOffer: ', targetOffer)
// TODO: Launch modal to let user know that the purchase is in progress.
// Generate a counter offer.
const bchDexLib = appData.dexLib
const { offerData, partialHex } = await bchDexLib.take.takeOffer(
targetOffer
)
// Generate a counter offer.
const bchDexLib = appData.dexLib
const { offerData, partialHex } = await bchDexLib.take.takeOffer(targetOffer)
console.log('offerData: ', offerData)
console.log('partialHex: ', partialHex)
console.log('offerData: ', offerData)
console.log('partialHex: ', partialHex)
// Upload the counter offer to Nostr.
const nostr = appData.nostr
const { eventId, noteId } = await nostr.testNostrUpload({
offerData,
partialHex
})
// Upload the counter offer to Nostr.
const nostr = appData.nostr
const { eventId, noteId } = await nostr.testNostrUpload({ offerData, partialHex })
console.log(
`Counter Offer uploaded to Nostr with this event ID: ${eventId}`
)
console.log(`https://astral.psfoundation.info/${noteId}`)
console.log(`Counter Offer uploaded to Nostr with this event ID: ${eventId}`)
console.log(`https://astral.psfoundation.info/${noteId}`)
setNoteId(noteId)
setSuccess(true)
setOnFetch(false)
} catch (error) {
setOnFetch(false)
setError(error.message)
}
}
const handleClose = () => {
console.log('handleClose()')
// Deny close if the purchase is in progress.
if (onFetch) {
return
}
// Call onSuccess() callback if the modal close after a success purchase.
if (success && onSuccess) {
onSuccess()
}
// Reset the state.
setError(false)
setSuccess(false)
setNoteId(false)
setShow(false)
// props.instance.setState({ showModal: false })
setOnFetch(false)
setShowConfirmation(true)
}
const handleOpen = () => {
console.log('handleOpen()')
setShow(true)
}
// Replace with dummy button until token data is loaded.
if (!props.token.tokenData) {
return (
@@ -59,45 +96,94 @@ function BuyButton (props) {
return (
<>
<Button variant='success' disabled={props.disabled} onClick={handleBuy}>Buy</Button>
<Modal show={show} onHide={handleClose}>
<Button
variant='success'
disabled={props.disabled}
onClick={() => {
setShow(true)
}}
>
Buy
</Button>
<Modal show={show} onHide={handleClose} dialogClassName='buy-modal'>
<Modal.Header closeButton>
<Modal.Title>Token Information</Modal.Title>
<Modal.Title>
{success
? (
<span style={{ color: 'green' }}>Successful Purchase</span>
)
: (
'Processing Purchase'
)}
</Modal.Title>
</Modal.Header>
<Modal.Body>
<Container>
<Row>
<Col xs={4}><b>Ticker</b>:</Col>
<Col xs={8}>{props.token.ticker}</Col>
</Row>
<Modal.Body className='text-center'>
{/** If the purchase is not confirmed, display the token details. and confirm button. */}
{showConfirmation && (
<Container className='text-start'>
<Row>
<Col xs={4}>
<b>Ticker</b>:
</Col>
<Col xs={8}>{token.ticker}</Col>
</Row>
<Row style={{ backgroundColor: '#eee' }}>
<Col xs={4}><b>Name</b>:</Col>
<Col xs={8}>{props.token.tokenData.genesisData.name}</Col>
</Row>
<Row>
<Col xs={4}>
<b>Name</b>:
</Col>
<Col xs={8}>{props.token.tokenData.genesisData.name}</Col>
</Row>
<Row style={{ backgroundColor: '#eee' }}>
<Col xs={4}>
<b>Price</b>:
</Col>
<Col xs={8}>
<strong>{token.usdPrice}</strong>
</Col>
</Row>
</Container>
)}
<Row>
<Col xs={4}><b>Token ID</b>:</Col>
<Col xs={8} style={{ wordBreak: 'break-all' }}>
<a href={`https://explorer.tokentiger.com/?tokenid=${props.token.tokenId}`} target='_blank' rel='noreferrer'>
{props.token.tokenId}
</a>
</Col>
</Row>
<Row style={{ backgroundColor: '#eee' }}>
<Col xs={4}><b>Decimals</b>:</Col>
<Col xs={8}>{props.token.tokenData.genesisData.decimals}</Col>
</Row>
<Row>
<Col xs={4}><b>Token Type</b>:</Col>
<Col xs={8}>{props.token.tokenType}</Col>
</Row>
{/** show purchase progress */}
<Container style={{ wordBreak: 'break-word' }}>
{onFetch && <Spinner animation='border' variant='primary' />}
{error && (
<span style={{ color: 'red', fontStyle: 'italic' }}>{error}</span>
)}
{success && (
<>
<p>
Note ID :
<a
href={`https://astral.psfoundation.info/${noteId}`}
target='_blank'
rel='noreferrer'
>
{' '}
{noteId}
</a>
</p>
</>
)}
</Container>
</Modal.Body>
<Modal.Footer />
<Modal.Footer className='text-center'>
{showConfirmation && (
<Row style={{ width: '100%' }}>
<Col xs={12} className='text-center'>
<Button
variant='success'
style={{ minWidth: '100px' }}
onClick={handleBuy}
>
Buy
</Button>
</Col>
</Row>
)}
</Modal.Footer>
</Modal>
</>
)
@@ -205,7 +205,7 @@ function NftsForSale (props) {
}
return url
}
// This function generates a Token Card for each token in the wallet.
function generateCards (offers) {
console.log('generateCards() offerData: ', offers)
@@ -221,6 +221,7 @@ function NftsForSale (props) {
<TokenCard
appData={appData}
token={thisToken}
handleRefresh={handleRefresh}
key={`${thisToken.tokenId + i}`}
/>
)
@@ -12,7 +12,7 @@ import InfoButton from './info-button'
import BuyButton from './buy-button'
function TokenCard (props) {
const { token, appData } = props
const { token, appData, handleRefresh } = props
const [icon, setIcon] = useState(token.icon)
const [tokenData, setTokenData] = useState(token.tokenData)
@@ -72,7 +72,7 @@ function TokenCard (props) {
<Col />
<Col>
<BuyButton token={token} disabled={!token.tokenData} appData={appData} />
<BuyButton token={token} disabled={!token.tokenData} appData={appData} onSuccess={handleRefresh} />
</Col>
</Row>
+4 -6
View File
@@ -205,7 +205,7 @@ class AsyncLoad {
try {
const { bchWallet, p2wdbRead, p2wdbWrite } = inObj
const dexLib = new BchDexLib({bchWallet, p2wdbRead, p2wdbWrite})
const dexLib = new BchDexLib({ bchWallet, p2wdbRead, p2wdbWrite })
return dexLib
} catch (error) {
@@ -219,7 +219,7 @@ class AsyncLoad {
try {
const { bchWallet } = inObj
const nostrLib = new Nostr({bchWallet})
const nostrLib = new Nostr({ bchWallet })
return nostrLib
} catch (error) {
@@ -234,9 +234,9 @@ class AsyncLoad {
const { bchWallet } = inObj
const p2wdbRead = new P2WDB.Read()
const p2wdbWrite = new P2WDB.Write({bchWallet})
const p2wdbWrite = new P2WDB.Write({ bchWallet })
return {p2wdbRead, p2wdbWrite}
return { p2wdbRead, p2wdbWrite }
} catch (error) {
console.error('Error in getP2WDBLib', error)
throw error
@@ -244,8 +244,6 @@ class AsyncLoad {
}
}
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}