Merge pull request #46 from lbryio/tx_log
Transaction log on Publish page
This commit is contained in:
commit
aa7abb2451
8 changed files with 231 additions and 36 deletions
45
js/app.js
45
js/app.js
|
@ -4,14 +4,11 @@ var App = React.createClass({
|
||||||
var match, param, val, viewingPage,
|
var match, param, val, viewingPage,
|
||||||
drawerOpenRaw = sessionStorage.getItem('drawerOpen');
|
drawerOpenRaw = sessionStorage.getItem('drawerOpen');
|
||||||
|
|
||||||
[match, param, val] = window.location.search.match(/\??([^=]*)(?:=(.*))?/);
|
[match, viewingPage, val] = window.location.search.match(/\??([^=]*)(?:=(.*))?/);
|
||||||
|
|
||||||
if (param && ['settings', 'discover', 'help', 'start', 'watch', 'report', 'files', 'claim', 'show', 'wallet', 'publish'].indexOf(param) != -1) {
|
|
||||||
viewingPage = param;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
viewingPage: viewingPage ? viewingPage : 'discover',
|
viewingPage: viewingPage,
|
||||||
drawerOpen: drawerOpenRaw !== null ? JSON.parse(drawerOpenRaw) : true,
|
drawerOpen: drawerOpenRaw !== null ? JSON.parse(drawerOpenRaw) : true,
|
||||||
pageArgs: val,
|
pageArgs: val,
|
||||||
};
|
};
|
||||||
|
@ -72,12 +69,28 @@ var App = React.createClass({
|
||||||
pageArgs: term
|
pageArgs: term
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
getHeaderLinks: function()
|
||||||
|
{
|
||||||
|
switch(this.state.viewingPage)
|
||||||
|
{
|
||||||
|
case 'wallet':
|
||||||
|
case 'send':
|
||||||
|
case 'receive':
|
||||||
|
case 'claim':
|
||||||
|
return {
|
||||||
|
'?wallet' : 'Overview',
|
||||||
|
'?send' : 'Send',
|
||||||
|
'?receive' : 'Receive',
|
||||||
|
'?claim' : 'Claim Beta Code'
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
getMainContent: function()
|
getMainContent: function()
|
||||||
{
|
{
|
||||||
switch(this.state.viewingPage)
|
switch(this.state.viewingPage)
|
||||||
{
|
{
|
||||||
case 'discover':
|
|
||||||
return <DiscoverPage query={this.state.pageArgs} />;
|
|
||||||
case 'settings':
|
case 'settings':
|
||||||
return <SettingsPage />;
|
return <SettingsPage />;
|
||||||
case 'help':
|
case 'help':
|
||||||
|
@ -93,23 +106,33 @@ var App = React.createClass({
|
||||||
case 'claim':
|
case 'claim':
|
||||||
return <ClaimCodePage />;
|
return <ClaimCodePage />;
|
||||||
case 'wallet':
|
case 'wallet':
|
||||||
return <WalletPage />;
|
case 'send':
|
||||||
|
case 'receive':
|
||||||
|
return <WalletPage viewingPage={this.state.viewingPage} />;
|
||||||
|
case 'send':
|
||||||
|
return <SendPage />;
|
||||||
|
case 'receive':
|
||||||
|
return <ReceivePage />;
|
||||||
case 'show':
|
case 'show':
|
||||||
return <DetailPage name={this.state.pageArgs} />;
|
return <DetailPage name={this.state.pageArgs} />;
|
||||||
case 'publish':
|
case 'publish':
|
||||||
return <PublishPage />;
|
return <PublishPage />;
|
||||||
|
case 'discover':
|
||||||
|
default:
|
||||||
|
return <DiscoverPage query={this.state.pageArgs} />;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
var mainContent = this.getMainContent();
|
var mainContent = this.getMainContent(),
|
||||||
|
headerLinks = this.getHeaderLinks();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
this.state.viewingPage == 'watch' ?
|
this.state.viewingPage == 'watch' ?
|
||||||
mainContent :
|
mainContent :
|
||||||
<div id="window" className={ this.state.drawerOpen ? 'drawer-open' : 'drawer-closed' }>
|
<div id="window" className={ this.state.drawerOpen ? 'drawer-open' : 'drawer-closed' }>
|
||||||
<Drawer onCloseDrawer={this.closeDrawer} viewingPage={this.state.viewingPage} />
|
<Drawer onCloseDrawer={this.closeDrawer} viewingPage={this.state.viewingPage} />
|
||||||
<div id="main-content">
|
<div id="main-content" className={ headerLinks ? 'with-sub-nav' : 'no-sub-nav' }>
|
||||||
<Header onOpenDrawer={this.openDrawer} onSearch={this.onSearch} />
|
<Header onOpenDrawer={this.openDrawer} onSearch={this.onSearch} links={headerLinks} viewingPage={this.state.viewingPage} />
|
||||||
{mainContent}
|
{mainContent}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -77,9 +77,10 @@ var CurrencySymbol = React.createClass({
|
||||||
var CreditAmount = React.createClass({
|
var CreditAmount = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
amount: React.PropTypes.number,
|
amount: React.PropTypes.number,
|
||||||
|
precision: React.PropTypes.number
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
var formattedAmount = lbry.formatCredits(this.props.amount);
|
var formattedAmount = lbry.formatCredits(this.props.amount, this.props.precision ? this.props.precision : 1);
|
||||||
return (
|
return (
|
||||||
<span className="credit-amount">
|
<span className="credit-amount">
|
||||||
<span style={creditAmountStyle}>{formattedAmount} {parseFloat(formattedAmount) == 1.0 ? 'credit' : 'credits'}</span>
|
<span style={creditAmountStyle}>{formattedAmount} {parseFloat(formattedAmount) == 1.0 ? 'credit' : 'credits'}</span>
|
||||||
|
|
|
@ -44,14 +44,41 @@ var Header = React.createClass({
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
return (
|
return (
|
||||||
<header id="header" className={this.state.isScrolled ? 'header-scrolled' : 'header-unscrolled'}>
|
<header id="header" className={ (this.state.isScrolled ? 'header-scrolled' : 'header-unscrolled') + ' ' + (this.props.links ? 'header-with-subnav' : 'header-no-subnav') }>
|
||||||
<Link onClick={this.props.onOpenDrawer} icon="icon-bars" className="open-drawer-link" />
|
<div className="header-top-bar">
|
||||||
<h1>{ this.state.title }</h1>
|
<Link onClick={this.props.onOpenDrawer} icon="icon-bars" className="open-drawer-link" />
|
||||||
<div className="header-search">
|
<h1>{ this.state.title }</h1>
|
||||||
<input type="search" onChange={this.onQueryChange}
|
<div className="header-search">
|
||||||
placeholder="Find movies, music, games, and more"/>
|
<input type="search" onChange={this.onQueryChange}
|
||||||
|
placeholder="Find movies, music, games, and more"/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{
|
||||||
|
this.props.links ?
|
||||||
|
<SubHeader links={this.props.links} viewingPage={this.props.viewingPage} /> :
|
||||||
|
''
|
||||||
|
}
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var SubHeader = React.createClass({
|
||||||
|
render: function() {
|
||||||
|
var links = [],
|
||||||
|
viewingUrl = '?' + this.props.viewingPage;
|
||||||
|
|
||||||
|
for (let link of Object.keys(this.props.links)) {
|
||||||
|
links.push(
|
||||||
|
<a href={link} key={link} className={ viewingUrl == link ? 'sub-header-selected' : 'sub-header-unselected' }>
|
||||||
|
{this.props.links[link]}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<nav className="sub-header">
|
||||||
|
{links}
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
|
@ -65,7 +65,7 @@ var ClaimCodePage = React.createClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleSkip: function() {
|
handleSkip: function() {
|
||||||
alert('Welcome to LBRY! You can visit the Settings page to redeem an invite code at any time.');
|
alert('Welcome to LBRY! You can visit the Wallet page to redeem an invite code at any time.');
|
||||||
window.location = '?landing';
|
window.location = '?landing';
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
|
|
|
@ -57,7 +57,7 @@ var SendToAddressSection = React.createClass({
|
||||||
return {
|
return {
|
||||||
address: "",
|
address: "",
|
||||||
amount: 0.0,
|
amount: 0.0,
|
||||||
balance: "Checking balance...",
|
balance: <BusyMessage message="Checking balance" />,
|
||||||
results: "",
|
results: "",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -106,7 +106,94 @@ var SendToAddressSection = React.createClass({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var TransactionList = React.createClass({
|
||||||
|
getInitialState: function() {
|
||||||
|
return {
|
||||||
|
transactionItems: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
componentWillMount: function() {
|
||||||
|
lbry.call('get_transaction_history', {}, (results) => {
|
||||||
|
if (results.length == 0) {
|
||||||
|
this.setState({ transactionItems: [] })
|
||||||
|
} else {
|
||||||
|
var transactionItems = [],
|
||||||
|
condensedTransactions = {};
|
||||||
|
|
||||||
|
results.forEach(function(tx) {
|
||||||
|
var txid = tx["txid"];
|
||||||
|
if (!(txid in condensedTransactions)) {
|
||||||
|
condensedTransactions[txid] = 0;
|
||||||
|
}
|
||||||
|
condensedTransactions[txid] += parseFloat(tx["value"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
results.reverse().forEach(function(tx) {
|
||||||
|
var txid = tx["txid"];
|
||||||
|
if (condensedTransactions[txid] && condensedTransactions[txid] > 0)
|
||||||
|
{
|
||||||
|
transactionItems.push({
|
||||||
|
id: txid,
|
||||||
|
date: new Date(parseInt(tx["timestamp"]) * 1000),
|
||||||
|
amount: condensedTransactions[txid]
|
||||||
|
});
|
||||||
|
delete condensedTransactions[txid];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setState({ transactionItems: transactionItems });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
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>
|
||||||
|
<td>{ item.date.toLocaleDateString() }</td>
|
||||||
|
<td>{ item.date.toLocaleTimeString() }</td>
|
||||||
|
<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({
|
var WalletPage = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
viewingPage: React.PropTypes.string,
|
||||||
|
},
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
document.title = "My Wallet";
|
document.title = "My Wallet";
|
||||||
},
|
},
|
||||||
|
@ -116,14 +203,14 @@ var WalletPage = React.createClass({
|
||||||
*/
|
*/
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
return {
|
return {
|
||||||
balance: "Checking balance...",
|
balance: null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
componentWillMount: function() {
|
componentWillMount: function() {
|
||||||
lbry.getBalance((results) => {
|
lbry.getBalance((results) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
balance: results,
|
balance: results,
|
||||||
});
|
})
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
|
@ -131,15 +218,13 @@ var WalletPage = React.createClass({
|
||||||
<main className="page">
|
<main className="page">
|
||||||
<section className="card">
|
<section className="card">
|
||||||
<h3>Balance</h3>
|
<h3>Balance</h3>
|
||||||
{this.state.balance} <CurrencySymbol />
|
{ this.state.balance === null ? <BusyMessage message="Checking balance" /> : ''}
|
||||||
</section>
|
{ this.state.balance !== null ? <CreditAmount amount={this.state.balance} precision={8} /> : '' }
|
||||||
<SendToAddressSection />
|
|
||||||
<NewAddressSection />
|
|
||||||
<section className="card">
|
|
||||||
<h3>Claim Invite Code</h3>
|
|
||||||
<Link href="?claim" label="Claim a LBRY beta invite code" button="alt" />
|
|
||||||
</section>
|
</section>
|
||||||
|
{ this.props.viewingPage === 'wallet' ? <TransactionList /> : '' }
|
||||||
|
{ this.props.viewingPage === 'send' ? <SendToAddressSection /> : '' }
|
||||||
|
{ this.props.viewingPage === 'receive' ? <NewAddressSection /> : '' }
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -67,7 +67,7 @@ $drawer-width: 240px;
|
||||||
#window.drawer-open
|
#window.drawer-open
|
||||||
{
|
{
|
||||||
#main-content { margin-left: $drawer-width; }
|
#main-content { margin-left: $drawer-width; }
|
||||||
.open-drawer-link { visibility: hidden; }
|
.open-drawer-link { display: none }
|
||||||
#header { padding-left: $drawer-width + $spacing-vertical / 2; }
|
#header { padding-left: $drawer-width + $spacing-vertical / 2; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,8 +75,12 @@ $drawer-width: 240px;
|
||||||
{
|
{
|
||||||
background: $color-primary;
|
background: $color-primary;
|
||||||
color: white;
|
color: white;
|
||||||
height: $header-height;
|
&.header-no-subnav {
|
||||||
padding: $spacing-vertical / 2;
|
height: $header-height;
|
||||||
|
}
|
||||||
|
&.header-with-subnav {
|
||||||
|
height: $header-height * 2;
|
||||||
|
}
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
@ -89,6 +93,10 @@ $drawer-width: 240px;
|
||||||
box-shadow: $default-box-shadow;
|
box-shadow: $default-box-shadow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.header-top-bar
|
||||||
|
{
|
||||||
|
padding: $spacing-vertical / 2;
|
||||||
|
}
|
||||||
.header-search
|
.header-search
|
||||||
{
|
{
|
||||||
margin-left: 60px;
|
margin-left: 60px;
|
||||||
|
@ -101,13 +109,54 @@ $drawer-width: 240px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nav.sub-header
|
||||||
|
{
|
||||||
|
background: $color-primary;
|
||||||
|
text-transform: uppercase;
|
||||||
|
padding: $spacing-vertical / 2;
|
||||||
|
> a
|
||||||
|
{
|
||||||
|
$sub-header-selected-underline-height: 2px;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 15px;
|
||||||
|
padding: 0 5px;
|
||||||
|
line-height: $header-height - $spacing-vertical - $sub-header-selected-underline-height;
|
||||||
|
color: #e8e8e8;
|
||||||
|
&:first-child
|
||||||
|
{
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
&:last-child
|
||||||
|
{
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
&.sub-header-selected
|
||||||
|
{
|
||||||
|
border-bottom: $sub-header-selected-underline-height solid #fff;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
&:hover
|
||||||
|
{
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#main-content
|
#main-content
|
||||||
{
|
{
|
||||||
background: $color-canvas;
|
background: $color-canvas;
|
||||||
min-height: calc(100vh - 60px); //should be -$header-height, but I'm dumb I guess? It wouldn't work
|
&.no-sub-nav
|
||||||
|
{
|
||||||
|
min-height: calc(100vh - 60px); //should be -$header-height, but I'm dumb I guess? It wouldn't work
|
||||||
|
main { margin-top: $header-height; }
|
||||||
|
}
|
||||||
|
&.with-sub-nav
|
||||||
|
{
|
||||||
|
min-height: calc(100vh - 120px); //should be -$header-height, but I'm dumb I guess? It wouldn't work
|
||||||
|
main { margin-top: $header-height * 2; }
|
||||||
|
}
|
||||||
main
|
main
|
||||||
{
|
{
|
||||||
margin-top: $header-height;
|
|
||||||
padding: $spacing-vertical;
|
padding: $spacing-vertical;
|
||||||
}
|
}
|
||||||
h2
|
h2
|
||||||
|
|
|
@ -195,6 +195,12 @@ input[type="text"], input[type="search"]
|
||||||
color: $color-meta-light;
|
color: $color-meta-light;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.empty
|
||||||
|
{
|
||||||
|
color: $color-meta-light;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
.form-row
|
.form-row
|
||||||
{
|
{
|
||||||
+ .form-row
|
+ .form-row
|
||||||
|
@ -209,4 +215,4 @@ input[type="text"], input[type="search"]
|
||||||
{
|
{
|
||||||
margin-top: $spacing-vertical;
|
margin-top: $spacing-vertical;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,4 +46,8 @@ table.table-standard {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table.table-stretch {
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
Loading…
Reference in a new issue