Swap parachain to multi calls (w/ useCallMulti) (#4414)
This commit is contained in:
parent
f930cce30c
commit
fd9110f432
@ -2,14 +2,14 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { LinkOption } from '@polkadot/apps-config/settings/types';
|
||||
import type { Option } from '@polkadot/types';
|
||||
import type { Balance, BlockNumber, HeadData, Header, ParaId, RelayChainBlockNumber } from '@polkadot/types/interfaces';
|
||||
import type { Codec, ITuple } from '@polkadot/types/types';
|
||||
import type { Option, Vec } from '@polkadot/types';
|
||||
import type { Balance, BlockNumber, HeadData, Header, ParaId } from '@polkadot/types/interfaces';
|
||||
import type { Codec } from '@polkadot/types/types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import { useApi, useCall, useParaApi } from '@polkadot/react-hooks';
|
||||
import { useApi, useCall, useCallMulti, useParaApi } from '@polkadot/react-hooks';
|
||||
import { BlockToTime, FormatBalance } from '@polkadot/react-query';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
@ -22,31 +22,40 @@ interface Props {
|
||||
lastInclusion?: [string, string];
|
||||
}
|
||||
|
||||
const transformDmpUmp = {
|
||||
transform: (list: Codec[][]) => list.length
|
||||
};
|
||||
type QueryResult = [Option<HeadData>, Option<BlockNumber>, Vec<Codec>, Vec<Codec>, Vec<Codec>, Vec<Codec>];
|
||||
|
||||
const transformHead = {
|
||||
transform: (headData: Option<HeadData>): string | null =>
|
||||
headData.isSome
|
||||
? sliceHex(headData.unwrap(), 18)
|
||||
: null
|
||||
};
|
||||
|
||||
// const transformMark = {
|
||||
// transform: (watermark: Option<BlockNumber>): BlockNumber | null =>
|
||||
// watermark.isSome
|
||||
// ? watermark.unwrap()
|
||||
// : null
|
||||
// };
|
||||
interface QueryState {
|
||||
headHex: string | null;
|
||||
updateAt: BlockNumber | null;
|
||||
qDmp: number;
|
||||
qUmp: number;
|
||||
qHrmpE: number;
|
||||
qHrmpI: number;
|
||||
}
|
||||
|
||||
const transformLast = {
|
||||
transform: (header: Header) => header.number.unwrap()
|
||||
};
|
||||
|
||||
const transformUpgrade = {
|
||||
transform: (opt: Option<ITuple<[RelayChainBlockNumber]>>): RelayChainBlockNumber | null =>
|
||||
opt.unwrapOr([null])[0]
|
||||
const transformMulti = {
|
||||
defaultValue: {
|
||||
headHex: null,
|
||||
qDmp: 0,
|
||||
qHrmpE: 0,
|
||||
qHrmpI: 0,
|
||||
qUmp: 0,
|
||||
updateAt: null
|
||||
},
|
||||
transform: ([headData, optUp, dmp, ump, hrmpE, hrmpI]: QueryResult): QueryState => ({
|
||||
headHex: headData.isSome
|
||||
? sliceHex(headData.unwrap(), 18)
|
||||
: null,
|
||||
qDmp: dmp.length,
|
||||
qHrmpE: hrmpE.length,
|
||||
qHrmpI: hrmpI.length,
|
||||
qUmp: ump.length,
|
||||
updateAt: optUp.unwrapOr(null)
|
||||
})
|
||||
};
|
||||
|
||||
function getChainLink (endpoints: LinkOption[]): React.ReactNode {
|
||||
@ -64,13 +73,15 @@ function Parachain ({ bestNumber, className = '', id, lastInclusion }: Props): R
|
||||
const { api: paraApi, endpoints } = useParaApi(id);
|
||||
const paraBest = useCall<BlockNumber>(paraApi?.derive.chain.bestNumber);
|
||||
const paraIssu = useCall<Balance>(paraApi?.query.balances?.totalIssuance);
|
||||
const headHex = useCall<string | null>(api.query.paras.heads, [id], transformHead);
|
||||
const updateAt = useCall<BlockNumber | null>(api.query.paras.futureCodeUpgrades, [id], transformUpgrade);
|
||||
const qDmp = useCall<number>(api.query.dmp.downwardMessageQueues, [id], transformDmpUmp);
|
||||
const qUmp = useCall<number>(api.query.dmp.relayDispatchQueues, [id], transformDmpUmp);
|
||||
const qHrmpE = useCall<number>(api.query.hrmp.hrmpEgressChannelsIndex, [id], transformDmpUmp);
|
||||
const qHrmpI = useCall<number>(api.query.hrmp.hrmpIngressChannelsIndex, [id], transformDmpUmp);
|
||||
const lastRelayNumber = useCall<BN>(lastInclusion && api.rpc.chain.getHeader, [lastInclusion && lastInclusion[1]], transformLast);
|
||||
const paraInfo = useCallMulti<QueryState>([
|
||||
[api.query.paras.heads, id],
|
||||
[api.query.paras.futureCodeUpgrades, id],
|
||||
[api.query.dmp.downwardMessageQueues, id],
|
||||
[api.query.ump.relayDispatchQueues, id],
|
||||
[api.query.hrmp.hrmpEgressChannelsIndex, id],
|
||||
[api.query.hrmp.hrmpIngressChannelsIndex, id]
|
||||
], transformMulti) as QueryState; // we have a default value, cast is safe
|
||||
|
||||
const blockDelay = useMemo(
|
||||
() => lastRelayNumber && bestNumber && bestNumber.sub(lastRelayNumber).subn(1),
|
||||
@ -86,7 +97,7 @@ function Parachain ({ bestNumber, className = '', id, lastInclusion }: Props): R
|
||||
<tr className={className}>
|
||||
<td className='number'><h1>{formatNumber(id)}</h1></td>
|
||||
<td className='together'>{chainLink}</td>
|
||||
<td className='all start together hash'>{headHex}</td>
|
||||
<td className='all start together hash'>{paraInfo.headHex}</td>
|
||||
<td className='number'>{blockDelay && <BlockToTime blocks={blockDelay} />}</td>
|
||||
<td className='number'>
|
||||
{lastInclusion && lastRelayNumber && (
|
||||
@ -97,15 +108,15 @@ function Parachain ({ bestNumber, className = '', id, lastInclusion }: Props): R
|
||||
<td className='number media--900'>{paraBest && <>{formatNumber(paraBest)}</>}</td>
|
||||
<td className='number media--1100'>{paraIssu && <FormatBalance valueFormatted={paraIssu.toHuman()} />}</td>
|
||||
<td className='number media--1300'>
|
||||
{updateAt && bestNumber && (
|
||||
{paraInfo.updateAt && bestNumber && (
|
||||
<>
|
||||
<BlockToTime blocks={bestNumber.sub(updateAt)} />
|
||||
#{formatNumber(updateAt)}
|
||||
<BlockToTime blocks={bestNumber.sub(paraInfo.updateAt)} />
|
||||
#{formatNumber(paraInfo.updateAt)}
|
||||
</>
|
||||
)}
|
||||
</td>
|
||||
<td className='number media--1200'>
|
||||
{formatNumber(qUmp)} / {formatNumber(qDmp)} / {formatNumber(qHrmpE)} / {formatNumber(qHrmpI)}
|
||||
{formatNumber(paraInfo.qUmp)} / {formatNumber(paraInfo.qDmp)} / {formatNumber(paraInfo.qHrmpE)} / {formatNumber(paraInfo.qHrmpI)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
|
||||
@ -11,6 +11,7 @@ export { useBlockTime } from './useBlockTime';
|
||||
export { useBlocksPerDays } from './useBlocksPerDays';
|
||||
export { useCacheKey } from './useCacheKey';
|
||||
export { useCall } from './useCall';
|
||||
export { useCallMulti } from './useCallMulti';
|
||||
export { useContracts } from './useContracts';
|
||||
export { useFormField } from './useFormField';
|
||||
export { useDebounce } from './useDebounce';
|
||||
|
||||
@ -4,12 +4,13 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
import type { Codec } from '@polkadot/types/types';
|
||||
import type { CallOptions, CallParam, CallParams } from './types';
|
||||
import type { MountedRef } from './useIsMountedRef';
|
||||
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { isNull, isUndefined } from '@polkadot/util';
|
||||
|
||||
import { MountedRef, useIsMountedRef } from './useIsMountedRef';
|
||||
import { useIsMountedRef } from './useIsMountedRef';
|
||||
|
||||
type VoidFn = () => void;
|
||||
|
||||
@ -28,7 +29,7 @@ interface TrackFn {
|
||||
};
|
||||
}
|
||||
|
||||
interface Tracker {
|
||||
export interface Tracker {
|
||||
isActive: boolean;
|
||||
serialized: string | null;
|
||||
subscriber: TrackFnResult | null;
|
||||
@ -39,7 +40,7 @@ interface TrackerRef {
|
||||
}
|
||||
|
||||
// the default transform, just returns what we have
|
||||
function transformIdentity <T> (value: unknown): T {
|
||||
export function transformIdentity <T> (value: unknown): T {
|
||||
return value as T;
|
||||
}
|
||||
|
||||
@ -54,7 +55,7 @@ function extractParams <T> (fn: unknown, params: unknown[], { paramMap = transfo
|
||||
}
|
||||
|
||||
// unsubscribe and remove from the tracker
|
||||
function unsubscribe (tracker: TrackerRef): void {
|
||||
export function unsubscribe (tracker: TrackerRef): void {
|
||||
tracker.current.isActive = false;
|
||||
|
||||
if (tracker.current.subscriber) {
|
||||
|
||||
76
packages/react-hooks/src/useCallMulti.ts
Normal file
76
packages/react-hooks/src/useCallMulti.ts
Normal file
@ -0,0 +1,76 @@
|
||||
// Copyright 2017-2021 @polkadot/react-hooks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { ApiPromise } from '@polkadot/api';
|
||||
import type { QueryableStorageMultiArg } from '@polkadot/api/types';
|
||||
import type { Tracker } from './useCall';
|
||||
import type { MountedRef } from './useIsMountedRef';
|
||||
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { useApi } from './useApi';
|
||||
import { transformIdentity, unsubscribe } from './useCall';
|
||||
import { useIsMountedRef } from './useIsMountedRef';
|
||||
|
||||
interface TrackerRef {
|
||||
current: Tracker;
|
||||
}
|
||||
|
||||
interface CallOptions <T> {
|
||||
defaultValue?: T;
|
||||
transform?: (value: any) => T;
|
||||
}
|
||||
|
||||
// subscribe, trying to play nice with the browser threads
|
||||
function subscribe <T> (api: ApiPromise, mountedRef: MountedRef, tracker: TrackerRef, calls: QueryableStorageMultiArg<'promise'>[], setValue: (value: T) => void, { transform = transformIdentity }: CallOptions<T> = {}): void {
|
||||
unsubscribe(tracker);
|
||||
|
||||
setTimeout((): void => {
|
||||
if (mountedRef.current) {
|
||||
if (calls.length) {
|
||||
// swap to acive mode
|
||||
tracker.current.isActive = true;
|
||||
|
||||
tracker.current.subscriber = api.queryMulti(calls, (value): void => {
|
||||
// we use the isActive flag here since .subscriber may not be set on immediate callback)
|
||||
if (mountedRef.current && tracker.current.isActive) {
|
||||
mountedRef.current && tracker.current.isActive && setValue(
|
||||
transform(value)
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
tracker.current.subscriber = null;
|
||||
}
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// very much copied from useCall
|
||||
export function useCallMulti <T> (calls?: QueryableStorageMultiArg<'promise'>[] | null | false, options?: CallOptions<T>): T | undefined {
|
||||
const { api } = useApi();
|
||||
const mountedRef = useIsMountedRef();
|
||||
const tracker = useRef<Tracker>({ isActive: false, serialized: null, subscriber: null });
|
||||
const [value, setValue] = useState<T | undefined>((options || {}).defaultValue);
|
||||
|
||||
// initial effect, we need an un-subscription
|
||||
useEffect((): () => void => {
|
||||
return () => unsubscribe(tracker);
|
||||
}, []);
|
||||
|
||||
// on changes, re-subscribe
|
||||
useEffect((): void => {
|
||||
// check if we have a function & that we are mounted
|
||||
if (mountedRef.current && calls) {
|
||||
const serialized = JSON.stringify(calls);
|
||||
|
||||
if (serialized !== tracker.current.serialized) {
|
||||
tracker.current.serialized = serialized;
|
||||
|
||||
subscribe(api, mountedRef, tracker, calls, setValue, options);
|
||||
}
|
||||
}
|
||||
}, [api, calls, options, mountedRef]);
|
||||
|
||||
return value;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user