123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- import React from "react";
- import { Card, Header, Button, Icon, Message } from "semantic-ui-react";
- import { ProposalType } from "../runtime/transport";
- import { blake2AsHex } from '@polkadot/util-crypto';
- import styled from 'styled-components';
- import AddressMini from '@polkadot/react-components/AddressMiniJoy';
- import TxButton from '@polkadot/joy-utils/TxButton';
- import { ProposalId } from "@joystream/types/lib/proposals";
- import { MemberId } from "@joystream/types/lib/members";
- import ProfilePreview from "@polkadot/joy-utils/MemberProfilePreview";
- import { useTransport } from "../runtime";
- import { usePromise } from "../utils";
- import { Profile } from "@joystream/types/lib/members";
- import { Option } from "@polkadot/types/";
- import { formatBalance } from "@polkadot/util";
- import PromiseComponent from "./PromiseComponent";
- type BodyProps = {
- title: string;
- description: string;
- params: any[];
- type: ProposalType;
- iAmProposer: boolean;
- proposalId: number | ProposalId;
- proposerId: number | MemberId;
- isCancellable: boolean;
- cancellationFee: number;
- };
- function ProposedAddress(props: { address?: string | null }) {
- if (props.address === null || props.address === undefined) {
- return <>NONE</>;
- }
- return (
- <AddressMini value={props.address} isShort={false} isPadded={false} withAddress={true} style={{ padding: 0 }} />
- );
- }
- function ProposedMember(props: { memberId?: MemberId | number | null }) {
- if (props.memberId === null || props.memberId === undefined) {
- return <>NONE</>;
- }
- const memberId: MemberId | number = props.memberId;
- const transport = useTransport();
- const [ member, error, loading ] = usePromise<Option<Profile> | null>(
- () => transport.memberProfile(memberId),
- null
- );
- const profile = member && member.unwrapOr(null);
- return (
- <PromiseComponent error={error} loading={loading} message="Fetching profile...">
- { profile ? (
- <ProfilePreview
- avatar_uri={ profile.avatar_uri.toString() }
- root_account={ profile.root_account.toString() }
- handle={ profile.handle.toString() }
- link={ true }
- />
- ) : 'Profile not found' }
- </PromiseComponent>
- );
- }
- // The methods for parsing params by Proposal type.
- // They take the params as array and return { LABEL: VALUE } object.
- const paramParsers: { [x in ProposalType]: (params: any[]) => { [key: string]: string | number | JSX.Element } } = {
- Text: ([content]) => ({
- Content: content
- }),
- RuntimeUpgrade: ([wasm]) => {
- const buffer: Buffer = Buffer.from(wasm.replace("0x", ""), "hex");
- return {
- "Blake2b256 hash of WASM code": blake2AsHex(buffer, 256),
- "File size": buffer.length + " bytes"
- };
- },
- SetElectionParameters: ([params]) => ({
- "Announcing period": params.announcing_period + " blocks",
- "Voting period": params.voting_period + " blocks",
- "Revealing period": params.revealing_period + " blocks",
- "Council size": params.council_size + " members",
- "Candidacy limit": params.candidacy_limit + " members",
- "New term duration": params.new_term_duration + " blocks",
- "Min. council stake": formatBalance(params.min_council_stake),
- "Min. voting stake": formatBalance(params.min_voting_stake)
- }),
- Spending: ([amount, account]) => ({
- Amount: formatBalance(amount),
- Account: <ProposedAddress address={account} />
- }),
- SetLead: ([memberId, accountId]) => ({
- "Member": <ProposedMember memberId={ memberId } />,
- "Account id": <ProposedAddress address={accountId} />
- }),
- SetContentWorkingGroupMintCapacity: ([capacity]) => ({
- "Mint capacity": formatBalance(capacity)
- }),
- EvictStorageProvider: ([accountId]) => ({
- "Storage provider account": <ProposedAddress address={accountId} />
- }),
- SetValidatorCount: ([count]) => ({
- "Validator count": count
- }),
- SetStorageRoleParameters: ([params]) => ({
- "Min. stake": formatBalance(params.min_stake),
- // "Min. actors": params.min_actors,
- "Max. actors": params.max_actors,
- Reward: formatBalance(params.reward),
- "Reward period": params.reward_period + " blocks",
- // "Bonding period": params.bonding_period + " blocks",
- "Unbonding period": params.unbonding_period + " blocks",
- // "Min. service period": params.min_service_period + " blocks",
- // "Startup grace period": params.startup_grace_period + " blocks",
- "Entry request fee": formatBalance(params.entry_request_fee)
- })
- };
- const ProposalParams = styled.div`
- display: grid;
- font-weight: bold;
- grid-template-columns: min-content 1fr;
- grid-row-gap: 0.5rem;
- @media screen and (max-width: 767px) {
- grid-template-columns: 1fr;
- }
- `;
- const ProposalParamName = styled.div`
- margin-right: 1rem;
- white-space: nowrap;
- `;
- const ProposalParamValue = styled.div`
- color: black;
- word-wrap: break-word;
- word-break: break-all;
- @media screen and (max-width: 767px) {
- margin-top: -0.25rem;
- }
- `;
- export default function Body({
- type,
- title,
- description,
- params = [],
- iAmProposer,
- proposalId,
- proposerId,
- isCancellable,
- cancellationFee
- }: BodyProps) {
- const parseParams = paramParsers[type];
- const parsedParams = parseParams(params);
- return (
- <Card fluid>
- <Card.Content>
- <Card.Header>
- <Header as="h1">{title}</Header>
- </Card.Header>
- <Card.Description>{description}</Card.Description>
- <Header as="h4">Parameters:</Header>
- <ProposalParams>
- { Object.entries(parsedParams).map(([paramName, paramValue]) => (
- <React.Fragment key={paramName}>
- <ProposalParamName>{paramName}:</ProposalParamName>
- <ProposalParamValue>{paramValue}</ProposalParamValue>
- </React.Fragment>
- ))}
- </ProposalParams>
- { iAmProposer && isCancellable && (<>
- <Message warning active>
- <Message.Content>
- <Message.Header>Proposal cancellation</Message.Header>
- <p style={{ margin: '0.5em 0', padding: '0' }}>
- You can only cancel your proposal while it's still in the Voting Period.
- </p>
- <p style={{ margin: '0.5em 0', padding: '0' }}>
- The cancellation fee for this type of proposal is:
- <b>{ cancellationFee ? formatBalance(cancellationFee) : 'NONE' }</b>
- </p>
- <Button.Group color="red">
- <TxButton
- params={ [ proposerId, proposalId ] }
- tx={ "proposalsEngine.cancelProposal" }
- onClick={ sendTx => { sendTx(); } }
- className={'icon left labeled'}
- >
- <Icon name="cancel" inverted />
- Withdraw proposal
- </TxButton>
- </Button.Group>
- </Message.Content>
- </Message>
- </>) }
- </Card.Content>
- </Card>
- );
- }
|