Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
317 additions
and
90 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,312 @@ | ||
// External Dependencies | ||
import React, { Component } from 'react'; | ||
import { connect } from 'react-redux'; | ||
import { reduxForm, Field, reset, change, touch } from 'redux-form'; | ||
import styled from '@emotion/styled'; | ||
|
||
// Internal Global Dependencies | ||
import * as RPC from 'scripts/rpc'; | ||
import Text from 'components/Text'; | ||
import Icon from 'components/Icon'; | ||
import Button from 'components/Button'; | ||
import TextField from 'components/TextField'; | ||
import Select from 'components/Select'; | ||
import FormField from 'components/FormField'; | ||
import InputGroup from 'components/InputGroup'; | ||
import UIController from 'components/UIController'; | ||
import Link from 'components/Link'; | ||
import { rpcErrorHandler } from 'utils/form'; | ||
|
||
// Internal Local Dependencies | ||
import LookupAddressModal from './LookupAddressModal'; | ||
|
||
// Resources | ||
import sendIcon from 'images/send.sprite.svg'; | ||
import addressBookIcon from 'images/address-book.sprite.svg'; | ||
|
||
const formName = 'sendNXS'; | ||
const floatRegex = /^[0-9]+(.[0-9]*)?$/; | ||
|
||
const SendFormComponent = styled.form({ | ||
maxWidth: 620, | ||
margin: '0 auto', | ||
}); | ||
|
||
const SendAmount = styled.div({ | ||
display: 'flex', | ||
}); | ||
|
||
const SendAmountField = styled.div({ | ||
flex: 1, | ||
}); | ||
|
||
const SendAmountEqual = styled.div({ | ||
display: 'flex', | ||
alignItems: 'flex-end', | ||
padding: '.1em .6em', | ||
fontSize: '1.2em', | ||
}); | ||
|
||
const SendFormButtons = styled.div({ | ||
display: 'flex', | ||
justifyContent: 'space-between', | ||
marginTop: '2em', | ||
}); | ||
|
||
const getAccountOptions = AccountChanger => { | ||
if (AccountChanger) { | ||
return AccountChanger.map(e => ({ | ||
value: e.name, | ||
display: `${e.name} (${e.val} NXS)`, | ||
})); | ||
} | ||
return []; | ||
}; | ||
|
||
const getNxsFiatPrice = (rawNXSvalues, fiatCurrency) => { | ||
if (rawNXSvalues) { | ||
const marketInfo = rawNXSvalues.find(e => e.name === fiatCurrency); | ||
if (marketInfo) { | ||
return marketInfo.price; | ||
} | ||
} | ||
return null; | ||
}; | ||
|
||
const getDefaultSendFrom = AccountChanger => { | ||
if (AccountChanger && AccountChanger.length > 0) { | ||
if (AccountChanger.find(acc => acc.name === 'default')) { | ||
return 'default'; | ||
} else { | ||
return AccountChanger[0].name; | ||
} | ||
} | ||
return null; | ||
}; | ||
|
||
const mapStateToProps = ({ | ||
sendReceive: { AccountChanger }, | ||
settings: { fiatCurrency, minConfirmations }, | ||
common: { rawNXSvalues, encrypted, loggedIn }, | ||
}) => { | ||
return { | ||
accountOptions: getAccountOptions(AccountChanger), | ||
fiatCurrency: fiatCurrency, | ||
minConfirmations: minConfirmations, | ||
encrypted: encrypted, | ||
loggedIn: loggedIn, | ||
nxsFiatPrice: getNxsFiatPrice(rawNXSvalues, fiatCurrency), | ||
initialValues: { | ||
sendFrom: getDefaultSendFrom(AccountChanger), | ||
sendTo: null, | ||
amount: 0, | ||
fiatAmount: 0, | ||
message: '', | ||
}, | ||
}; | ||
}; | ||
|
||
const mapDispatchToProps = dispatch => ({ | ||
updateRecipient: address => dispatch(change(formName, 'sendTo', address)), | ||
updateNxsAmount: amount => dispatch(change(formName, 'amount', amount)), | ||
updateFiatAmount: fiatAmount => | ||
dispatch(change(formName, 'fiatAmount', fiatAmount)), | ||
touch: () => dispatch(touch()), | ||
}); | ||
|
||
@connect( | ||
mapStateToProps, | ||
mapDispatchToProps | ||
) | ||
@reduxForm({ | ||
form: formName, | ||
validate: ({ sendFrom, sendTo, amount }) => { | ||
const errors = {}; | ||
if (!sendFrom) { | ||
errors.sendFrom = 'No accounts selected'; | ||
} | ||
if (!sendTo) { | ||
errors.sendTo = <Text id="Alert.InvalidAddress" />; | ||
} | ||
if (!amount || parseFloat(amount) <= 0) { | ||
errors.amount = <Text id="Alert.InvalidAmount" />; | ||
} | ||
return errors; | ||
}, | ||
asyncBlurFields: ['sendTo'], | ||
asyncValidate: async ({ sendTo }) => { | ||
if (sendTo) { | ||
try { | ||
const result = await RPC.PROMISE('validateaddress', [sendTo]); | ||
if (!result.isvalid) { | ||
throw { sendTo: <Text id="Alert.InvalidAddress" /> }; | ||
} | ||
if (result.ismine) { | ||
throw { sendTo: <Text id="Alert.registeredToThis" /> }; | ||
} | ||
} catch (err) { | ||
throw { sendTo: err }; | ||
} | ||
} | ||
return null; | ||
}, | ||
onSubmit: ({ sendFrom, sendTo, amount, message }, dispatch, props) => { | ||
const params = [ | ||
sendFrom, | ||
sendTo, | ||
parseFloat(amount), | ||
parseInt(props.minConfirmations), | ||
]; | ||
if (message) params.push(message); | ||
return RPC.PROMISE('sendfrom', params); | ||
}, | ||
onSubmitSuccess: (result, dispatch, props) => { | ||
UIController.openSuccessDialog({ | ||
message: <Text id="Alert.Sent" />, | ||
}); | ||
dispatch(reset(formName)); | ||
props.getAccountData(); | ||
}, | ||
onSubmitFail: rpcErrorHandler('Error Saving Settings'), | ||
}) | ||
export default class SendForm extends Component { | ||
nxsToFiat = (e, value) => { | ||
if (floatRegex.test(value)) { | ||
const nxs = parseFloat(value); | ||
const { nxsFiatPrice } = this.props; | ||
if (nxsFiatPrice) { | ||
const fiat = nxs * nxsFiatPrice; | ||
this.props.updateFiatAmount(fiat.toFixed(2)); | ||
} | ||
} | ||
}; | ||
|
||
fiatToNxs = (e, value) => { | ||
if (floatRegex.test(value)) { | ||
const fiat = parseFloat(value); | ||
const { nxsFiatPrice } = this.props; | ||
if (nxsFiatPrice) { | ||
const nxs = fiat / nxsFiatPrice; | ||
this.props.updateNxsAmount(nxs.toFixed(5)); | ||
} | ||
} | ||
}; | ||
|
||
lookupAddress = () => { | ||
UIController.openModal(LookupAddressModal, { | ||
updateRecipient: this.props.updateRecipient, | ||
}); | ||
}; | ||
|
||
confirmSend = () => { | ||
const { handleSubmit, invalid, encrypted, loggedIn, touch } = this.props; | ||
|
||
if (invalid) { | ||
// Mark the form touched so that the validation errors will be shown | ||
touch(); | ||
return; | ||
} | ||
|
||
if (encrypted && !loggedIn) { | ||
const modalId = UIController.openErrorDialog({ | ||
message: 'You are not logged in', | ||
note: ( | ||
<> | ||
<p>You need to log in to your wallet before sending transactions</p> | ||
<Link | ||
to="/Settings/Security" | ||
onClick={() => { | ||
UIController.removeModal(modalId); | ||
}} | ||
> | ||
Log in now | ||
</Link> | ||
</> | ||
), | ||
}); | ||
return; | ||
} | ||
|
||
UIController.openConfirmDialog({ | ||
question: <Text id="sendReceive.SendTransaction" />, | ||
yesCallback: handleSubmit, | ||
}); | ||
}; | ||
|
||
render() { | ||
const { accountOptions, fiatCurrency } = this.props; | ||
return ( | ||
<SendFormComponent onSubmit={this.confirmSend}> | ||
<FormField label="Send From"> | ||
<Field | ||
component={Select.RF} | ||
name="sendFrom" | ||
options={accountOptions} | ||
/> | ||
</FormField> | ||
|
||
<FormField label="Send To"> | ||
<InputGroup> | ||
<Field | ||
component={TextField.RF} | ||
name="sendTo" | ||
placeholder="Recipient Address" | ||
/> | ||
<Button fitHeight className="relative" onClick={this.lookupAddress}> | ||
<Icon spaceRight icon={addressBookIcon} /> | ||
<Text id="sendReceive.Contacts" /> | ||
</Button> | ||
</InputGroup> | ||
</FormField> | ||
|
||
<SendAmount> | ||
<SendAmountField> | ||
<FormField connectLabel label={<Text id="sendReceive.Amount" />}> | ||
<Field | ||
component={TextField.RF} | ||
name="amount" | ||
placeholder="0.00000" | ||
onChange={this.nxsToFiat} | ||
/> | ||
</FormField> | ||
</SendAmountField> | ||
|
||
<SendAmountEqual>=</SendAmountEqual> | ||
|
||
<SendAmountField> | ||
<FormField connectLabel label={fiatCurrency}> | ||
<Field | ||
component={TextField.RF} | ||
name="fiatAmount" | ||
placeholder="0.00" | ||
onChange={this.fiatToNxs} | ||
/> | ||
</FormField> | ||
</SendAmountField> | ||
</SendAmount> | ||
|
||
<Text id="sendReceive.EnterYourMessage"> | ||
{placeholder => ( | ||
<FormField connectLabel label={<Text id="sendReceive.Message" />}> | ||
<Field | ||
component={TextField.RF} | ||
name="message" | ||
multiline | ||
rows={1} | ||
placeholder={placeholder} | ||
/> | ||
</FormField> | ||
)} | ||
</Text> | ||
|
||
<SendFormButtons> | ||
<div /> | ||
<Button type="submit" skin="primary"> | ||
<Icon icon={sendIcon} spaceRight /> | ||
<Text id="sendReceive.SendNow" /> | ||
</Button> | ||
</SendFormButtons> | ||
</SendFormComponent> | ||
); | ||
} | ||
} |
Oops, something went wrong.