Sort parachain lists with known first (#5011)

* Sort parachain lists with known first

* leases re-sort

* Adjust sorting
This commit is contained in:
Jaco Greeff 2021-04-02 22:19:52 +03:00 committed by GitHub
parent e1d3919d65
commit 498a4818ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 150 additions and 70 deletions

View File

@ -99,7 +99,7 @@ function Auction ({ auctionInfo, campaigns, className, winningData }: Props): Re
blockNumber={blockNumber}
isFirst={index === 0}
isLatest={round === 0}
key={`${blockNumber.toString()}:${index}`}
key={`${blockNumber.toString()}:${value.key}`}
value={value}
/>
))}

View File

@ -2,11 +2,13 @@
// SPDX-License-Identifier: Apache-2.0
import type BN from 'bn.js';
import type { ParaId } from '@polkadot/types/interfaces';
import type { Campaign, LeasePeriod } from '../types';
import React, { useMemo, useRef } from 'react';
import { Table } from '@polkadot/react-components';
import { useIsParasLinked } from '@polkadot/react-hooks';
import { useTranslation } from '../translate';
import Fund from './Fund';
@ -18,19 +20,45 @@ interface Props {
value: Campaign[] | null;
}
function extractLists (value: Campaign[] | null, leasePeriod?: LeasePeriod): [Campaign[] | null, Campaign[] | null] {
function extractLists (value: Campaign[] | null, leasePeriod?: LeasePeriod): [Campaign[] | null, Campaign[] | null, ParaId[] | null] {
const currentPeriod = leasePeriod?.currentPeriod;
let active: Campaign[] | null = null;
let ended: Campaign[] | null = null;
let allIds: ParaId[] | null = null;
return value && currentPeriod
? [
value.filter(({ firstSlot, isCapped, isEnded, isWinner }) => !(isCapped || isEnded || isWinner) && currentPeriod.lte(firstSlot)),
value.filter(({ firstSlot, isCapped, isEnded, isWinner }) => (isCapped || isEnded || isWinner) || currentPeriod.gt(firstSlot))
]
: [null, null];
if (value && currentPeriod) {
active = value.filter(({ firstSlot, isCapped, isEnded, isWinner }) => !(isCapped || isEnded || isWinner) && currentPeriod.lte(firstSlot));
ended = value.filter(({ firstSlot, isCapped, isEnded, isWinner }) => (isCapped || isEnded || isWinner) || currentPeriod.gt(firstSlot));
allIds = value.map(({ paraId }) => paraId);
}
return [active, ended, allIds];
}
function sortList (hasLinksMap: Record<string, boolean>, list?: Campaign[] | null): Campaign[] | null | undefined {
return list?.sort(({ key: a }, { key: b }): number => {
const aKnown = hasLinksMap[a] || false;
const bKnown = hasLinksMap[b] || false;
return aKnown === bKnown
? 0
: aKnown
? -1
: 1;
});
}
function Funds ({ bestNumber, className, leasePeriod, value }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const [active, ended, allIds] = useMemo(
() => extractLists(value, leasePeriod),
[leasePeriod, value]
);
const hasLinksMap = useIsParasLinked(allIds);
const [activeSorted, endedSorted] = useMemo(
() => [sortList(hasLinksMap, active), sortList(hasLinksMap, ended)],
[active, ended, hasLinksMap]
);
const headerActiveRef = useRef([
[t('ongoing'), 'start', 2],
@ -57,19 +85,14 @@ function Funds ({ bestNumber, className, leasePeriod, value }: Props): React.Rea
[]
]);
const [active, ended] = useMemo(
() => extractLists(value, leasePeriod),
[leasePeriod, value]
);
return (
<>
<Table
className={className}
empty={value && active && t<string>('No active campaigns found')}
empty={value && activeSorted && t<string>('No active campaigns found')}
header={headerActiveRef.current}
>
{active?.map((fund) => (
{activeSorted?.map((fund) => (
<Fund
bestNumber={bestNumber}
isOngoing
@ -80,10 +103,10 @@ function Funds ({ bestNumber, className, leasePeriod, value }: Props): React.Rea
</Table>
<Table
className={className}
empty={value && ended && t<string>('No completed campaigns found')}
empty={value && endedSorted && t<string>('No completed campaigns found')}
header={headedEndedRef.current}
>
{ended?.map((fund) => (
{endedSorted?.map((fund) => (
<Fund
bestNumber={bestNumber}
key={fund.accountId}

View File

@ -12,7 +12,7 @@ import BN from 'bn.js';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { Table } from '@polkadot/react-components';
import { useApi, useBestNumber, useCall, useCallMulti } from '@polkadot/react-hooks';
import { useApi, useBestNumber, useCall, useCallMulti, useIsParasLinked } from '@polkadot/react-hooks';
import { useTranslation } from '../translate';
import useHrmp from '../useHrmp';
@ -61,10 +61,11 @@ function extractScheduledIds (scheduled: ScheduledProposals[] = []): Record<stri
}), all), {});
}
function mapValidators (ids: ParaId[] | undefined, validators: AccountId[] | null, validatorGroups: ParaValidatorIndex[][] | null, activeIndices: ParaValidatorIndex[] | null): ValidatorInfo[][] {
function mapValidators (ids: ParaId[] | undefined, validators: AccountId[] | null, validatorGroups: ParaValidatorIndex[][] | null, activeIndices: ParaValidatorIndex[] | null): Record<string, ValidatorInfo[]> {
return activeIndices && validators && validatorGroups && ids && ids.length === validatorGroups.length
? validatorGroups.map((indices) =>
indices
? validatorGroups.reduce((all: Record<string, ValidatorInfo[]>, indices, index) => ({
...all,
[ids[index].toString()]: indices
.map((indexActive) => [indexActive, activeIndices[indexActive.toNumber()]])
.filter(([, a]) => a)
.map(([indexActive, indexValidator]) => ({
@ -72,8 +73,8 @@ function mapValidators (ids: ParaId[] | undefined, validators: AccountId[] | nul
indexValidator,
validatorId: validators[indexValidator.toNumber()]
}))
)
: [];
}), {})
: {};
}
function extractEvents (api: ApiPromise, lastBlock: SignedBlockExtended, prev: LastEvents): LastEvents {
@ -125,6 +126,21 @@ function extractActions (actionsQueue: QueuedAction[], knownIds?: [ParaId, strin
: {};
}
function extractIds (hasLinksMap: Record<string, boolean>, ids?: ParaId[]): [ParaId, string][] | undefined {
return ids
?.map((id): [ParaId, string] => [id, id.toString()])
.sort(([aId, aIds], [bId, bIds]): number => {
const aKnown = hasLinksMap[aIds] || false;
const bKnown = hasLinksMap[bIds] || false;
return aKnown === bKnown
? aId.cmp(bId)
: aKnown
? -1
: 1;
});
}
function Parachains ({ actionsQueue, ids, leasePeriod, scheduled }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
@ -137,6 +153,7 @@ function Parachains ({ actionsQueue, ids, leasePeriod, scheduled }: Props): Reac
api.query.shared?.activeValidatorIndices
], optionsMulti);
const hrmp = useHrmp();
const hasLinksMap = useIsParasLinked(ids);
const headerRef = useRef([
[t('parachains'), 'start', 2],
@ -163,8 +180,8 @@ function Parachains ({ actionsQueue, ids, leasePeriod, scheduled }: Props): Reac
);
const knownIds = useMemo(
() => ids?.map((id): [ParaId, string] => [id, id.toString()]),
[ids]
() => extractIds(hasLinksMap, ids),
[ids, hasLinksMap]
);
const nextActions = useMemo(
@ -183,7 +200,7 @@ function Parachains ({ actionsQueue, ids, leasePeriod, scheduled }: Props): Reac
empty={knownIds && t<string>('There are no registered parachains')}
header={headerRef.current}
>
{knownIds?.map(([id, key], index): React.ReactNode => (
{knownIds?.map(([id, key]): React.ReactNode => (
<Parachain
bestNumber={bestNumber}
channelDst={hrmp?.dst[id.toString()]}
@ -197,7 +214,7 @@ function Parachains ({ actionsQueue, ids, leasePeriod, scheduled }: Props): Reac
leasePeriod={leasePeriod}
nextAction={nextActions[key]}
sessionValidators={validators}
validators={validatorMap[index]}
validators={validatorMap[key]}
/>
))}
</Table>

View File

@ -6,10 +6,10 @@ import type { AccountId, BalanceOf, ParaId } from '@polkadot/types/interfaces';
import type { ITuple } from '@polkadot/types/types';
import type { LeaseInfo, LeasePeriod, QueuedAction } from '../types';
import React, { useRef } from 'react';
import React, { useCallback, useRef } from 'react';
import { Table } from '@polkadot/react-components';
import { useApi, useCall } from '@polkadot/react-hooks';
import { useApi, useCall, useIsParasLinked } from '@polkadot/react-hooks';
import { useTranslation } from '../translate';
import Actions from './Actions';
@ -24,47 +24,60 @@ interface Props {
type ParaMap = [ParaId, LeaseInfo[]][];
const optLeases = {
transform: ([[paraIds], leases]: [[ParaId[]], Option<ITuple<[AccountId, BalanceOf]>>[][]]): ParaMap =>
paraIds
.reduce((all: ParaMap, id, index): ParaMap => {
all.push([
id,
leases[index]
.map((optLease, period): LeaseInfo | null => {
if (optLease.isNone) {
return null;
}
function extractParaMap (hasLinksMap: Record<string, boolean>, paraIds: ParaId[], leases: Option<ITuple<[AccountId, BalanceOf]>>[][]): ParaMap {
return paraIds
.reduce((all: ParaMap, id, index): ParaMap => {
all.push([
id,
leases[index]
.map((optLease, period): LeaseInfo | null => {
if (optLease.isNone) {
return null;
}
const [accountId, balance] = optLease.unwrap();
const [accountId, balance] = optLease.unwrap();
return {
accountId,
balance,
period
};
})
.filter((item): item is LeaseInfo => !!item)
]);
return {
accountId,
balance,
period
};
})
.filter((item): item is LeaseInfo => !!item)
]);
return all;
}, [])
.sort(([aId, aLeases], [bId, bLeases]) =>
aLeases.length && bLeases.length
? (aLeases[0].period - bLeases[0].period) || aId.cmp(bId)
: aLeases.length
? -1
: bLeases.length
? 1
: aId.cmp(bId)
),
withParamsTransform: true
};
return all;
}, [])
.sort(([aId, aLeases], [bId, bLeases]): number => {
const aKnown = hasLinksMap[aId.toString()] || false;
const bKnown = hasLinksMap[bId.toString()] || false;
return aLeases.length && bLeases.length
? (aLeases[0].period - bLeases[0].period) || aId.cmp(bId)
: aLeases.length
? -1
: bLeases.length
? 1
: aKnown === bKnown
? aId.cmp(bId)
: aKnown
? -1
: 1;
});
}
function Parathreads ({ actionsQueue, className, ids, leasePeriod }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const leaseMap = useCall<ParaMap>(ids && api.query.slots.leases.multi, [ids], optLeases);
const hasLinksMap = useIsParasLinked(ids);
const leaseMap = useCall<ParaMap>(ids && api.query.slots.leases.multi, [ids], {
transform: useCallback(
([[paraIds], leases]: [[ParaId[]], Option<ITuple<[AccountId, BalanceOf]>>[][]]): ParaMap =>
extractParaMap(hasLinksMap, paraIds, leases),
[hasLinksMap]
),
withParamsTransform: true
});
const headerRef = useRef([
[t('parathreads'), 'start', 3],

View File

@ -86,6 +86,7 @@ export interface WinnerData {
accountId: string;
firstSlot: BN;
isCrowdloan: boolean;
key: string;
lastSlot: BN;
paraId: ParaId;
value: BN;

View File

@ -138,6 +138,7 @@ const optFundMulti = {
firstSlot: info.firstSlot,
info,
isCrowdloan: true,
key: paraId.toString(),
lastSlot: info.lastSlot,
paraId,
value: info.raised

View File

@ -41,6 +41,7 @@ function extractWinners (auctionInfo: AuctionInfo, optData: Option<WinningData>)
accountId: accountId.toString(),
firstSlot: period.addn(first),
isCrowdloan: u8aEq(CROWD_PREFIX, accountId.subarray(0, CROWD_PREFIX.length)),
key: paraId.toString(),
lastSlot: period.addn(last),
paraId,
value

View File

@ -33,7 +33,7 @@ export { useOwnEraRewards } from './useOwnEraRewards';
export { useOwnStashes, useOwnStashIds } from './useOwnStashes';
export { useOwnStashInfos } from './useOwnStashInfos';
export { useParaApi } from './useParaApi';
export { useParaEndpoints } from './useParaEndpoints';
export { useIsParasLinked, useParaEndpoints } from './useParaEndpoints';
export { usePassword } from './usePassword';
export { useRegistrars } from './useRegistrars';
export { useSavedFlags } from './useSavedFlags';

View File

@ -11,19 +11,43 @@ import { bnToBn } from '@polkadot/util';
import { useApi } from './useApi';
function extractResults (genesisHash: string, paraId: BN | number): LinkOption[] {
function extractRelayEndpoints (genesisHash: string): LinkOption[] {
return createWsEndpoints((key: string, value: string | undefined) => value || key).filter(({ genesisHashRelay }) =>
genesisHash === genesisHashRelay
);
}
function extractParaEndpoints (allEndpoints: LinkOption[], paraId: BN | number): LinkOption[] {
const numId = bnToBn(paraId).toNumber();
return createWsEndpoints((key: string, value: string | undefined) => value || key).filter(({ genesisHashRelay, paraId }) =>
paraId === numId && genesisHashRelay === genesisHash
return allEndpoints.filter(({ paraId }) => paraId === numId);
}
function useRelayEndpoints (): LinkOption[] {
const { api } = useApi();
return useMemo(
() => extractRelayEndpoints(api.genesisHash.toHex()),
[api]
);
}
export function useParaEndpoints (paraId: BN | number): LinkOption[] {
const { api } = useApi();
const endpoints = useRelayEndpoints();
return useMemo(
() => extractResults(api.genesisHash.toHex(), paraId),
[api, paraId]
() => extractParaEndpoints(endpoints, paraId),
[endpoints, paraId]
);
}
export function useIsParasLinked (ids?: (BN | number)[] | null): Record<string, boolean> {
const endpoints = useRelayEndpoints();
return useMemo(
() => ids
? ids.reduce((all: Record<string, boolean>, id) => ({ ...all, [id.toString()]: extractParaEndpoints(endpoints, id).length !== 0 }), {})
: {},
[endpoints, ids]
);
}