Merge branch 'master' into dh-redux-devtool

This commit is contained in:
Chris Troutner
2021-05-21 05:51:20 -07:00
11 changed files with 252 additions and 195 deletions
+2
View File
@@ -19,3 +19,5 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
Have Fun!
+1 -1
View File
@@ -46,7 +46,7 @@ class JsonWebTokens extends React.Component {
id='jwt' id='jwt'
name='JWT' name='JWT'
placeholder='Enter FullStack.cash JWT' placeholder='Enter FullStack.cash JWT'
label='FullStack.cash JWT' label='Enter FullStack.cash JWT to increase rate limits.'
labelPosition='above' labelPosition='above'
onChange={_this.handleUpdate} onChange={_this.handleUpdate}
/> />
@@ -211,13 +211,11 @@ class Send extends React.Component {
async getMaxAmount () { async getMaxAmount () {
try { try {
const bchWalletLib = _this.props.bchWallet const bchWalletLib = _this.props.bchWallet
// Ensure the wallet UTXOs are up-to-date. // Ensure the wallet UTXOs are up-to-date.
const walletAddr = bchWalletLib.walletInfo.address const walletAddr = bchWalletLib.walletInfo.address
await bchWalletLib.utxos.initUtxoStore(walletAddr) await bchWalletLib.utxos.initUtxoStore(walletAddr)
const utxos = bchWalletLib.utxos.bchUtxos const utxos = bchWalletLib.utxos.utxoStore.bchUtxos
if (!utxos.length) { if (!utxos.length) {
throw new Error('No BCH Utxos to spend!') throw new Error('No BCH Utxos to spend!')
} }
+22 -13
View File
@@ -91,17 +91,21 @@ class Tokens extends React.Component {
<> <>
<Row> <Row>
{_this.state.tokens.map((val, i) => { {_this.state.tokens.map((val, i) => {
return ( if (val.qty > 0) {
<Col sm={4} key={`token-${i}`}> return (
<TokenCard <Col sm={4} key={`token-${i}`}>
key={`token-${i}`} <TokenCard
id={`token-${i}`} key={`token-${i}`}
token={val} id={`token-${i}`}
showToken={_this.showToken} token={val}
selectToken={_this.selectToken} showToken={_this.showToken}
/> selectToken={_this.selectToken}
</Col> />
) </Col>
)
} else {
return <span key={`token-${i}`} />
}
})} })}
</Row> </Row>
</> </>
@@ -109,6 +113,7 @@ class Tokens extends React.Component {
</Content> </Content>
)} )}
<TokenModal <TokenModal
bchWallet={_this.props.bchWallet}
token={ token={
_this.state.selectedTokenToView _this.state.selectedTokenToView
? _this.state.selectedTokenToView ? _this.state.selectedTokenToView
@@ -116,6 +121,7 @@ class Tokens extends React.Component {
} }
handleOnHide={_this.onHandleToggleModal} handleOnHide={_this.onHandleToggleModal}
show={_this.state.showModal} show={_this.state.showModal}
explorerURL={_this.state.explorerURL}
/> />
</> </>
) )
@@ -198,10 +204,13 @@ class Tokens extends React.Component {
ele.scrollIntoView({ behavior: 'smooth' }) ele.scrollIntoView({ behavior: 'smooth' })
} }
onHandleToggleModal () { onHandleToggleModal (refresh = null) {
_this.setState({ _this.setState({
showModal: !_this.state.showModal showModal: !_this.state.showModal
}) })
if (refresh) {
_this.handleGetTokens(true)
}
} }
handleError (error) { handleError (error) {
@@ -247,7 +256,7 @@ class Tokens extends React.Component {
let explorerURL let explorerURL
if (bchjs.restURL.includes('abc.fullstack')) { if (bchjs.restURL.includes('abc.fullstack')) {
explorerURL = 'https://explorer.bitcoinabc.org/tx' explorerURL = 'https://explorer.be.cash/tx'
} else { } else {
explorerURL = 'https://explorer.bitcoin.com/bch/tx' explorerURL = 'https://explorer.bitcoin.com/bch/tx'
} }
+15 -7
View File
@@ -139,10 +139,23 @@ class SendTokens extends React.Component {
// console.log(_this.state) // console.log(_this.state)
} }
// Sets the qty property with
// the corresponding token decimals
floorQty (qty, decimals) {
try {
const a = qty * Math.pow(10, decimals)
const b = Math.floor(a)
const result = b / Math.pow(10, decimals)
return result
} catch (error) {
console.warn(error)
}
}
async handleSendMax () { async handleSendMax () {
const { qty } = _this.props.selectedToken const { qty, decimals } = _this.props.selectedToken
_this.setState({ _this.setState({
amountSat: qty amountSat: _this.floorQty(qty, decimals)
}) })
} }
@@ -182,11 +195,6 @@ class SendTokens extends React.Component {
const walletAddr = bchWalletLib.walletInfo.address const walletAddr = bchWalletLib.walletInfo.address
await bchWalletLib.utxos.initUtxoStore(walletAddr) await bchWalletLib.utxos.initUtxoStore(walletAddr)
// For some reason, the utxo categories do not get populated, so we have
// to do it manually.
bchWalletLib.utxos.bchUtxos = await bchWalletLib.utxos.getBchUtxos()
bchWalletLib.utxos.tokenUtxos = await bchWalletLib.utxos.getTokenUtxos()
// Used for debugging. // Used for debugging.
// console.log(`receiver: ${JSON.stringify(receiver, null, 2)}`) // console.log(`receiver: ${JSON.stringify(receiver, null, 2)}`)
// console.log( // console.log(
@@ -13,7 +13,7 @@ class TokenCard extends React.Component {
render () { render () {
const token = _this.props.token const token = _this.props.token
console.log(`token: ${JSON.stringify(token, null, 2)}`) // console.log(`token: ${JSON.stringify(token, null, 2)}`)
return ( return (
<> <>
+194 -134
View File
@@ -11,7 +11,10 @@ class TokenModal extends React.Component {
_this = this _this = this
this.state = { this.state = {
copySuccess: '', copySuccess: '',
isBurnView: false isBurnView: false,
txId: '',
errMsg: '',
inFetch: false
} }
this.modalFooter = ( this.modalFooter = (
@@ -36,6 +39,15 @@ class TokenModal extends React.Component {
/> />
</> </>
) )
this.onDoneFooter = (
<>
<Button
text='Close'
pullLeft
onClick={() => this.handleBurnAll(false)}
/>
</>
)
} }
render () { render () {
@@ -46,7 +58,11 @@ class TokenModal extends React.Component {
title={_this.state.isBurnView ? `Burn All ${token.name}` : token.name} title={_this.state.isBurnView ? `Burn All ${token.name}` : token.name}
modal modal
modalFooter={ modalFooter={
_this.state.isBurnView ? this.burnFooter : this.modalFooter !_this.state.isBurnView
? this.modalFooter
: _this.state.txId || _this.state.errMsg
? this.onDoneFooter
: this.burnFooter
} }
show={_this.props.show} show={_this.props.show}
modalCloseButton modalCloseButton
@@ -54,138 +70,159 @@ class TokenModal extends React.Component {
> >
<Row> <Row>
<Col sm={12}> <Col sm={12}>
{_this.state.isBurnView {_this.state.isBurnView && !_this.state.txId && (
? ( <Box loaded={!_this.state.inFetch} className='border-none'>
<p> <p>
Are you sure you want to burn {`${token.qty} `} Are you sure you want to burn {`${token.qty} `}
tokens? tokens?
</p> </p>
) </Box>
: ( )}
<Box className=' border-none '>
<Row> {_this.state.isBurnView && _this.state.txId && (
<Col <div className='text-center '>
sm={12} <p>Transaction ID:</p>
className='text-center tokenModal-info-container' <a
> target='_blank'
<Row className='tokenModal-info-content mt-1 text-left'> rel='noopener noreferrer'
<Col xs={12}> href={`${_this.props.explorerURL}/${_this.state.txId}`}
<Row> >
<Col xs={12} sm={3}> {_this.state.txId}
<b>TokenId:</b> </a>
</Col> </div>
<Col xs={9} sm={7}> )}
{token.tokenId}
</Col> {_this.state.isBurnView && _this.state.errMsg && (
<Col <div className='text-center'>
xs={3} <p className='error-color'> {_this.state.errMsg}</p>
sm={2} </div>
className={ )}
{!_this.state.isBurnView && (
<Box className=' border-none '>
<Row>
<Col
sm={12}
className='text-center tokenModal-info-container'
>
<Row className='tokenModal-info-content mt-1 text-left'>
<Col xs={12}>
<Row>
<Col xs={12} sm={3}>
<b>TokenId:</b>
</Col>
<Col xs={9} sm={7}>
{token.tokenId}
</Col>
<Col
xs={3}
sm={2}
className={
_this.state.copySuccess ? 'nopadding' : '' _this.state.copySuccess ? 'nopadding' : ''
} }
>
{_this.state.copySuccess === 'tokenId'
? (
<div className='copied-text'>
<span>Copied!</span>
</div>
)
: (
<FontAwesomeIcon
className='icon btn-animation'
style={{ cssFloat: 'right' }}
size='lg'
onClick={() =>
_this.copyToClipBoard('tokenId')}
icon='copy'
/>
)}
</Col>
</Row>
</Col>
</Row>
<Row className='tokenModal-info-content mt-1 text-left'>
<Col xs={12}>
<Row>
<Col xs={12} sm={3}>
<b>URL:</b>
</Col>
<Col xs={12} sm={9}>
<a
href={`http://${token.url}`}
target='_blank'
rel='noopener noreferrer'
> >
{_this.state.copySuccess === 'tokenId' {token.url}
? ( </a>
<div className='copied-text'> </Col>
<span>Copied!</span> </Row>
</div> </Col>
) </Row>
: ( <Row className='tokenModal-info-content mt-1 text-left'>
<FontAwesomeIcon <Col xs={12}>
className='icon btn-animation' <Row>
style={{ cssFloat: 'right' }} <Col xs={12} sm={3}>
size='lg' <b>Ticker:</b>
onClick={() => </Col>
_this.copyToClipBoard('tokenId')} <Col xs={12} sm={9}>
icon='copy' {token.ticker}
/> </Col>
)} </Row>
</Col> </Col>
</Row> </Row>
</Col> <Row className='tokenModal-info-content mt-1 text-left'>
</Row> <Col xs={12}>
<Row className='tokenModal-info-content mt-1 text-left'> <Row>
<Col xs={12}> <Col xs={12} sm={3}>
<Row> <b>Name:</b>
<Col xs={12} sm={3}> </Col>
<b>URL:</b> <Col xs={12} sm={9}>
</Col> {token.name}
<Col xs={12} sm={9}> </Col>
<a </Row>
href={`http://${token.url}`} </Col>
target='_blank' </Row>
rel='noopener noreferrer' <Row className='tokenModal-info-content mt-1 text-left'>
> <Col xs={12}>
{token.url} <Row>
</a> <Col xs={12} sm={3}>
</Col> <b>Balance:</b>
</Row> </Col>
</Col> <Col xs={12} sm={9}>
</Row> {token.qty}
<Row className='tokenModal-info-content mt-1 text-left'> </Col>
<Col xs={12}> </Row>
<Row> </Col>
<Col xs={12} sm={3}> </Row>
<b>Ticker:</b> <Row className='tokenModal-info-content mt-1 text-left'>
</Col> <Col xs={12}>
<Col xs={12} sm={9}> <Row>
{token.ticker} <Col xs={12} sm={3}>
</Col> <b>Decimals:</b>
</Row> </Col>
</Col> <Col xs={12} sm={9}>
</Row> {token.decimals}
<Row className='tokenModal-info-content mt-1 text-left'> </Col>
<Col xs={12}> </Row>
<Row> </Col>
<Col xs={12} sm={3}> </Row>
<b>Name:</b> <Row className='tokenModal-info-content mt-1 text-left'>
</Col> <Col xs={12}>
<Col xs={12} sm={9}> <Row>
{token.name} <Col xs={12} sm={3}>
</Col> <b>TokenType:</b>
</Row> </Col>
</Col> <Col xs={12} sm={9}>
</Row> {token.tokenType}
<Row className='tokenModal-info-content mt-1 text-left'> </Col>
<Col xs={12}> </Row>
<Row> </Col>
<Col xs={12} sm={3}> </Row>
<b>Balance:</b> </Col>
</Col> </Row>
<Col xs={12} sm={9}> </Box>
{token.qty} )}
</Col>
</Row>
</Col>
</Row>
<Row className='tokenModal-info-content mt-1 text-left'>
<Col xs={12}>
<Row>
<Col xs={12} sm={3}>
<b>Decimals:</b>
</Col>
<Col xs={12} sm={9}>
{token.decimals}
</Col>
</Row>
</Col>
</Row>
<Row className='tokenModal-info-content mt-1 text-left'>
<Col xs={12}>
<Row>
<Col xs={12} sm={3}>
<b>TokenType:</b>
</Col>
<Col xs={12} sm={9}>
{token.tokenType}
</Col>
</Row>
</Col>
</Row>
</Col>
</Row>
</Box>
)}
</Col> </Col>
</Row> </Row>
</Content> </Content>
@@ -224,19 +261,24 @@ class TokenModal extends React.Component {
} }
handleModal () { handleModal () {
_this.props.handleOnHide() // if txId exist refresh tokens on close
_this.props.handleOnHide(_this.state.txId)
setTimeout(() => { setTimeout(() => {
_this.setState({ _this.setState({
isBurnView: false isBurnView: false,
txId: '',
errMsg: '',
inFetch: false
}) })
}, 200) }, 200)
} }
handleBurnAll (isConfirmed) { async handleBurnAll (isConfirmed) {
try { try {
// Dismiss // Dismiss
if (!isConfirmed) { if (!isConfirmed) {
_this.hideModal() _this.handleModal()
return return
} }
@@ -244,13 +286,31 @@ class TokenModal extends React.Component {
* BURN ALL * BURN ALL
* *
*/ */
console.log('Hello Word') _this.setState({
} catch (error) {} inFetch: true
})
const { bchWallet, token } = _this.props
const result = await bchWallet.burnAll(token.tokenId)
console.log('burn txid: ', result)
_this.setState({
txId: result,
inFetch: false
})
} catch (error) {
console.warn(error)
_this.setState({
errMsg: error.message,
inFetch: false
})
}
} }
} }
TokenModal.propTypes = { TokenModal.propTypes = {
token: PropTypes.object.isRequired, token: PropTypes.object.isRequired,
show: PropTypes.bool.isRequired, show: PropTypes.bool.isRequired,
handleOnHide: PropTypes.func.isRequired handleOnHide: PropTypes.func.isRequired,
bchWallet: PropTypes.object, // get minimal-slp-wallet instance
explorerURL: PropTypes.string
} }
export default TokenModal export default TokenModal
+4 -7
View File
@@ -4,10 +4,7 @@ import { Row, Col, Box, Button, Inputs } from 'adminlte-2-react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
// import BchWallet from 'minimal-slp-wallet' // import BchWallet from 'minimal-slp-wallet'
const BchWallet = const BchWallet = typeof window !== 'undefined' ? window.SlpWallet : null
typeof window !== 'undefined'
? window.SlpWallet
: null
const { Text } = Inputs const { Text } = Inputs
@@ -50,7 +47,7 @@ class ImportWallet extends React.Component {
<Col sm={12} className='text-center mt-2 mb-2'> <Col sm={12} className='text-center mt-2 mb-2'>
<Row className='flex justify-content-center'> <Row className='flex justify-content-center'>
<Col sm={8}> <Col sm={8}>
<div> <form autoComplete='off'>
<Text <Text
id='import-mnemonic' id='import-mnemonic'
name='mnemonic' name='mnemonic'
@@ -59,7 +56,7 @@ class ImportWallet extends React.Component {
labelPosition='above' labelPosition='above'
onChange={_this.handleUpdate} onChange={_this.handleUpdate}
/> />
</div> </form>
</Col> </Col>
</Row> </Row>
</Col> </Col>
@@ -151,7 +148,7 @@ class ImportWallet extends React.Component {
let currentRate let currentRate
if (bchjs.restURL.includes('abc.fullstack')) { if (bchjs.restURL.includes('abc.fullstack')) {
currentRate = await bchjs.Price.getBchaUsd() * 100 currentRate = (await bchjs.Price.getBchaUsd()) * 100
} else { } else {
// BCHN price. // BCHN price.
currentRate = (await bchjs.Price.getUsd()) * 100 currentRate = (await bchjs.Price.getUsd()) * 100
@@ -147,29 +147,6 @@ class WalletInfo extends React.Component {
)} )}
</Col> </Col>
</Row> </Row>
<Row className='wallet-info-content mt-1 text-left'>
<Col xs={8} sm={9} lg={10}>
<span>
<b>Address: </b> {_this.state.address}
</span>
</Col>
<Col xs={4} sm={3} lg={2} className='text-right'>
{_this.state.copySuccess === 'address'
? (
<div className='copied-text'>
<span>Copied!</span>
</div>
)
: (
<FontAwesomeIcon
className='icon btn-animation'
size='lg'
onClick={() => _this.copyToClipBoard('address')}
icon='copy'
/>
)}
</Col>
</Row>
<Row className='wallet-info-content mt-1 text-left'> <Row className='wallet-info-content mt-1 text-left'>
<Col xs={8} sm={9} lg={10}> <Col xs={8} sm={9} lg={10}>
<span> <span>
+1 -1
View File
@@ -51,7 +51,7 @@ class Footer extends React.Component {
this.setState({ this.setState({
ipfsHash: hash, ipfsHash: hash,
ipfsHashLink: `https://ipfs-gateway.fullstack.cash/ipfs/${hash}` ipfsHashLink: `https://ipfs.io/ipfs/${hash}`
}) })
} }
+11 -5
View File
@@ -3,17 +3,23 @@ import React from 'react'
class VersionStatus extends React.Component { class VersionStatus extends React.Component {
constructor (props) { constructor (props) {
super(props) super(props)
this.state = {} this.state = {
show: false
}
} }
render () { render () {
return ( return (
<> <>
<div className='version-status'> {this.state.show && (
<div> <div className='version-status'>
<p><b>Warning: Open Alpha - things will break</b></p> <div>
<p>
<b>Warning: Open Beta - this app is under active develpment.</b>
</p>
</div>
</div> </div>
</div> )}
</> </>
) )
} }