Para updates (#4906)
* Para updates * Update packages/page-parachains/src/Crowdloan/Fund.tsx * Handle current Rococo for registrar.paras
This commit is contained in:
parent
011740ad30
commit
270a73f09a
@ -3,13 +3,15 @@
|
||||
|
||||
import type BN from 'bn.js';
|
||||
import type { AuctionIndex, BlockNumber, LeasePeriodOf } from '@polkadot/types/interfaces';
|
||||
import type { OwnedId, OwnerInfo } from '../types';
|
||||
|
||||
import React, { useMemo, useState } from 'react';
|
||||
|
||||
import { Button, Dropdown, InputAddress, InputBalance, InputNumber, Modal, TxButton } from '@polkadot/react-components';
|
||||
import { Button, Dropdown, InputBalance, Modal, TxButton } from '@polkadot/react-components';
|
||||
import { useAccounts, useApi, useBestNumber, useToggle } from '@polkadot/react-hooks';
|
||||
import { BN_ZERO, formatNumber } from '@polkadot/util';
|
||||
|
||||
import InputOwner from '../InputOwner';
|
||||
import { useTranslation } from '../translate';
|
||||
import { RANGES } from './constants';
|
||||
|
||||
@ -17,6 +19,7 @@ interface Props {
|
||||
auctionInfo: [LeasePeriodOf, BlockNumber] | null;
|
||||
className?: string;
|
||||
id: AuctionIndex | null;
|
||||
ownedIds: OwnedId[];
|
||||
}
|
||||
|
||||
interface Option {
|
||||
@ -24,14 +27,13 @@ interface Option {
|
||||
value: number;
|
||||
}
|
||||
|
||||
function Bid ({ auctionInfo, className, id }: Props): React.ReactElement<Props> {
|
||||
function Bid ({ auctionInfo, className, id, ownedIds }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const { hasAccounts } = useAccounts();
|
||||
const bestNumber = useBestNumber();
|
||||
const [accountId, setAccountId] = useState<string | null>(null);
|
||||
const [{ accountId, paraId }, setOwnerInfo] = useState<OwnerInfo>({ accountId: null, paraId: 0 });
|
||||
const [amount, setAmount] = useState<BN | undefined>(BN_ZERO);
|
||||
const [paraId, setParaId] = useState<BN | undefined>(BN_ZERO);
|
||||
const [range, setRange] = useState(0);
|
||||
const [isOpen, toggleOpen] = useToggle();
|
||||
|
||||
@ -57,7 +59,7 @@ function Bid ({ auctionInfo, className, id }: Props): React.ReactElement<Props>
|
||||
<>
|
||||
<Button
|
||||
icon='plus'
|
||||
isDisabled={!hasAccounts || !id || !leasePeriod || !endBlock || bestNumber?.gte(endBlock.add(api.consts.auctions.endingPeriod as BlockNumber))}
|
||||
isDisabled={!ownedIds.length || !hasAccounts || !id || !leasePeriod || !endBlock || bestNumber?.gte(endBlock.add(api.consts.auctions.endingPeriod as BlockNumber))}
|
||||
label={t<string>('Bid')}
|
||||
onClick={toggleOpen}
|
||||
/>
|
||||
@ -68,23 +70,10 @@ function Bid ({ auctionInfo, className, id }: Props): React.ReactElement<Props>
|
||||
size='large'
|
||||
>
|
||||
<Modal.Content>
|
||||
<Modal.Columns hint={t<string>('This account will be associated with the bid. This should match the registrar for the parachain.')}>
|
||||
<InputAddress
|
||||
label={t<string>('bid from')}
|
||||
onChange={setAccountId}
|
||||
type='account'
|
||||
value={accountId}
|
||||
/>
|
||||
</Modal.Columns>
|
||||
<Modal.Columns hint={t<string>('The parachain id this bid is placed for')}>
|
||||
<InputNumber
|
||||
autoFocus
|
||||
defaultValue={paraId?.toString()}
|
||||
isZeroable={false}
|
||||
label={t<string>('parachain id')}
|
||||
onChange={setParaId}
|
||||
/>
|
||||
</Modal.Columns>
|
||||
<InputOwner
|
||||
onChange={setOwnerInfo}
|
||||
ownedIds={ownedIds}
|
||||
/>
|
||||
<Modal.Columns hint={t<string>('The amount to to bid for this parachain slot')}>
|
||||
<InputBalance
|
||||
isZeroable={false}
|
||||
@ -105,7 +94,7 @@ function Bid ({ auctionInfo, className, id }: Props): React.ReactElement<Props>
|
||||
<TxButton
|
||||
accountId={accountId}
|
||||
icon='plus'
|
||||
isDisabled={!paraId?.gt(BN_ZERO) || !amount?.gt(BN_ZERO) || !leasePeriod}
|
||||
isDisabled={!paraId || !amount?.gt(BN_ZERO) || !leasePeriod}
|
||||
label={t<string>('Bid')}
|
||||
onStart={toggleOpen}
|
||||
params={[paraId, id, leasePeriod?.addn(RANGES[range][0]), leasePeriod?.addn(RANGES[range][1]), amount]}
|
||||
|
||||
@ -39,7 +39,7 @@ function WinRanges ({ auctionInfo, blockNumber, className = '', isFirst, isLates
|
||||
<td className='number'><h1>{formatNumber(paraId)}</h1></td>
|
||||
<td><ParaLink id={paraId} /></td>
|
||||
<td className='address'><AddressMini value={accountId} /></td>
|
||||
<td className='number'><Digits value={`${formatNumber(leasePeriod.addn(range[0]))} - ${formatNumber(leasePeriod.addn(range[1]))}`} /></td>
|
||||
<td className='all number'><Digits value={`${formatNumber(leasePeriod.addn(range[0]))} - ${formatNumber(leasePeriod.addn(range[1]))}`} /></td>
|
||||
<td className='number'><FormatBalance value={value} /></td>
|
||||
</tr>
|
||||
);
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
import type { Option } from '@polkadot/types';
|
||||
import type { AuctionIndex, BlockNumber, LeasePeriodOf } from '@polkadot/types/interfaces';
|
||||
import type { ITuple } from '@polkadot/types/types';
|
||||
import type { OwnedId } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
@ -17,6 +18,7 @@ import useWinningData from './useWinningData';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
ownedIds: OwnedId[];
|
||||
}
|
||||
|
||||
interface QueryState {
|
||||
@ -35,7 +37,7 @@ const optionsMulti = {
|
||||
})
|
||||
};
|
||||
|
||||
function Auctions ({ className }: Props): React.ReactElement<Props> {
|
||||
function Auctions ({ className, ownedIds }: Props): React.ReactElement<Props> {
|
||||
const { api } = useApi();
|
||||
const { auctionInfo, numAuctions } = useCallMulti<QueryState>([
|
||||
api.query.auctions.auctionCounter,
|
||||
@ -53,7 +55,8 @@ function Auctions ({ className }: Props): React.ReactElement<Props> {
|
||||
<Button.Group>
|
||||
<Bid
|
||||
auctionInfo={auctionInfo}
|
||||
id={ numAuctions}
|
||||
id={numAuctions}
|
||||
ownedIds={ownedIds}
|
||||
/>
|
||||
</Button.Group>
|
||||
<Auction
|
||||
|
||||
@ -7,8 +7,8 @@ import type { Campaign } from './types';
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import { AddressMini, Digits, ParaLink } from '@polkadot/react-components';
|
||||
import { useApi } from '@polkadot/react-hooks';
|
||||
import { AddressMini, Digits, ParaLink, TxButton } from '@polkadot/react-components';
|
||||
import { useAccounts, useApi } from '@polkadot/react-hooks';
|
||||
import { BlockToTime, FormatBalance } from '@polkadot/react-query';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
@ -25,6 +25,16 @@ interface Props {
|
||||
function Fund ({ bestNumber, className, isOngoing, value: { info: { cap, depositor, end, firstSlot, lastSlot, raised, retiring }, paraId } }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const { allAccounts } = useAccounts();
|
||||
|
||||
const isDepositor = useMemo(
|
||||
(): boolean => {
|
||||
const address = depositor.toString();
|
||||
|
||||
return allAccounts.some((a) => a === address);
|
||||
},
|
||||
[allAccounts, depositor]
|
||||
);
|
||||
|
||||
const blocksLeft = useMemo(
|
||||
() => bestNumber && end.gt(bestNumber) && end.sub(bestNumber),
|
||||
@ -42,6 +52,7 @@ function Fund ({ bestNumber, className, isOngoing, value: { info: { cap, deposit
|
||||
);
|
||||
|
||||
const canContribute = blocksLeft && !isCapped && retiring.isFalse;
|
||||
const canDissolve = raised.isZero();
|
||||
|
||||
return (
|
||||
<tr className={className}>
|
||||
@ -58,7 +69,7 @@ function Fund ({ bestNumber, className, isOngoing, value: { info: { cap, deposit
|
||||
}
|
||||
</td>
|
||||
<td className='address'><AddressMini value={depositor} /></td>
|
||||
<td className='number together'>
|
||||
<td className='all number together'>
|
||||
{blocksLeft && (
|
||||
<BlockToTime value={blocksLeft} />
|
||||
)}
|
||||
@ -83,6 +94,16 @@ function Fund ({ bestNumber, className, isOngoing, value: { info: { cap, deposit
|
||||
raised={raised}
|
||||
/>
|
||||
)}
|
||||
{canDissolve && (
|
||||
<TxButton
|
||||
accountId={depositor}
|
||||
icon='times'
|
||||
isDisabled={!isDepositor}
|
||||
label={t<string>('Dissolve')}
|
||||
params={[paraId]}
|
||||
tx={api.tx.crowdloan.dissolve}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
)}
|
||||
</tr>
|
||||
|
||||
@ -2,30 +2,31 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type BN from 'bn.js';
|
||||
import type { OwnedId, OwnerInfo } from '../types';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { Button, InputAddress, InputBalance, InputNumber, Modal, TxButton } from '@polkadot/react-components';
|
||||
import { useAccounts, useApi, useToggle } from '@polkadot/react-hooks';
|
||||
import { Button, InputBalance, InputNumber, Modal, TxButton } from '@polkadot/react-components';
|
||||
import { useApi, useToggle } from '@polkadot/react-hooks';
|
||||
import { BN_ZERO } from '@polkadot/util';
|
||||
|
||||
import InputOwner from '../InputOwner';
|
||||
import { useTranslation } from '../translate';
|
||||
|
||||
interface Props {
|
||||
bestNumber?: BN;
|
||||
className?: string;
|
||||
ownedIds: OwnedId[];
|
||||
}
|
||||
|
||||
function FundAdd ({ bestNumber, className }: Props): React.ReactElement<Props> {
|
||||
function FundAdd ({ bestNumber, className, ownedIds }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const { hasAccounts } = useAccounts();
|
||||
const [accountId, setAccountId] = useState<string | null>(null);
|
||||
const [{ accountId, paraId }, setOwnerInfo] = useState<OwnerInfo>({ accountId: null, paraId: 0 });
|
||||
const [cap, setCap] = useState<BN | undefined>();
|
||||
const [endBlock, setEndBlock] = useState<BN | undefined>();
|
||||
const [firstSlot, setFirstSlot] = useState<BN | undefined>();
|
||||
const [lastSlot, setLastSlot] = useState<BN | undefined>();
|
||||
const [paraId, setParaId] = useState<BN | undefined>();
|
||||
const [isOpen, toggleOpen] = useToggle();
|
||||
|
||||
const isEndError = !bestNumber || !endBlock || endBlock.lt(bestNumber);
|
||||
@ -37,7 +38,7 @@ function FundAdd ({ bestNumber, className }: Props): React.ReactElement<Props> {
|
||||
<>
|
||||
<Button
|
||||
icon='plus'
|
||||
isDisabled={!hasAccounts}
|
||||
isDisabled={!ownedIds.length}
|
||||
label={t<string>('Add fund')}
|
||||
onClick={toggleOpen}
|
||||
/>
|
||||
@ -48,22 +49,10 @@ function FundAdd ({ bestNumber, className }: Props): React.ReactElement<Props> {
|
||||
size='large'
|
||||
>
|
||||
<Modal.Content>
|
||||
<Modal.Columns hint={t<string>('This account will be associated with the fund and pay the deposit. This should match the registrar for the parachain.')}>
|
||||
<InputAddress
|
||||
label={t<string>('propose from')}
|
||||
onChange={setAccountId}
|
||||
type='account'
|
||||
value={accountId}
|
||||
/>
|
||||
</Modal.Columns>
|
||||
<Modal.Columns hint={t<string>('The parachain id this fund applies to')}>
|
||||
<InputNumber
|
||||
autoFocus
|
||||
isZeroable={false}
|
||||
label={t<string>('parachain id')}
|
||||
onChange={setParaId}
|
||||
/>
|
||||
</Modal.Columns>
|
||||
<InputOwner
|
||||
onChange={setOwnerInfo}
|
||||
ownedIds={ownedIds}
|
||||
/>
|
||||
<Modal.Columns hint={t<string>('The amount to be raised in this funding campaign')}>
|
||||
<InputBalance
|
||||
isZeroable={false}
|
||||
@ -94,7 +83,7 @@ function FundAdd ({ bestNumber, className }: Props): React.ReactElement<Props> {
|
||||
<TxButton
|
||||
accountId={accountId}
|
||||
icon='plus'
|
||||
isDisabled={!paraId?.gt(BN_ZERO) || !cap?.gt(BN_ZERO) || !firstSlot?.gte(BN_ZERO) || isEndError || isLastError}
|
||||
isDisabled={!paraId || !cap?.gt(BN_ZERO) || !firstSlot?.gte(BN_ZERO) || isEndError || isLastError}
|
||||
label={t<string>('Add')}
|
||||
onStart={toggleOpen}
|
||||
params={[paraId, cap, firstSlot, lastSlot, endBlock, null]}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type BN from 'bn.js';
|
||||
import type { Balance, BlockNumber, ParaId } from '@polkadot/types/interfaces';
|
||||
import type { Balance, BalanceOf, BlockNumber, ParaId } from '@polkadot/types/interfaces';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
@ -30,7 +30,7 @@ function FundContribute ({ cap, className, paraId, raised }: Props): React.React
|
||||
// TODO verifier signature
|
||||
|
||||
const remaining = cap.sub(raised);
|
||||
const isAmountBelow = !amount || amount.lt(api.consts.crowdloan.minContribution as BlockNumber);
|
||||
const isAmountBelow = !amount || amount.lt(api.consts.crowdloan.minContribution as BalanceOf);
|
||||
const isAmountOver = !!(amount && amount.gt(remaining));
|
||||
const isAmountError = isAmountBelow || isAmountOver;
|
||||
|
||||
@ -60,6 +60,7 @@ function FundContribute ({ cap, className, paraId, raised }: Props): React.React
|
||||
<Modal.Columns hint={t<string>('The amount to contribute. Should be less than the remaining value and more than the minimum contribution amount.')}>
|
||||
<InputBalance
|
||||
autoFocus
|
||||
defaultValue={api.consts.crowdloan.minContribution as BalanceOf}
|
||||
isError={isAmountError}
|
||||
isZeroable={false}
|
||||
label={t<string>('contribution')}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
// Copyright 2017-2021 @polkadot/app-parachains authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { OwnedId } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { Button } from '@polkadot/react-components';
|
||||
@ -13,9 +15,10 @@ import useFunds from './useFunds';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
ownedIds: OwnedId[];
|
||||
}
|
||||
|
||||
function Crowdloan ({ className }: Props): React.ReactElement<Props> {
|
||||
function Crowdloan ({ className, ownedIds }: Props): React.ReactElement<Props> {
|
||||
const bestNumber = useBestNumber();
|
||||
const { activeCap, activeRaised, funds, totalCap, totalRaised } = useFunds();
|
||||
|
||||
@ -29,7 +32,10 @@ function Crowdloan ({ className }: Props): React.ReactElement<Props> {
|
||||
totalRaised={totalRaised}
|
||||
/>
|
||||
<Button.Group>
|
||||
<FundAdd bestNumber={bestNumber} />
|
||||
<FundAdd
|
||||
bestNumber={bestNumber}
|
||||
ownedIds={ownedIds}
|
||||
/>
|
||||
</Button.Group>
|
||||
<Funds
|
||||
bestNumber={bestNumber}
|
||||
|
||||
64
packages/page-parachains/src/InputOwner.tsx
Normal file
64
packages/page-parachains/src/InputOwner.tsx
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright 2017-2021 @polkadot/app-parachains authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { OwnedId, OwnerInfo } from './types';
|
||||
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { Dropdown, InputAddress, Modal } from '@polkadot/react-components';
|
||||
|
||||
import { useTranslation } from './translate';
|
||||
|
||||
interface Props {
|
||||
onChange: (owner: OwnerInfo) => void;
|
||||
ownedIds: OwnedId[];
|
||||
}
|
||||
|
||||
function InputOwner ({ onChange, ownedIds }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const [accountId, setAccountId] = useState<string | null>(null);
|
||||
const [paraId, setParaId] = useState<number>(0);
|
||||
|
||||
useEffect((): void => {
|
||||
accountId && paraId && onChange({ accountId, paraId });
|
||||
}, [accountId, onChange, paraId]);
|
||||
|
||||
const owners = useMemo(
|
||||
() => ownedIds.map(({ manager }) => manager),
|
||||
[ownedIds]
|
||||
);
|
||||
|
||||
const optIds = useMemo(
|
||||
() => ownedIds
|
||||
.filter(({ manager }) => manager === accountId)
|
||||
.map(({ paraId }) => ({ text: paraId.toString(), value: paraId.toNumber() })),
|
||||
[accountId, ownedIds]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal.Columns hint={t<string>('This account that has been used to register the parachain. This will pay all associated fees.')}>
|
||||
<InputAddress
|
||||
filter={owners}
|
||||
label={t<string>('parachain owner')}
|
||||
onChange={setAccountId}
|
||||
type='account'
|
||||
value={accountId}
|
||||
/>
|
||||
</Modal.Columns>
|
||||
{accountId && (
|
||||
<Modal.Columns hint={t<string>('The parachain id, as associated with the account on the registrar')}>
|
||||
<Dropdown
|
||||
defaultValue={optIds[0].value}
|
||||
key={accountId}
|
||||
label={t<string>('parachain id')}
|
||||
onChange={setParaId}
|
||||
options={optIds}
|
||||
/>
|
||||
</Modal.Columns>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(InputOwner);
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2017-2021 @polkadot/app-parachains authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Option, Vec } from '@polkadot/types';
|
||||
import type { AccountId, BlockNumber, CandidatePendingAvailability, HeadData, Header, ParaId, ParaLifecycle } from '@polkadot/types/interfaces';
|
||||
import type { bool, Option, Vec } from '@polkadot/types';
|
||||
import type { AccountId, BlockNumber, CandidatePendingAvailability, HeadData, Header, ParaId, ParaInfo, ParaLifecycle } from '@polkadot/types/interfaces';
|
||||
import type { Codec } from '@polkadot/types/types';
|
||||
import type { EventMapInfo, QueuedAction } from './types';
|
||||
|
||||
@ -12,7 +12,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { AddressMini, Badge, Expander, ParaLink } from '@polkadot/react-components';
|
||||
import { useApi, useCall, useCallMulti, useParaApi } from '@polkadot/react-hooks';
|
||||
import { BlockToTime } from '@polkadot/react-query';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
import { formatNumber, isFunction } from '@polkadot/util';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
import { sliceHex } from '../util';
|
||||
@ -31,11 +31,12 @@ interface Props {
|
||||
validators?: AccountId[];
|
||||
}
|
||||
|
||||
type QueryResult = [Option<HeadData>, Option<BlockNumber>, Option<ParaLifecycle>, Vec<Codec>, Vec<Codec>, Vec<Codec>, Vec<Codec>, Option<BlockNumber>, Option<CandidatePendingAvailability>];
|
||||
type QueryResult = [Option<HeadData>, Option<BlockNumber>, Option<ParaLifecycle>, Vec<Codec>, Vec<Codec>, Vec<Codec>, Vec<Codec>, Option<BlockNumber>, Option<CandidatePendingAvailability>, Option<ParaInfo | bool>];
|
||||
|
||||
interface QueryState {
|
||||
headHex: string | null;
|
||||
lifecycle: ParaLifecycle | null;
|
||||
paraInfo: ParaInfo | null;
|
||||
pendingAvail: CandidatePendingAvailability | null;
|
||||
updateAt: BlockNumber | null;
|
||||
qDmp: number;
|
||||
@ -45,6 +46,12 @@ interface QueryState {
|
||||
watermark: BlockNumber | null;
|
||||
}
|
||||
|
||||
function getParaInfo (info: ParaInfo | bool | null): ParaInfo | null {
|
||||
return !info || isFunction((info as bool).isTrue)
|
||||
? null
|
||||
: info as ParaInfo;
|
||||
}
|
||||
|
||||
const transformHeader = {
|
||||
transform: (header: Header) => header.number.unwrap()
|
||||
};
|
||||
@ -53,6 +60,7 @@ const optionsMulti = {
|
||||
defaultValue: {
|
||||
headHex: null,
|
||||
lifecycle: null,
|
||||
paraInfo: null,
|
||||
pendingAvail: null,
|
||||
qDmp: 0,
|
||||
qHrmpE: 0,
|
||||
@ -61,11 +69,12 @@ const optionsMulti = {
|
||||
updateAt: null,
|
||||
watermark: null
|
||||
},
|
||||
transform: ([headData, optUp, optLifecycle, dmp, ump, hrmpE, hrmpI, optWm, optPending]: QueryResult): QueryState => ({
|
||||
transform: ([headData, optUp, optLifecycle, dmp, ump, hrmpE, hrmpI, optWm, optPending, optInfo]: QueryResult): QueryState => ({
|
||||
headHex: headData.isSome
|
||||
? sliceHex(headData.unwrap())
|
||||
: null,
|
||||
lifecycle: optLifecycle.unwrapOr(null),
|
||||
paraInfo: getParaInfo(optInfo.unwrapOr(null)),
|
||||
pendingAvail: optPending.unwrapOr(null),
|
||||
qDmp: dmp.length,
|
||||
qHrmpE: hrmpE.length,
|
||||
@ -99,7 +108,8 @@ function Parachain ({ bestNumber, className = '', id, isScheduled, lastBacked, l
|
||||
[api.query.hrmp.hrmpEgressChannelsIndex, id],
|
||||
[api.query.hrmp.hrmpIngressChannelsIndex, id],
|
||||
[api.query.hrmp.hrmpWatermarks, id],
|
||||
[api.query.inclusion.pendingAvailability, id]
|
||||
[api.query.inclusion.pendingAvailability, id],
|
||||
[api.query.registrar.paras, id]
|
||||
], optionsMulti);
|
||||
const [nonBacked, setNonBacked] = useState<AccountId[]>([]);
|
||||
|
||||
|
||||
@ -3,16 +3,17 @@
|
||||
|
||||
import type BN from 'bn.js';
|
||||
import type { Option, Vec } from '@polkadot/types';
|
||||
import type { AccountId, BalanceOf, HeadData, ParaGenesisArgs, ParaId, ParaLifecycle } from '@polkadot/types/interfaces';
|
||||
import type { AccountId, BalanceOf, HeadData, ParaGenesisArgs, ParaId, ParaInfo, ParaLifecycle } from '@polkadot/types/interfaces';
|
||||
import type { ITuple } from '@polkadot/types/types';
|
||||
import type { LeaseInfo, QueuedAction } from './types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { ParaLink } from '@polkadot/react-components';
|
||||
import { AddressSmall, ParaLink } from '@polkadot/react-components';
|
||||
import { useApi, useCallMulti } from '@polkadot/react-hooks';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
import { sliceHex } from '../util';
|
||||
import Lifecycle from './Lifecycle';
|
||||
|
||||
@ -24,17 +25,19 @@ interface Props {
|
||||
|
||||
interface MultiState {
|
||||
headHex: string | null;
|
||||
leases: LeaseInfo[];
|
||||
leases: LeaseInfo[] | null;
|
||||
lifecycle: ParaLifecycle | null;
|
||||
manager: AccountId | null;
|
||||
}
|
||||
|
||||
const optMulti = {
|
||||
defaultValue: {
|
||||
headHex: null,
|
||||
leases: [],
|
||||
lifecycle: null
|
||||
leases: null,
|
||||
lifecycle: null,
|
||||
manager: null
|
||||
},
|
||||
transform: ([optHead, optGenesis, optLifecycle, leases]: [Option<HeadData>, Option<ParaGenesisArgs>, Option<ParaLifecycle>, Vec<Option<ITuple<[AccountId, BalanceOf]>>>]): MultiState => ({
|
||||
transform: ([optHead, optGenesis, optLifecycle, optInfo, leases]: [Option<HeadData>, Option<ParaGenesisArgs>, Option<ParaLifecycle>, Option<ParaInfo>, Vec<Option<ITuple<[AccountId, BalanceOf]>>>]): MultiState => ({
|
||||
headHex: optHead.isSome
|
||||
? sliceHex(optHead.unwrap())
|
||||
: optGenesis.isSome
|
||||
@ -57,16 +60,21 @@ const optMulti = {
|
||||
})
|
||||
.filter((item): item is LeaseInfo => !!item)
|
||||
: [],
|
||||
lifecycle: optLifecycle.unwrapOr(null)
|
||||
lifecycle: optLifecycle.unwrapOr(null),
|
||||
manager: optInfo.isSome
|
||||
? optInfo.unwrap().manager
|
||||
: null
|
||||
})
|
||||
};
|
||||
|
||||
function Upcoming ({ currentPeriod, id, nextAction }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const { headHex, leases, lifecycle } = useCallMulti<MultiState>([
|
||||
const { headHex, leases, lifecycle, manager } = useCallMulti<MultiState>([
|
||||
[api.query.paras.heads, id],
|
||||
[api.query.paras.upcomingParasGenesis, id],
|
||||
[api.query.paras.paraLifecycles, id],
|
||||
[api.query.registrar?.paras, id],
|
||||
[api.query.slots?.leases, id]
|
||||
], optMulti);
|
||||
|
||||
@ -74,6 +82,7 @@ function Upcoming ({ currentPeriod, id, nextAction }: Props): React.ReactElement
|
||||
<tr>
|
||||
<td className='number'><h1>{formatNumber(id)}</h1></td>
|
||||
<td className='badge together'><ParaLink id={id} /></td>
|
||||
<td className='address'>{manager && <AddressSmall value={manager} />}</td>
|
||||
<td className='start together hash'>{headHex}</td>
|
||||
<td className='start'>
|
||||
<Lifecycle
|
||||
@ -83,9 +92,11 @@ function Upcoming ({ currentPeriod, id, nextAction }: Props): React.ReactElement
|
||||
</td>
|
||||
<td className='all' />
|
||||
<td className='start together'>
|
||||
{currentPeriod &&
|
||||
leases.map(({ period }) => formatNumber(currentPeriod.addn(period))).join(', ')
|
||||
}
|
||||
{currentPeriod && leases && (
|
||||
leases.length
|
||||
? leases.map(({ period }) => formatNumber(currentPeriod.addn(period))).join(', ')
|
||||
: t('None')
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
|
||||
@ -22,7 +22,7 @@ function Upcomings ({ actionsQueue, currentPeriod, ids }: Props): React.ReactEle
|
||||
const { t } = useTranslation();
|
||||
|
||||
const headerRef = useRef([
|
||||
[t('upcoming'), 'start', 2],
|
||||
[t('upcoming'), 'start', 3],
|
||||
[t('head'), 'start'],
|
||||
[t('lifecycle'), 'start'],
|
||||
[],
|
||||
|
||||
@ -12,8 +12,8 @@ import { FormatBalance } from '@polkadot/react-query';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
import { useProposal } from '../useProposals';
|
||||
import { sliceHex } from '../util';
|
||||
import { useProposal } from './useProposals';
|
||||
|
||||
interface Props {
|
||||
approvedIds: ParaId[];
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// Copyright 2017-2021 @polkadot/app-parachains authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
import React, { useRef } from 'react';
|
||||
import { Route, Switch } from 'react-router';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
@ -9,12 +9,13 @@ import styled from 'styled-components';
|
||||
import { Tabs } from '@polkadot/react-components';
|
||||
import { useApi } from '@polkadot/react-hooks';
|
||||
|
||||
import useProposals from './Proposals/useProposals';
|
||||
import Auctions from './Auctions';
|
||||
import Crowdloan from './Crowdloan';
|
||||
import Overview from './Overview';
|
||||
import Proposals from './Proposals';
|
||||
import { useTranslation } from './translate';
|
||||
import useOwnedIds from './useOwnedIds';
|
||||
import useProposals from './useProposals';
|
||||
|
||||
interface Props {
|
||||
basePath: string;
|
||||
@ -25,9 +26,10 @@ function ParachainsApp ({ basePath, className }: Props): React.ReactElement<Prop
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const { pathname } = useLocation();
|
||||
const ownedIds = useOwnedIds();
|
||||
const proposals = useProposals();
|
||||
|
||||
const items = useMemo(() => [
|
||||
const items = useRef([
|
||||
{
|
||||
isRoot: true,
|
||||
name: 'overview',
|
||||
@ -45,20 +47,20 @@ function ParachainsApp ({ basePath, className }: Props): React.ReactElement<Prop
|
||||
name: 'crowdloan',
|
||||
text: t<string>('Crowdloan')
|
||||
}
|
||||
].filter((q): q is { name: string; text: string } => !!q), [api, t]);
|
||||
].filter((q): q is { name: string; text: string } => !!q));
|
||||
|
||||
return (
|
||||
<main className={className}>
|
||||
<Tabs
|
||||
basePath={basePath}
|
||||
items={items}
|
||||
items={items.current}
|
||||
/>
|
||||
<Switch>
|
||||
<Route path={`${basePath}/auctions`}>
|
||||
<Auctions />
|
||||
<Auctions ownedIds={ownedIds} />
|
||||
</Route>
|
||||
<Route path={`${basePath}/crowdloan`}>
|
||||
<Crowdloan />
|
||||
<Crowdloan ownedIds={ownedIds} />
|
||||
</Route>
|
||||
<Route path={`${basePath}/proposals`}>
|
||||
<Proposals proposals={proposals} />
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// Copyright 2017-2021 @polkadot/app-parachains authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { ParachainProposal, ParaId, SessionIndex } from '@polkadot/types/interfaces';
|
||||
import type { ParachainProposal, ParaId, ParaInfo, SessionIndex } from '@polkadot/types/interfaces';
|
||||
|
||||
export interface ProposalExt {
|
||||
id: ParaId;
|
||||
@ -20,3 +20,14 @@ export interface Proposals {
|
||||
proposalIds: ParaId[];
|
||||
scheduled: ScheduledProposals[];
|
||||
}
|
||||
|
||||
export interface OwnedId {
|
||||
manager: string;
|
||||
paraId: ParaId;
|
||||
paraInfo: ParaInfo;
|
||||
}
|
||||
|
||||
export interface OwnerInfo {
|
||||
accountId: string | null;
|
||||
paraId: number;
|
||||
}
|
||||
|
||||
51
packages/page-parachains/src/useOwnedIds.ts
Normal file
51
packages/page-parachains/src/useOwnedIds.ts
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright 2017-2021 @polkadot/app-parachains authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { bool, Option } from '@polkadot/types';
|
||||
import type { ParaId, ParaInfo } from '@polkadot/types/interfaces';
|
||||
import type { OwnedId } from './types';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { useAccounts, useApi, useEventTrigger } from '@polkadot/react-hooks';
|
||||
import { isFunction } from '@polkadot/util';
|
||||
|
||||
// Current Rococo returns a bool value for this entry
|
||||
function isParaInfo (info: ParaInfo | bool): info is ParaInfo {
|
||||
return !isFunction((info as bool).isTrue);
|
||||
}
|
||||
|
||||
export default function useOwnedIds (): OwnedId[] {
|
||||
const { api } = useApi();
|
||||
const { allAccounts } = useAccounts();
|
||||
const trigger = useEventTrigger([api.events.registrar?.Registered]);
|
||||
const [state, setState] = useState<OwnedId[]>([]);
|
||||
|
||||
useEffect((): void => {
|
||||
allAccounts && trigger &&
|
||||
api.query.registrar?.paras
|
||||
.entries<Option<ParaInfo | bool>, [ParaId]>()
|
||||
.then((entries) => setState(() =>
|
||||
entries
|
||||
.map(([{ args: [paraId] }, optInfo]): OwnedId | null => {
|
||||
if (!optInfo.isSome) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const paraInfo = optInfo.unwrap();
|
||||
|
||||
return isParaInfo(paraInfo)
|
||||
? {
|
||||
manager: paraInfo.manager.toString(),
|
||||
paraId,
|
||||
paraInfo
|
||||
}
|
||||
: null;
|
||||
})
|
||||
.filter((info): info is OwnedId => !!info && allAccounts.some((a) => a === info.manager))
|
||||
))
|
||||
.catch(console.error);
|
||||
}, [allAccounts, api, trigger]);
|
||||
|
||||
return state;
|
||||
}
|
||||
@ -3,7 +3,7 @@
|
||||
|
||||
import type { Option } from '@polkadot/types';
|
||||
import type { ParachainProposal, ParaId, SessionIndex } from '@polkadot/types/interfaces';
|
||||
import type { ProposalExt, Proposals, ScheduledProposals } from '../types';
|
||||
import type { ProposalExt, Proposals, ScheduledProposals } from './types';
|
||||
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
Loading…
Reference in New Issue
Block a user