Adjust list layouts (#8905)

* Adjust list layouts

* Generic useCache{Map, Value}

* usePoints with useCacheValue
This commit is contained in:
Jaco 2023-01-31 19:56:15 +02:00 committed by GitHub
parent a9aa22dbe6
commit 57fd733985
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 347 additions and 383 deletions

View File

@ -0,0 +1,105 @@
// Copyright 2017-2023 @polkadot/app-staking authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { SessionInfo, Validator } from '../../types';
import React, { useMemo, useRef } from 'react';
import { Table, Tag } from '@polkadot/react-components';
import { useToggle } from '@polkadot/react-hooks';
import { FormatBalance } from '@polkadot/react-query';
import { formatNumber } from '@polkadot/util';
import useExposure from '../useExposure';
import useHeartbeat from '../useHeartbeat';
import Bottom from './Row/Bottom';
import Middle from './Row/Middle';
import Top from './Row/Top';
interface Props {
className?: string;
isRelay: boolean;
points?: number;
sessionInfo: SessionInfo;
toggleFavorite: (stashId: string) => void;
validator: Validator;
}
interface PropsExpanded {
className?: string;
validator: Validator;
}
function EntryExpanded ({ className = '' }: PropsExpanded): React.ReactElement<PropsExpanded> {
return <td className={className} />;
}
function Entry ({ className = '', isRelay, points, sessionInfo, toggleFavorite, validator }: Props): React.ReactElement<Props> {
const [isExpanded, toggleExpanded] = useToggle();
const pointsRef = useRef<{ counter: number, points: number }>({ counter: 0, points: 0 });
const exposure = useExposure(validator, sessionInfo);
const heartbeat = useHeartbeat(validator, sessionInfo);
const pointsAnimClass = useMemo(
(): string => {
if (!points || pointsRef.current.points === points) {
return '';
}
pointsRef.current.points = points;
pointsRef.current.counter = (pointsRef.current.counter + 1) % 25;
return `greyAnim-${pointsRef.current.counter}`;
},
[points, pointsRef]
);
return (
<>
<Top
className={className}
heartbeat={heartbeat}
isExpanded={isExpanded}
isRelay={isRelay}
toggleExpanded={toggleExpanded}
toggleFavorite={toggleFavorite}
validator={validator}
>
{points && (
<Tag
className={`${pointsAnimClass} absolute`}
color='lightgrey'
label={formatNumber(points)}
/>
)}
</Top>
<Middle
className={className}
isExpanded={isExpanded}
>
<Table.Column.Balance
className='relative'
label={
exposure?.waiting && (
<>
<span>(</span>
<FormatBalance value={exposure.waiting.total} />
<span>, {exposure.waiting.others.length})</span>
</>
)
}
value={exposure?.clipped?.total}
withLoading
/>
</Middle>
<Bottom
className={className}
isExpanded={isExpanded}
>
<EntryExpanded validator={validator} />
</Bottom>
</>
);
}
export default React.memo(Entry);

View File

@ -2,103 +2,55 @@
// SPDX-License-Identifier: Apache-2.0
import type { SessionInfo, Validator } from '../../types';
import type { UsePoints } from '../types';
import React, { useMemo, useRef } from 'react';
import React, { useRef } from 'react';
import { Table, Tag } from '@polkadot/react-components';
import { useToggle } from '@polkadot/react-hooks';
import { FormatBalance } from '@polkadot/react-query';
import { formatNumber } from '@polkadot/util';
import Legend from '@polkadot/app-staking/Legend';
import { Table } from '@polkadot/react-components';
import { useNextTick } from '@polkadot/react-hooks';
import Bottom from './Row/Bottom';
import Middle from './Row/Middle';
import Top from './Row/Top';
import useExposure from './useExposure';
import useHeartbeat from './useHeartbeat';
import { useTranslation } from '../../translate';
import Entry from './Entry';
interface Props {
className?: string;
isRelay: boolean;
points?: number;
points?: UsePoints;
sessionInfo: SessionInfo;
toggleFavorite: (stashId: string) => void;
validator: Validator;
validatorsActive?: Validator[];
}
interface PropsExpanded {
className?: string;
validator: Validator;
}
function Active ({ className = '', isRelay, points, sessionInfo, toggleFavorite, validatorsActive }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const isNextTick = useNextTick();
function ActiveExpanded ({ className = '' }: PropsExpanded): React.ReactElement<PropsExpanded> {
return <td className={className} />;
}
function Active ({ className = '', isRelay, points, sessionInfo, toggleFavorite, validator }: Props): React.ReactElement<Props> {
const [isExpanded, toggleExpanded] = useToggle();
const pointsRef = useRef<{ counter: number, points: number }>({ counter: 0, points: 0 });
const exposure = useExposure(validator, sessionInfo);
const heartbeat = useHeartbeat(validator, sessionInfo);
const pointsAnimClass = useMemo(
(): string => {
if (!points || pointsRef.current.points === points) {
return '';
}
pointsRef.current.points = points;
pointsRef.current.counter = (pointsRef.current.counter + 1) % 25;
return `greyAnim-${pointsRef.current.counter}`;
},
[points, pointsRef]
);
const header = useRef<[string?, string?, number?][]>([
// favorite, badges, details, expand
[t<string>('validators'), 'start', 4]
]);
return (
<>
<Top
className={className}
heartbeat={heartbeat}
isExpanded={isExpanded}
isRelay={isRelay}
toggleExpanded={toggleExpanded}
toggleFavorite={toggleFavorite}
validator={validator}
>
{points && (
<Tag
className={`${pointsAnimClass} absolute`}
color='lightgrey'
label={formatNumber(points)}
/>
)}
</Top>
<Middle
className={className}
isExpanded={isExpanded}
>
<Table.Column.Balance
className='relative'
label={
exposure?.waiting && (
<>
<span>(</span>
<FormatBalance value={exposure.waiting.total} />
<span>, {exposure.waiting.others.length})</span>
</>
)
}
value={exposure?.clipped?.total}
withLoading
<Table
className={className}
empty={isNextTick && validatorsActive && t<string>('No session validators found')}
emptySpinner={t<string>('Retrieving session validators')}
header={header.current}
isSplit
legend={<Legend isRelay={isRelay} />}
>
{isNextTick && validatorsActive?.map((v) => (
<Entry
isRelay={isRelay}
key={v.key}
points={points?.[v.stashId]}
sessionInfo={sessionInfo}
toggleFavorite={toggleFavorite}
validator={v}
/>
</Middle>
<Bottom
className={className}
isExpanded={isExpanded}
>
<ActiveExpanded validator={validator} />
</Bottom>
</>
))}
</Table>
);
}

View File

@ -1,57 +0,0 @@
// Copyright 2017-2023 @polkadot/app-staking authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { SessionInfo, Validator } from '../types';
import type { UsePoints } from './types';
import React, { useRef } from 'react';
import Legend from '@polkadot/app-staking/Legend';
import { Table } from '@polkadot/react-components';
import { useNextTick } from '@polkadot/react-hooks';
import { useTranslation } from '../translate';
import Active from './Active';
interface Props {
className?: string;
isRelay: boolean;
points?: UsePoints;
sessionInfo: SessionInfo;
toggleFavorite: (stashId: string) => void;
validatorsActive?: Validator[];
}
function ListActive ({ className = '', isRelay, points, sessionInfo, toggleFavorite, validatorsActive }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const isNextTick = useNextTick();
const header = useRef<[string?, string?, number?][]>([
// favorite, badges, details, expand
[t<string>('validators'), 'start', 4]
]);
return (
<Table
className={className}
empty={isNextTick && validatorsActive && t<string>('No session validators found')}
emptySpinner={t<string>('Retrieving session validators')}
header={header.current}
isSplit
legend={<Legend isRelay={isRelay} />}
>
{isNextTick && validatorsActive?.map((v) => (
<Active
isRelay={isRelay}
key={v.key}
points={points?.[v.stashId]}
sessionInfo={sessionInfo}
toggleFavorite={toggleFavorite}
validator={v}
/>
))}
</Table>
);
}
export default React.memo(ListActive);

View File

@ -1,55 +0,0 @@
// Copyright 2017-2023 @polkadot/app-staking authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { SessionInfo, Validator } from '../types';
import React, { useRef } from 'react';
import Legend from '@polkadot/app-staking/Legend';
import { Table } from '@polkadot/react-components';
import { useNextTick } from '@polkadot/react-hooks';
import { useTranslation } from '../translate';
import useValidatorsWaiting from '../useValidatorsWaiting';
import Waiting from './Waiting';
interface Props {
className?: string;
favorites: string[];
isRelay: boolean;
sessionInfo: SessionInfo;
toggleFavorite: (stashId: string) => void;
validatorsActive?: Validator[];
}
function ListActive ({ className = '', favorites, isRelay, sessionInfo, toggleFavorite, validatorsActive }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const isNextTick = useNextTick();
const validatorsWaiting = useValidatorsWaiting(favorites, sessionInfo, validatorsActive);
const header = useRef<[string?, string?, number?][]>([
// favorite, badges, details, expand
[t<string>('waiting'), 'start', 4]
]);
return (
<Table
className={className}
empty={isNextTick && validatorsWaiting && t<string>('No waiting validators found')}
emptySpinner={t<string>('Retrieving waiting validators')}
header={header.current}
isSplit
legend={<Legend isRelay={isRelay} />}
>
{isNextTick && validatorsWaiting?.map((v) => (
<Waiting
key={v.key}
toggleFavorite={toggleFavorite}
validator={v}
/>
))}
</Table>
);
}
export default React.memo(ListActive);

View File

@ -0,0 +1,57 @@
// Copyright 2017-2023 @polkadot/app-staking authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Validator } from '../../types';
import React from 'react';
import { useToggle } from '@polkadot/react-hooks';
import Bottom from '../Active/Row/Bottom';
import Middle from '../Active/Row/Middle';
import Top from '../Active/Row/Top';
interface Props {
className?: string;
toggleFavorite: (stashId: string) => void;
validator: Validator;
}
interface PropsExpanded {
className?: string;
validator: Validator;
}
function EntryExpanded ({ className = '' }: PropsExpanded): React.ReactElement<PropsExpanded> {
return <td className={className} />;
}
function Entry ({ className = '', toggleFavorite, validator }: Props): React.ReactElement<Props> {
const [isExpanded, toggleExpanded] = useToggle();
return (
<>
<Top
className={className}
isExpanded={isExpanded}
toggleExpanded={toggleExpanded}
toggleFavorite={toggleFavorite}
validator={validator}
/>
<Middle
className={className}
isExpanded={isExpanded}
>
<td />
</Middle>
<Bottom
className={className}
isExpanded={isExpanded}
>
<EntryExpanded validator={validator} />
</Bottom>
</>
);
}
export default React.memo(Entry);

View File

@ -1,56 +1,54 @@
// Copyright 2017-2023 @polkadot/app-staking authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Validator } from '../../types';
import type { SessionInfo, Validator } from '../../types';
import React from 'react';
import React, { useRef } from 'react';
import { useToggle } from '@polkadot/react-hooks';
import Legend from '@polkadot/app-staking/Legend';
import { Table } from '@polkadot/react-components';
import { useNextTick } from '@polkadot/react-hooks';
import Bottom from '../Active/Row/Bottom';
import Middle from '../Active/Row/Middle';
import Top from '../Active/Row/Top';
import { useTranslation } from '../../translate';
import useValidatorsWaiting from '../../useValidatorsWaiting';
import Entry from './Entry';
interface Props {
className?: string;
favorites: string[];
isRelay: boolean;
sessionInfo: SessionInfo;
toggleFavorite: (stashId: string) => void;
validator: Validator;
validatorsActive?: Validator[];
}
interface PropsExpanded {
className?: string;
validator: Validator;
}
function Waiting ({ className = '', favorites, isRelay, sessionInfo, toggleFavorite, validatorsActive }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const isNextTick = useNextTick();
const validatorsWaiting = useValidatorsWaiting(favorites, sessionInfo, validatorsActive);
function WaitingExpanded ({ className = '' }: PropsExpanded): React.ReactElement<PropsExpanded> {
return <td className={className} />;
}
function Waiting ({ className = '', toggleFavorite, validator }: Props): React.ReactElement<Props> {
const [isExpanded, toggleExpanded] = useToggle();
const header = useRef<[string?, string?, number?][]>([
// favorite, badges, details, expand
[t<string>('waiting'), 'start', 4]
]);
return (
<>
<Top
className={className}
isExpanded={isExpanded}
toggleExpanded={toggleExpanded}
toggleFavorite={toggleFavorite}
validator={validator}
/>
<Middle
className={className}
isExpanded={isExpanded}
>
<td />
</Middle>
<Bottom
className={className}
isExpanded={isExpanded}
>
<WaitingExpanded validator={validator} />
</Bottom>
</>
<Table
className={className}
empty={isNextTick && validatorsWaiting && t<string>('No waiting validators found')}
emptySpinner={t<string>('Retrieving waiting validators')}
header={header.current}
isSplit
legend={<Legend isRelay={isRelay} />}
>
{isNextTick && validatorsWaiting?.map((v) => (
<Entry
key={v.key}
toggleFavorite={toggleFavorite}
validator={v}
/>
))}
</Table>
);
}

View File

@ -10,9 +10,9 @@ import { Button, ToggleGroup } from '@polkadot/react-components';
import { useTranslation } from '../translate';
import useValidatorsActive from '../useValidatorsActive';
import ListActive from './ListActive';
import ListWaiting from './ListWaiting';
import Active from './Active';
import usePoints from './usePoints';
import Waiting from './Waiting';
interface Props {
className?: string;
@ -44,7 +44,7 @@ function Validators ({ className = '', favorites, isRelay, sessionInfo, toggleFa
</Button.Group>
{intentIndex === 0
? (
<ListActive
<Active
isRelay={isRelay}
points={points}
sessionInfo={sessionInfo}
@ -53,7 +53,7 @@ function Validators ({ className = '', favorites, isRelay, sessionInfo, toggleFa
/>
)
: (
<ListWaiting
<Waiting
favorites={favorites}
isRelay={isRelay}
sessionInfo={sessionInfo}

View File

@ -2,19 +2,15 @@
// SPDX-License-Identifier: Apache-2.0
import type { PalletStakingExposure } from '@polkadot/types/lookup';
import type { SessionInfo, Validator } from '../../types';
import type { UseExposure, UseExposureExposure } from '../types';
import type { SessionInfo, Validator } from '../types';
import type { UseExposure, UseExposureExposure } from './types';
import { useEffect, useMemo } from 'react';
import { useMemo } from 'react';
import { createNamedHook, useApi, useCall } from '@polkadot/react-hooks';
import { BN, objectSpread } from '@polkadot/util';
import { BN } from '@polkadot/util';
interface CacheEntry extends UseExposure {
activeEra: BN;
}
type Cache = Record<string, CacheEntry>;
import { useCacheMap } from '../useCacheMap';
const OPT_EXPOSURE = {
transform: ({ others, own, total }: PalletStakingExposure): UseExposureExposure => ({
@ -48,8 +44,6 @@ function getResult (exposure: UseExposureExposure, clipped: UseExposureExposure)
return { clipped, exposure, waiting };
}
const cache: Cache = {};
function useExposureImpl ({ stashId }: Validator, { activeEra }: SessionInfo): UseExposure | undefined {
const { api } = useApi();
@ -66,21 +60,7 @@ function useExposureImpl ({ stashId }: Validator, { activeEra }: SessionInfo): U
[clipExposure, fullExposure]
);
// clear the cached result on era changes
useEffect((): void => {
if (activeEra && (!cache[stashId] || !activeEra.eq(cache[stashId].activeEra))) {
cache[stashId] = { activeEra };
}
}, [activeEra, stashId]);
// update the cached result on exposure changes
useEffect((): void => {
if (result && activeEra && cache[stashId] && activeEra.eq(cache[stashId].activeEra)) {
cache[stashId] = objectSpread<CacheEntry>({ activeEra }, result);
}
}, [activeEra, result, stashId]);
return result || cache[stashId];
return useCacheMap('useExposure', stashId, activeEra, result);
}
export default createNamedHook('useExposure', useExposureImpl);

View File

@ -3,20 +3,15 @@
import type { Option, u32 } from '@polkadot/types';
import type { Codec } from '@polkadot/types/types';
import type { BN } from '@polkadot/util';
import type { SessionInfo, Validator } from '../../types';
import type { UseHeartbeat } from '../types';
import type { SessionInfo, Validator } from '../types';
import type { UseHeartbeat } from './types';
import { useEffect, useMemo } from 'react';
import { useMemo } from 'react';
import { createNamedHook, useApi, useCall } from '@polkadot/react-hooks';
import { isBoolean, isNumber, objectSpread } from '@polkadot/util';
import { isBoolean, isNumber } from '@polkadot/util';
interface CacheEntry extends UseHeartbeat {
currentSession: BN;
}
type Cache = Record<string, CacheEntry>;
import { useCacheMap } from '../useCacheMap';
const EMPTY: UseHeartbeat = {};
@ -31,8 +26,6 @@ const OPT_BEATS = {
receivedHeartbeats.isSome
};
const cache: Cache = {};
function useHeartbeatImpl ({ stashId, stashIndex }: Validator, { currentSession }: SessionInfo): UseHeartbeat {
const { api } = useApi();
@ -57,21 +50,7 @@ function useHeartbeatImpl ({ stashId, stashIndex }: Validator, { currentSession
[authoredBlocks, receivedHeartbeats]
);
// when the session changes, empty the cache
useEffect((): void => {
if (currentSession && (!cache[stashId] || !currentSession.eq(cache[stashId].currentSession))) {
cache[stashId] = { currentSession };
}
}, [currentSession, stashId]);
// set the new heartbeat info based on retrieved
useEffect((): void => {
if (result && currentSession && currentSession.eq(cache[stashId].currentSession)) {
cache[stashId] = objectSpread<CacheEntry>({ currentSession }, result);
}
}, [currentSession, result, stashId]);
return result || cache[stashId] || EMPTY;
return useCacheMap('useHeartbeat', stashId, currentSession, result) || EMPTY;
}
export default createNamedHook('useHeartbeat', useHeartbeatImpl);

View File

@ -2,19 +2,15 @@
// SPDX-License-Identifier: Apache-2.0
import type { PalletStakingEraRewardPoints } from '@polkadot/types/lookup';
import type { BN } from '@polkadot/util';
import type { SessionInfo } from '../types';
import type { UsePoints } from './types';
import { useEffect, useMemo } from 'react';
import { useMemo } from 'react';
import { createNamedHook, useApi, useCall } from '@polkadot/react-hooks';
import { BN_ZERO } from '@polkadot/util';
interface Cache {
activeEra: BN;
points?: UsePoints;
}
import { useCacheValue } from '../useCacheValue';
const OPT_POINTS = {
transform: ({ individual }: PalletStakingEraRewardPoints): UsePoints =>
@ -27,8 +23,6 @@ const OPT_POINTS = {
}, {})
};
let cache: Cache | undefined;
function usePointsImpl ({ activeEra }: SessionInfo): UsePoints | undefined {
const { api } = useApi();
@ -39,17 +33,7 @@ function usePointsImpl ({ activeEra }: SessionInfo): UsePoints | undefined {
const points = useCall(queryParams && api.query.staking.erasRewardPoints, queryParams, OPT_POINTS);
useEffect((): void => {
if (activeEra && points) {
cache = { activeEra, points };
}
}, [activeEra, points]);
return points || (
cache &&
activeEra?.eq(cache.activeEra) &&
cache.points
) || undefined;
return useCacheValue('usePoints', activeEra, points);
}
export default createNamedHook('usePoints', usePointsImpl);

View File

@ -3,6 +3,11 @@
import type { BN } from '@polkadot/util';
export interface CacheValue<T> {
check: BN;
value?: T;
}
export interface SessionInfo {
activeEra?: BN | null;
currentEra?: BN | null;

View File

@ -0,0 +1,34 @@
// Copyright 2017-2023 @polkadot/app-staking authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { BN } from '@polkadot/util';
import type { CacheValue } from './types';
import { useEffect } from 'react';
import { getResultValue } from './useCacheValue';
type CacheSection = `use${'Exposure' | 'Heartbeat'}`;
const cache: Record<CacheSection, Record<string, CacheValue<any>>> = {
useExposure: {},
useHeartbeat: {}
};
export function useCacheMap <T> (section: CacheSection, id: string, check?: BN | null, value?: T): T | undefined {
// ensure that the specific id has an entry
useEffect((): void => {
if (check && (!cache[section][id] || !cache[section][id].check.eq(check))) {
cache[section][id] = { check };
}
}, [check, id, section]);
// update the cached result on value changes
useEffect((): void => {
if (value && check && cache[section][id] && cache[section][id].check.eq(check)) {
cache[section][id] = { check, value };
}
}, [check, id, section, value]);
return getResultValue(cache[section][id] as CacheValue<T>, check, value);
}

View File

@ -0,0 +1,45 @@
// Copyright 2017-2023 @polkadot/app-staking authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { CacheValue } from './types';
import { useEffect } from 'react';
import { BN } from '@polkadot/util';
type CacheSection = `use${'ElectedValidators' | 'Points' | 'ValidatorsActive' | 'ValidatorsAll' | 'ValidatorsWaiting'}`;
const cache: Record<CacheSection, CacheValue<any>> = {
useElectedValidators: { check: new BN(-1) },
usePoints: { check: new BN(-1) },
useValidatorsActive: { check: new BN(-1) },
useValidatorsAll: { check: new BN(-1) },
useValidatorsWaiting: { check: new BN(-1) }
};
export function getResultValue <T> (cached?: CacheValue<T>, check?: BN | null, value?: T): T | undefined {
return value || (
check &&
cached &&
cached.check.eq(check) &&
cached.value
) || undefined;
}
export function useCacheValue <T> (section: CacheSection, check?: BN | null, value?: T): T | undefined {
// ensure that the specific id has an entry
useEffect((): void => {
if (check && !cache[section].check.eq(check)) {
cache[section] = { check };
}
}, [check, section]);
// update the cached result on value changes
useEffect((): void => {
if (value && check && cache[section].check.eq(check)) {
cache[section] = { check, value };
}
}, [check, section, value]);
return getResultValue(cache[section] as CacheValue<T>, check, value);
}

View File

@ -3,17 +3,13 @@
import type { StorageKey, u32 } from '@polkadot/types';
import type { AccountId32 } from '@polkadot/types/interfaces';
import type { BN } from '@polkadot/util';
import type { SessionInfo } from './types';
import { useEffect, useMemo } from 'react';
import { useMemo } from 'react';
import { createNamedHook, useApi, useMapKeys } from '@polkadot/react-hooks';
interface Cache {
currentEra: BN;
elected: string[];
}
import { useCacheValue } from './useCacheValue';
const OPT_ELECTED = {
transform: (keys: StorageKey<[u32, AccountId32]>[]): string[] =>
@ -22,33 +18,17 @@ const OPT_ELECTED = {
)
};
// since this would only change once per era for the full runtime,
// we just store it globally to not have expensive map-key lookups
let cache: Cache | undefined;
function useElectedValidatorsImpl ({ currentEra }: SessionInfo): string[] | undefined {
const { api } = useApi();
const electedParams = useMemo(
() => currentEra && (!cache || !cache.currentEra.eq(currentEra))
? [currentEra]
: null,
() => currentEra && [currentEra],
[currentEra]
);
const elected = useMapKeys(electedParams && api.query.staking.erasStakers, electedParams, OPT_ELECTED);
useEffect((): void => {
if (currentEra && elected) {
cache = { currentEra, elected };
}
}, [currentEra, elected]);
return elected || (
cache &&
currentEra?.eq(cache.currentEra) &&
cache.elected
) || undefined;
return useCacheValue('useElectedValidators', currentEra, elected);
}
export default createNamedHook('useElectedValidators', useElectedValidatorsImpl);

View File

@ -2,20 +2,13 @@
// SPDX-License-Identifier: Apache-2.0
import type { AccountId32 } from '@polkadot/types/interfaces';
import type { BN } from '@polkadot/util';
import type { SessionInfo, Validator } from './types';
import { useEffect } from 'react';
import { createNamedHook, useApi, useCall } from '@polkadot/react-hooks';
import { useCacheValue } from './useCacheValue';
import useTaggedValidators from './useTaggedValidators';
interface Cache {
activeEra: BN;
tagged: Validator[];
}
const OPT_VALIDATORS = {
transform: (validators: AccountId32[]): Validator[] =>
validators.map((a, stashIndex) => {
@ -32,24 +25,12 @@ const OPT_VALIDATORS = {
})
};
let cache: Cache | undefined;
function useValidatorsActiveImpl (favorites: string[], sessionInfo: SessionInfo): Validator[] | undefined {
const { api } = useApi();
const sessionValidators = useCall(api.query.session.validators, undefined, OPT_VALIDATORS);
const tagged = useTaggedValidators(favorites, sessionInfo, sessionValidators);
useEffect((): void => {
if (tagged && sessionInfo.activeEra) {
cache = { activeEra: sessionInfo.activeEra, tagged };
}
}, [sessionInfo, tagged]);
return tagged || (
cache &&
sessionInfo.activeEra?.eq(cache.activeEra) &&
cache.tagged
) || undefined;
return useCacheValue('useValidatorsActive', sessionInfo.activeEra, tagged);
}
export default createNamedHook('useValidatorsActive', useValidatorsActiveImpl);

View File

@ -6,10 +6,11 @@ import type { StorageKey } from '@polkadot/types';
import type { AccountId32, EventRecord } from '@polkadot/types/interfaces';
import type { SessionInfo, Validator } from './types';
import { useEffect, useMemo } from 'react';
import { useMemo } from 'react';
import { createNamedHook, useApi, useEventChanges, useMapKeys } from '@polkadot/react-hooks';
import { useCacheValue } from './useCacheValue';
import useTaggedValidators from './useTaggedValidators';
const EMPTY_PARAMS: unknown[] = [];
@ -49,8 +50,6 @@ function mapValidators (validators?: AccountId32[]): Validator[] | undefined {
});
}
let cache: Validator[] | undefined;
function useValidatorsAllImpl (favorites: string[], sessionInfo: SessionInfo): Validator[] | undefined {
const { api } = useApi();
const startValue = useMapKeys(api.query.staking.validators, EMPTY_PARAMS, OPT_VALIDATORS);
@ -66,13 +65,7 @@ function useValidatorsAllImpl (favorites: string[], sessionInfo: SessionInfo): V
const tagged = useTaggedValidators(favorites, sessionInfo, validatorsIndexed);
useEffect((): void => {
if (tagged) {
cache = tagged;
}
}, [tagged, validatorsIndexed]);
return tagged || cache;
return useCacheValue('useValidatorsAll', sessionInfo.currentEra, tagged);
}
export default createNamedHook('useValidatorsAll', useValidatorsAllImpl);

View File

@ -1,28 +1,21 @@
// Copyright 2017-2023 @polkadot/app-staking authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { BN } from '@polkadot/util';
import type { SessionInfo, Validator } from './types';
import { useEffect, useMemo } from 'react';
import { useMemo } from 'react';
import { createNamedHook } from '@polkadot/react-hooks';
import { useCacheValue } from './useCacheValue';
import useValidatorsAll from './useValidatorsAll';
interface Cache {
activeEra: BN;
tagged: Validator[];
}
function excludeValidators (from: Validator[], exclude: Validator[]): Validator[] {
return from.filter(({ stashId }) =>
!exclude.some((v) => v.stashId === stashId)
);
}
let cache: Cache | undefined;
function useValidatorsWaitingImpl (favorites: string[], sessionInfo: SessionInfo, activeValidators?: Validator[]): Validator[] | undefined {
const allValidators = useValidatorsAll(favorites, sessionInfo);
@ -33,17 +26,7 @@ function useValidatorsWaitingImpl (favorites: string[], sessionInfo: SessionInfo
[activeValidators, allValidators]
);
useEffect((): void => {
if (tagged && sessionInfo.activeEra) {
cache = { activeEra: sessionInfo.activeEra, tagged };
}
}, [sessionInfo, tagged]);
return tagged || (
cache &&
sessionInfo.activeEra?.eq(cache.activeEra) &&
cache.tagged
) || undefined;
return useCacheValue('useValidatorsWaiting', sessionInfo.activeEra, tagged);
}
export default createNamedHook('useValidatorsWaiting', useValidatorsWaitingImpl);