index.tsx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. // Copyright 2017-2020 @polkadot/app-staking authors & contributors
  2. // This software may be modified and distributed under the terms
  3. // of the Apache-2.0 license. See the LICENSE file for details.
  4. import { Balance } from '@polkadot/types/interfaces';
  5. import { DeriveAccountInfo, DeriveStakingQuery } from '@polkadot/api-derive/types';
  6. import BN from 'bn.js';
  7. import React, { useCallback, useEffect, useState } from 'react';
  8. import { AddressSmall, Icon } from '@polkadot/react-components';
  9. import { useApi, useCall } from '@polkadot/react-hooks';
  10. import { FormatBalance } from '@polkadot/react-query';
  11. import { checkVisibility } from '../../util';
  12. import Favorite from './Favorite';
  13. import NominatedBy from './NominatedBy';
  14. import Status from './Status';
  15. import StakeOther from './StakeOther';
  16. interface Props {
  17. address: string;
  18. className?: string;
  19. filterName: string;
  20. hasQueries: boolean;
  21. isElected: boolean;
  22. isFavorite: boolean;
  23. isMain?: boolean;
  24. lastBlock?: string;
  25. nominatedBy?: [string, number][];
  26. onlineCount?: false | number;
  27. onlineMessage?: boolean;
  28. points?: string;
  29. toggleFavorite: (accountId: string) => void;
  30. withIdentity: boolean;
  31. }
  32. interface StakingState {
  33. commission?: string;
  34. nominators: [string, Balance][];
  35. stakeTotal?: BN;
  36. stakeOther?: BN;
  37. stakeOwn?: BN;
  38. }
  39. const PERBILL_PERCENT = 10_000_000;
  40. function expandInfo ({ exposure, validatorPrefs }: DeriveStakingQuery): StakingState {
  41. let nominators: [string, Balance][] = [];
  42. let stakeTotal: BN | undefined;
  43. let stakeOther: BN | undefined;
  44. let stakeOwn: BN | undefined;
  45. if (exposure) {
  46. nominators = exposure.others.map(({ value, who }): [string, Balance] => [who.toString(), value.unwrap()]);
  47. stakeTotal = exposure.total.unwrap();
  48. stakeOwn = exposure.own.unwrap();
  49. stakeOther = stakeTotal.sub(stakeOwn);
  50. }
  51. const commission = validatorPrefs?.commission?.unwrap();
  52. return {
  53. commission: commission
  54. ? `${(commission.toNumber() / PERBILL_PERCENT).toFixed(2)}%`
  55. : undefined,
  56. nominators,
  57. stakeOther,
  58. stakeOwn,
  59. stakeTotal
  60. };
  61. }
  62. function Address ({ address, className = '', filterName, hasQueries, isElected, isFavorite, isMain, lastBlock, nominatedBy, onlineCount, onlineMessage, points, toggleFavorite, withIdentity }: Props): React.ReactElement<Props> | null {
  63. const { api } = useApi();
  64. const accountInfo = useCall<DeriveAccountInfo>(api.derive.accounts.info, [address]);
  65. const stakingInfo = useCall<DeriveStakingQuery>(api.derive.staking.query, [address]);
  66. const [{ commission, nominators, stakeOther, stakeOwn, stakeTotal }, setStakingState] = useState<StakingState>({ nominators: [] });
  67. const [isVisible, setIsVisible] = useState(true);
  68. useEffect((): void => {
  69. stakingInfo && setStakingState(expandInfo(stakingInfo));
  70. }, [stakingInfo]);
  71. useEffect((): void => {
  72. setIsVisible(
  73. checkVisibility(api, address, filterName, withIdentity, accountInfo)
  74. );
  75. }, [api, accountInfo, address, filterName, withIdentity]);
  76. const _onQueryStats = useCallback(
  77. (): void => {
  78. window.location.hash = `/staking/query/${address}`;
  79. },
  80. [address]
  81. );
  82. if (!isVisible) {
  83. return null;
  84. }
  85. return (
  86. <tr className={className}>
  87. <td className='badge together'>
  88. <Favorite
  89. address={address}
  90. isFavorite={isFavorite}
  91. toggleFavorite={toggleFavorite}
  92. />
  93. <Status
  94. isElected={isElected}
  95. numNominators={nominatedBy?.length}
  96. onlineCount={onlineCount}
  97. onlineMessage={onlineMessage}
  98. />
  99. </td>
  100. <td className='address'>
  101. <AddressSmall value={address} />
  102. </td>
  103. {isMain
  104. ? (
  105. <StakeOther
  106. nominators={nominators}
  107. stakeOther={stakeOther}
  108. />
  109. )
  110. : <NominatedBy nominators={nominatedBy} />
  111. }
  112. <td className='number'>
  113. {stakeOwn?.gtn(0) && (
  114. <FormatBalance value={stakeOwn} />
  115. )}
  116. </td>
  117. <td className='number'>
  118. {stakeTotal && <FormatBalance value={stakeTotal} />}
  119. </td>
  120. <td className='number'>
  121. {commission}
  122. </td>
  123. {isMain && (
  124. <>
  125. <td className='number'>
  126. {points}
  127. </td>
  128. <td className='number'>
  129. {lastBlock}
  130. </td>
  131. </>
  132. )}
  133. <td>
  134. {hasQueries && (
  135. <Icon
  136. icon='chart-line'
  137. onClick={_onQueryStats}
  138. />
  139. )}
  140. </td>
  141. </tr>
  142. );
  143. }
  144. export default React.memo(Address);