import React, { ReactNode } from 'react'; import { compose } from 'recompose'; import { connect } from 'react-redux'; import { Link } from 'react-router-dom'; import Markdown from 'components/Markdown'; import LinkableTabs from 'components/LinkableTabs'; import Loader from 'components/Loader'; import { proposalActions } from 'modules/proposals'; import { bindActionCreators, Dispatch } from 'redux'; import { AppState } from 'store/reducers'; import { STATUS } from 'types'; import { Tabs, Icon, Dropdown, Menu, Button, Alert } from 'antd'; import { AlertProps } from 'antd/lib/alert'; import ExceptionPage from 'components/ExceptionPage'; import HeaderDetails from 'components/HeaderDetails'; import CampaignBlock from './CampaignBlock'; import TeamBlock from './TeamBlock'; import RFPBlock from './RFPBlock'; import Milestones from './Milestones'; import CommentsTab from './Comments'; import UpdatesTab from './Updates'; import ContributorsTab from './Contributors'; import UpdateModal from './UpdateModal'; import CancelModal from './CancelModal'; import classnames from 'classnames'; import { withRouter } from 'react-router'; import SocialShare from 'components/SocialShare'; import './index.less'; interface OwnProps { proposalId: number; isPreview?: boolean; } interface StateProps { detail: AppState['proposal']['detail']; isFetchingDetail: AppState['proposal']['isFetchingDetail']; detailError: AppState['proposal']['detailError']; user: AppState['auth']['user']; } interface DispatchProps { fetchProposal: proposalActions.TFetchProposal; } type Props = StateProps & DispatchProps & OwnProps; interface State { isBodyExpanded: boolean; isBodyOverflowing: boolean; isUpdateOpen: boolean; isCancelOpen: boolean; } export class ProposalDetail extends React.Component { state: State = { isBodyExpanded: false, isBodyOverflowing: false, isUpdateOpen: false, isCancelOpen: false, }; bodyEl: HTMLElement | null = null; componentDidMount() { // always refresh from server this.props.fetchProposal(this.props.proposalId); if (this.props.detail) { this.checkBodyOverflow(); } if (typeof window !== 'undefined') { window.addEventListener('resize', this.checkBodyOverflow); } } componentWillUnmount() { if (typeof window !== 'undefined') { window.removeEventListener('resize', this.checkBodyOverflow); } } componentDidUpdate() { if (this.props.detail) { this.checkBodyOverflow(); } } render() { const { user, detail: proposal, isPreview, detailError } = this.props; const { isBodyExpanded, isBodyOverflowing, isCancelOpen, isUpdateOpen } = this.state; const showExpand = !isBodyExpanded && isBodyOverflowing; const wrongProposal = proposal && proposal.proposalId !== this.props.proposalId; if (detailError) { return ; } if (!proposal || wrongProposal) { return ; } const isTrustee = !!proposal.team.find(tm => tm.userid === (user && user.userid)); const isLive = proposal.status === STATUS.LIVE; const adminMenu = ( Post an Update Cancel proposal ); // BANNER const statusBanner = { [STATUS.PENDING]: { blurb: ( <> Your proposal is being reviewed and is only visible to the team. You will get an email when it is complete. ), type: 'warning', }, [STATUS.APPROVED]: { blurb: ( <> Your proposal has been approved! It is currently only visible to the team. Visit your profile's pending tab to publish. ), type: 'success', }, [STATUS.REJECTED]: { blurb: ( <> Your proposal was rejected and is only visible to the team. Visit your{' '} profile's pending tab for more information. ), type: 'error', }, [STATUS.STAKING]: { blurb: ( <> Your proposal is awaiting a staking contribution. Visit your{' '} profile's pending tab for more information. ), type: 'warning', }, } as { [key in STATUS]: { blurb: ReactNode; type: AlertProps['type'] } }; let banner = statusBanner[proposal.status]; if (isPreview) { banner = { blurb: 'This is a preview of your proposal. It has not yet been published.', type: 'info', }; } return (
{banner && (
)}
{isLive && (
)}

{proposal ? proposal.title :  }

(this.bodyEl = el)} className={classnames({ ['Proposal-top-main-block-bodyText']: true, ['is-expanded']: isBodyExpanded, })} > {proposal ? : }
{showExpand && ( )}
{isLive && isTrustee && (
)}
{proposal.rfp && }
{isTrustee && ( <> )}
); } private expandBody = () => { this.setState({ isBodyExpanded: true }); }; private checkBodyOverflow = () => { const { isBodyExpanded, isBodyOverflowing } = this.state; if (isBodyExpanded || !this.bodyEl) { return; } if (isBodyOverflowing && this.bodyEl.scrollHeight <= this.bodyEl.clientHeight) { this.setState({ isBodyOverflowing: false }); } else if ( !isBodyOverflowing && this.bodyEl.scrollHeight > this.bodyEl.clientHeight ) { this.setState({ isBodyOverflowing: true }); } }; private openUpdateModal = () => this.setState({ isUpdateOpen: true }); private closeUpdateModal = () => this.setState({ isUpdateOpen: false }); private openCancelModal = () => this.setState({ isCancelOpen: true }); private closeCancelModal = () => this.setState({ isCancelOpen: false }); } function mapStateToProps(state: AppState, _: OwnProps) { return { detail: state.proposal.detail, isFetchingDetail: state.proposal.isFetchingDetail, detailError: state.proposal.detailError, user: state.auth.user, }; } function mapDispatchToProps(dispatch: Dispatch) { return bindActionCreators({ ...proposalActions }, dispatch); } const withConnect = connect( mapStateToProps, mapDispatchToProps, ); const ConnectedProposal = compose( withRouter, withConnect, )(ProposalDetail); export default ConnectedProposal;