Browse Source

Transaction history improvements - WIP

all-modes
petitPapillon 8 years ago
parent
commit
394f34e46f
  1. 1
      react/package.json
  2. 14
      react/src/components/dashboard/main/dashboard.scss
  3. 2
      react/src/components/dashboard/walletsBalance/walletsBalance.js
  4. 45
      react/src/components/dashboard/walletsData/pagination.js
  5. 103
      react/src/components/dashboard/walletsData/pagination.render.js
  6. 181
      react/src/components/dashboard/walletsData/walletsData.js
  7. 173
      react/src/components/dashboard/walletsData/walletsData.render.js
  8. 3
      react/src/styles/index.scss
  9. 4
      react/src/translate/en.js

1
react/package.json

@ -45,6 +45,7 @@
"react-redux": "^5.0.3",
"react-router": "^3.0.2",
"react-router-redux": "^4.0.4",
"react-table": "~6.5.1",
"react-transform-catch-errors": "^1.0.2",
"react-transform-hmr": "^1.0.4",
"redux": "^3.6.0",

14
react/src/components/dashboard/main/dashboard.scss

@ -133,4 +133,18 @@
// walletsNativeTxInfo.js
.height-170 {
height: 170px;
}
.rt-tr.-odd div,
.rt-tr.-even div {
padding-top: 2px;
padding-bottom: 2px;
}
.rt-tr-group.-odd .-padRow {
display: none;
}
.ReactTable .rt-tfoot .rt-td {
text-align: center;
}

2
react/src/components/dashboard/walletsBalance/walletsBalance.js

@ -95,7 +95,7 @@ class WalletsBalance extends React.Component {
const _translationComponents = translate(_translationID).split('<br>');
return _translationComponents.map((_translation) =>
<span>
<span key={_translation}>
{_translation}
<br />
</span>

45
react/src/components/dashboard/walletsData/pagination.js

@ -0,0 +1,45 @@
import React, { Component } from 'react';
import PaginationRender from './pagination.render';
export default class TablePaginationRenderer extends Component {
constructor (props) {
super();
this.getSafePage = this.getSafePage.bind(this);
this.changePage = this.changePage.bind(this);
this.applyPage = this.applyPage.bind(this);
this.state = {
page: props.page
}
}
componentWillReceiveProps (nextProps) {
this.setState({ page: nextProps.page });
}
getSafePage (page) {
if (isNaN(page)) {
page = this.props.page;
}
return Math.min(Math.max(page, 0), this.props.pages - 1);
}
changePage (page) {
page = this.getSafePage(page);
this.setState({ page });
if (this.props.page !== page) {
this.props.onPageChange(page);
}
}
applyPage (e) {
e && e.preventDefault();
const page = this.state.page;
this.changePage(page === '' ? this.props.page : page);
}
render () {
return PaginationRender.call(this);
}
}

103
react/src/components/dashboard/walletsData/pagination.render.js

@ -0,0 +1,103 @@
import React from 'react';
import classnames from 'classnames';
const defaultButton = props =>
<button type='button' {...props} className='-btn'>
{props.children}
</button>
const PaginationRender = function() {
const {
// Computed
pages,
// Props
page,
showPageSizeOptions,
pageSizeOptions,
pageSize,
showPageJump,
canPrevious,
canNext,
onPageSizeChange,
className,
PreviousComponent = defaultButton,
NextComponent = defaultButton,
} = this.props;
return (
<div
className={classnames(className, '-pagination')}
style={this.props.paginationStyle}
>
<div className='-previous'>
<PreviousComponent
onClick={e => {
if (!canPrevious) return;
this.changePage(page - 1)
}}
disabled={!canPrevious}
>
{this.props.previousText}
</PreviousComponent>
</div>
<div className='-center'>
<span className='-pageInfo'>
{this.props.pageText}{' '}
{showPageJump
?
<div className='-pageJump'>
<input
type={this.state.page === '' ? 'text' : 'number'}
onChange={e => {
const val = e.target.value;
console.error('onchange', val);
this.changePage(val - 1);
}}
value={this.state.page === '' ? '' : this.state.page + 1}
onBlur={this.applyPage}
onKeyPress={e => {
if (e.which === 13 || e.keyCode === 13) {
this.applyPage();
}
}}
/>
</div>
:
<span className='-currentPage'>
{page + 1}
</span>}{' '}
{this.props.ofText}{' '}
<span className='-totalPages'>{pages || 1}</span>
</span>
{showPageSizeOptions &&
<span className='select-wrap -pageSizeOptions'>
<select
onChange={e => onPageSizeChange(Number(e.target.value))}
value={pageSize}
>
{pageSizeOptions.map((option, i) => {
return (
<option key={i} value={option}>
{option} {this.props.rowsText}
</option>
)
})}
</select>
</span>}
</div>
<div className='-next'>
<NextComponent
onClick={e => {
if (!canNext) return;
this.changePage(page + 1)
}}
disabled={!canNext}
>
{this.props.nextText}
</NextComponent>
</div>
</div>
)
};
export default PaginationRender;

181
react/src/components/dashboard/walletsData/walletsData.js

@ -20,13 +20,14 @@ import {
} from '../../../actions/actionCreators';
import Store from '../../../store';
import {
PaginationItemRender,
PaginationItemsPerPageSelectorRender,
PaginationRender,
AddressTypeRender,
TransactionDetailRender,
AddressItemRender,
TxHistoryListRender,
AddressListRender,
WalletsDataRender
} from './walletsData.render';
import { secondsToString } from '../../../util/time';
import { SocketProvider } from 'socket.io-react';
import io from 'socket.io-client';
@ -46,8 +47,9 @@ class WalletsData extends React.Component {
currentStackLength: 0,
totalStackLength: 0,
useCache: true,
itemsListColumns: this.generateItemsListColumns()
};
this.updateInput = this.updateInput.bind(this);
this.toggleBasiliskActionsMenu = this.toggleBasiliskActionsMenu.bind(this);
this.basiliskRefreshAction = this.basiliskRefreshAction.bind(this);
this.basiliskConnectionAction = this.basiliskConnectionAction.bind(this);
@ -79,6 +81,78 @@ class WalletsData extends React.Component {
);
}
generateItemsListColumns() {
let columns = [];
if (this.isNativeMode()) {
columns.push({
Header: translate('INDEX.TYPE'),
Footer: translate('INDEX.TYPE'),
Cell: AddressTypeRender()
});
}
columns.push(...[
{
id: 'direction',
Header: translate('INDEX.DIRECTION'),
Footer: translate('INDEX.DIRECTION'),
accessor: (tx) => this.renderTxType(tx.category || tx.type)
},
{
Header: translate('INDEX.CONFIRMATIONS'),
Footer: translate('INDEX.CONFIRMATIONS'),
headerClassName: 'hidden-xs hidden-sm',
footerClassName: 'hidden-xs hidden-sm',
className: 'hidden-xs hidden-sm',
accessor: 'confirmations'
},
{
id: 'amount',
Header: translate('INDEX.AMOUNT'),
Footer: translate('INDEX.AMOUNT'),
accessor: (tx) => tx.amount || translate('DASHBOARD.UNKNOWN')
},
{
id: 'timestamp',
Header: translate('INDEX.TIME'),
Footer: translate('INDEX.TIME'),
accessor: (tx) => secondsToString(tx.blocktime || tx.timestamp || tx.time)
}
]);
if (this.isFullMode()) {
columns.push({
Header: translate('INDEX.DEST_ADDRESS'),
Footer: translate('INDEX.DEST_ADDRESS'),
accessor: 'address'
});
}
if (this.isNativeMode()) {
columns.push({
id: 'destination-address',
Header: translate('INDEX.DEST_ADDRESS'),
Footer: translate('INDEX.DEST_ADDRESS'),
accessor: (tx) => this.renderAddress(tx)
});
}
const txDetailColumnCssClasses = this.isBasiliskMode() ? 'hidden-xs hidden-sm text-center' : 'hidden-xs hidden-sm';
columns.push({
id: 'tx-detail',
Header: translate('INDEX.TX_DETAIL'),
Footer: translate('INDEX.TX_DETAIL'),
headerClassName: txDetailColumnCssClasses,
footerClassName: txDetailColumnCssClasses,
className: txDetailColumnCssClasses,
Cell: props => TransactionDetailRender.call(this, props.index)
});
return columns;
}
handleClickOutside(e) {
if (e.srcElement.className !== 'btn dropdown-toggle btn-info' &&
(e.srcElement.offsetParent && e.srcElement.offsetParent.className !== 'btn dropdown-toggle btn-info') &&
@ -193,17 +267,6 @@ class WalletsData extends React.Component {
Store.dispatch(displayNotariesModal(true));
}
updateInput(e) {
let historyToSplit = sortByDate(this.props.ActiveCoin.txhistory);
historyToSplit = historyToSplit.slice(0, e.target.value);
this.setState({
[e.target.name]: e.target.value,
activePage: 1,
itemsList: historyToSplit,
});
}
toggleTxInfoModal(display, txIndex) {
Store.dispatch(toggleDashboardTxInfoModal(display, txIndex));
}
@ -223,14 +286,8 @@ class WalletsData extends React.Component {
if (!this.state.itemsList ||
(this.state.itemsList && !this.state.itemsList.length) ||
(props.ActiveCoin.txhistory !== this.props.ActiveCoin.txhistory)) {
let historyToSplit = sortByDate(this.props.ActiveCoin.txhistory);
historyToSplit = historyToSplit.slice(
(this.state.activePage - 1) * this.state.itemsPerPage,
this.state.activePage * this.state.itemsPerPage
);
this.setState(Object.assign({}, this.state, {
itemsList: historyToSplit,
itemsList: sortByDate(this.props.ActiveCoin.txhistory),
}));
}
}
@ -245,58 +302,10 @@ class WalletsData extends React.Component {
itemsList: 'loading',
}));
}
}
updateCurrentPage(page) {
let historyToSplit = sortByDate(this.props.ActiveCoin.txhistory);
historyToSplit = historyToSplit.slice(
(page - 1) * this.state.itemsPerPage,
page * this.state.itemsPerPage
);
this.setState(Object.assign({}, this.state, {
activePage: page,
itemsList: historyToSplit,
}));
}
renderPaginationItems() {
let items = [];
for (let i = 0; i < Math.ceil(this.props.ActiveCoin.txhistory.length / this.state.itemsPerPage); i++) {
items.push(
PaginationItemRender.call(this, i)
);
}
return items;
}
renderPaginationItemsPerPageSelector() {
if (this.props.ActiveCoin.txhistory &&
this.state.itemsList !== 'loading' &&
this.props.ActiveCoin.txhistory.length > 10) {
return PaginationItemsPerPageSelectorRender.call(this);
} else {
return null;
}
}
renderPagination() {
if (this.props.ActiveCoin.txhistory &&
this.state.itemsList !== 'loading' &&
this.props.ActiveCoin.txhistory.length > 10) {
const _paginationFrom = ((this.state.activePage - 1) * this.state.itemsPerPage) + 1;
const _paginationTo = this.state.activePage * this.state.itemsPerPage;
return PaginationRender.call(
this,
_paginationFrom,
_paginationTo
);
} else {
return null;
}
this.setState({
itemsListColumns: this.generateItemsListColumns()
});
}
renderTxType(category) {
@ -362,19 +371,9 @@ class WalletsData extends React.Component {
return (
<div>{ translate('INDEX.NO_DATA') }</div>
);
} else {
if (this.state.itemsList &&
this.state.itemsList.length &&
this.state.itemsList !== 'no data') {
return this.state.itemsList.map((tx, index) =>
TxHistoryListRender.call(
this,
tx,
index
)
);
}
}
return TxHistoryListRender.call(this);
}
updateAddressSelection(address, type, amount) {
@ -444,13 +443,7 @@ class WalletsData extends React.Component {
}
items.push(
<li key={address}>
<a onClick={ () => this.updateAddressSelection(address, type, _amount) }>
<i className={ 'icon fa-eye' + (type === 'public' ? '' : '-slash') }></i>&nbsp;&nbsp;
<span className="text">[ { _amount } { _coin } ]{ address }</span>
<span className="glyphicon glyphicon-ok check-mark"></span>
</a>
</li>
AddressItemRender.call(this, address, type, _amount, _coin)
);
}
@ -479,9 +472,7 @@ class WalletsData extends React.Component {
return _addresses.public[i].amount;
} else {
const address = _addresses.public[i].address;
const _amount = _cache && _cache[_coin] && _cache[_coin][address] && _cache[_coin][address].getbalance.data && _cache[_coin][address].getbalance.data.balance ? _cache[_coin][address].getbalance.data.balance : 'N/A';
return _amount;
return _cache && _cache[_coin] && _cache[_coin][address] && _cache[_coin][address].getbalance.data && _cache[_coin][address].getbalance.data.balance ? _cache[_coin][address].getbalance.data.balance : 'N/A';
}
}
}

173
react/src/components/dashboard/walletsData/walletsData.render.js

@ -4,102 +4,41 @@ import WalletsBasiliskRefresh from '../walletsBasiliskRefresh/walletsBasiliskRef
import WalletsBasiliskConnection from '../walletsBasiliskConnection/walletsBasiliskConnection';
import WalletsNotariesList from '../walletsNotariesList/walletsNotariesList';
import WalletsCacheData from '../walletsCacheData/walletsCacheData';
import { secondsToString } from '../../../util/time';
import ReactTable from 'react-table';
import TablePaginationRenderer from './pagination';
// TODO: clean basilisk dropdown menu
export const PaginationItemRender = function(i) {
export const AddressTypeRender = function() {
return (
<li
key={ `${i}-pagination-link` }
className={ 'paginate_button' + (this.state.activePage === i + 1 ? ' active' : '') }>
<a
key={ `${i}-pagination` }
onClick={ this.state.activePage !== (i + 1) ? () => this.updateCurrentPage(i + 1) : null }>{ i + 1 }</a>
</li>
<td>
<span className="label label-default">
<i className="icon fa-eye"></i> { translate('IAPI.PUBLIC_SM') }
</span>
</td>
);
};
export const PaginationItemsPerPageSelectorRender = function() {
export const TransactionDetailRender = function(transactionIndex) {
return (
<div className="dataTables_length">
<label>
{ translate('INDEX.SHOW') }&nbsp;
<select
name="itemsPerPage"
className="form-control input-sm"
onChange={ this.updateInput }>
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
</select>&nbsp;
{ translate('INDEX.ENTRIES_SM') }
</label>
</div>
<button
type="button"
className="btn btn-xs white btn-info waves-effect waves-light btn-kmdtxid"
onClick={ () => this.toggleTxInfoModal(!this.props.ActiveCoin.showTransactionInfo, transactionIndex) }>
<i className="icon fa-search"></i>
</button>
);
};
export const PaginationRender = function(paginationFrom, paginationTo) {
const disableNextBtn = this.state.activePage >= Math.floor(this.props.ActiveCoin.txhistory.length / this.state.itemsPerPage);
export const AddressItemRender = function(address, type, amount, coin) {
return (
<div className="row unselectable">
<div className="col-sm-5">
<div className="dataTables_info">
{ translate('INDEX.SHOWING') }&nbsp;
{ paginationFrom }&nbsp;
{ translate('INDEX.TO_ALT') }&nbsp;
{ paginationTo }&nbsp;
{ translate('INDEX.OF') }&nbsp;
{ this.props.ActiveCoin.txhistory.length }&nbsp;
{ translate('INDEX.ENTRIES_SM') }
</div>
</div>
<div className="col-sm-7">
<div className="dataTables_paginate paging_simple_numbers">
<ul className="pagination">
<li className={ 'paginate_button previous' + (this.state.activePage === 1 ? ' disabled' : '') }>
<a onClick={ () => this.updateCurrentPage(this.state.activePage - 1) }>{ translate('INDEX.PREVIOUS') }</a>
</li>
{ this.renderPaginationItems() }
<li className={ 'paginate_button next' + (disableNextBtn ? ' disabled' : '') }>
<a onClick={ () => this.updateCurrentPage(this.state.activePage + 1) }>{ translate('INDEX.NEXT') }</a>
</li>
</ul>
</div>
</div>
</div>
);
};
export const TxHistoryListRender = function(tx, index) {
return (
<tr key={ tx.txid + tx.amount }>
{ this.isNativeMode() ?
<td>
<span className="label label-default">
<i className="icon fa-eye"></i> { translate('IAPI.PUBLIC_SM') }
</span>
</td>
:
null
}
<td>{ this.renderTxType(tx.category || tx.type) }</td>
<td>{ tx.confirmations }</td>
<td>{ tx.amount || translate('DASHBOARD.UNKNOWN') }</td>
<td>{ secondsToString(tx.blocktime || tx.timestamp || tx.time) }</td>
<td className={ this.isFullMode() ? '' : 'hide' }>{ tx.address }</td>
<td className={ this.isNativeMode() ? '' : 'hide' }>{ this.renderAddress(tx) }</td>
<td className={ this.isBasiliskMode() ? 'text-center' : '' }>
<button
type="button"
className="btn btn-xs white btn-info waves-effect waves-light btn-kmdtxid"
onClick={ () => this.toggleTxInfoModal(!this.props.ActiveCoin.showTransactionInfo, ((this.state.activePage - 1) * this.state.itemsPerPage) + index) }>
<i className="icon fa-search"></i>
</button>
</td>
</tr>
<li key={address}>
<a onClick={ () => this.updateAddressSelection(address, type, amount) }>
<i className={ 'icon fa-eye' + (type === 'public' ? '' : '-slash') }></i>&nbsp;&nbsp;
<span className="text">[ { amount } { coin } ]{ address }</span>
<span className="glyphicon glyphicon-ok check-mark"></span>
</a>
</li>
);
};
@ -128,6 +67,21 @@ export const AddressListRender = function() {
);
};
export const TxHistoryListRender = function() {
return (
<ReactTable
data={this.state.itemsList}
columns={this.state.itemsListColumns}
sortable={true}
filterable={true}
className='-striped -highlight'
PaginationComponent={TablePaginationRenderer}
nextText={translate('INDEX.NEXT_PAGE')}
previousText={translate('INDEX.PREVIOUS_PAGE')}
/>
);
};
export const WalletsDataRender = function() {
return (
<span>
@ -210,58 +164,9 @@ export const WalletsDataRender = function() {
{ this.renderAddressList() }
</div>
</div>
<div className="row pagination-container">
<div className="col-sm-6">
{ this.renderPaginationItemsPerPageSelector() }
</div>
<div className="col-sm-6">
<div className="dataTables_filter">
<label>
{ translate('INDEX.SEARCH') }: <input type="search" className="form-control input-sm" disabled="true" />
</label>
</div>
</div>
</div>
<div className="row">
<table
className="table table-hover dataTable table-striped"
width="100%">
<thead>
<tr>
{ this.isNativeMode() ?
<th>{ translate('INDEX.TYPE') }</th>
:
null
}
<th>{ translate('INDEX.DIRECTION') }</th>
<th className="hidden-xs hidden-sm">{ translate('INDEX.CONFIRMATIONS') }</th>
<th>{ translate('INDEX.AMOUNT') }</th>
<th>{ translate('INDEX.TIME') }</th>
<th className={ this.isBasiliskMode() ? 'hide' : '' }>{ translate('INDEX.DEST_ADDRESS') }</th>
<th className={ this.isBasiliskMode() ? 'hidden-xs hidden-sm text-center' : 'hidden-xs hidden-sm' }>{ translate('INDEX.TX_DETAIL') }</th>
</tr>
</thead>
<tbody>
{ this.renderTxHistoryList() }
</tbody>
<tfoot>
<tr>
{ this.isNativeMode() ?
<th>{ translate('INDEX.TYPE') }</th>
:
null
}
<th>{ translate('INDEX.DIRECTION') }</th>
<th>{ translate('INDEX.CONFIRMATIONS') }</th>
<th>{ translate('INDEX.AMOUNT') }</th>
<th>{ translate('INDEX.TIME') }</th>
<th className={ this.isBasiliskMode() ? 'hide' : '' }>{ translate('INDEX.DEST_ADDRESS') }</th>
<th className={ this.isBasiliskMode() ? 'hidden-xs hidden-sm text-center' : '' }>{ translate('INDEX.TX_DETAIL') }</th>
</tr>
</tfoot>
</table>
{ this.renderTxHistoryList() }
</div>
{ this.renderPagination() }
</div>
</div>
</div>

3
react/src/styles/index.scss

@ -33,4 +33,5 @@
@import '../components/addcoin/addcoin.scss';
@import '../components/dashboard/main/dashboard.scss';
@import '../components/login/login.scss';
@import '../components/overrides.scss';
@import '../components/overrides.scss';
@import '~react-table/react-table.css';

4
react/src/translate/en.js

@ -249,7 +249,9 @@ export const _lang = {
'INFO': 'Info',
'ENTER': 'Enter',
'ADDR_SM': 'address',
'ACTIVATING': 'Activating'
'ACTIVATING': 'Activating',
'NEXT_PAGE': 'Next Page',
'PREVIOUS_PAGE': 'Previous Page'
},
'ATOMIC': {
'RAW_OUTPUT': 'Raw Output',

Loading…
Cancel
Save