Rework auction list display (#4837)
* Rework auction list display * Adjust
This commit is contained in:
parent
44b38580bd
commit
93538d55f6
@ -6,7 +6,6 @@ import type { AuctionIndex, BlockNumber, LeasePeriodOf } from '@polkadot/types/i
|
||||
import React, { useRef } from 'react';
|
||||
|
||||
import { Table } from '@polkadot/react-components';
|
||||
import { BN_ZERO } from '@polkadot/util';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
import useWinningData from './useWinningData';
|
||||
@ -14,14 +13,13 @@ import WinBlock from './WinBlock';
|
||||
|
||||
interface Props {
|
||||
auctionInfo: [LeasePeriodOf, BlockNumber] | null;
|
||||
bestNumber?: BlockNumber;
|
||||
className?: string;
|
||||
numAuctions: AuctionIndex | null;
|
||||
}
|
||||
|
||||
function Auction ({ auctionInfo, bestNumber, className, numAuctions }: Props): React.ReactElement<Props> {
|
||||
function Auction ({ auctionInfo, className, numAuctions }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const winningData = useWinningData();
|
||||
const winningData = useWinningData(auctionInfo);
|
||||
|
||||
const headerRef = useRef([
|
||||
[t('winners'), 'start', 4],
|
||||
@ -29,26 +27,21 @@ function Auction ({ auctionInfo, bestNumber, className, numAuctions }: Props): R
|
||||
[t('value'), 'number']
|
||||
]);
|
||||
|
||||
const offset = auctionInfo && bestNumber && bestNumber.gt(auctionInfo[1])
|
||||
? bestNumber.sub(auctionInfo[1])
|
||||
: BN_ZERO;
|
||||
|
||||
return (
|
||||
<Table
|
||||
className={className}
|
||||
empty={winningData && numAuctions && auctionInfo
|
||||
? t<string>('No winners in this auction')
|
||||
: t<string>('No ongoing auction')
|
||||
}
|
||||
empty={numAuctions && winningData && (
|
||||
auctionInfo && !winningData.length
|
||||
? t<string>('No winners in this auction')
|
||||
: t<string>('No ongoing auction')
|
||||
)}
|
||||
header={headerRef.current}
|
||||
>
|
||||
{offset && bestNumber && winningData?.map((value, count) => (
|
||||
{winningData?.map((value, count) => (
|
||||
<WinBlock
|
||||
bestNumber={bestNumber}
|
||||
isEven={!!(count % 2)}
|
||||
isLatest={count === 0}
|
||||
key={value.blockNumber.toString()}
|
||||
offset={offset}
|
||||
value={value}
|
||||
/>
|
||||
))}
|
||||
|
||||
@ -22,10 +22,10 @@ function Bid ({ className, id }: Props): React.ReactElement<Props> {
|
||||
const { api } = useApi();
|
||||
const { hasAccounts } = useAccounts();
|
||||
const [accountId, setAccountId] = useState<string | null>(null);
|
||||
const [amount, setAmount] = useState<BN | undefined>();
|
||||
const [firstSlot, setFirstSlot] = useState<BN | undefined>();
|
||||
const [lastSlot, setLastSlot] = useState<BN | undefined>();
|
||||
const [paraId, setParaId] = useState<BN | undefined>();
|
||||
const [amount, setAmount] = useState<BN | undefined>(BN_ZERO);
|
||||
const [firstSlot, setFirstSlot] = useState<BN | undefined>(BN_ZERO);
|
||||
const [lastSlot, setLastSlot] = useState<BN | undefined>(BN_ZERO);
|
||||
const [paraId, setParaId] = useState<BN | undefined>(BN_ZERO);
|
||||
const [isOpen, toggleOpen] = useToggle();
|
||||
|
||||
const isLastError = !lastSlot || !firstSlot || lastSlot.lt(firstSlot) || lastSlot.gt(firstSlot.addn(3));
|
||||
@ -62,6 +62,7 @@ function Bid ({ className, id }: Props): React.ReactElement<Props> {
|
||||
<Modal.Column>
|
||||
<InputNumber
|
||||
autoFocus
|
||||
defaultValue={paraId?.toString()}
|
||||
isZeroable={false}
|
||||
label={t<string>('parachain id')}
|
||||
onChange={setParaId}
|
||||
@ -86,10 +87,12 @@ function Bid ({ className, id }: Props): React.ReactElement<Props> {
|
||||
<Modal.Columns>
|
||||
<Modal.Column>
|
||||
<InputNumber
|
||||
defaultValue={firstSlot?.toString()}
|
||||
label={t<string>('first slot')}
|
||||
onChange={setFirstSlot}
|
||||
/>
|
||||
<InputNumber
|
||||
defaultValue={lastSlot?.toString()}
|
||||
isError={isLastError}
|
||||
label={t<string>('last slot')}
|
||||
onChange={setLastSlot}
|
||||
|
||||
@ -6,19 +6,21 @@ import type { AuctionIndex, BlockNumber, LeasePeriodOf } from '@polkadot/types/i
|
||||
import React from 'react';
|
||||
|
||||
import { CardSummary, SummaryBox } from '@polkadot/react-components';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
|
||||
interface Props {
|
||||
auctionInfo: [LeasePeriodOf, BlockNumber] | null;
|
||||
bestNumber?: BlockNumber;
|
||||
className?: string;
|
||||
numAuctions?: AuctionIndex | null;
|
||||
}
|
||||
|
||||
function Summary ({ auctionInfo, bestNumber, className, numAuctions }: Props): React.ReactElement<Props> {
|
||||
function Summary ({ auctionInfo, className, numAuctions }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const bestNumber = useCall<BlockNumber>(api.derive.chain.bestNumber);
|
||||
const [period, ending] = auctionInfo || [];
|
||||
|
||||
return (
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
// Copyright 2017-2021 @polkadot/app-crowdloan authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type BN from 'bn.js';
|
||||
import type { BlockNumber } from '@polkadot/types/interfaces';
|
||||
import type { Winning } from './types';
|
||||
|
||||
import React from 'react';
|
||||
@ -10,26 +8,22 @@ import React from 'react';
|
||||
import WinRange from './WinRange';
|
||||
|
||||
interface Props {
|
||||
bestNumber: BlockNumber;
|
||||
className?: string;
|
||||
isEven: boolean;
|
||||
isLatest: boolean;
|
||||
offset: BN;
|
||||
value: Winning;
|
||||
}
|
||||
|
||||
function WinBlock ({ bestNumber, className = '', isEven, isLatest, offset, value: { blockNumber, winners } }: Props): React.ReactElement<Props> {
|
||||
function WinBlock ({ className = '', isEven, isLatest, value: { blockNumber, winners } }: Props): React.ReactElement<Props> {
|
||||
return (
|
||||
<>
|
||||
{winners.map((value, index) => (
|
||||
<WinRange
|
||||
bestNumber={bestNumber}
|
||||
blockNumber={blockNumber}
|
||||
className={`${className} ${isEven ? 'isEven' : 'isOdd'} ${index === (winners.length - 1) ? '' : 'noBorder'}`}
|
||||
isFirst={index === 0}
|
||||
isLatest={isLatest}
|
||||
key={index}
|
||||
offset={offset}
|
||||
value={value}
|
||||
/>
|
||||
))}
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type BN from 'bn.js';
|
||||
import type { BlockNumber } from '@polkadot/types/interfaces';
|
||||
import type { WinnerData } from './types';
|
||||
|
||||
import React from 'react';
|
||||
@ -14,16 +13,14 @@ import { formatNumber } from '@polkadot/util';
|
||||
import { useTranslation } from '../translate';
|
||||
|
||||
interface Props {
|
||||
bestNumber: BlockNumber;
|
||||
blockNumber: BlockNumber;
|
||||
blockNumber: BN;
|
||||
className?: string;
|
||||
isFirst: boolean;
|
||||
isLatest: boolean;
|
||||
offset: BN;
|
||||
value: WinnerData;
|
||||
}
|
||||
|
||||
function WinRanges ({ bestNumber, blockNumber, className = '', isFirst, isLatest, offset, value: { accountId, paraId, range, value } }: Props): React.ReactElement<Props> {
|
||||
function WinRanges ({ blockNumber, className = '', isFirst, isLatest, value: { accountId, paraId, range, value } }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
@ -33,7 +30,7 @@ function WinRanges ({ bestNumber, blockNumber, className = '', isFirst, isLatest
|
||||
<h1>
|
||||
{isLatest
|
||||
? t<string>('latest')
|
||||
: <>#{formatNumber(bestNumber.sub(offset).iadd(blockNumber))}</>
|
||||
: <>#{formatNumber(blockNumber)}</>
|
||||
}
|
||||
</h1>
|
||||
)}
|
||||
|
||||
@ -8,7 +8,7 @@ import type { ITuple } from '@polkadot/types/types';
|
||||
import React from 'react';
|
||||
|
||||
import { Button } from '@polkadot/react-components';
|
||||
import { useApi, useCall, useCallMulti } from '@polkadot/react-hooks';
|
||||
import { useApi, useCallMulti } from '@polkadot/react-hooks';
|
||||
|
||||
import Auction from './Auction';
|
||||
import Bid from './Bid';
|
||||
@ -36,7 +36,6 @@ const optionsMulti = {
|
||||
|
||||
function Auctions ({ className }: Props): React.ReactElement<Props> {
|
||||
const { api } = useApi();
|
||||
const bestNumber = useCall<BlockNumber>(api.derive.chain.bestNumber);
|
||||
const { auctionInfo, numAuctions } = useCallMulti<QueryState>([
|
||||
api.query.auctions.auctionCounter,
|
||||
api.query.auctions.auctionInfo
|
||||
@ -46,7 +45,6 @@ function Auctions ({ className }: Props): React.ReactElement<Props> {
|
||||
<div className={className}>
|
||||
<Summary
|
||||
auctionInfo={auctionInfo}
|
||||
bestNumber={bestNumber}
|
||||
numAuctions={numAuctions}
|
||||
/>
|
||||
<Button.Group>
|
||||
@ -54,7 +52,6 @@ function Auctions ({ className }: Props): React.ReactElement<Props> {
|
||||
</Button.Group>
|
||||
<Auction
|
||||
auctionInfo={auctionInfo}
|
||||
bestNumber={bestNumber}
|
||||
numAuctions={numAuctions}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
// Copyright 2017-2021 @polkadot/app-crowdloan authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { AccountId, BalanceOf, BlockNumber, ParaId } from '@polkadot/types/interfaces';
|
||||
import type BN from 'bn.js';
|
||||
import type { AccountId, BalanceOf, ParaId } from '@polkadot/types/interfaces';
|
||||
|
||||
export interface WinnerData {
|
||||
accountId: AccountId;
|
||||
@ -11,6 +12,6 @@ export interface WinnerData {
|
||||
}
|
||||
|
||||
export interface Winning {
|
||||
blockNumber: BlockNumber;
|
||||
blockNumber: BN;
|
||||
winners: WinnerData[];
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Option, StorageKey } from '@polkadot/types';
|
||||
import type { BlockNumber, WinningData } from '@polkadot/types/interfaces';
|
||||
import type { BlockNumber, LeasePeriodOf, WinningData } from '@polkadot/types/interfaces';
|
||||
import type { WinnerData, Winning } from './types';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
@ -16,18 +16,10 @@ const RANGES = [
|
||||
'3-3'
|
||||
];
|
||||
|
||||
function flattenData (winning: Winning[]): Winning[] {
|
||||
return winning
|
||||
.sort((a, b) => b.blockNumber.cmp(a.blockNumber))
|
||||
.filter(({ winners }, index, winning) =>
|
||||
index === 0 ||
|
||||
JSON.stringify({ winners }) !== JSON.stringify({ winners: winning[index - 1].winners })
|
||||
);
|
||||
}
|
||||
|
||||
function extractData (values: [StorageKey<[BlockNumber]>, Option<WinningData>][]): Winning[] {
|
||||
return flattenData(
|
||||
values.reduce<Winning[]>((winning, [{ args: [blockNumber] }, optData]): Winning[] => {
|
||||
function extractData (endBlock: BlockNumber | null, values: [StorageKey<[BlockNumber]>, Option<WinningData>][]): Winning[] {
|
||||
return values
|
||||
.sort(([{ args: [a] }], [{ args: [b] }]) => a.cmp(b))
|
||||
.reduce((all: Winning[], [{ args: [blockOffset] }, optData]): Winning[] => {
|
||||
if (optData.isSome) {
|
||||
const winners = optData.unwrap().reduce<WinnerData[]>((winners, optEntry, index): WinnerData[] => {
|
||||
if (optEntry.isSome) {
|
||||
@ -39,17 +31,23 @@ function extractData (values: [StorageKey<[BlockNumber]>, Option<WinningData>][]
|
||||
return winners;
|
||||
}, []);
|
||||
|
||||
if (winners.length) {
|
||||
winning.push({ blockNumber, winners });
|
||||
}
|
||||
winners.length && (
|
||||
all.length === 0 ||
|
||||
JSON.stringify({ winners }) !== JSON.stringify({ winners: all[all.length - 1].winners })
|
||||
) && all.push({
|
||||
blockNumber: endBlock
|
||||
? blockOffset.add(endBlock)
|
||||
: blockOffset,
|
||||
winners
|
||||
});
|
||||
}
|
||||
|
||||
return winning;
|
||||
return all;
|
||||
}, [])
|
||||
);
|
||||
.reverse();
|
||||
}
|
||||
|
||||
export default function useWinningData (): Winning[] | null {
|
||||
export default function useWinningData (auctionInfo: [LeasePeriodOf, BlockNumber] | null): Winning[] | null {
|
||||
const { api } = useApi();
|
||||
const [winning, setWinning] = useState<Winning[] | null>(null);
|
||||
const trigger = useEventTrigger([api.events.auctions.BidAccepted]);
|
||||
@ -58,9 +56,11 @@ export default function useWinningData (): Winning[] | null {
|
||||
trigger &&
|
||||
api.query.auctions.winning
|
||||
.entries<Option<WinningData>, [BlockNumber]>()
|
||||
.then((values) => setWinning(extractData(values)))
|
||||
.then((values) => setWinning(
|
||||
extractData(auctionInfo && auctionInfo[1], values))
|
||||
)
|
||||
.catch(console.error);
|
||||
}, [api, trigger]);
|
||||
}, [api, auctionInfo, trigger]);
|
||||
|
||||
return winning;
|
||||
}
|
||||
|
||||
@ -5,11 +5,11 @@ import type BN from 'bn.js';
|
||||
import type { UInt } from '@polkadot/types';
|
||||
import type { Balance, ParaId } from '@polkadot/types/interfaces';
|
||||
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { Button, InputAddress, InputBalance, MarkWarning, Modal, TxButton } from '@polkadot/react-components';
|
||||
import { useAccounts, useApi, useToggle } from '@polkadot/react-hooks';
|
||||
import { BN_ZERO, formatBalance } from '@polkadot/util';
|
||||
import { formatBalance } from '@polkadot/util';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
|
||||
@ -28,21 +28,19 @@ function FundContribute ({ cap, className, paraId, raised }: Props): React.React
|
||||
const [accountId, setAccountId] = useState<string | null>(null);
|
||||
const [amount, setAmount] = useState<BN | undefined>();
|
||||
|
||||
const maxAmount = useMemo(
|
||||
() => cap.sub(raised),
|
||||
[cap, raised]
|
||||
);
|
||||
|
||||
// TODO verifier signature
|
||||
|
||||
const remaining = cap.sub(raised);
|
||||
const allowContribute = remaining.gte(api.consts.crowdloan.minContribution as UInt);
|
||||
const isAmountBelow = !amount || amount.lt(api.consts.crowdloan.minContribution as UInt);
|
||||
const isAmountError = !amount || !amount.gt(BN_ZERO) || isAmountBelow;
|
||||
const isAmountOver = !!(amount && amount.gt(remaining));
|
||||
const isAmountError = isAmountBelow || isAmountOver;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
icon='plus'
|
||||
isDisabled={!hasAccounts}
|
||||
isDisabled={!hasAccounts || !allowContribute}
|
||||
label={t<string>('Contribute')}
|
||||
onClick={toggleOpen}
|
||||
/>
|
||||
@ -73,11 +71,13 @@ function FundContribute ({ cap, className, paraId, raised }: Props): React.React
|
||||
isError={isAmountError}
|
||||
isZeroable={false}
|
||||
label={t<string>('contribution')}
|
||||
maxValue={maxAmount}
|
||||
onChange={setAmount}
|
||||
/>
|
||||
{isAmountBelow && (
|
||||
<MarkWarning content={t<string>('The amount is less than the minimum allowed contribution of {{min}}', { replace: { min: formatBalance(api.consts.crowdloan.minContribution as UInt) } })} />
|
||||
<MarkWarning content={t<string>('The amount is less than the minimum allowed contribution of {{value}}', { replace: { value: formatBalance(api.consts.crowdloan.minContribution as UInt) } })} />
|
||||
)}
|
||||
{isAmountOver && (
|
||||
<MarkWarning content={t<string>('The amount is more than the remaining contribution needed {{value}}', { replace: { value: formatBalance(remaining) } })} />
|
||||
)}
|
||||
</Modal.Column>
|
||||
<Modal.Column>
|
||||
|
||||
@ -45,24 +45,24 @@ function Progress ({ className = '', isDisabled, size = 'normal', total, value }
|
||||
return null;
|
||||
}
|
||||
|
||||
const drawAngle = angle % 360;
|
||||
|
||||
return (
|
||||
<div className={`ui--Progress${isDisabled ? ' isDisabled' : ''} ${size}Size ${className}`}>
|
||||
<div className='background highlight--bg' />
|
||||
<Clip
|
||||
angle={
|
||||
angle <= 180
|
||||
? angle.toFixed(1)
|
||||
drawAngle <= 180
|
||||
? drawAngle.toFixed(1)
|
||||
: '180'
|
||||
}
|
||||
type='first'
|
||||
/>
|
||||
<Clip
|
||||
angle={
|
||||
angle <= 180
|
||||
drawAngle <= 180
|
||||
? '0'
|
||||
: angle >= 360
|
||||
? '180'
|
||||
: (angle - 180).toFixed(1)
|
||||
: (drawAngle - 180).toFixed(1)
|
||||
}
|
||||
type='second'
|
||||
/>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user