mirror of
https://github.com/Permissionless-Software-Foundation/psf-memo-client.git
synced 2026-09-21 16:52:02 -07:00
feat(send): Ported bch-send View
This commit is contained in:
Generated
+10
@@ -16,6 +16,7 @@
|
||||
"axios": "0.27.2",
|
||||
"bch-message-lib": "2.2.1",
|
||||
"bootstrap": "5.2.0",
|
||||
"qrcode.react": "4.2.0",
|
||||
"query-string": "7.1.1",
|
||||
"react": "19.0.0",
|
||||
"react-bootstrap": "2.10.7",
|
||||
@@ -14888,6 +14889,15 @@
|
||||
"teleport": ">=0.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/qrcode.react": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-4.2.0.tgz",
|
||||
"integrity": "sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.10.3",
|
||||
"license": "BSD-3-Clause",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"axios": "0.27.2",
|
||||
"bch-message-lib": "2.2.1",
|
||||
"bootstrap": "5.2.0",
|
||||
"qrcode.react": "4.2.0",
|
||||
"query-string": "7.1.1",
|
||||
"react": "19.0.0",
|
||||
"react-bootstrap": "2.10.7",
|
||||
|
||||
+17
@@ -31,3 +31,20 @@
|
||||
padding: 0.5rem;
|
||||
|
||||
}
|
||||
|
||||
#address-switch {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
/* Remove input number arrows Chrome, Safari, Edge, Opera */
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Remove input number arrows Firefox */
|
||||
input[type=number] {
|
||||
--moz-appearance: textfield;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
This card displays the users balance in BCH.
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
import React from 'react'
|
||||
import { Container, Row, Col, Card } from 'react-bootstrap'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faCoins } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
const BalanceCard = (props) => {
|
||||
const { appData } = props
|
||||
|
||||
const bchjs = appData.wallet.bchjs
|
||||
const sats = appData.bchWalletState.bchBalance
|
||||
const bchBalance = bchjs.BitcoinCash.toBitcoinCash(sats)
|
||||
const usdBalance = bchjs.Util.floor2(bchBalance * appData.bchWalletState.bchUsdPrice)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card>
|
||||
<Card.Body>
|
||||
<Card.Title style={{ textAlign: 'center' }}>
|
||||
<h2><FontAwesomeIcon icon={faCoins} size='lg' /> Balance</h2>
|
||||
</Card.Title>
|
||||
<br />
|
||||
|
||||
<Container>
|
||||
<Row>
|
||||
<Col>
|
||||
<b>USD</b>: ${usdBalance}
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
<b>BCH</b>: {bchBalance}
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
<b>Satoshis</b>: {sats}
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default BalanceCard
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
This View allows sending and receiving of BCH
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
import React from 'react'
|
||||
import { Container, Row, Col } from 'react-bootstrap'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faCircleQuestion } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
// Local libraries
|
||||
import RefreshBchBalanceButton from './refresh-bch-balance-button'
|
||||
import SendCard from './send-card'
|
||||
import BalanceCard from './balance-card'
|
||||
import ReceiveCard from './receive-card'
|
||||
|
||||
// Working array for storing modal output.
|
||||
// this.modalBody = []
|
||||
|
||||
function BchSend ({ appData }) {
|
||||
return (
|
||||
<>
|
||||
<Container>
|
||||
<Row>
|
||||
<Col xs={6}>
|
||||
<RefreshBchBalanceButton
|
||||
appData={appData}
|
||||
/>
|
||||
</Col>
|
||||
<Col xs={6} style={{ textAlign: 'right' }}>
|
||||
<a href='https://youtu.be/KN1ZMWoLoGs' target='_blank' rel='noreferrer'>
|
||||
<FontAwesomeIcon icon={faCircleQuestion} size='lg' />
|
||||
</a>
|
||||
</Col>
|
||||
</Row>
|
||||
<br />
|
||||
|
||||
<Row>
|
||||
<Col style={{ textAlign: 'center' }}>
|
||||
<BalanceCard appData={appData} />
|
||||
</Col>
|
||||
</Row>
|
||||
<br />
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
<SendCard
|
||||
appData={appData}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<br />
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
<ReceiveCard appData={appData} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default BchSend
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
This card displays the users BCH and SLP address and QR code
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
import React, { useState } from 'react'
|
||||
import { Container, Row, Col, Card, Form } from 'react-bootstrap'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faWallet } from '@fortawesome/free-solid-svg-icons'
|
||||
import { QRCodeSVG } from 'qrcode.react'
|
||||
|
||||
const ReceiveCard = ({ appData }) => {
|
||||
const [addrSwitch, setAddrSwitch] = useState(false)
|
||||
const [displayCopyMsg, setDisplayCopyMsg] = useState(false)
|
||||
|
||||
// Determine which address to display
|
||||
const addrToDisplay = !addrSwitch
|
||||
? appData.bchWalletState.cashAddress
|
||||
: appData.bchWalletState.slpAddress
|
||||
|
||||
// Copy the selected address to the clipboard when the QR image is clicked
|
||||
const handleCopyAddress = async (value) => {
|
||||
appData.appUtil.copyToClipboard(value)
|
||||
|
||||
// Display the copied message
|
||||
setDisplayCopyMsg(true)
|
||||
|
||||
// Clear the copied message after some time
|
||||
setTimeout(() => {
|
||||
setDisplayCopyMsg(false)
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
// Event handler for address switch toggle
|
||||
const handleAddrSwitchToggle = (event) => {
|
||||
setAddrSwitch(event.target.checked)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card>
|
||||
<Card.Body style={{ textAlign: 'center' }}>
|
||||
<Card.Title>
|
||||
<h2><FontAwesomeIcon icon={faWallet} size='lg' /> Receive</h2>
|
||||
</Card.Title>
|
||||
<br />
|
||||
|
||||
<Container>
|
||||
<Row>
|
||||
<Col style={{ color: 'green', marginBottom: '20px' }}>
|
||||
{displayCopyMsg ? 'Copied' : null}
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
<QRCodeSVG
|
||||
style={{ cursor: 'pointer' }}
|
||||
className='qr-code'
|
||||
value={addrToDisplay}
|
||||
size={256}
|
||||
fgColor='#333'
|
||||
onClick={() => { handleCopyAddress(addrToDisplay) }}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col style={{ marginTop: '20px' }}>
|
||||
<p>{addrToDisplay}</p>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row>
|
||||
<Col xs={4} />
|
||||
<Col xs={4}>
|
||||
<Form>
|
||||
<Form.Check
|
||||
type='switch'
|
||||
id='address-switch'
|
||||
onChange={e => handleAddrSwitchToggle(e)}
|
||||
/>
|
||||
</Form>
|
||||
</Col>
|
||||
<Col xs={4} />
|
||||
</Row>
|
||||
</Container>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ReceiveCard
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
This library exports a RefreshBalance functional Component and a
|
||||
refreshBalance() function.
|
||||
The RefreshBalance Component is rendered as a hidden Waiting modal.
|
||||
When the refreshBalance() function is called, it causes the modal to
|
||||
appear while the wallet balance is updated. Once updated, the modal is hidden
|
||||
again.
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
import React, { useEffect, useState, useCallback } from 'react'
|
||||
|
||||
// Local libraries
|
||||
import WaitingModal from '../../waiting-modal'
|
||||
|
||||
export default function RefreshBchBalance (props) {
|
||||
// Dependency injections of props
|
||||
const { ref } = props
|
||||
|
||||
// State
|
||||
const [showWaitingModal, setShowWaitingModal] = useState(false)
|
||||
const [modalBody, setModalBody] = useState([])
|
||||
const [hideSpinner] = useState(false)
|
||||
|
||||
// Add a new line to the waiting modal.
|
||||
const addToModal = useCallback((inStr) => {
|
||||
// console.log('addToModal() inStr: ', inStr)
|
||||
setModalBody(prevBody => {
|
||||
// console.log('prevBody: ', prevBody)
|
||||
prevBody.push(inStr)
|
||||
return prevBody
|
||||
})
|
||||
}, [])
|
||||
|
||||
// Update the balance of the wallet.
|
||||
const handleRefreshBalance = useCallback(async (appData) => {
|
||||
try {
|
||||
setModalBody([])
|
||||
|
||||
// Throw up the waiting modal
|
||||
setShowWaitingModal(true)
|
||||
|
||||
addToModal('Updating wallet balance...')
|
||||
|
||||
// Get handles on app data.
|
||||
const walletState = appData.bchWalletState
|
||||
const cashAddr = appData.bchWalletState.cashAddress
|
||||
const wallet = appData.wallet
|
||||
|
||||
// Get the latest balance of the wallet.
|
||||
const newBalance = await wallet.getBalance({ bchAddress: cashAddr })
|
||||
|
||||
addToModal('Updating BCH per USD price...')
|
||||
const bchUsdPrice = await wallet.getUsd()
|
||||
|
||||
// Update the wallet state.
|
||||
walletState.bchBalance = newBalance
|
||||
walletState.bchUsdPrice = bchUsdPrice
|
||||
appData.updateBchWalletState({ walletState, appData })
|
||||
|
||||
setShowWaitingModal(false)
|
||||
setModalBody([])
|
||||
} catch (err) {
|
||||
console.error('Error while trying to update BCH balance: ', err)
|
||||
|
||||
addToModal([`Error: ${err.message}`])
|
||||
setShowWaitingModal(false)
|
||||
}
|
||||
}, [addToModal])
|
||||
|
||||
// add a ref to the handleRefreshBalance function
|
||||
// This is used to call the function from the parent component.
|
||||
useEffect(() => {
|
||||
if (ref && !ref.current) ref.current = { handleRefreshBalance }
|
||||
}, [ref, handleRefreshBalance])
|
||||
|
||||
return (
|
||||
<>
|
||||
<>
|
||||
{showWaitingModal && (
|
||||
<WaitingModal
|
||||
heading='Refreshing BCH Balance'
|
||||
body={modalBody}
|
||||
hideSpinner={hideSpinner}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
This component is displayed as a button. When clicked, it loads the
|
||||
RefreshBchBalance component, which renders a waiting modal while the wallet
|
||||
balance is refreshed.
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
import React, { useRef } from 'react'
|
||||
import { Button } from 'react-bootstrap'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faRedo } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
// Local libraries
|
||||
import RefreshBchBalance from './refresh-balance'
|
||||
|
||||
function RefreshBchBalanceButton (props) {
|
||||
// Dependency injections of props
|
||||
const appData = props.appData
|
||||
|
||||
// Child function references
|
||||
const refreshBchBalanceRef = useRef()
|
||||
|
||||
// Update the balance of the wallet.
|
||||
async function handleButtonRefreshBalance (appData) {
|
||||
// Call the child function
|
||||
refreshBchBalanceRef.current.handleRefreshBalance(appData)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button variant='success' onClick={() => { handleButtonRefreshBalance(appData) }}>
|
||||
<FontAwesomeIcon icon={faRedo} size='lg' /> Refresh
|
||||
</Button>
|
||||
|
||||
<RefreshBchBalance appData={appData} ref={refreshBchBalanceRef} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default RefreshBchBalanceButton
|
||||
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
This component controls sending of BCH.
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
import React, { useState, useRef } from 'react'
|
||||
import { Container, Row, Col, Card, Form, Button } from 'react-bootstrap'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faPaperPlane, faPaste, faRandom } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
// Local libraries
|
||||
import WaitingModal from '../../waiting-modal'
|
||||
import RefreshBchBalance from './refresh-balance'
|
||||
|
||||
function SendCard (props) {
|
||||
// Dependency injection through props
|
||||
const appData = props.appData
|
||||
|
||||
// Modal State
|
||||
const [modalBody, setModalBody] = useState([])
|
||||
const [hideSpinner, setHideSpinner] = useState(false)
|
||||
const [hideWaitingModal, setHideWaitingModal] = useState(true)
|
||||
const [hideModal, setHideModal] = useState(true)
|
||||
|
||||
// Form State
|
||||
const [bchAddr, setBchAddr] = useState('')
|
||||
const [amountStr, setAmountStr] = useState('')
|
||||
const [amountUnits, setAmountUnits] = useState('USD')
|
||||
const [oppositeUnits, setOppositeUnits] = useState('BCH')
|
||||
const [oppositeQty, setOppositeQty] = useState(0)
|
||||
|
||||
// Child function references
|
||||
const refreshBchBalanceRef = useRef()
|
||||
|
||||
// Update the balance of the wallet.
|
||||
async function handleButtonRefreshBalance (appData) {
|
||||
// Call the child function
|
||||
refreshBchBalanceRef.current.handleRefreshBalance(appData)
|
||||
}
|
||||
|
||||
// Encapsulate the state for this component into a single object that can
|
||||
// be passed around to subfunctions and subcomponents.
|
||||
const sendCardData = {
|
||||
modalBody,
|
||||
setModalBody,
|
||||
hideSpinner,
|
||||
setHideSpinner,
|
||||
hideWaitingModal,
|
||||
setHideWaitingModal,
|
||||
hideModal,
|
||||
setHideModal,
|
||||
bchAddr,
|
||||
setBchAddr,
|
||||
amountStr,
|
||||
setAmountStr,
|
||||
amountUnits,
|
||||
setAmountUnits,
|
||||
oppositeUnits,
|
||||
setOppositeUnits,
|
||||
oppositeQty,
|
||||
setOppositeQty
|
||||
}
|
||||
|
||||
// This function is called when the modal is closed.
|
||||
function onModalClose () {
|
||||
sendCardData.setHideModal(true)
|
||||
|
||||
handleButtonRefreshBalance(appData)
|
||||
}
|
||||
|
||||
async function pasteFromClipboard () {
|
||||
try {
|
||||
const addr = await appData.appUtil.readFromClipboard()
|
||||
sendCardData.setBchAddr(addr)
|
||||
} catch (err) {
|
||||
// Browser implementation. Exit quietly.
|
||||
}
|
||||
}
|
||||
|
||||
// This is an on-change event handler that updates the amount calculated in
|
||||
// both BCH and USD as the user types.
|
||||
function handleUpdateAmount (inObj = {}) {
|
||||
try {
|
||||
const { event, appData, sendCardData } = inObj
|
||||
|
||||
// Update the state of the text box.
|
||||
let amountStr = event.target.value
|
||||
sendCardData.setAmountStr(amountStr)
|
||||
if (!amountStr) amountStr = '0'
|
||||
|
||||
// Convert the string to a number.
|
||||
const amountQty = parseFloat(amountStr)
|
||||
|
||||
const bchUsdPrice = appData.bchWalletState.bchUsdPrice
|
||||
const bchjs = appData.wallet.bchjs
|
||||
|
||||
// Initialize local variables
|
||||
let oppositeQty = 0
|
||||
// const amountUsd = 0
|
||||
// const amountBch = 0
|
||||
|
||||
// Calculate the amount in the opposite units.
|
||||
const currentUnit = sendCardData.amountUnits
|
||||
if (currentUnit.includes('USD')) {
|
||||
// Convert USD to BCH
|
||||
oppositeQty = bchjs.Util.floor8(amountQty / bchUsdPrice)
|
||||
// amountUsd = amountQty
|
||||
// amountBch = oppositeQty
|
||||
} else {
|
||||
// Convert BCH to USD
|
||||
oppositeQty = bchjs.Util.floor2(amountQty * bchUsdPrice)
|
||||
// amountUsd = oppositeQty
|
||||
// amountBch = amountQty
|
||||
}
|
||||
|
||||
// Update app state
|
||||
sendCardData.setOppositeQty(oppositeQty)
|
||||
} catch (err) {
|
||||
/* exit quietly */
|
||||
console.log('Error: ', err)
|
||||
}
|
||||
}
|
||||
|
||||
// This is a click event handler that toggles the units between BCH and USD.
|
||||
function handleSwitchUnits ({ sendCardData }) {
|
||||
// Toggle the unit
|
||||
let newUnit = ''
|
||||
let oppositeUnits = ''
|
||||
const oldUnit = sendCardData.amountUnits
|
||||
if (oldUnit.includes('USD')) {
|
||||
newUnit = 'BCH'
|
||||
oppositeUnits = 'USD'
|
||||
} else {
|
||||
newUnit = 'USD'
|
||||
oppositeUnits = 'BCH'
|
||||
}
|
||||
|
||||
// Clear the Amount text box
|
||||
sendCardData.setAmountStr('')
|
||||
sendCardData.setOppositeQty(0)
|
||||
|
||||
// Persist the new units.
|
||||
sendCardData.setAmountUnits(newUnit)
|
||||
sendCardData.setOppositeUnits(oppositeUnits)
|
||||
}
|
||||
|
||||
// Add a new line to the waiting modal.
|
||||
function addToModal (inStr, sendCardData) {
|
||||
sendCardData.setModalBody(prevBody => {
|
||||
prevBody.push(inStr)
|
||||
return prevBody
|
||||
})
|
||||
}
|
||||
|
||||
// Send BCH based to the address in the form, and the amount specified in the
|
||||
// form.
|
||||
async function handleSendBch ({ sendCardData, appData }) {
|
||||
console.log('Sending BCH')
|
||||
try {
|
||||
// Clear the modal body
|
||||
sendCardData.setModalBody([])
|
||||
sendCardData.setHideSpinner(false)
|
||||
|
||||
// Open the modal
|
||||
sendCardData.setHideModal(false)
|
||||
|
||||
let amountBch
|
||||
if (sendCardData.amountUnits === 'USD') {
|
||||
amountBch = sendCardData.oppositeQty
|
||||
} else {
|
||||
amountBch = parseFloat(sendCardData.amountStr)
|
||||
}
|
||||
console.log('amountBch: ', amountBch)
|
||||
|
||||
if (amountBch < 0.00000546) throw new Error('Trying to send less than dust.')
|
||||
|
||||
let bchAddr = sendCardData.bchAddr
|
||||
let infoStr = `Sending ${amountBch} BCH ($${sendCardData.amountUsd} USD) to ${bchAddr}`
|
||||
console.log(infoStr)
|
||||
|
||||
// Update modal
|
||||
addToModal('Preparing to send bch...', sendCardData)
|
||||
|
||||
const wallet = appData.wallet
|
||||
const bchjs = wallet.bchjs
|
||||
|
||||
// If the address is an SLP address, convert it to a cash address.
|
||||
if (bchAddr.includes('simpleledger:')) {
|
||||
bchAddr = bchjs.SLP.Address.toCashAddress(bchAddr)
|
||||
}
|
||||
|
||||
// Convert the BCH to satoshis
|
||||
const sats = bchjs.BitcoinCash.toSatoshi(amountBch)
|
||||
|
||||
// Update the wallets UTXOs
|
||||
infoStr = 'Updating UTXOs...'
|
||||
console.log(infoStr)
|
||||
addToModal(infoStr, sendCardData)
|
||||
await wallet.getUtxos()
|
||||
|
||||
const receivers = [{
|
||||
address: bchAddr,
|
||||
amountSat: sats
|
||||
}]
|
||||
const txid = await wallet.send(receivers)
|
||||
|
||||
// Display TXID
|
||||
infoStr = `txid: ${txid}`
|
||||
// console.log(infoStr)
|
||||
// modalBody.push(infoStr)
|
||||
addToModal(infoStr, sendCardData)
|
||||
|
||||
// Link to block explorer
|
||||
const explorerUrl = `https://blockchair.com/bitcoin-cash/transaction/${txid}`
|
||||
const explorerLink = (<a href={`${explorerUrl}`} target='_blank' rel='noreferrer'>Block Explorer</a>)
|
||||
// modalBody.push(explorerLink)
|
||||
addToModal(explorerLink, sendCardData)
|
||||
|
||||
sendCardData.setHideSpinner(true)
|
||||
sendCardData.setBchAddr('')
|
||||
sendCardData.setAmountStr('')
|
||||
} catch (err) {
|
||||
console.log('Error in handleSendBch(): ', err)
|
||||
|
||||
sendCardData.setModalBody([`Error: ${err.message}`])
|
||||
sendCardData.setHideSpinner(true)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{
|
||||
hideModal
|
||||
? null
|
||||
: (<WaitingModal
|
||||
heading='Sending BCH'
|
||||
body={modalBody}
|
||||
hideSpinner={hideSpinner}
|
||||
closeFunc={onModalClose}
|
||||
closeModalData={{ appData, sendCardData }}
|
||||
/>)
|
||||
}
|
||||
|
||||
<RefreshBchBalance appData={appData} ref={refreshBchBalanceRef} />
|
||||
|
||||
<Card>
|
||||
<Card.Body>
|
||||
<Card.Title style={{ textAlign: 'center' }}>
|
||||
<h2><FontAwesomeIcon icon={faPaperPlane} size='lg' /> Send</h2>
|
||||
</Card.Title>
|
||||
<br />
|
||||
|
||||
<Container>
|
||||
<Row>
|
||||
<Col style={{ textAlign: 'center' }}>
|
||||
<b>BCH Address:</b>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row>
|
||||
<Col xs={12} style={{ textAlign: 'center' }}>
|
||||
<Form>
|
||||
<Form.Group controlId='formBasicEmail' style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Form.Control
|
||||
style={{ marginRight: '1rem' }}
|
||||
type='text'
|
||||
placeholder='bitcoincash:qqlrzp23w08434twmvr4fxw672whkjy0py26r63g3d'
|
||||
onChange={e => setBchAddr(e.target.value)}
|
||||
value={bchAddr}
|
||||
/>
|
||||
<FontAwesomeIcon
|
||||
style={{ cursor: 'pointer' }}
|
||||
icon={faPaste}
|
||||
size='lg'
|
||||
onClick={(e) => pasteFromClipboard()}
|
||||
/>
|
||||
</Form.Group>
|
||||
</Form>
|
||||
</Col>
|
||||
|
||||
</Row>
|
||||
<br />
|
||||
|
||||
<Row>
|
||||
<Col style={{ textAlign: 'center' }}>
|
||||
<b>Amount:</b>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row>
|
||||
<Col xs={12}>
|
||||
<Form style={{ paddingBottom: '10px' }}>
|
||||
<Form.Group controlId='formBasicEmail' style={{ textAlign: 'center' }}>
|
||||
<Form.Control
|
||||
type='number'
|
||||
onChange={(event) => handleUpdateAmount({ event, appData, sendCardData })}
|
||||
value={amountStr}
|
||||
/>
|
||||
</Form.Group>
|
||||
</Form>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col xs={6}>
|
||||
<strong>Units</strong> : {amountUnits}
|
||||
<FontAwesomeIcon
|
||||
style={{ cursor: 'pointer', marginLeft: '5px' }}
|
||||
icon={faRandom}
|
||||
size='lg'
|
||||
onClick={(e) => handleSwitchUnits({ sendCardData, appData })}
|
||||
/>
|
||||
</Col>
|
||||
<Col xs={6} style={{ textAlign: 'right' }}>
|
||||
<strong>{oppositeUnits}</strong> : {oppositeQty}
|
||||
</Col>
|
||||
</Row>
|
||||
<br />
|
||||
|
||||
<Row>
|
||||
<Col style={{ textAlign: 'center' }}>
|
||||
<Button onClick={(e) => handleSendBch({ sendCardData, appData })}>Send</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
</Container>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default SendCard
|
||||
@@ -17,6 +17,7 @@ import Placeholder2 from './placeholder2'
|
||||
import Placeholder3 from './placeholder3'
|
||||
import ServerSelectView from './servers/select-server-view'
|
||||
import SelectServerButton from './servers/select-server-button'
|
||||
import BchSend from './bch-send'
|
||||
|
||||
function AppBody (props) {
|
||||
// Dependency injection through props
|
||||
@@ -27,6 +28,7 @@ function AppBody (props) {
|
||||
<Routes>
|
||||
<Route path='/' element={<GetBalance wallet={appData.wallet} />} />
|
||||
<Route path='/balance' element={<GetBalance wallet={appData.wallet} />} />
|
||||
<Route path='/bch' element={<BchSend appData={appData} />} />
|
||||
<Route path='/wallet' element={<Wallet appData={appData} />} />
|
||||
<Route path='/placeholder2' element={<Placeholder2 />} />
|
||||
<Route path='/placeholder3' element={<Placeholder3 />} />
|
||||
|
||||
@@ -44,6 +44,13 @@ function NavMenu (props) {
|
||||
>
|
||||
Check Balance
|
||||
</NavLink>
|
||||
<NavLink
|
||||
className={(currentPath === '/bch') ? 'nav-link-active' : 'nav-link-inactive'}
|
||||
to='/bch'
|
||||
onClick={handleClickEvent}
|
||||
>
|
||||
BCH
|
||||
</NavLink>
|
||||
<NavLink
|
||||
className={currentPath === '/wallet' ? 'nav-link-active' : 'nav-link-inactive'}
|
||||
to='/wallet'
|
||||
|
||||
Reference in New Issue
Block a user