Show send instructions on profile pending contributions.
This commit is contained in:
parent
5a922cefee
commit
6c8ce3f87d
|
@ -220,6 +220,7 @@ export function postProposalContribution(
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
export function postProposalComment(payload: {
|
||||
proposalId: number;
|
||||
parentCommentId?: number;
|
||||
|
@ -232,3 +233,10 @@ export function postProposalComment(payload: {
|
|||
export function deleteProposalContribution(contributionId: string | number) {
|
||||
return axios.delete(`/api/v1/proposals/contribution/${contributionId}`);
|
||||
}
|
||||
|
||||
export function getProposalContribution(
|
||||
proposalId: number,
|
||||
contributionId: number,
|
||||
): Promise<{ data: ContributionWithAddresses }> {
|
||||
return axios.get(`/api/v1/proposals/${proposalId}/contributions/${contributionId}`);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
border-bottom: 1px solid rgba(#000, 0.06);
|
||||
|
||||
&-qr {
|
||||
position: relative;
|
||||
padding: 0.5rem;
|
||||
margin-right: 1rem;
|
||||
border-radius: 4px;
|
||||
|
@ -28,6 +29,16 @@
|
|||
canvas {
|
||||
display: block;
|
||||
}
|
||||
&.is-loading canvas {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.ant-spin {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
}
|
||||
|
||||
&-info {
|
||||
|
|
|
@ -67,8 +67,13 @@ export default class PaymentInfo extends React.Component<Props, State> {
|
|||
</Radio.Group>
|
||||
|
||||
<div className="PaymentInfo-uri">
|
||||
<div className="PaymentInfo-uri-qr">
|
||||
{uri ? <QRCode value={uri} /> : <Spin />}
|
||||
<div className={
|
||||
classnames('PaymentInfo-uri-qr', !uri && 'is-loading')
|
||||
}>
|
||||
<span style={{ opacity: uri ? 1 : 0 }}>
|
||||
<QRCode value={uri || ''} />
|
||||
</span>
|
||||
{!uri && <Spin size="large" />}
|
||||
</div>
|
||||
<div className="PaymentInfo-uri-info">
|
||||
<CopyInput
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
import React from 'react';
|
||||
import { Modal } from 'antd';
|
||||
import Result from 'ant-design-pro/lib/Result';
|
||||
import { postProposalContribution } from 'api/api';
|
||||
import { postProposalContribution, getProposalContribution } from 'api/api';
|
||||
import { ContributionWithAddresses } from 'types';
|
||||
import PaymentInfo from './PaymentInfo';
|
||||
|
||||
interface OwnProps {
|
||||
isVisible: boolean;
|
||||
proposalId: number;
|
||||
proposalId?: number;
|
||||
contributionId?: number;
|
||||
amount?: string;
|
||||
hasNoButtons?: boolean;
|
||||
handleClose(): void;
|
||||
}
|
||||
|
||||
|
@ -28,17 +30,21 @@ export default class ContributionModal extends React.Component<Props, State> {
|
|||
};
|
||||
|
||||
componentWillUpdate(nextProps: Props) {
|
||||
const { isVisible, proposalId } = nextProps
|
||||
if (isVisible && this.props.isVisible !== isVisible) {
|
||||
this.fetchAddresses(proposalId);
|
||||
}
|
||||
else if (proposalId !== this.props.proposalId) {
|
||||
this.fetchAddresses(proposalId);
|
||||
const { isVisible, proposalId, contributionId } = nextProps;
|
||||
// When modal is opened and proposalId is provided or changed
|
||||
if (isVisible && proposalId) {
|
||||
if (
|
||||
this.props.isVisible !== isVisible ||
|
||||
proposalId !== this.props.proposalId
|
||||
) {
|
||||
this.fetchAddresses(proposalId, contributionId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isVisible, handleClose } = this.props;
|
||||
const { isVisible, handleClose, hasNoButtons } = this.props;
|
||||
const { hasSent, contribution, error } = this.state;
|
||||
let content;
|
||||
|
||||
|
@ -68,11 +74,12 @@ export default class ContributionModal extends React.Component<Props, State> {
|
|||
<Modal
|
||||
title="Make your contribution"
|
||||
visible={isVisible}
|
||||
closable={hasSent}
|
||||
maskClosable={hasSent}
|
||||
closable={hasSent || hasNoButtons}
|
||||
maskClosable={hasSent || hasNoButtons}
|
||||
okText={hasSent ? 'Done' : 'I’ve sent it'}
|
||||
onOk={hasSent ? handleClose : this.confirmSend}
|
||||
onCancel={handleClose}
|
||||
footer={hasNoButtons ? '' : undefined}
|
||||
centered
|
||||
>
|
||||
{content}
|
||||
|
@ -80,12 +87,20 @@ export default class ContributionModal extends React.Component<Props, State> {
|
|||
);
|
||||
}
|
||||
|
||||
private async fetchAddresses(proposalId: number) {
|
||||
private async fetchAddresses(
|
||||
proposalId: number,
|
||||
contributionId?: number,
|
||||
) {
|
||||
try {
|
||||
const res = await postProposalContribution(
|
||||
proposalId,
|
||||
this.props.amount || '0',
|
||||
);
|
||||
let res;
|
||||
if (contributionId) {
|
||||
res = await getProposalContribution(proposalId, contributionId);
|
||||
} else {
|
||||
res = await postProposalContribution(
|
||||
proposalId,
|
||||
this.props.amount || '0',
|
||||
);
|
||||
}
|
||||
this.setState({ contribution: res.data });
|
||||
} catch(err) {
|
||||
this.setState({ error: err.message });
|
||||
|
|
|
@ -12,6 +12,7 @@ import './ProfileContribution.less';
|
|||
interface OwnProps {
|
||||
userId: number;
|
||||
contribution: UserContribution;
|
||||
showSendInstructions(contribution: UserContribution): void;
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
|
@ -30,7 +31,6 @@ class ProfileContribution extends React.Component<Props> {
|
|||
let tag;
|
||||
let actions: React.ReactNode;
|
||||
if (isConfirmed) {
|
||||
// TODO: Link to block explorer
|
||||
actions = (
|
||||
<a
|
||||
href={formatTxExplorerUrl(contribution.txId as string)}
|
||||
|
@ -50,12 +50,15 @@ class ProfileContribution extends React.Component<Props> {
|
|||
>
|
||||
<a>Delete</a>
|
||||
</Popconfirm>
|
||||
<a>Contact support</a>
|
||||
<Link to="/support">Contact support</Link>
|
||||
</>;
|
||||
} else {
|
||||
tag = <Tag color="orange">Pending</Tag>;
|
||||
// TODO: Show ContributionModal
|
||||
actions = <a>View instructions</a>;
|
||||
actions = (
|
||||
<a onClick={() => this.props.showSendInstructions(contribution)}>
|
||||
View send instructions
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -21,7 +21,9 @@ import ProfileComment from './ProfileComment';
|
|||
import ProfileInvite from './ProfileInvite';
|
||||
import Placeholder from 'components/Placeholder';
|
||||
import Exception from 'pages/exception';
|
||||
import ContributionModal from 'components/ContributionModal';
|
||||
import './style.less';
|
||||
import { UserContribution } from 'types';
|
||||
|
||||
interface StateProps {
|
||||
usersMap: AppState['users']['map'];
|
||||
|
@ -35,7 +37,15 @@ interface DispatchProps {
|
|||
|
||||
type Props = RouteComponentProps<any> & StateProps & DispatchProps;
|
||||
|
||||
class Profile extends React.Component<Props> {
|
||||
interface State {
|
||||
activeContribution: UserContribution | null;
|
||||
}
|
||||
|
||||
class Profile extends React.Component<Props, State> {
|
||||
state: State = {
|
||||
activeContribution: null,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.fetchData();
|
||||
}
|
||||
|
@ -50,6 +60,8 @@ class Profile extends React.Component<Props> {
|
|||
render() {
|
||||
const userLookupParam = this.props.match.params.id;
|
||||
const { authUser, match } = this.props;
|
||||
const { activeContribution } = this.state;
|
||||
|
||||
if (!userLookupParam) {
|
||||
if (authUser && authUser.userid) {
|
||||
return <Redirect to={`/profile/${authUser.userid}`} />;
|
||||
|
@ -128,7 +140,12 @@ class Profile extends React.Component<Props> {
|
|||
<div>
|
||||
{noneFunded && <Placeholder subtitle="Has not funded any proposals yet" />}
|
||||
{contributions.map(c => (
|
||||
<ProfileContribution key={c.id} userId={user.userid} contribution={c} />
|
||||
<ProfileContribution
|
||||
key={c.id}
|
||||
userId={user.userid}
|
||||
contribution={c}
|
||||
showSendInstructions={this.openContributionModal}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Tabs.TabPane>
|
||||
|
@ -160,9 +177,20 @@ class Profile extends React.Component<Props> {
|
|||
</Tabs.TabPane>
|
||||
)}
|
||||
</Tabs>
|
||||
|
||||
<ContributionModal
|
||||
isVisible={!!activeContribution}
|
||||
proposalId={
|
||||
activeContribution ? activeContribution.proposal.proposalId : undefined
|
||||
}
|
||||
contributionId={activeContribution ? activeContribution.id : undefined}
|
||||
hasNoButtons
|
||||
handleClose={this.closeContributionModal}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private fetchData() {
|
||||
const { match } = this.props;
|
||||
const userLookupId = match.params.id;
|
||||
|
@ -171,6 +199,9 @@ class Profile extends React.Component<Props> {
|
|||
this.props.fetchUserInvites(userLookupId);
|
||||
}
|
||||
}
|
||||
|
||||
private openContributionModal = (c: UserContribution) => this.setState({ activeContribution: c });
|
||||
private closeContributionModal = () => this.setState({ activeContribution: null });
|
||||
}
|
||||
|
||||
const TabTitle = (disp: string, count: number) => (
|
||||
|
|
|
@ -2,7 +2,6 @@ import * as React from 'react';
|
|||
import { storiesOf } from '@storybook/react';
|
||||
|
||||
import { ProposalCampaignBlock } from 'components/Proposal/CampaignBlock';
|
||||
import Contributors from 'components/Proposal/Contributors';
|
||||
|
||||
import 'styles/style.less';
|
||||
import 'components/Proposal/style.less';
|
||||
|
@ -53,9 +52,4 @@ storiesOf('Proposal', module)
|
|||
<div style={{ display: 'flex', flexWrap: 'wrap' }}>
|
||||
<CampaignBlocks style={{ margin: '0 12px' }} />
|
||||
</div>
|
||||
))
|
||||
.add('Contributors', () => (
|
||||
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<Contributors />
|
||||
</div>
|
||||
));
|
||||
|
|
|
@ -2,7 +2,7 @@ import { Zat } from 'utils/units';
|
|||
import { Proposal, User } from 'types';
|
||||
|
||||
export interface Contribution {
|
||||
id: string;
|
||||
id: number;
|
||||
txId: string;
|
||||
amount: string;
|
||||
dateCreated: number;
|
||||
|
|
Loading…
Reference in New Issue