cleaned up transaction history, added header subnav style

This commit is contained in:
Jeremy Kauffman 2016-08-27 10:12:56 -04:00
parent f538c9a775
commit b4dac55d26
8 changed files with 227 additions and 79 deletions

View file

@ -4,14 +4,11 @@ var App = React.createClass({
var match, param, val, viewingPage,
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 {
viewingPage: viewingPage ? viewingPage : 'discover',
viewingPage: viewingPage,
drawerOpen: drawerOpenRaw !== null ? JSON.parse(drawerOpenRaw) : true,
pageArgs: val,
};
@ -72,12 +69,28 @@ var App = React.createClass({
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()
{
switch(this.state.viewingPage)
{
case 'discover':
return <DiscoverPage query={this.state.pageArgs} />;
case 'settings':
return <SettingsPage />;
case 'help':
@ -93,23 +106,33 @@ var App = React.createClass({
case 'claim':
return <ClaimCodePage />;
case 'wallet':
return <WalletPage />;
case 'send':
case 'receive':
return <WalletPage viewingPage={this.state.viewingPage} />;
case 'send':
return <SendPage />;
case 'receive':
return <ReceivePage />;
case 'show':
return <DetailPage name={this.state.pageArgs} />;
case 'publish':
return <PublishPage />;
case 'discover':
default:
return <DiscoverPage query={this.state.pageArgs} />;
}
},
render: function() {
var mainContent = this.getMainContent();
var mainContent = this.getMainContent(),
headerLinks = this.getHeaderLinks();
return (
this.state.viewingPage == 'watch' ?
mainContent :
<div id="window" className={ this.state.drawerOpen ? 'drawer-open' : 'drawer-closed' }>
<Drawer onCloseDrawer={this.closeDrawer} viewingPage={this.state.viewingPage} />
<div id="main-content">
<Header onOpenDrawer={this.openDrawer} onSearch={this.onSearch} />
<div id="main-content" className={ headerLinks ? 'with-sub-nav' : 'no-sub-nav' }>
<Header onOpenDrawer={this.openDrawer} onSearch={this.onSearch} links={headerLinks} viewingPage={this.state.viewingPage} />
{mainContent}
</div>
</div>

View file

@ -77,9 +77,10 @@ var CurrencySymbol = React.createClass({
var CreditAmount = React.createClass({
propTypes: {
amount: React.PropTypes.number,
precision: React.PropTypes.number
},
render: function() {
var formattedAmount = lbry.formatCredits(this.props.amount);
var formattedAmount = lbry.formatCredits(this.props.amount, this.props.precision ? this.props.precision : 1);
return (
<span className="credit-amount">
<span style={creditAmountStyle}>{formattedAmount} {parseFloat(formattedAmount) == 1.0 ? 'credit' : 'credits'}</span>

View file

@ -44,14 +44,41 @@ var Header = React.createClass({
},
render: function() {
return (
<header id="header" className={this.state.isScrolled ? 'header-scrolled' : 'header-unscrolled'}>
<Link onClick={this.props.onOpenDrawer} icon="icon-bars" className="open-drawer-link" />
<h1>{ this.state.title }</h1>
<div className="header-search">
<input type="search" onChange={this.onQueryChange}
placeholder="Find movies, music, games, and more"/>
<header id="header" className={ (this.state.isScrolled ? 'header-scrolled' : 'header-unscrolled') + ' ' + (this.props.links ? 'header-with-subnav' : 'header-no-subnav') }>
<div className="header-top-bar">
<Link onClick={this.props.onOpenDrawer} icon="icon-bars" className="open-drawer-link" />
<h1>{ this.state.title }</h1>
<div className="header-search">
<input type="search" onChange={this.onQueryChange}
placeholder="Find movies, music, games, and more"/>
</div>
</div>
{
this.props.links ?
<SubHeader links={this.props.links} viewingPage={this.props.viewingPage} /> :
''
}
</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>
);
}
});

View file

@ -65,7 +65,7 @@ var ClaimCodePage = React.createClass({
});
},
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';
},
render: function() {

View file

@ -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({
propTypes: {
viewingPage: React.PropTypes.string,
},
componentDidMount: function() {
document.title = "My Wallet";
},
@ -116,8 +203,7 @@ var WalletPage = React.createClass({
*/
getInitialState: function() {
return {
balance: <BusyMessage message="Checking balance" />,
txlog: <BusyMessage message="Loading transactions" />,
balance: null,
}
},
componentWillMount: function() {
@ -126,66 +212,18 @@ var WalletPage = React.createClass({
balance: results,
})
});
lbry.call('get_transaction_history', {}, (results) => {
var out = '(You should never see this message. -- wallet.js WalletPage componentWillMount)'
if (results.length == 0) {
out = 'No transactions yet.';
} else {
var condensedTransactions = {};
var rows = [];
rows.push(<tr>
<th>Amount</th>
<th>Time</th>
<th>Date</th>
<th>Transaction</th>
</tr>);
results.forEach(function(tx) {
var txid = tx["txid"];
if (!(txid in condensedTransactions)) {
condensedTransactions[txid] = 0;
}
condensedTransactions[txid] += parseFloat(tx["amount"]);
});
results.reverse().forEach(function(tx) {
var txid = tx["txid"];
var txval = condensedTransactions[txid];
var txdate = new Date(parseInt(tx["time"])*1000);
if (txid in condensedTransactions && txval != 0) {
rows.push(<tr key={txid}>
<td>{ (txval>0 ? '+' : '' ) + txval }</td>
<td>{ txdate.toLocaleTimeString() }</td>
<td>{ txdate.toLocaleDateString() }</td>
<td>
<a className="transaction_explorer_link" href={"https://explorer.lbry.io/tx/"+txid}>{txid}</a>
</td>
</tr>);
delete condensedTransactions[tx["txid"]];
}
});
out = <table className="table-standard"><tbody>{rows}</tbody></table>
}
this.setState({
txlog: out,
})
});
},
render: function() {
return (
<main className="page">
<section className="card">
<h3>Balance</h3>
{this.state.balance} <CurrencySymbol />
</section>
<SendToAddressSection />
<NewAddressSection />
<section className="card">
<h3>Claim Invite Code</h3>
<Link href="?claim" label="Claim a LBRY beta invite code" button="alt" />
</section>
<section className="card" style={{'overflowX': 'auto'}}>
<h3>Transaction History</h3>
{this.state.txlog}
{ this.state.balance === null ? <BusyMessage message="Checking balance" /> : ''}
{ this.state.balance !== null ? <CreditAmount amount={this.state.balance} precision={8} /> : '' }
</section>
{ this.props.viewingPage === 'wallet' ? <TransactionList /> : '' }
{ this.props.viewingPage === 'send' ? <SendToAddressSection /> : '' }
{ this.props.viewingPage === 'receive' ? <NewAddressSection /> : '' }
</main>
);
}

View file

@ -67,7 +67,7 @@ $drawer-width: 240px;
#window.drawer-open
{
#main-content { margin-left: $drawer-width; }
.open-drawer-link { visibility: hidden; }
.open-drawer-link { display: none }
#header { padding-left: $drawer-width + $spacing-vertical / 2; }
}
@ -75,8 +75,12 @@ $drawer-width: 240px;
{
background: $color-primary;
color: white;
height: $header-height;
padding: $spacing-vertical / 2;
&.header-no-subnav {
height: $header-height;
}
&.header-with-subnav {
height: $header-height * 2;
}
position: fixed;
top: 0;
left: 0;
@ -89,6 +93,10 @@ $drawer-width: 240px;
box-shadow: $default-box-shadow;
}
}
.header-top-bar
{
padding: $spacing-vertical / 2;
}
.header-search
{
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
{
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
{
margin-top: $header-height;
padding: $spacing-vertical;
}
h2

View file

@ -201,6 +201,12 @@ input[type="text"], input[type="search"], textarea
color: $color-meta-light;
}
.empty
{
color: $color-meta-light;
font-style: italic;
}
.form-row
{
+ .form-row

View file

@ -47,4 +47,8 @@ table.table-standard {
}
}
}
}
table.table-stretch {
width: 100%;
}