Add staking favorites (top of list) (#1866)

This commit is contained in:
Jaco Greeff 2019-11-11 19:58:13 +01:00 committed by GitHub
parent 6ddfaf0ef3
commit 2a52609f23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 79 additions and 20 deletions

View File

@ -24,8 +24,10 @@ interface Props extends I18nProps {
defaultName: string;
filter: ValidatorFilter;
isElected: boolean;
isFavorite: boolean;
lastAuthors?: string[];
myAccounts: string[];
onFavorite: (accountId: string) => void;
points?: Points;
recentlyOnline?: DerivedHeartbeats;
stakingInfo?: DerivedStaking;
@ -44,7 +46,7 @@ interface StakingState {
const WITH_VALIDATOR_PREFS = { validatorPayment: true };
function Address ({ address, authorsMap, className, defaultName, filter, isElected, lastAuthors, myAccounts, points, recentlyOnline, stakingInfo, t, withNominations = true }: Props): React.ReactElement<Props> | null {
function Address ({ address, authorsMap, className, defaultName, filter, isElected, isFavorite, lastAuthors, myAccounts, onFavorite, points, recentlyOnline, stakingInfo, t, withNominations = true }: Props): React.ReactElement<Props> | null {
const { isSubstrateV2 } = useContext(ApiContext);
const [extraInfo, setExtraInfo] = useState<[React.ReactNode, React.ReactNode][] | undefined>();
const [hasActivity, setHasActivity] = useState(true);
@ -120,13 +122,23 @@ function Address ({ address, authorsMap, className, defaultName, filter, isElect
const lastBlockNumber = authorsMap[stashId];
const isAuthor = lastAuthors && lastAuthors.includes(stashId);
const _onFavorite = (): void => onFavorite(stashId);
return (
<AddressCard
buttons={
<div className='staking--Address-info'>
{lastBlockNumber && (
<div className={`blockNumberV${isSubstrateV2 ? '2' : '1'} ${isAuthor && 'isCurrent'}`}>#{lastBlockNumber}</div>
{isSubstrateV2 && (
<div className='extras'>
{lastBlockNumber && (
<div className={`blockNumberV${isSubstrateV2 ? '2' : '1'} ${isAuthor && 'isCurrent'}`}>#{lastBlockNumber}</div>
)}
<Icon
className={`favorite ${isFavorite && 'isSelected'}`}
name={isFavorite ? 'star' : 'star outline'}
onClick={_onFavorite}
/>
</div>
)}
{controllerId && (
<div>
@ -200,6 +212,22 @@ function Address ({ address, authorsMap, className, defaultName, filter, isElect
export default withMulti(
styled(Address)`
.extras {
display: inline-block;
margin-bottom: 0.75rem;
.favorite {
cursor: pointer;
display: inline-block;
margin-left: 0.5rem;
margin-right: -0.25rem;
&.isSelected {
color: goldenrod;
}
}
}
.blockNumberV1,
.blockNumberV2 {
border-radius: 0.25rem;
@ -220,7 +248,6 @@ export default withMulti(
.blockNumberV2 {
display: inline-block;
margin-bottom: 0.75rem;
padding: 0.25rem 0;
&.isCurrent {

View File

@ -2,14 +2,15 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { AccountId } from '@polkadot/types/interfaces';
import { DerivedHeartbeats, DerivedStakingOverview } from '@polkadot/api-derive/types';
import { I18nProps } from '@polkadot/react-components/types';
import { AccountId } from '@polkadot/types/interfaces';
import { ValidatorFilter } from '../types';
import React, { useContext, useEffect, useState } from 'react';
import { ApiContext } from '@polkadot/react-api';
import { Columar, Column, Dropdown, FilterOverlay } from '@polkadot/react-components';
import store from 'store';
import keyring from '@polkadot/ui-keyring';
import translate from '../translate';
@ -23,17 +24,21 @@ interface Props extends I18nProps {
stakingOverview?: DerivedStakingOverview;
}
function renderColumn (myAccounts: string[], addresses: AccountId[] | string[], defaultName: string, withOnline: boolean, filter: string, { authorsMap, lastAuthors, recentlyOnline, stakingOverview }: Props, pointIndexes?: number[]): React.ReactNode {
return (addresses as AccountId[]).map((address, index): React.ReactNode => (
const STORE_FAVS = 'staking:favorites';
function renderColumn (myAccounts: string[], favorites: string[], addresses: string[], defaultName: string, withOnline: boolean, filter: string, onFavorite: (accountId: string) => void, { authorsMap, lastAuthors, recentlyOnline, stakingOverview }: Props, pointIndexes?: number[]): React.ReactNode {
return addresses.map((address, index): React.ReactNode => (
<Address
address={address}
authorsMap={authorsMap}
defaultName={defaultName}
filter={filter}
isElected={stakingOverview && stakingOverview.currentElected.some((accountId): boolean => accountId.eq(address))}
isFavorite={favorites.includes(address)}
lastAuthors={lastAuthors}
key={address.toString()}
key={address}
myAccounts={myAccounts}
onFavorite={onFavorite}
points={
stakingOverview && pointIndexes && pointIndexes[index] !== -1
? stakingOverview.eraPoints.individual[pointIndexes[index]]
@ -48,28 +53,55 @@ function renderColumn (myAccounts: string[], addresses: AccountId[] | string[],
));
}
function filterAccounts (list: string[] = [], without: AccountId[] | string[]): string[] {
return list.filter((accountId): boolean => !without.includes(accountId as any));
function filterAccounts (list: string[] = [], without: string[], favorites: string[]): string[] {
return list
.filter((accountId): boolean => !without.includes(accountId as any))
.sort((a, b): number => {
const aIdx = favorites.includes(a);
const bIdx = favorites.includes(b);
return aIdx === bIdx
? 0
: aIdx
? -1
: 1;
});
}
function accountsToString (accounts: AccountId[]): string[] {
return accounts.map((accountId): string => accountId.toString());
}
function CurrentList (props: Props): React.ReactElement<Props> {
const { isSubstrateV2 } = useContext(ApiContext);
const [favorites, setFavorites] = useState<string[]>(store.get(STORE_FAVS, []));
const [filter, setFilter] = useState<ValidatorFilter>('all');
const [myAccounts] = useState(keyring.getAccounts().map(({ address }): string => address));
const [{ electedFiltered, nextFiltered, pointIndexes }, setFiltered] = useState<{ electedFiltered: string[]; nextFiltered: string[]; pointIndexes: number[] }>({ electedFiltered: [], nextFiltered: [], pointIndexes: [] });
const [{ elected, pointIndexes, validators, waiting }, setFiltered] = useState<{ elected: string[]; pointIndexes: number[]; validators: string[]; waiting: string[] }>({ elected: [], pointIndexes: [], validators: [], waiting: [] });
const { next, stakingOverview, t } = props;
useEffect((): void => {
if (stakingOverview) {
const elected = stakingOverview.currentElected.map((accountId): string => accountId.toString());
const validators = filterAccounts(accountsToString(stakingOverview.validators), [], favorites);
const elected = isSubstrateV2 ? filterAccounts(accountsToString(stakingOverview.currentElected), validators, favorites) : [];
setFiltered({
electedFiltered: isSubstrateV2 ? filterAccounts(elected, stakingOverview.validators) : [],
nextFiltered: filterAccounts(next, elected),
pointIndexes: stakingOverview.validators.map((validator): number => elected.indexOf(validator.toString()))
elected,
pointIndexes: validators.map((validator): number => stakingOverview.currentElected.indexOf(validator as any)),
validators,
waiting: filterAccounts(next, elected, favorites)
});
}
}, [next, stakingOverview]);
}, [favorites, next, stakingOverview]);
const _onFavorite = (accountId: string): void => {
const newFavs = favorites.includes(accountId)
? favorites.filter((thisOne): boolean => thisOne !== accountId)
: [...favorites, accountId];
store.set(STORE_FAVS, newFavs);
setFavorites(newFavs);
};
return (
<div>
@ -94,16 +126,16 @@ function CurrentList (props: Props): React.ReactElement<Props> {
emptyText={t('No addresses found')}
headerText={t('validators')}
>
{stakingOverview && renderColumn(myAccounts, stakingOverview.validators, t('validator'), true, filter, props, pointIndexes)}
{stakingOverview && renderColumn(myAccounts, favorites, validators, t('validator'), true, filter, _onFavorite, props, pointIndexes)}
</Column>
<Column
emptyText={t('No addresses found')}
headerText={t('next up')}
>
{(electedFiltered.length !== 0 || nextFiltered.length !== 0) && (
{(elected.length !== 0 || waiting.length !== 0) && (
<>
{renderColumn(myAccounts, electedFiltered, t('intention'), false, filter, props)}
{renderColumn(myAccounts, nextFiltered, t('intention'), false, filter, props)}
{renderColumn(myAccounts, favorites, elected, t('intention'), false, filter, _onFavorite, props)}
{renderColumn(myAccounts, favorites, waiting, t('intention'), false, filter, _onFavorite, props)}
</>
)}
</Column>