forked from polkadot-js/apps
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'alephzero' into cp-123d90aa01
- Loading branch information
Showing
31 changed files
with
1,311 additions
and
337 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
71 changes: 71 additions & 0 deletions
71
packages/page-staking/src/FutureCommittee/Address/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
// Copyright 2017-2025 @polkadot/app-staking authors & contributors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
import React, { useCallback, useMemo } from 'react'; | ||
|
||
import { AddressSmall, Badge, Icon } from '@polkadot/react-components'; | ||
import { checkVisibility } from '@polkadot/react-components/util'; | ||
import { useAddressToDomain, useApi, useDeriveAccountInfo } from '@polkadot/react-hooks'; | ||
|
||
interface Props { | ||
address: string; | ||
currentSessionCommittee: boolean; | ||
filterName: string; | ||
nextSessionInCommittee?: number; | ||
} | ||
|
||
function useAddressCalls (address: string) { | ||
const accountInfo = useDeriveAccountInfo(address); | ||
|
||
return { accountInfo }; | ||
} | ||
|
||
function queryAddress (address: string) { | ||
window.location.hash = `/staking/query/${address}`; | ||
} | ||
|
||
function Address ({ address, currentSessionCommittee, filterName, nextSessionInCommittee }: Props): React.ReactElement<Props> | null { | ||
const { api } = useApi(); | ||
const { accountInfo } = useAddressCalls(address); | ||
const { primaryDomain: domain } = useAddressToDomain(address); | ||
|
||
const isVisible = useMemo( | ||
() => accountInfo ? checkVisibility(api, address, { ...accountInfo, domain }, filterName) : true, | ||
[api, accountInfo, address, domain, filterName] | ||
); | ||
|
||
const onQueryStats = useCallback( | ||
() => queryAddress(address), | ||
[address] | ||
); | ||
|
||
if (!isVisible) { | ||
return null; | ||
} | ||
|
||
return ( | ||
<tr> | ||
<td className='address'> | ||
<AddressSmall value={address} /> | ||
</td> | ||
<td className='number'> | ||
<Badge | ||
color={currentSessionCommittee ? 'green' : 'transparent'} | ||
icon={currentSessionCommittee ? 'check' : undefined} | ||
/> | ||
</td> | ||
<td className='number'> | ||
{nextSessionInCommittee ?? 'No next committee in the current era'} | ||
</td> | ||
<td className='number'> | ||
<Icon | ||
className='staking--stats highlight--color' | ||
icon='chart-line' | ||
onClick={onQueryStats} | ||
/> | ||
</td> | ||
</tr> | ||
); | ||
} | ||
|
||
export default React.memo(Address); |
120 changes: 120 additions & 0 deletions
120
packages/page-staking/src/FutureCommittee/FutureValidators.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
// Copyright 2017-2025 @polkadot/app-staking authors & contributors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
import React, { useMemo, useRef, useState } from 'react'; | ||
|
||
import { Table } from '@polkadot/react-components'; | ||
import { useApi } from '@polkadot/react-hooks'; | ||
|
||
import Filtering from '../Filtering.js'; | ||
import { useEraValidators } from '../Performance/useEraValidators.js'; | ||
import useFutureSessionCommittee from '../Performance/useFutureSessionCommittee.js'; | ||
import { useTranslation } from '../translate.js'; | ||
import useSessionValidators from '../useSessionValidators.js'; | ||
import Address from './Address/index.js'; | ||
|
||
interface Props { | ||
currentSession: number; | ||
maximumSessionNumber: number; | ||
} | ||
|
||
interface ListEntry { | ||
accountId: string; | ||
currentSessionCommittee: boolean; | ||
nextSessionInCommittee?: number; | ||
} | ||
|
||
function range (size: number, startAt = 0) { | ||
return [...Array(size).keys()].map((i) => i + startAt); | ||
} | ||
|
||
function FutureValidators ({ currentSession, maximumSessionNumber }: Props): React.ReactElement<Props> { | ||
const { api } = useApi(); | ||
const { t } = useTranslation(); | ||
|
||
const [nameFilter, setNameFilter] = useState<string>(''); | ||
|
||
const sessionValidators = useSessionValidators(api); | ||
|
||
const eraValidatorsAddresses = useEraValidators(currentSession); | ||
const eraValidators = useMemo(() => { | ||
if (eraValidatorsAddresses && eraValidatorsAddresses.length > 0) { | ||
return eraValidatorsAddresses; | ||
} | ||
|
||
return []; | ||
}, [eraValidatorsAddresses] | ||
); | ||
|
||
const futureSessions = useMemo(() => { | ||
if (currentSession < maximumSessionNumber) { | ||
return range(maximumSessionNumber - currentSession, currentSession + 1); | ||
} | ||
|
||
return []; | ||
}, [currentSession, maximumSessionNumber]); | ||
const futureSessionCommittee = useFutureSessionCommittee(futureSessions ?? []); | ||
|
||
const validatorsList: ListEntry[] = useMemo(() => { | ||
if (futureSessionCommittee.length > 0 && sessionValidators && sessionValidators.length > 0) { | ||
return eraValidators.map((accountId) => { | ||
const nextCommittee = futureSessionCommittee.find((futureCommittee) => { | ||
return futureCommittee.producers.find((producer) => producer === accountId) !== undefined; | ||
}); | ||
|
||
return { | ||
accountId, | ||
currentSessionCommittee: sessionValidators.includes(accountId), | ||
nextSessionInCommittee: nextCommittee?.session | ||
}; | ||
}).sort((a, b) => { | ||
return Number(b.currentSessionCommittee) - Number(a.currentSessionCommittee); | ||
}); | ||
} | ||
|
||
return []; | ||
}, [futureSessionCommittee, eraValidators, sessionValidators]); | ||
|
||
const headerRef = useRef<[string, string, number?][]>( | ||
[ | ||
[t('validators'), 'start', 1], | ||
[t('current session committee'), 'expand'], | ||
[t('next session committee'), 'expand'], | ||
[t('stats'), 'expand'] | ||
] | ||
); | ||
|
||
return ( | ||
<Table | ||
empty={ | ||
validatorsList && t('No active validators found') | ||
} | ||
emptySpinner={ | ||
<> | ||
{!validatorsList && <div>{t('Preparing validator list')}</div>} | ||
</> | ||
} | ||
filter={ | ||
<div className='staking--optionsBar'> | ||
<Filtering | ||
nameFilter={nameFilter} | ||
setNameFilter={setNameFilter} | ||
/> | ||
</div> | ||
} | ||
header={headerRef.current} | ||
> | ||
{validatorsList.map(({ accountId, currentSessionCommittee, nextSessionInCommittee }): React.ReactNode => ( | ||
<Address | ||
address={accountId} | ||
currentSessionCommittee={currentSessionCommittee} | ||
filterName={nameFilter} | ||
key={accountId} | ||
nextSessionInCommittee={nextSessionInCommittee} | ||
/> | ||
))} | ||
</Table> | ||
); | ||
} | ||
|
||
export default React.memo(FutureValidators); |
23 changes: 23 additions & 0 deletions
23
packages/page-staking/src/FutureCommittee/SummarySession.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// Copyright 2017-2025 @polkadot/app-explorer authors & contributors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
import React from 'react'; | ||
|
||
import { CardSummary } from '@polkadot/react-components'; | ||
import { formatNumber } from '@polkadot/util'; | ||
|
||
interface Props { | ||
session: number; | ||
} | ||
|
||
function SummarySession ({ session }: Props): React.ReactElement<Props> { | ||
return ( | ||
<> | ||
<CardSummary label={'session'}> | ||
#{formatNumber(session)} | ||
</CardSummary> | ||
</> | ||
); | ||
} | ||
|
||
export default React.memo(SummarySession); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// Copyright 2017-2025 @polkadot/app-staking authors & contributors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
import React from 'react'; | ||
|
||
import { MarkWarning, Spinner, SummaryBox } from '@polkadot/react-components'; | ||
import { useApi } from '@polkadot/react-hooks'; | ||
|
||
import useSessionInfo from '../Performance/useSessionInfo.js'; | ||
import FutureValidators from './FutureValidators.js'; | ||
import SummarySession from './SummarySession.js'; | ||
|
||
function FutureCommitteePage (): React.ReactElement { | ||
const { api } = useApi(); | ||
|
||
const sessionInfo = useSessionInfo(); | ||
|
||
if (!api.runtimeChain.toString().includes('Aleph Zero')) { | ||
return ( | ||
<MarkWarning content={'Unsupported chain.'} /> | ||
); | ||
} | ||
|
||
if (sessionInfo === undefined) { | ||
return ( | ||
<Spinner label={'loading data'} /> | ||
); | ||
} | ||
|
||
return ( | ||
<div className='staking--Performance'> | ||
<SummaryBox> | ||
<section> | ||
<SummarySession | ||
session={sessionInfo.currentSession} | ||
/> | ||
</section> | ||
</SummaryBox> | ||
<FutureValidators | ||
currentSession={sessionInfo.currentSession} | ||
maximumSessionNumber={sessionInfo.maximumSessionNumber} | ||
/> | ||
</div> | ||
); | ||
} | ||
|
||
export default React.memo(FutureCommitteePage); |
Oops, something went wrong.