Skip to content

Commit

Permalink
Merge pull request #867 from LiskHQ/818-show-votes-of-non-delegate-ac…
Browse files Browse the repository at this point in the history
…count

Show votes of non delegate account - Closes #818
  • Loading branch information
michaeltomasik committed Jun 7, 2018
2 parents f39452f + ea98e4e commit 62f4de7
Show file tree
Hide file tree
Showing 11 changed files with 210 additions and 135 deletions.
2 changes: 2 additions & 0 deletions i18n/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"2nd passphrase (Fee: 5 LSK)": "2nd passphrase (Fee: 5 LSK)",
"About": "About",
"Access extra features": "Access extra features",
"Account Info": "Account Info",
"Account initialization": "Account initialization",
"Active": "Active",
"Activity": "Activity",
Expand Down Expand Up @@ -118,6 +119,7 @@
"ID lock in": "ID lock in",
"In": "In",
"Incoming": "Incoming",
"Info": "Info",
"Initialize Lisk ID": "Initialize Lisk ID",
"Input": "Input",
"Insufficient funds": "Insufficient funds",
Expand Down
86 changes: 86 additions & 0 deletions src/components/transactions/accountList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { FontIcon } from '../fontIcon';
import routes from './../../constants/routes';
import styles from './delegateStatistics.css';

class AccountList extends React.Component {
constructor(props) {
super(props);
this.state = {
showVotesNumber: 35,
showVotersNumber: 35,
loadAllVotes: false,
votersFilterQuery: '',
votesFilterQuery: '',
votersSize: (props.voters && props.voters.length) || 0,
votesSize: (props.votes && props.votes.length) || 0,
};
}

showMore(name, amount = 100) {
const newAmount = this.state[name] + amount;
this.setState({ [name]: newAmount });
}

filterList(data, filterQuery) {
if (this.state[filterQuery] !== '') {
data = data.filter((obj) => {
const name = obj.props.children.trim();
return name.includes(this.state[filterQuery]);
});
}
return data;
}

getFormatedDelegates(dataName, filterQuery) {
const data = this.props[dataName] ? this.props[dataName].map((user, key) => (
<Link className={`${styles.addressLink} ${styles.clickable} voter-address ${filterQuery}-row`}
to={`${routes.explorer.path}${routes.accounts.path}/${user.address}`}
key={`${key}-${dataName}`}>
{`${user.username || user.address} `}
</Link>
)) : [];

return this.filterList(data, filterQuery);
}

search(filterName, e) {
const { value } = e.target;
this.setState({
[filterName]: value,
});
}

clearSearch(filterQuery) {
this.search(filterQuery, { target: { value: '' } });
}

renderSearchFilter(filterQuery, placeholder) {
return (
<div className={`${styles.search} ${styles.filter} search ${filterQuery}`}>
<FontIcon className={styles.search} value='search' id='searchIcon'/>
<input type='text'
name='query'
className={`search ${styles.desktopInput} ${this.state[filterQuery].length > 0 ? styles.dirty : ''} `}
value={this.state[filterQuery]}
onChange={this.search.bind(this, filterQuery)}
placeholder={placeholder}/>
<input type='text'
name='query'
className={`${styles.mobileInput} ${this.state[filterQuery].length > 0 ? styles.dirty : ''} `}
value={this.state[filterQuery]}
onChange={this.search.bind(this, filterQuery)}
placeholder={this.props.t('Filter')}/>
<FontIcon
id='cleanIcon'
className={`${styles.clean} clean-icon`}
value='close'
onClick={ this.clearSearch.bind(this, filterQuery) }/>
</div>
);
}
}

export default AccountList;

2 changes: 2 additions & 0 deletions src/components/transactions/delegateStatistics.css
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,8 @@

@media (--xSmall-viewport) {
.details {
padding-bottom: 40px;

& .row {
padding-bottom: 0px;

Expand Down
130 changes: 5 additions & 125 deletions src/components/transactions/delegateStatistics.js
Original file line number Diff line number Diff line change
@@ -1,92 +1,13 @@
import React from 'react';
import grid from 'flexboxgrid/dist/flexboxgrid.css';
import { Link } from 'react-router-dom';
import { translate } from 'react-i18next';
import { FontIcon } from '../fontIcon';
import routes from './../../constants/routes';
import UserVotes from './userVotes';
import VotedDelegates from './votedDelegates';
import styles from './delegateStatistics.css';

class DelegateStatistics extends React.Component {
constructor(props) {
super(props);
this.state = {
showVotesNumber: 35,
showVotersNumber: 35,
loadAllVotes: false,
votersFilterQuery: '',
votesFilterQuery: '',
votersSize: (props.voters && props.voters.length) || 0,
votesSize: (props.votes && props.votes.length) || 0,
};
}

showMore(name, amount = 100) {
const newAmount = this.state[name] + amount;
this.setState({ [name]: newAmount });
}

filterList(data, filterQuery) {
if (this.state[filterQuery] !== '') {
data = data.filter((obj) => {
const name = obj.props.children.trim();
return name.includes(this.state[filterQuery]);
});
}
return data;
}

getFormatedDelegates(dataName, filterQuery) {
const data = this.props[dataName] ? this.props[dataName].map((user, key) => (
<Link className={`${styles.addressLink} ${styles.clickable} voter-address ${filterQuery}-row`}
to={`${routes.explorer.path}${routes.accounts.path}/${user.address}`}
key={`${key}-${dataName}`}>
{`${user.username || user.address} `}
</Link>
)) : [];

return this.filterList(data, filterQuery);
}

search(filterName, e) {
const { value } = e.target;
this.setState({
[filterName]: value,
});
}

clearSearch(filterQuery) {
this.search(filterQuery, { target: { value: '' } });
}

renderSearchFilter(filterQuery, placeholder) {
return (
<div className={`${styles.search} ${styles.filter} search ${filterQuery}`}>
<FontIcon className={styles.search} value='search' id='searchIcon'/>
<input type='text'
name='query'
className={`search ${styles.desktopInput} ${this.state[filterQuery].length > 0 ? styles.dirty : ''} `}
value={this.state[filterQuery]}
onChange={this.search.bind(this, filterQuery)}
placeholder={placeholder}/>
<input type='text'
name='query'
className={`${styles.mobileInput} ${this.state[filterQuery].length > 0 ? styles.dirty : ''} `}
value={this.state[filterQuery]}
onChange={this.search.bind(this, filterQuery)}
placeholder={this.props.t('Filter')}/>
<FontIcon
id='cleanIcon'
className={`${styles.clean} clean-icon`}
value='close'
onClick={ this.clearSearch.bind(this, filterQuery) }/>
</div>
);
}

render() {
const { delegate, t } = this.props;
const votesInterspered = this.getFormatedDelegates('votes', 'votesFilterQuery');
const votersInterspered = this.getFormatedDelegates('voters', 'votersFilterQuery');
const { delegate } = this.props;

let status = '';
if (delegate && delegate.rank) {
Expand Down Expand Up @@ -122,49 +43,8 @@ class DelegateStatistics extends React.Component {
</div>
</div>
</div>
<div className={`transactions-detail-view ${grid.row} ${grid['between-md']} ${grid['between-sm']} ${styles.row} votes`}>
<div className={`${grid['col-xs-12']} ${grid['col-sm-12']} ${grid['col-md-12']}`}>
<div className={styles.label}>
<div className='votes-value'>
{this.props.t('Votes of an account')}
{` (${this.state.votesSize})`}
</div>
{this.renderSearchFilter('votesFilterQuery', t('Filter votes'))}
</div>
<div className={styles.value}>
{votesInterspered && votesInterspered
.slice(0, this.state.showVotesNumber)}
</div>
{votesInterspered.length > this.state.showVotesNumber
&& this.state.votesFilterQuery === '' ?
<div onClick={() => { this.showMore('showVotesNumber'); }} className={`${styles.showMore} showMore show-votes`}>
<FontIcon className={styles.arrowDown} value='arrow-down'/>
{this.props.t('Show more')}
</div> : ''
}
</div>
</div>
<div className={`transactions-detail-view ${grid.row} ${grid['between-md']} ${grid['between-sm']} ${styles.row} voters`}>
<div className={`${grid['col-xs-12']} ${grid['col-sm-12']} ${grid['col-md-12']}`}>
<div className={styles.label}>
<div className='voters-value'>
{this.props.t('Who voted for a delegate')}
{` (${this.state.votersSize})`}
</div>
{this.renderSearchFilter('votersFilterQuery', t('Filter voters'))}
</div>
<div className={styles.value}>
{votersInterspered && votersInterspered
.slice(0, this.state.showVotersNumber)}
</div>
{votersInterspered.length > this.state.showVotersNumber ?
<div onClick={() => { this.showMore('showVotersNumber'); }} className={`${styles.showMore} showMore show-voters`}>
<FontIcon className={styles.arrowDown} value='arrow-down'/>
{this.props.t('Show more')}
</div> : ''
}
</div>
</div>
<UserVotes {...this.props} />
<VotedDelegates {...this.props} />
</div>
);
}
Expand Down
22 changes: 17 additions & 5 deletions src/components/transactions/transactionList.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import txTypes from './../../constants/transactionTypes';
import styles from './transactionList.css';
import { parseSearchParams } from './../../utils/searchParams';
import DelegateStatistics from './delegateStatistics';
import UserVotes from './userVotes';

class TransactionsList extends React.Component {
componentWillReceiveProps(nextProps) {
Expand All @@ -34,7 +35,6 @@ class TransactionsList extends React.Component {

render() {
const {
filter,
transactions,
loading,
dashboard,
Expand All @@ -43,10 +43,13 @@ class TransactionsList extends React.Component {
loadMore,
t,
} = this.props;
// All, incoming, outgoing are filter values. To be more consistance with other possible tabs
// We can refer to props.filter as tabObj
const tabObj = this.props.filter;

const fixIncomingFilter = (transaction) => {
const isTypeNonSend = transaction.type !== txTypes.send;
const isFilterIncoming = filter && filter.value === txFilters.incoming;
const isFilterIncoming = tabObj && tabObj.value === txFilters.incoming;
const isAccountInit = transaction.type === txTypes.send
&& transaction.senderId === transaction.recipientId;

Expand All @@ -57,17 +60,17 @@ class TransactionsList extends React.Component {
// istanbul ignore else
if (transactions.length === 0) {
// istanbul ignore else
if (dashboard || (filter && filter.value !== txFilters.all)) {
if (dashboard || (tabObj && tabObj.value !== txFilters.all)) {
return <p className={`${styles.empty} hasPaddingRow empty-message`}>
{t('There are no {{filterName}} transactions.', {
filterName: filter && filter.name ? filter.name.toLowerCase() : '',
filterName: tabObj && tabObj.name ? tabObj.name.toLowerCase() : '',
})}
</p>;
}
return null;
}

const isDelegateStatistics = filter && (filter.value === txFilters.statistics);
const isDelegateStatistics = tabObj && (tabObj.value === txFilters.statistics);

if (isDelegateStatistics) {
return <DelegateStatistics
Expand All @@ -76,6 +79,15 @@ class TransactionsList extends React.Component {
voters={this.props.voters} />;
}

const isAccountInfo = tabObj && (tabObj.value === txFilters.accountInfo);

if (isAccountInfo) {
return <UserVotes
delegate={this.props.delegate}
votes={this.props.votes}
voters={this.props.voters} />;
}

return <div className={`${styles.results} transaction-results`}>
<TransactionsHeader tableStyle={tableStyle}></TransactionsHeader>
{transactions
Expand Down
3 changes: 2 additions & 1 deletion src/components/transactions/transactionList.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ describe('TransactionsList', () => {
},
});
wrapper = mount(<TransactionsList {...propsDelegateStatistics} />, options);
expect(wrapper.find('.delegate-statistics')).to.have.lengthOf(1);
expect(wrapper.find('.user-votes')).to.have.lengthOf(1);
expect(wrapper.find('.voters')).to.have.lengthOf(1);
});
});
13 changes: 10 additions & 3 deletions src/components/transactions/transactionOverview.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,19 @@ class TransactionsOverview extends React.Component {
className: 'filter-out',
},
];
if (this.props.delegate && Object.keys(this.props.delegate).length !== 0) {
filters.push({

if (this.props.delegate && Object.values(this.props.delegate).length > 0) {
filters[txFilters.statistics] = {
name: this.isSmallScreen() ? this.props.t('Stats') : this.props.t('Delegate statistics'),
value: txFilters.statistics,
className: 'delegate-statistics',
});
};
} else {
filters[txFilters.accountInfo] = {
name: this.isSmallScreen() ? this.props.t('Info') : this.props.t('Account Info'),
value: txFilters.accountInfo,
className: 'account-info',
};
}

return (
Expand Down
Loading

0 comments on commit 62f4de7

Please sign in to comment.