Display schedule on democracy (#3287)

This commit is contained in:
Jaco Greeff 2020-07-29 19:21:21 +02:00 committed by GitHub
parent b7e9dc72da
commit d048f06e60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 180 additions and 2 deletions

View File

@ -0,0 +1,49 @@
// Copyright 2017-2020 @polkadot/app-democracy authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { BlockNumber } from '@polkadot/types/interfaces';
import { ScheduledExt } from './types';
import React from 'react';
import { CallExpander } from '@polkadot/react-components';
import { useApi, useCall } from '@polkadot/react-hooks';
import { BlockToTime } from '@polkadot/react-query';
import { formatNumber } from '@polkadot/util';
interface Props {
className?: string;
value: ScheduledExt;
}
function Scheduled ({ className = '', value: { call, count, maybePeriodic, nextBlockNumber } }: Props): React.ReactElement<Props> {
const { api } = useApi();
const bestNumber = useCall<BlockNumber>(api.derive.chain.bestNumber, []);
const period = maybePeriodic.unwrapOr(null);
return (
<tr className={className}>
<td className='all'><CallExpander value={call} /></td>
<td className='number together'>
{bestNumber && (
<>
<BlockToTime blocks={nextBlockNumber.sub(bestNumber)} />
#{formatNumber(nextBlockNumber)}
</>
)}
</td>
<td className='number together'>
{period && (
formatNumber(period[0])
)}
</td>
<td className='number together'>
{period && (
formatNumber(count) + ' / ' + formatNumber(period[1])
)}
</td>
</tr>
);
}
export default React.memo(Scheduled);

View File

@ -0,0 +1,89 @@
// Copyright 2017-2020 @polkadot/app-democracy authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { BlockNumber, Scheduled } from '@polkadot/types/interfaces';
import { ScheduledExt } from './types';
import React, { useMemo } from 'react';
import { Table } from '@polkadot/react-components';
import { useApi, useCall } from '@polkadot/react-hooks';
import { Option } from '@polkadot/types';
import { useTranslation } from '../translate';
import ScheduledView from './Scheduled';
interface Props {
className?: string;
}
function Schedule ({ className = '' }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const bestNumber = useCall<BlockNumber>(api.derive.chain.bestNumber, []);
const items = useCall<ScheduledExt[]>(api.query.scheduler.agenda.entries as any, [], {
transform: (entries: [{ args: [BlockNumber] }, Option<Scheduled>[]][]): ScheduledExt[] =>
entries
.filter(([, vecSchedOpt]) => vecSchedOpt.some((schedOpt) => schedOpt.isSome))
.reduce((items: ScheduledExt[], [key, vecSchedOpt]): ScheduledExt[] => {
const blockNumber = key.args[0];
return vecSchedOpt
.filter((schedOpt) => schedOpt.isSome)
.map((schedOpt) => schedOpt.unwrap())
.reduce((items: ScheduledExt[], { call, maybeId, maybePeriodic, priority }, index) => {
items.push({ blockNumber, call, count: 0, key: `${blockNumber.toString()}-${index}`, maybeId, maybePeriodic, nextBlockNumber: blockNumber, priority });
return items;
}, items);
}, [])
});
const header = useMemo(() => [
[t('scheduled'), 'start'],
[t('remaining')],
[t('period')],
[t('count')]
], [t]);
let scheduled: ScheduledExt[] | undefined;
if (bestNumber && items) {
scheduled = items
.map((schedule): ScheduledExt => {
if (schedule.maybePeriodic.isSome) {
const [inc, max] = schedule.maybePeriodic.unwrap();
const nextBlockNumber = schedule.blockNumber.toBn();
let count = 0;
while (max.gtn(count) && nextBlockNumber.lt(bestNumber)) {
count++;
nextBlockNumber.iadd(inc);
}
schedule.count = count;
schedule.nextBlockNumber = nextBlockNumber;
}
return schedule;
})
.filter(({ nextBlockNumber }) => nextBlockNumber.gt(bestNumber));
}
return (
<Table
className={className}
empty={scheduled?.length === 0 && t<string>('No active schedules')}
header={header}
>
{scheduled?.map((scheduled): React.ReactNode => (
<ScheduledView
key={scheduled.key}
value={scheduled}
/>
))}
</Table>
);
}
export default React.memo(Schedule);

View File

@ -14,6 +14,7 @@ import DispatchQueue from './DispatchQueue';
import Externals from './Externals';
import Proposals from './Proposals';
import Referendums from './Referendums';
import Scheduler from './Scheduler';
import Summary from './Summary';
import PreImage from './PreImage';
import Propose from './Propose';
@ -54,6 +55,9 @@ function Overview ({ className }: Props): React.ReactElement<Props> {
<Proposals />
<DispatchQueue />
<Externals />
{api.query.scheduler && (
<Scheduler />
)}
</div>
);
}

View File

@ -0,0 +1,19 @@
// Copyright 2017-2020 @polkadot/app-democracy authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { BlockNumber, Call, SchedulePeriod, SchedulePriority } from '@polkadot/types/interfaces';
import BN from 'bn.js';
import { Bytes, Option } from '@polkadot/types';
export interface ScheduledExt {
blockNumber: BlockNumber;
call: Call;
count: number;
key: string;
maybeId: Option<Bytes>;
maybePeriodic: Option<SchedulePeriod>;
priority: SchedulePriority;
nextBlockNumber: BN;
}

View File

@ -11,6 +11,7 @@ import { bnToBn } from '@polkadot/util';
interface Props {
className?: string;
isDisabled?: boolean;
size?: 'normal' | 'small'
total?: UInt | BN | number | null;
value?: UInt | BN | number | null;
}
@ -33,7 +34,7 @@ function DivClip ({ angle, type }: RotateProps): React.ReactElement<RotateProps>
const Clip = React.memo(DivClip);
function Progress ({ className = '', isDisabled, total, value }: Props): React.ReactElement<Props> | null {
function Progress ({ className = '', isDisabled, size = 'normal', total, value }: Props): React.ReactElement<Props> | null {
const _total = bnToBn(total || 0);
const angle = _total.gtn(0)
? (bnToBn(value || 0).muln(36000).div(_total).toNumber() / 100)
@ -44,7 +45,7 @@ function Progress ({ className = '', isDisabled, total, value }: Props): React.R
}
return (
<div className={`ui--Progress${isDisabled ? ' isDisabled' : ''} ${className}`}>
<div className={`ui--Progress${isDisabled ? ' isDisabled' : ''} ${size}Size ${className}`}>
<div className='background ui--highlight--bg' />
<Clip
angle={
@ -141,4 +142,20 @@ export default React.memo(styled(Progress)`
text-shadow: 0 0 2px #f5f4f3;
}
}
&.smallSize {
height: 2.25rem;
width: 2.25rem;
.inner {
bottom: 0.1875rem;
left: 0.1875rem;
right: 0.1875rem;
top: 0.1875rem;
}
div {
font-size: 0.625rem;
}
}
`);