Merge pull request #49 from grant-project/show-contribution-amounts-frontend

Show Proposal Contribution Amounts
This commit is contained in:
William O'Beirne 2018-09-17 11:59:27 -04:00 committed by GitHub
commit 210b656939
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 90 additions and 22 deletions

View File

@ -12,11 +12,11 @@ import { RadioChangeEvent } from 'antd/lib/radio';
import TrusteeFields from './TrusteeFields';
import MilestoneFields, { Milestone } from './MilestoneFields';
import CreateSuccess from './CreateSuccess';
import { computePercentage } from 'utils/helpers';
import { getAmountError } from 'utils/validators';
import MarkdownEditor from 'components/MarkdownEditor';
import * as Styled from './styled';
import { Wei, toWei } from 'utils/units';
import BN from 'bn.js';
interface StateProps {
crowdFundLoading: AppState['web3']['crowdFundLoading'];
crowdFundError: AppState['web3']['crowdFundError'];
@ -74,8 +74,8 @@ const DEFAULT_STATE: State = {
milestoneDeadline: 60 * 60 * 24 * 7,
};
function milestoneToMilestoneAmount(milestone: Milestone, raiseGoal: number) {
return computePercentage(raiseGoal, milestone.payoutPercent);
function milestoneToMilestoneAmount(milestone: Milestone, raiseGoal: Wei) {
return raiseGoal.divn(100).mul(new BN(milestone.payoutPercent));
}
class CreateProposal extends React.Component<Props, State> {
@ -159,7 +159,7 @@ class CreateProposal extends React.Component<Props, State> {
};
createCrowdFund = async () => {
const { contract, createCrowdFund, web3 } = this.props;
const { contract, createCrowdFund } = this.props;
const {
title,
proposalBody,
@ -173,10 +173,9 @@ class CreateProposal extends React.Component<Props, State> {
} = this.state;
const backendData = { content: proposalBody, title, category };
const targetInWei = web3.utils.toWei(String(amountToRaise), 'ether');
const targetInWei = toWei(amountToRaise, 'ether');
const milestoneAmounts = milestones.map(milestone =>
milestoneToMilestoneAmount(milestone, targetInWei),
Wei(milestoneToMilestoneAmount(milestone, targetInWei)),
);
const immediateFirstMilestonePayout = milestones[0].immediatePayout;

View File

@ -0,0 +1,54 @@
import React from 'react';
import { Spin } from 'antd';
import { CrowdFund } from 'modules/proposals/reducers';
import UserRow from 'components/UserRow';
import * as ProposalStyled from '../styled';
import Placeholder from 'components/Placeholder';
import UnitDisplay from 'components/UnitDisplay';
interface Props {
crowdFund: CrowdFund;
}
const ContributorsBlock = ({ crowdFund }: Props) => {
let content;
if (crowdFund) {
if (crowdFund.contributors.length) {
content = crowdFund.contributors.map(contributor => (
<UserRow
key={contributor.address}
address={contributor.address}
secondary={<UnitDisplay value={contributor.contributionAmount} symbol="ETH" />}
/>
));
} else {
content = (
<Placeholder
style={{ minHeight: '220px' }}
title="No contributors found"
subtitle={`
It appears that your campaign hasn't yet been funded.
Check back later once you've received at least one contribution!
`}
/>
);
}
} else {
content = <Spin />;
}
return (
<ProposalStyled.SideBlock>
{crowdFund.contributors.length ? (
<>
<ProposalStyled.BlockTitle>Contributors</ProposalStyled.BlockTitle>
<ProposalStyled.Block>{content}</ProposalStyled.Block>
</>
) : (
content
)}
</ProposalStyled.SideBlock>
);
};
export default ContributorsBlock;

View File

@ -15,6 +15,7 @@ import Milestones from './Milestones';
import CommentsTab from './Comments';
import UpdatesTab from './Updates';
import GovernanceTab from './Governance';
import ContributorsTab from './Contributors';
// import CommunityTab from './Community';
import * as Styled from './styled';
import { withRouter } from 'next/router';
@ -113,9 +114,12 @@ class ProposalDetail extends React.Component<Props, State> {
<div style={{ marginTop: '1.5rem' }} />
<UpdatesTab proposalId={proposal.proposalId} />
</Tabs.TabPane>
<Tabs.TabPane tab="Governance" key="governanc">
<Tabs.TabPane tab="Governance" key="governance">
<GovernanceTab proposal={proposal} />
</Tabs.TabPane>
<Tabs.TabPane tab="Contributors" key="contributors">
<ContributorsTab crowdFund={proposal.crowdFund} />
</Tabs.TabPane>
</Tabs>
)}
</Styled.Container>

View File

@ -5,11 +5,10 @@ import * as Styled from './styled';
interface Props {
address: string;
secondary?: React.ReactNode;
}
// TODO - don't hardcode monero image
const UserRow = ({ address }: Props) => (
const UserRow = ({ address, secondary }: Props) => (
<Styled.Container>
<Styled.Avatar>
<Identicon address={address} />
@ -18,7 +17,7 @@ const UserRow = ({ address }: Props) => (
<Styled.InfoMain>
<ShortAddress address={address} />
</Styled.InfoMain>
<Styled.InfoSecondary>{/* user.title */}</Styled.InfoSecondary>
{secondary && <Styled.InfoSecondary>{secondary}</Styled.InfoSecondary>}
</Styled.Info>
</Styled.Container>
);

View File

@ -14,7 +14,7 @@ export interface User {
export interface Contributor {
address: string;
contributionAmount: string;
contributionAmount: Wei;
refundVote: boolean;
refunded: boolean;
proportionalContribution: string;

View File

@ -7,6 +7,7 @@ import { sleep } from 'utils/helpers';
import { fetchProposal, fetchProposals } from 'modules/proposals/actions';
import { PROPOSAL_CATEGORY } from 'api/constants';
import { AppState } from 'store/reducers';
import { Wei } from 'utils/units';
type GetState = () => AppState;
@ -97,10 +98,10 @@ interface MilestoneData {
}
interface ProposalContractData {
ethAmount: number | string; // TODO: BigNumber
ethAmount: Wei;
payOutAddress: string;
trusteesAddresses: string[];
milestoneAmounts: number[] | string[]; // TODO: BigNumber
milestoneAmounts: Wei[];
milestones: MilestoneData[];
durationInMinutes: number;
milestoneVotingPeriodInMinutes: number;

View File

@ -5,7 +5,3 @@ export function isNumeric(n: any) {
export async function sleep(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
export function computePercentage(num: number, percent: number) {
return (num / 100) * percent;
}

View File

@ -93,7 +93,13 @@ const fromWei = (wei: Wei, unit: UnitKey) => {
return baseToConvertedUnit(wei.toString(), decimal);
};
const toWei = (value: string, decimal: number): Wei => {
const toWei = (value: string, unitType: number | UnitKey): Wei => {
let decimal;
if (typeof unitType === 'number') {
decimal = unitType;
} else if (typeof unitType === 'string') {
decimal = getDecimalFromEtherUnit(unitType);
}
const wei = convertedToBaseUnit(value, decimal);
return Wei(wei);
};

View File

@ -2,6 +2,7 @@ import Web3 from 'web3';
import { CrowdFund, Milestone, MILESTONE_STATE } from 'modules/proposals/reducers';
import { collectArrayElements } from 'utils/web3Utils';
import { Wei } from 'utils/units';
import BN from 'bn.js';
export async function getCrowdFundState(
crowdFundContract: any,
@ -26,7 +27,10 @@ export async function getCrowdFundState(
? 100
: balance.divn(100).isZero()
? 0
: target.div(balance.divn(100)).toNumber();
: balance
.mul(new BN(100))
.div(target)
.toNumber();
const amountVotingForRefund = isRaiseGoalReached
? Wei(await crowdFundContract.methods.amountVotingForRefund().call({ from: account }))
: Wei('0');
@ -103,6 +107,11 @@ export async function getCrowdFundState(
.call({ form: account }),
),
);
contributor.contributionAmount = Wei(
await crowdFundContract.methods
.getContributorContributionAmount(addr)
.call({ from: account }),
);
return contributor;
}),
);