2016-11-22 21:19:08 +01:00
|
|
|
import React from 'react';
|
|
|
|
import lbry from '../lbry.js';
|
|
|
|
import {Link} from '../component/link.js';
|
|
|
|
import Modal from '../component/modal.js';
|
|
|
|
import {Address, BusyMessage, CreditAmount} from '../component/common.js';
|
|
|
|
|
|
|
|
|
2016-09-01 08:40:23 +02:00
|
|
|
var addressRefreshButtonStyle = {
|
|
|
|
fontSize: '11pt',
|
|
|
|
};
|
|
|
|
var AddressSection = React.createClass({
|
2016-10-19 09:13:39 +02:00
|
|
|
_refreshAddress: function(event) {
|
|
|
|
if (typeof event !== 'undefined') {
|
|
|
|
event.preventDefault();
|
|
|
|
}
|
|
|
|
|
2017-04-10 19:44:54 +02:00
|
|
|
lbry.getUnusedAddress((address) => {
|
|
|
|
window.localStorage.setItem('wallet_address', address);
|
|
|
|
this.setState({
|
|
|
|
address: address,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
_getNewAddress: function(event) {
|
|
|
|
if (typeof event !== 'undefined') {
|
|
|
|
event.preventDefault();
|
|
|
|
}
|
|
|
|
|
2016-09-01 08:40:23 +02:00
|
|
|
lbry.getNewAddress((address) => {
|
2016-11-22 21:28:16 +01:00
|
|
|
window.localStorage.setItem('wallet_address', address);
|
2016-07-19 00:40:15 +02:00
|
|
|
this.setState({
|
2016-09-01 08:40:23 +02:00
|
|
|
address: address,
|
|
|
|
});
|
2016-07-19 00:40:15 +02:00
|
|
|
});
|
|
|
|
},
|
2017-04-10 19:44:54 +02:00
|
|
|
|
2016-07-19 00:40:15 +02:00
|
|
|
getInitialState: function() {
|
|
|
|
return {
|
2016-09-01 08:40:23 +02:00
|
|
|
address: null,
|
2016-10-21 09:56:37 +02:00
|
|
|
modal: null,
|
2016-09-01 08:40:23 +02:00
|
|
|
}
|
|
|
|
},
|
|
|
|
componentWillMount: function() {
|
2016-11-22 21:28:16 +01:00
|
|
|
var address = window.localStorage.getItem('wallet_address');
|
2016-09-01 08:40:23 +02:00
|
|
|
if (address === null) {
|
2016-09-02 07:46:27 +02:00
|
|
|
this._refreshAddress();
|
2016-09-01 08:40:23 +02:00
|
|
|
} else {
|
2016-09-02 07:46:27 +02:00
|
|
|
lbry.checkAddressIsMine(address, (isMine) => {
|
|
|
|
if (isMine) {
|
|
|
|
this.setState({
|
|
|
|
address: address,
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
this._refreshAddress();
|
|
|
|
}
|
2016-09-01 08:40:23 +02:00
|
|
|
});
|
2016-07-19 00:40:15 +02:00
|
|
|
}
|
|
|
|
},
|
|
|
|
render: function() {
|
|
|
|
return (
|
2016-08-08 02:20:14 +02:00
|
|
|
<section className="card">
|
2016-09-01 08:40:23 +02:00
|
|
|
<h3>Wallet Address</h3>
|
2017-04-10 19:44:54 +02:00
|
|
|
<Address address={this.state.address} /> <Link text="Get new address" icon='icon-refresh' onClick={this._getNewAddress} style={addressRefreshButtonStyle} />
|
2016-10-19 09:13:39 +02:00
|
|
|
<input type='submit' className='hidden' />
|
2016-09-01 08:40:23 +02:00
|
|
|
<div className="help">
|
|
|
|
<p>Other LBRY users may send credits to you by entering this address on the "Send" page.</p>
|
|
|
|
You can generate a new address at any time, and any previous addresses will continue to work. Using multiple addresses can be helpful for keeping track of incoming payments from multiple sources.
|
|
|
|
</div>
|
2016-08-03 10:07:36 +02:00
|
|
|
</section>
|
2016-07-19 00:40:15 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
var SendToAddressSection = React.createClass({
|
2016-10-19 09:13:39 +02:00
|
|
|
handleSubmit: function(event) {
|
|
|
|
if (typeof event !== 'undefined') {
|
|
|
|
event.preventDefault();
|
|
|
|
}
|
|
|
|
|
2016-08-02 15:35:18 +02:00
|
|
|
if ((this.state.balance - this.state.amount) < 1)
|
2016-08-02 11:34:30 +02:00
|
|
|
{
|
2016-10-21 09:56:37 +02:00
|
|
|
this.setState({
|
|
|
|
modal: 'insufficientBalance',
|
|
|
|
});
|
2016-08-02 11:34:30 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-07-19 00:40:15 +02:00
|
|
|
this.setState({
|
|
|
|
results: "",
|
|
|
|
});
|
|
|
|
|
2016-07-19 01:18:16 +02:00
|
|
|
lbry.sendToAddress(this.state.amount, this.state.address, (results) => {
|
2016-07-19 00:40:15 +02:00
|
|
|
if(results === true)
|
|
|
|
{
|
|
|
|
this.setState({
|
2016-07-22 17:59:20 +02:00
|
|
|
results: "Your transaction was successfully placed in the queue.",
|
2016-07-19 00:40:15 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.setState({
|
|
|
|
results: "Something went wrong: " + results
|
|
|
|
});
|
|
|
|
}
|
2016-07-22 17:33:05 +02:00
|
|
|
}, (error) => {
|
|
|
|
this.setState({
|
2017-03-23 20:42:40 +01:00
|
|
|
results: "Something went wrong: " + error.message
|
2016-07-22 17:33:05 +02:00
|
|
|
})
|
2016-07-19 00:40:15 +02:00
|
|
|
});
|
|
|
|
},
|
2016-10-21 09:56:37 +02:00
|
|
|
closeModal: function() {
|
|
|
|
this.setState({
|
|
|
|
modal: null,
|
|
|
|
});
|
|
|
|
},
|
2016-07-19 00:40:15 +02:00
|
|
|
getInitialState: function() {
|
|
|
|
return {
|
|
|
|
address: "",
|
|
|
|
amount: 0.0,
|
2016-08-22 18:16:43 +02:00
|
|
|
balance: <BusyMessage message="Checking balance" />,
|
2016-07-19 00:40:15 +02:00
|
|
|
results: "",
|
|
|
|
}
|
|
|
|
},
|
|
|
|
componentWillMount: function() {
|
|
|
|
lbry.getBalance((results) => {
|
|
|
|
this.setState({
|
|
|
|
balance: results,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
setAmount: function(event) {
|
|
|
|
this.setState({
|
2016-07-22 17:33:05 +02:00
|
|
|
amount: parseFloat(event.target.value),
|
2016-07-19 00:40:15 +02:00
|
|
|
})
|
|
|
|
},
|
|
|
|
setAddress: function(event) {
|
|
|
|
this.setState({
|
|
|
|
address: event.target.value,
|
|
|
|
})
|
|
|
|
},
|
|
|
|
render: function() {
|
|
|
|
return (
|
2016-08-08 02:20:14 +02:00
|
|
|
<section className="card">
|
2016-10-19 09:13:39 +02:00
|
|
|
<form onSubmit={this.handleSubmit}>
|
|
|
|
<h3>Send Credits</h3>
|
2016-08-08 05:31:21 +02:00
|
|
|
<div className="form-row">
|
2016-10-19 09:13:39 +02:00
|
|
|
<label htmlFor="amount">Amount</label>
|
|
|
|
<input id="amount" type="text" size="10" onChange={this.setAmount}></input>
|
2016-08-08 05:31:21 +02:00
|
|
|
</div>
|
2016-10-19 09:13:39 +02:00
|
|
|
<div className="form-row">
|
|
|
|
<label htmlFor="address">Recipient address</label>
|
|
|
|
<input id="address" type="text" size="60" onChange={this.setAddress}></input>
|
|
|
|
</div>
|
|
|
|
<div className="form-row form-row-submit">
|
|
|
|
<Link button="primary" label="Send" onClick={this.handleSubmit} disabled={!(parseFloat(this.state.amount) > 0.0) || this.state.address == ""} />
|
|
|
|
<input type='submit' className='hidden' />
|
|
|
|
</div>
|
|
|
|
{
|
|
|
|
this.state.results ?
|
|
|
|
<div className="form-row">
|
|
|
|
<h4>Results</h4>
|
|
|
|
{this.state.results}
|
|
|
|
</div>
|
|
|
|
: ''
|
|
|
|
}
|
|
|
|
</form>
|
2017-01-13 23:05:09 +01:00
|
|
|
<Modal isOpen={this.state.modal === 'insufficientBalance'} contentLabel="Insufficient balance"
|
|
|
|
onConfirmed={this.closeModal}>
|
2016-10-21 09:56:37 +02:00
|
|
|
Insufficient balance: after this transaction you would have less than 1 LBC in your wallet.
|
|
|
|
</Modal>
|
2016-08-08 05:31:21 +02:00
|
|
|
</section>
|
2016-07-19 00:40:15 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2016-08-27 16:12:56 +02:00
|
|
|
|
|
|
|
var TransactionList = React.createClass({
|
2016-08-08 02:20:14 +02:00
|
|
|
getInitialState: function() {
|
|
|
|
return {
|
2016-08-27 16:12:56 +02:00
|
|
|
transactionItems: null,
|
2016-08-08 02:20:14 +02:00
|
|
|
}
|
|
|
|
},
|
|
|
|
componentWillMount: function() {
|
2016-08-20 21:52:03 +02:00
|
|
|
lbry.call('get_transaction_history', {}, (results) => {
|
|
|
|
if (results.length == 0) {
|
2016-08-27 16:12:56 +02:00
|
|
|
this.setState({ transactionItems: [] })
|
2016-08-20 21:52:03 +02:00
|
|
|
} else {
|
2016-08-27 16:12:56 +02:00
|
|
|
var transactionItems = [],
|
|
|
|
condensedTransactions = {};
|
2016-08-20 21:52:03 +02:00
|
|
|
results.forEach(function(tx) {
|
2016-08-20 23:52:31 +02:00
|
|
|
var txid = tx["txid"];
|
|
|
|
if (!(txid in condensedTransactions)) {
|
|
|
|
condensedTransactions[txid] = 0;
|
|
|
|
}
|
2016-09-05 19:20:34 +02:00
|
|
|
condensedTransactions[txid] += parseFloat(tx["value"]);
|
2016-08-20 21:52:03 +02:00
|
|
|
});
|
2016-08-24 07:08:58 +02:00
|
|
|
results.reverse().forEach(function(tx) {
|
2016-08-20 23:52:31 +02:00
|
|
|
var txid = tx["txid"];
|
2016-09-03 22:28:05 +02:00
|
|
|
if (condensedTransactions[txid] && condensedTransactions[txid] != 0)
|
2016-08-27 16:12:56 +02:00
|
|
|
{
|
|
|
|
transactionItems.push({
|
|
|
|
id: txid,
|
2016-11-11 11:09:43 +01:00
|
|
|
date: tx["timestamp"] ? (new Date(parseInt(tx["timestamp"]) * 1000)) : null,
|
2016-08-27 16:12:56 +02:00
|
|
|
amount: condensedTransactions[txid]
|
|
|
|
});
|
|
|
|
delete condensedTransactions[txid];
|
2016-08-20 23:52:31 +02:00
|
|
|
}
|
|
|
|
});
|
2016-08-27 16:12:56 +02:00
|
|
|
|
|
|
|
this.setState({ transactionItems: transactionItems });
|
2016-08-20 21:52:03 +02:00
|
|
|
}
|
2016-08-27 16:12:56 +02:00
|
|
|
});
|
|
|
|
},
|
|
|
|
render: function() {
|
|
|
|
var rows = [];
|
|
|
|
if (this.state.transactionItems && this.state.transactionItems.length > 0)
|
|
|
|
{
|
|
|
|
this.state.transactionItems.forEach(function(item) {
|
|
|
|
rows.push(
|
|
|
|
<tr key={item.id}>
|
|
|
|
<td>{ (item.amount > 0 ? '+' : '' ) + item.amount }</td>
|
2016-11-11 11:09:43 +01:00
|
|
|
<td>{ item.date ? item.date.toLocaleDateString() : <span className="empty">(Transaction pending)</span> }</td>
|
|
|
|
<td>{ item.date ? item.date.toLocaleTimeString() : <span className="empty">(Transaction pending)</span> }</td>
|
2016-08-27 16:12:56 +02:00
|
|
|
<td>
|
|
|
|
<a className="button-text" href={"https://explorer.lbry.io/tx/"+item.id} target="_blank">{item.id.substr(0, 7)}</a>
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return (
|
|
|
|
<section className="card">
|
|
|
|
<h3>Transaction History</h3>
|
|
|
|
{ this.state.transactionItems === null ? <BusyMessage message="Loading transactions" /> : '' }
|
|
|
|
{ this.state.transactionItems && rows.length === 0 ? <div className="empty">You have no transactions.</div> : '' }
|
|
|
|
{ this.state.transactionItems && rows.length > 0 ?
|
|
|
|
<table className="table-standard table-stretch">
|
|
|
|
<thead>
|
|
|
|
<tr>
|
|
|
|
<th>Amount</th>
|
|
|
|
<th>Date</th>
|
|
|
|
<th>Time</th>
|
|
|
|
<th>Transaction</th>
|
|
|
|
</tr>
|
|
|
|
</thead>
|
|
|
|
<tbody>
|
|
|
|
{rows}
|
|
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
: ''
|
|
|
|
}
|
|
|
|
</section>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
var WalletPage = React.createClass({
|
2017-03-26 20:30:18 +02:00
|
|
|
_balanceSubscribeId: null,
|
|
|
|
|
2016-08-27 16:12:56 +02:00
|
|
|
propTypes: {
|
|
|
|
viewingPage: React.PropTypes.string,
|
|
|
|
},
|
|
|
|
componentDidMount: function() {
|
|
|
|
document.title = "My Wallet";
|
|
|
|
},
|
|
|
|
/*
|
|
|
|
Below should be refactored so that balance is shared all of wallet page. Or even broader?
|
|
|
|
What is the proper React pattern for sharing a global state like balance?
|
|
|
|
*/
|
|
|
|
getInitialState: function() {
|
|
|
|
return {
|
|
|
|
balance: null,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
componentWillMount: function() {
|
2017-03-26 20:30:18 +02:00
|
|
|
this._balanceSubscribeId = lbry.balanceSubscribe((results) => {
|
2016-08-20 21:52:03 +02:00
|
|
|
this.setState({
|
2016-08-27 16:12:56 +02:00
|
|
|
balance: results,
|
2016-08-20 21:52:03 +02:00
|
|
|
})
|
2016-08-08 02:20:14 +02:00
|
|
|
});
|
|
|
|
},
|
2017-03-26 20:30:18 +02:00
|
|
|
componentWillUnmount: function() {
|
|
|
|
if (this._balanceSubscribeId) {
|
|
|
|
lbry.balanceUnsubscribe(this._balanceSubscribeId);
|
|
|
|
}
|
|
|
|
},
|
2016-07-19 00:40:15 +02:00
|
|
|
render: function() {
|
|
|
|
return (
|
|
|
|
<main className="page">
|
2016-08-08 02:20:14 +02:00
|
|
|
<section className="card">
|
|
|
|
<h3>Balance</h3>
|
2016-08-27 16:12:56 +02:00
|
|
|
{ this.state.balance === null ? <BusyMessage message="Checking balance" /> : ''}
|
|
|
|
{ this.state.balance !== null ? <CreditAmount amount={this.state.balance} precision={8} /> : '' }
|
2016-08-20 21:52:03 +02:00
|
|
|
</section>
|
2016-08-27 16:12:56 +02:00
|
|
|
{ this.props.viewingPage === 'wallet' ? <TransactionList /> : '' }
|
|
|
|
{ this.props.viewingPage === 'send' ? <SendToAddressSection /> : '' }
|
2016-09-01 08:40:23 +02:00
|
|
|
{ this.props.viewingPage === 'receive' ? <AddressSection /> : '' }
|
2016-07-19 00:40:15 +02:00
|
|
|
</main>
|
|
|
|
);
|
|
|
|
}
|
2016-08-22 18:16:43 +02:00
|
|
|
});
|
2016-11-22 21:19:08 +01:00
|
|
|
|
|
|
|
export default WalletPage;
|