2021-03-25 12:24:49 +01:00
// @flow
import React from 'react' ;
import Button from 'component/button' ;
import { FormField , Form } from 'component/common/form' ;
import { Lbryio } from 'lbryinc' ;
import Card from 'component/common/card' ;
import LbcSymbol from 'component/common/lbc-symbol' ;
import Spinner from 'component/spinner' ;
import Nag from 'component/common/nag' ;
import CopyableText from 'component/copyableText' ;
2021-04-08 11:02:37 +02:00
import Icon from 'component/common/icon' ;
2021-03-25 12:24:49 +01:00
import QRCode from 'component/common/qr-code' ;
import usePersistedState from 'effects/use-persisted-state' ;
import * as ICONS from 'constants/icons' ;
import * as MODALS from 'constants/modal_types' ;
2021-04-05 07:12:19 +02:00
import * as PAGES from 'constants/pages' ;
2021-03-25 12:24:49 +01:00
import { clipboard } from 'electron' ;
import I18nMessage from 'component/i18nMessage' ;
2021-04-05 07:12:19 +02:00
import { Redirect , useHistory } from 'react-router' ;
2021-03-25 12:24:49 +01:00
2021-04-08 11:02:37 +02:00
const ENABLE _ALTERNATIVE _COINS = true ;
2021-03-25 12:24:49 +01:00
2021-04-08 11:02:37 +02:00
const BTC _SATOSHIS = 100000000 ;
const LBC _MAX = 21000000 ;
const LBC _MIN = 1 ;
2021-03-25 12:24:49 +01:00
const IS _DEV = process . env . NODE _ENV !== 'production' ;
const DEBOUNCE _BTC _CHANGE _MS = 400 ;
const INTERNAL _APIS _DOWN = 'internal_apis_down' ;
2021-04-08 11:02:37 +02:00
const BTC _API _STATUS _PENDING = 'NEW' ; // Started swap, waiting for coin.
const BTC _API _STATUS _CONFIRMING = 'PENDING' ; // Coin receiving, waiting confirmation.
const BTC _API _STATUS _PROCESSING = 'COMPLETED' ; // Coin confirmed. Sending LBC.
const BTC _API _STATUS _UNRESOLVED = 'UNRESOLVED' ; // Underpaid, overpaid, etc.
const BTC _API _STATUS _EXPIRED = 'EXPIRED' ; // Charge expired (60 minutes).
2021-03-25 12:24:49 +01:00
const BTC _API _STATUS _ERROR = 'Error' ;
const ACTION _MAIN = 'action_main' ;
const ACTION _STATUS _PENDING = 'action_pending' ;
2021-04-05 08:07:37 +02:00
const ACTION _STATUS _CONFIRMING = 'action_confirming' ;
2021-03-25 12:24:49 +01:00
const ACTION _STATUS _PROCESSING = 'action_processing' ;
const ACTION _STATUS _SUCCESS = 'action_success' ;
const ACTION _PAST _SWAPS = 'action_past_swaps' ;
2021-04-08 11:02:37 +02:00
const NAG _API _STATUS _PENDING = 'Waiting to receive your crypto.' ;
const NAG _API _STATUS _CONFIRMING = 'Confirming transaction.' ;
const NAG _API _STATUS _PROCESSING = 'Crypto received. Sending your Credits.' ;
const NAG _API _STATUS _SUCCESS = 'Credits sent. You should see it in your wallet.' ;
2021-03-25 12:24:49 +01:00
const NAG _API _STATUS _ERROR = 'An error occurred on the previous swap.' ;
const NAG _SWAP _CALL _FAILED = 'Failed to initiate swap.' ;
// const NAG_STATUS_CALL_FAILED = 'Failed to query swap status.';
const NAG _SERVER _DOWN = 'The system is currently down. Come back later.' ;
const NAG _RATE _CALL _FAILED = 'Unable to obtain exchange rate. Try again later.' ;
2021-04-08 11:02:37 +02:00
const NAG _EXPIRED = 'Swap expired.' ;
2021-03-25 12:24:49 +01:00
type Props = {
receiveAddress : string ,
2021-04-04 09:10:55 +02:00
coinSwaps : Array < CoinSwapInfo > ,
2021-04-05 07:12:19 +02:00
isAuthenticated : boolean ,
2021-03-25 12:24:49 +01:00
doToast : ( { message : string } ) => void ,
2021-04-04 09:10:55 +02:00
addCoinSwap : ( CoinSwapInfo ) => void ,
2021-09-11 17:02:34 +02:00
removeCoinSwap : ( string ) => void ,
2021-03-25 12:24:49 +01:00
getNewAddress : ( ) => void ,
checkAddressIsMine : ( string ) => void ,
openModal : ( string , { } ) => void ,
2021-04-08 11:02:37 +02:00
queryCoinSwapStatus : ( string ) => void ,
2021-03-25 12:24:49 +01:00
} ;
function WalletSwap ( props : Props ) {
2021-04-05 07:12:19 +02:00
const {
receiveAddress ,
doToast ,
coinSwaps ,
isAuthenticated ,
addCoinSwap ,
2021-09-11 17:02:34 +02:00
removeCoinSwap ,
2021-04-05 07:12:19 +02:00
getNewAddress ,
checkAddressIsMine ,
openModal ,
2021-04-08 11:02:37 +02:00
queryCoinSwapStatus ,
2021-04-05 07:12:19 +02:00
} = props ;
2021-03-25 12:24:49 +01:00
2021-04-09 05:11:57 +02:00
const [ btc , setBtc ] = React . useState ( 0 ) ;
const [ lbcError , setLbcError ] = React . useState ( ) ;
const [ lbc , setLbc ] = usePersistedState ( 'swap-desired-lbc' , LBC _MIN ) ;
2021-03-25 12:24:49 +01:00
const [ action , setAction ] = React . useState ( ACTION _MAIN ) ;
const [ nag , setNag ] = React . useState ( null ) ;
const [ showQr , setShowQr ] = React . useState ( false ) ;
const [ isFetchingRate , setIsFetchingRate ] = React . useState ( false ) ;
const [ isSwapping , setIsSwapping ] = React . useState ( false ) ;
const [ isRefreshingStatus , setIsRefreshingStatus ] = React . useState ( false ) ;
2021-04-05 07:12:19 +02:00
const { location } = useHistory ( ) ;
2021-04-08 11:02:37 +02:00
const [ swap , setSwap ] = React . useState ( { } ) ;
const [ coin , setCoin ] = React . useState ( 'bitcoin' ) ;
const [ lastStatusQuery , setLastStatusQuery ] = React . useState ( ) ;
const { goBack } = useHistory ( ) ;
2021-03-25 12:24:49 +01:00
2021-04-09 05:11:57 +02:00
function formatCoinAmountString ( amount ) {
return amount === 0 ? '---' : amount . toLocaleString ( undefined , { minimumFractionDigits : 8 } ) ;
2021-03-25 12:24:49 +01:00
}
function returnToMainAction ( ) {
setIsSwapping ( false ) ;
setAction ( ACTION _MAIN ) ;
2021-04-08 11:02:37 +02:00
setSwap ( null ) ;
2021-03-25 12:24:49 +01:00
}
2021-09-11 17:02:34 +02:00
function handleRemoveSwap ( chargeCode ) {
openModal ( MODALS . CONFIRM , {
title : _ _ ( 'Remove Swap' ) ,
subtitle : < I18nMessage tokens = { { address : < em > { ` ${ chargeCode } ` } < / em > } } > Remove % address % ? < / I18nMessage > ,
body : < p className = "help--warning" > { _ _ ( 'This process cannot be reversed.' ) } < / p > ,
onConfirm : ( closeModal ) => {
removeCoinSwap ( chargeCode ) ;
closeModal ( ) ;
} ,
2021-03-25 12:24:49 +01:00
} ) ;
}
// Ensure 'receiveAddress' is populated
React . useEffect ( ( ) => {
if ( ! receiveAddress ) {
getNewAddress ( ) ;
} else {
checkAddressIsMine ( receiveAddress ) ;
}
} , [ receiveAddress , getNewAddress , checkAddressIsMine ] ) ;
2021-04-09 05:11:57 +02:00
// Get 'btc/rate' and calculate required BTC.
2021-03-25 12:24:49 +01:00
React . useEffect ( ( ) => {
2021-04-09 05:11:57 +02:00
if ( isNaN ( lbc ) || lbc === 0 ) {
setBtc ( 0 ) ;
2021-03-25 12:24:49 +01:00
return ;
}
setIsFetchingRate ( true ) ;
const timer = setTimeout ( ( ) => {
Lbryio . call ( 'btc' , 'rate' , { satoshi : BTC _SATOSHIS } )
2021-04-08 11:02:37 +02:00
. then ( ( rate ) => {
2021-03-25 12:24:49 +01:00
setIsFetchingRate ( false ) ;
2021-04-09 05:11:57 +02:00
setBtc ( ( lbc * Math . round ( BTC _SATOSHIS * rate ) ) / BTC _SATOSHIS ) ;
2021-03-25 12:24:49 +01:00
} )
2021-04-08 11:02:37 +02:00
. catch ( ( ) => {
2021-03-25 12:24:49 +01:00
setIsFetchingRate ( false ) ;
2021-04-09 05:11:57 +02:00
setBtc ( 0 ) ;
2021-03-25 12:24:49 +01:00
setNag ( { msg : NAG _RATE _CALL _FAILED , type : 'error' } ) ;
} ) ;
} , DEBOUNCE _BTC _CHANGE _MS ) ;
return ( ) => clearTimeout ( timer ) ;
2021-04-09 05:11:57 +02:00
} , [ lbc ] ) ;
2021-03-25 12:24:49 +01:00
2021-04-08 11:02:37 +02:00
// Resolve 'swap' with the latest info from 'coinSwaps'
2021-03-25 12:24:49 +01:00
React . useEffect ( ( ) => {
2021-04-08 11:02:37 +02:00
const swapInfo = swap && coinSwaps . find ( ( x ) => x . chargeCode === swap . chargeCode ) ;
if ( ! swapInfo ) {
return ;
2021-03-25 12:24:49 +01:00
}
2021-04-08 11:02:37 +02:00
const jsonSwap = JSON . stringify ( swap ) ;
const jsonSwapInfo = JSON . stringify ( swapInfo ) ;
if ( jsonSwap !== jsonSwapInfo ) {
setSwap ( { ... swapInfo } ) ;
2021-03-25 12:24:49 +01:00
}
2021-04-08 11:02:37 +02:00
if ( ! swapInfo . status ) {
return ;
}
switch ( swapInfo . status . status ) {
case BTC _API _STATUS _PENDING :
setAction ( ACTION _STATUS _PENDING ) ;
setNag ( { msg : NAG _API _STATUS _PENDING , type : 'helpful' } ) ;
break ;
case BTC _API _STATUS _CONFIRMING :
setAction ( ACTION _STATUS _CONFIRMING ) ;
setNag ( { msg : NAG _API _STATUS _CONFIRMING , type : 'helpful' } ) ;
break ;
case BTC _API _STATUS _PROCESSING :
if ( swapInfo . status . lbcTxid ) {
setAction ( ACTION _STATUS _SUCCESS ) ;
setNag ( { msg : NAG _API _STATUS _SUCCESS , type : 'helpful' } ) ;
setIsSwapping ( false ) ;
} else {
setAction ( ACTION _STATUS _PROCESSING ) ;
setNag ( { msg : NAG _API _STATUS _PROCESSING , type : 'helpful' } ) ;
}
break ;
case BTC _API _STATUS _ERROR :
setNag ( { msg : NAG _API _STATUS _ERROR , type : 'error' } ) ;
break ;
case INTERNAL _APIS _DOWN :
setNag ( { msg : NAG _SERVER _DOWN , type : 'error' } ) ;
break ;
case BTC _API _STATUS _EXPIRED :
setNag ( { msg : NAG _EXPIRED , type : 'error' } ) ;
if ( action === ACTION _PAST _SWAPS ) {
setAction ( ACTION _STATUS _PENDING ) ;
}
break ;
case BTC _API _STATUS _UNRESOLVED :
setNag ( {
msg : _ _ (
'Received amount did not match order code %chargeCode%. Contact hello@lbry.com to resolve the payment.' ,
{ chargeCode : swapInfo . chargeCode }
) ,
type : 'error' ,
} ) ;
if ( action === ACTION _PAST _SWAPS ) {
setAction ( ACTION _STATUS _PENDING ) ;
}
break ;
default :
setNag ( { msg : swapInfo . status . status , type : 'error' } ) ;
break ;
}
} , [ swap , coinSwaps ] ) ;
2021-03-25 12:24:49 +01:00
2021-04-09 05:11:57 +02:00
// Validate entered LBC
2021-03-25 12:24:49 +01:00
React . useEffect ( ( ) => {
let msg ;
2021-04-09 05:11:57 +02:00
if ( lbc < LBC _MIN ) {
msg = _ _ ( 'The amount needs to be higher' ) ;
} else if ( lbc > LBC _MAX ) {
msg = _ _ ( 'The amount is too high' ) ;
2021-03-25 12:24:49 +01:00
}
2021-04-09 05:11:57 +02:00
setLbcError ( msg ) ;
} , [ lbc ] ) ;
2021-03-25 12:24:49 +01:00
// 'Refresh' button feedback
React . useEffect ( ( ) => {
let timer ;
if ( isRefreshingStatus ) {
timer = setTimeout ( ( ) => {
setIsRefreshingStatus ( false ) ;
} , 1000 ) ;
}
return ( ) => clearTimeout ( timer ) ;
} , [ isRefreshingStatus ] ) ;
2021-04-08 11:02:37 +02:00
function getCoinAddress ( coin ) {
if ( swap && swap . sendAddresses ) {
return swap . sendAddresses [ coin ] ;
}
return '' ;
}
function getCoinSendAmountStr ( coin ) {
if ( swap && swap . sendAmounts && swap . sendAmounts [ coin ] ) {
return ` ${ swap . sendAmounts [ coin ] . amount } ${ swap . sendAmounts [ coin ] . currency } ` ;
}
return '' ;
}
function currencyToCoin ( currency ) {
const MAP = {
DAI : 'dai' ,
USDC : 'usdc' ,
BTC : 'bitcoin' ,
ETH : 'ethereum' ,
LTC : 'litecoin' ,
BCH : 'bitcoincash' ,
} ;
return MAP [ currency ] || 'bitcoin' ;
}
function getSentAmountStr ( swapInfo ) {
if ( swapInfo && swapInfo . status ) {
const currency = swapInfo . status . receiptCurrency ;
const coin = currencyToCoin ( currency ) ;
return getCoinSendAmountStr ( coin ) ;
}
return '' ;
}
function getCoinLabel ( coin ) {
const COIN _LABEL = {
dai : 'Dai' ,
usdc : 'USD Coin' ,
bitcoin : 'Bitcoin' ,
ethereum : 'Ethereum' ,
litecoin : 'Litecoin' ,
bitcoincash : 'Bitcoin Cash' ,
} ;
return COIN _LABEL [ coin ] || coin ;
}
function getLbcAmountStrForSwap ( swap ) {
if ( swap && swap . lbcAmount ) {
2021-04-09 05:11:57 +02:00
return formatCoinAmountString ( swap . lbcAmount ) ;
2021-04-08 11:02:37 +02:00
}
return '---' ;
}
2021-03-25 12:24:49 +01:00
function handleStartSwap ( ) {
setIsSwapping ( true ) ;
2021-04-08 11:02:37 +02:00
setSwap ( null ) ;
2021-03-25 12:24:49 +01:00
setNag ( null ) ;
Lbryio . call ( 'btc' , 'swap' , {
2021-04-09 05:11:57 +02:00
lbc _satoshi _requested : parseInt ( lbc * BTC _SATOSHIS + 0.5 ) ,
btc _satoshi _provided : parseInt ( btc * BTC _SATOSHIS + 0.5 ) ,
2021-03-25 12:24:49 +01:00
pay _to _wallet _address : receiveAddress ,
} )
2021-04-08 11:02:37 +02:00
. then ( ( response ) => {
2021-04-09 05:11:57 +02:00
const btcAmount = response . Charge . data . pricing [ 'bitcoin' ] . amount ;
const rate = response . Exchange . rate ;
2021-05-04 17:08:01 +02:00
const timeline = response . Charge . data . timeline ;
const lastTimeline = timeline [ timeline . length - 1 ] ;
const newSwap = {
2021-04-08 11:02:37 +02:00
chargeCode : response . Exchange . charge _code ,
coins : Object . keys ( response . Charge . data . addresses ) ,
sendAddresses : response . Charge . data . addresses ,
sendAmounts : response . Charge . data . pricing ,
2021-04-09 05:11:57 +02:00
lbcAmount : ( btcAmount * BTC _SATOSHIS ) / rate ,
2021-05-04 17:08:01 +02:00
status : {
status : lastTimeline . status ,
receiptCurrency : lastTimeline . payment . value . currency ,
receiptTxid : lastTimeline . payment . transaction _id ,
lbcTxid : response . Exchange . lbc _txid || '' ,
} ,
2021-04-08 11:02:37 +02:00
} ;
2021-05-04 17:08:01 +02:00
setSwap ( { ... newSwap } ) ;
addCoinSwap ( { ... newSwap } ) ;
2021-03-25 12:24:49 +01:00
} )
. catch ( ( err ) => {
2021-05-15 08:01:00 +02:00
const translateError = ( err ) => {
// TODO: https://github.com/lbryio/lbry.go/issues/87
// Translate error codes instead of strings when it is available.
if ( err === 'users are currently limited to 4 transactions per month' ) {
return _ _ ( 'Users are currently limited to 4 completed swaps per month or 5 pending swaps.' ) ;
}
return err ;
} ;
setNag ( { msg : err === INTERNAL _APIS _DOWN ? NAG _SWAP _CALL _FAILED : translateError ( err . message ) , type : 'error' } ) ;
2021-03-25 12:24:49 +01:00
returnToMainAction ( ) ;
} ) ;
}
function handleViewPastSwaps ( ) {
setAction ( ACTION _PAST _SWAPS ) ;
setNag ( null ) ;
setIsRefreshingStatus ( true ) ;
2021-04-08 11:02:37 +02:00
const now = Date . now ( ) ;
if ( ! lastStatusQuery || now - lastStatusQuery > 30000 ) {
// There is a '200/minute' limit in the commerce API. If the history is
// long, or if the user goes trigger-happy, the limit could be reached
// easily. Statuses don't change often, so just limit it to every 30s.
setLastStatusQuery ( now ) ;
coinSwaps . forEach ( ( x ) => {
queryCoinSwapStatus ( x . chargeCode ) ;
} ) ;
}
2021-03-25 12:24:49 +01:00
}
2021-04-05 08:07:37 +02:00
function getShortStatusStr ( coinSwap : CoinSwapInfo ) {
2021-04-08 11:02:37 +02:00
const swapInfo = coinSwaps . find ( ( x ) => x . chargeCode === coinSwap . chargeCode ) ;
if ( ! swapInfo || ! swapInfo . status ) {
2021-03-25 12:24:49 +01:00
return '---' ;
}
let msg ;
2021-04-08 11:02:37 +02:00
switch ( swapInfo . status . status ) {
2021-03-25 12:24:49 +01:00
case BTC _API _STATUS _PENDING :
2021-04-08 11:02:37 +02:00
msg = _ _ ( 'Waiting' ) ;
2021-03-25 12:24:49 +01:00
break ;
2021-04-05 08:07:37 +02:00
case BTC _API _STATUS _CONFIRMING :
2021-04-08 11:02:37 +02:00
msg = _ _ ( 'Confirming' ) ;
2021-04-05 08:07:37 +02:00
break ;
2021-03-25 12:24:49 +01:00
case BTC _API _STATUS _PROCESSING :
2021-04-08 11:02:37 +02:00
if ( swapInfo . status . lbcTxid ) {
msg = _ _ ( 'Credits sent' ) ;
} else {
msg = _ _ ( 'Sending Credits' ) ;
}
2021-03-25 12:24:49 +01:00
break ;
case BTC _API _STATUS _ERROR :
msg = _ _ ( 'Failed' ) ;
break ;
2021-04-08 11:02:37 +02:00
case BTC _API _STATUS _EXPIRED :
msg = _ _ ( 'Expired' ) ;
break ;
case BTC _API _STATUS _UNRESOLVED :
msg = _ _ ( 'Unresolved' ) ;
break ;
2021-03-25 12:24:49 +01:00
default :
2021-04-08 11:02:37 +02:00
msg = swapInfo . status . status ;
2021-03-25 12:24:49 +01:00
// if (IS_DEV) throw new Error('Unhandled "status": ' + status.Status);
break ;
}
return msg ;
}
2021-04-08 11:02:37 +02:00
function getViewTransactionElement ( swap , isSend ) {
if ( ! swap || ! swap . status ) {
return '' ;
}
const explorerUrl = ( coin , txid ) => {
2021-04-12 12:48:04 +02:00
// It's unclear whether we can link to sites like blockchain.com.
// Don't do it for now.
return '' ;
2021-04-08 11:02:37 +02:00
} ;
2021-03-25 12:24:49 +01:00
if ( isSend ) {
2021-04-08 11:02:37 +02:00
const sendTxId = swap . status . receiptTxid ;
const url = explorerUrl ( swap . status . receiptCurrency , sendTxId ) ;
return sendTxId ? (
< >
{ url && < Button button = "link" href = { url } label = { _ _ ( 'View transaction' ) } / > }
{ ! url && (
< Button
button = "link"
2021-04-12 12:48:04 +02:00
label = { _ _ ( 'Copy transaction ID' ) }
2021-04-08 11:02:37 +02:00
title = { sendTxId }
onClick = { ( ) => {
clipboard . writeText ( sendTxId ) ;
doToast ( {
message : _ _ ( 'Transaction ID copied.' ) ,
} ) ;
} }
/ >
) }
< / >
2021-03-25 12:24:49 +01:00
) : null ;
} else {
2021-04-08 11:02:37 +02:00
const lbcTxId = swap . status . lbcTxid ;
2021-03-25 12:24:49 +01:00
return lbcTxId ? (
< Button button = "link" href = { ` https://explorer.lbry.com/tx/ ${ lbcTxId } ` } label = { _ _ ( 'View transaction' ) } / >
) : null ;
}
}
2021-04-08 11:02:37 +02:00
function getCloseButton ( ) {
return (
< >
< Button autoFocus button = "primary" label = { _ _ ( 'Close' ) } onClick = { ( ) => goBack ( ) } / >
< Icon
className = "icon--help"
icon = { ICONS . HELP }
tooltip
size = { 16 }
customTooltipText = { _ _ (
'This page can be closed while the transactions are in progress.\nYou can view the status later from:\n • Wallet » Swap » View Past Swaps'
) }
/ >
< / >
) ;
}
function getGap ( ) {
return < div className = "confirm__value" / > ; // better way?
}
2021-03-25 12:24:49 +01:00
function getActionElement ( ) {
switch ( action ) {
case ACTION _MAIN :
return actionMain ;
2021-04-05 08:07:37 +02:00
2021-03-25 12:24:49 +01:00
case ACTION _STATUS _PENDING :
return actionPending ;
2021-04-05 08:07:37 +02:00
case ACTION _STATUS _CONFIRMING :
return actionConfirmingSend ;
case ACTION _STATUS _PROCESSING : // fall-through
case ACTION _STATUS _SUCCESS :
2021-03-25 12:24:49 +01:00
return actionProcessingAndSuccess ;
2021-04-05 08:07:37 +02:00
2021-03-25 12:24:49 +01:00
case ACTION _PAST _SWAPS :
return actionPastSwaps ;
2021-04-05 08:07:37 +02:00
2021-03-25 12:24:49 +01:00
default :
if ( IS _DEV ) throw new Error ( 'Unhandled action: ' + action ) ;
return actionMain ;
}
}
const actionMain = (
< >
< div className = "section section--padded card--inline confirm__wrapper" >
< div className = "section" >
< FormField
autoFocus
2021-04-09 05:11:57 +02:00
label = {
< I18nMessage
tokens = { {
lbc : < LbcSymbol size = { 22 } / > ,
} }
>
Enter desired % lbc %
< / I18nMessage >
}
2021-03-25 12:24:49 +01:00
type = "number"
2021-04-09 05:11:57 +02:00
name = "lbc"
2021-03-25 12:24:49 +01:00
className = "form-field--price-amount--auto"
affixClass = "form-field--fix-no-height"
2021-04-09 05:11:57 +02:00
max = { LBC _MAX }
min = { LBC _MIN }
2021-03-25 12:24:49 +01:00
step = { 1 / BTC _SATOSHIS }
placeholder = "12.34"
2021-04-09 05:11:57 +02:00
value = { lbc }
error = { lbcError }
2021-03-25 12:24:49 +01:00
disabled = { isSwapping }
2021-04-09 05:11:57 +02:00
onChange = { ( event ) => setLbc ( parseFloat ( event . target . value ) ) }
2021-03-25 12:24:49 +01:00
/ >
2021-04-08 11:02:37 +02:00
{ getGap ( ) }
2021-04-09 05:11:57 +02:00
< div className = "confirm__label" > { _ _ ( 'Estimated BTC price' ) } < / div >
2021-03-25 12:24:49 +01:00
< div className = "confirm__value" >
2021-04-09 05:11:57 +02:00
{ formatCoinAmountString ( btc ) } { btc === 0 ? '' : 'BTC' }
2021-03-25 12:24:49 +01:00
{ isFetchingRate && < Spinner type = "small" / > }
< / div >
< / div >
< / div >
< div className = "section__actions" >
< Button
autoFocus
onClick = { handleStartSwap }
button = "primary"
2021-04-09 05:11:57 +02:00
disabled = { isSwapping || isNaN ( btc ) || btc === 0 || lbc === 0 || lbcError }
2021-03-25 12:24:49 +01:00
label = { isSwapping ? _ _ ( 'Processing...' ) : _ _ ( 'Start Swap' ) }
/ >
2021-04-08 11:02:37 +02:00
{ ! isSwapping && coinSwaps . length !== 0 && (
< Button button = "link" label = { _ _ ( 'View Past Swaps' ) } onClick = { handleViewPastSwaps } / >
) }
{ isSwapping && < Spinner type = "small" / > }
2021-03-25 12:24:49 +01:00
< / div >
< / >
) ;
const actionPending = (
< >
< div className = "section section--padded card--inline confirm__wrapper" >
< div className = "section" >
2021-04-08 11:02:37 +02:00
{ swap && swap . coins && ENABLE _ALTERNATIVE _COINS && (
< >
< FormField
type = "select"
name = "select_coin"
value = { coin }
label = { _ _ ( 'Alternative coins' ) }
onChange = { ( e ) => setCoin ( e . target . value ) }
>
{ swap . coins . map ( ( x ) => (
< option key = { x } value = { x } >
{ getCoinLabel ( x ) }
< / option >
) ) }
< / FormField >
{ getGap ( ) }
< / >
) }
2021-05-04 17:08:01 +02:00
< div className = "confirm__label" > { _ _ ( 'Send' ) } < / div >
2021-04-21 17:36:34 +02:00
< CopyableText
primaryButton
copyable = { getCoinSendAmountStr ( coin ) }
snackMessage = { _ _ ( 'Amount copied.' ) }
onCopy = { ( inputElem ) => {
const inputStr = inputElem . value ;
const selectEndIndex = inputStr . lastIndexOf ( ' ' ) ;
if ( selectEndIndex > - 1 && inputStr . substring ( 0 , selectEndIndex ) . match ( /[\d.]/ ) ) {
inputElem . setSelectionRange ( 0 , selectEndIndex , 'forward' ) ;
}
} }
/ >
2021-05-04 17:08:01 +02:00
< div className = "help" > { _ _ ( 'Use the copy button to ensure the EXACT amount is sent!' ) } < / div >
2021-04-08 11:02:37 +02:00
{ getGap ( ) }
2021-03-25 12:24:49 +01:00
< div className = "confirm__label" > { _ _ ( 'To' ) } < / div >
2021-04-08 11:02:37 +02:00
< CopyableText primaryButton copyable = { getCoinAddress ( coin ) } snackMessage = { _ _ ( 'Address copied.' ) } / >
< div className = "confirm__value--subitem" >
2021-03-25 12:24:49 +01:00
< Button
button = "link"
label = { showQr ? _ _ ( 'Hide QR code' ) : _ _ ( 'Show QR code' ) }
onClick = { ( ) => setShowQr ( ! showQr ) }
/ >
2021-04-08 11:02:37 +02:00
{ showQr && getCoinAddress ( coin ) && < QRCode value = { getCoinAddress ( coin ) } / > }
2021-03-25 12:24:49 +01:00
< / div >
2021-04-08 11:02:37 +02:00
{ getGap ( ) }
2021-03-25 12:24:49 +01:00
< div className = "confirm__label" > { _ _ ( 'Receive' ) } < / div >
2021-04-08 11:02:37 +02:00
< div className = "confirm__value" > { < LbcSymbol postfix = { getLbcAmountStrForSwap ( swap ) } size = { 22 } / > } < / div >
2021-03-25 12:24:49 +01:00
< / div >
< / div >
2021-04-08 11:02:37 +02:00
< div className = "section__actions" > { getCloseButton ( ) } < / div >
2021-03-25 12:24:49 +01:00
< / >
) ;
2021-04-05 08:07:37 +02:00
const actionConfirmingSend = (
< >
< div className = "section section--padded card--inline confirm__wrapper" >
< div className = "section" >
< div className = "confirm__label" > { _ _ ( 'Confirming' ) } < / div >
2021-04-08 11:02:37 +02:00
< div className = "confirm__value confirm__value--no-gap" > { getSentAmountStr ( swap ) } < / div >
< div className = "confirm__value--subitem" > { getViewTransactionElement ( swap , true ) } < / div >
2021-04-05 08:07:37 +02:00
< / div >
< / div >
2021-04-08 11:02:37 +02:00
< div className = "section__actions" > { getCloseButton ( ) } < / div >
2021-04-05 08:07:37 +02:00
< / >
) ;
2021-03-25 12:24:49 +01:00
const actionProcessingAndSuccess = (
< >
< div className = "section section--padded card--inline confirm__wrapper" >
< div className = "section" >
< div className = "confirm__label" > { _ _ ( 'Sent' ) } < / div >
2021-04-08 11:02:37 +02:00
< div className = "confirm__value confirm__value--no-gap" > { getSentAmountStr ( swap ) } < / div >
< div className = "confirm__value--subitem" > { getViewTransactionElement ( swap , true ) } < / div >
{ getGap ( ) }
2021-03-25 12:24:49 +01:00
< div className = "confirm__label" > { action === ACTION _STATUS _SUCCESS ? _ _ ( 'Received' ) : _ _ ( 'Receiving' ) } < / div >
2021-04-08 11:02:37 +02:00
< div className = "confirm__value confirm__value--no-gap" >
{ < LbcSymbol postfix = { getLbcAmountStrForSwap ( swap ) } size = { 22 } / > }
< / div >
{ action === ACTION _STATUS _SUCCESS && (
< div className = "confirm__value--subitem" > { getViewTransactionElement ( swap , false ) } < / div >
) }
2021-03-25 12:24:49 +01:00
< / div >
< / div >
2021-04-08 11:02:37 +02:00
< div className = "section__actions" > { getCloseButton ( ) } < / div >
2021-03-25 12:24:49 +01:00
< / >
) ;
const actionPastSwaps = (
< >
< div className = "section section--padded card--inline confirm__wrapper" >
< div className = "section" >
< div className = "table__wrapper" >
< table className = "table table--btc-swap" >
< thead >
< tr >
2021-04-08 11:02:37 +02:00
< th > { _ _ ( 'Code' ) } < / th >
2021-04-05 11:22:19 +02:00
< th > { _ _ ( 'Status' ) } < / th >
2021-03-25 12:24:49 +01:00
< th / >
< / tr >
< / thead >
< tbody >
2021-04-04 09:10:55 +02:00
{ coinSwaps . length === 0 && (
2021-03-25 12:24:49 +01:00
< tr >
< td > { '---' } < / td >
< / tr >
) }
2021-04-04 09:10:55 +02:00
{ coinSwaps . length !== 0 &&
coinSwaps . map ( ( x ) => {
2021-03-25 12:24:49 +01:00
return (
2021-04-08 11:02:37 +02:00
< tr key = { x . chargeCode } >
2021-03-25 12:24:49 +01:00
< td >
< Button
button = "link"
className = "button--hash-id"
2021-04-08 11:02:37 +02:00
title = { x . chargeCode }
label = { x . chargeCode }
2021-03-25 12:24:49 +01:00
onClick = { ( ) => {
2021-04-08 11:02:37 +02:00
setSwap ( { ... x } ) ;
2021-03-25 12:24:49 +01:00
} }
/ >
< / td >
2021-04-05 08:07:37 +02:00
< td > { isRefreshingStatus ? '...' : getShortStatusStr ( x ) } < / td >
2021-03-25 12:24:49 +01:00
< td >
< Button
button = "link"
icon = { ICONS . REMOVE }
2021-04-08 11:02:37 +02:00
title = { _ _ ( 'Remove swap' ) }
2021-09-11 17:02:34 +02:00
onClick = { ( ) => handleRemoveSwap ( x . chargeCode ) }
2021-03-25 12:24:49 +01:00
/ >
< / td >
< / tr >
) ;
} ) }
< / tbody >
< / table >
< / div >
< / div >
< / div >
< div className = "section__actions" >
2021-04-08 11:02:37 +02:00
< Button
autoFocus
button = "primary"
label = { _ _ ( 'Go Back' ) }
onClick = { ( ) => {
returnToMainAction ( ) ;
setNag ( null ) ;
} }
/ >
2021-04-04 09:10:55 +02:00
{ coinSwaps . length !== 0 && ! isRefreshingStatus && (
2021-03-25 12:24:49 +01:00
< Button button = "link" label = { _ _ ( 'Refresh' ) } onClick = { handleViewPastSwaps } / >
) }
{ isRefreshingStatus && < Spinner type = "small" / > }
< / div >
< / >
) ;
2021-04-05 07:12:19 +02:00
if ( ! isAuthenticated ) {
return < Redirect to = { ` / $ / ${ PAGES . AUTH _SIGNIN } ?redirect= ${ location . pathname } ` } / > ;
}
2021-03-25 12:24:49 +01:00
return (
< Form onSubmit = { handleStartSwap } >
< Card
2021-04-08 11:02:37 +02:00
title = { < I18nMessage tokens = { { lbc : < LbcSymbol size = { 22 } / > } } > Swap Crypto for % lbc % < / I18nMessage > }
2021-04-21 17:36:34 +02:00
subtitle = { _ _ (
'Send crypto to the address provided and you will be sent an equivalent amount of Credits. You can pay with BCH, LTC, ETH, USDC or DAI after starting the swap.'
) }
2021-03-25 12:24:49 +01:00
actions = { getActionElement ( ) }
nag = { nag ? < Nag relative type = { nag . type } message = { _ _ ( nag . msg ) } / > : null }
/ >
< / Form >
) ;
}
export default WalletSwap ;