import { Civil } from "@joincivil/core" ; import { EthAddress } from "@joincivil/typescript-types" ; import { debounce } from "lodash" ; import * as React from "react" ; import { Subscription } from "rxjs/Subscription" ; import styled from "styled-components" ; import { TransactionButton , Transaction , TransactionButtonNoModal , TransactionButtonInnerProps , } from "./TransactionButton" ; import { fonts , colors } from "./styleConstants" ; import { QuestionToolTip } from "./QuestionToolTip" ; export interface DetailTransactionButtonProps { civil ? : Civil ; transactions : Transaction [ ] ; estimateFunctions ? : Array < ( ... args : any [ ] ) => Promise < number > > ; requiredNetwork ? : string ; Button ? : React . FunctionComponent < TransactionButtonInnerProps > ; noModal ? : boolean ; disabled ? : boolean ; preExecuteTransactions ? ( ) : any ; } export interface DetailTransactionButtonState { price : number ; priceFailed : boolean ; isProgressModalVisible : boolean ; currentNetwork : string ; currentAccount ? : EthAddress ; ethereumUpdates ? : Subscription ; } const Wrapper = styled . div `
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: top;
  margin-bottom: 15px;
` ; const DetailSection = styled . div `
  width: 50%;
` ; const SmallHeader = styled . h4 `
  font-family: ${ fonts . SANS_SERIF };
  font-weight: 500;
  font-size: 14px;
  margin-bottom: 10px;
  margin-top: 0px;
` ; const SmallText = styled . p `
  font-family: ${ fonts . SANS_SERIF };
  color: ${ colors . accent . CIVIL_GRAY_2 };
  font-size: 13px;
  line-height: 18px;
  display: flex;
  align-items: center;
  margin: 0;
` ; const Link = styled . a `
  font-family: ${ fonts . SANS_SERIF };
  font-size: 11px;
  text-decoration: none;
  font-weight: 600;
  color: ${ colors . primary . CIVIL_BLUE_1 };
` ; const ToolTipLink = styled . a `
  color: ${ colors . basic . WHITE };
` ; export const TransactionPopUpWarning = styled . p `
  && {
    color: ${ colors . accent . CIVIL_GRAY_2 };
    font-size: 12px;
  }
` ; const ButtonWrapper = styled . div `
  max-width: 200px;
` ; export class DetailTransactionButton extends React . Component < DetailTransactionButtonProps , DetailTransactionButtonState > { constructor ( props ) { super ( props ) ; this . state = { price : 0 , priceFailed : false , isProgressModalVisible : false , currentNetwork : "" , } ; this . divinePrice = debounce ( this . divinePrice . bind ( this ) , 1000 ) ; } public async componentWillReceiveProps ( nextProps ) { await this . divinePrice ( nextProps . estimateFunctions ) ; } public async divinePrice ( estimateFunctions ? ) { if ( ! this . isDisabled ( ) && estimateFunctions && estimateFunctions . length ) { try { const gas = ( await Promise . all ( estimateFunctions . map ( async ( item ) => item ( ) ) ) ) . reduce ( ( acc , item ) => acc + item , 0 ) ; const gasPrice = await this . props . civil ! . getGasPrice ( ) ; this . setState ( { price : gasPrice . mul ( this . props . civil ! . toBigNumber ( gas ) ) . div ( this . props . civil ! . toBigNumber ( 10 ) . pow ( 18 ) ) . toNumber ( ) , priceFailed : false , } ) ; } catch ( error ) { this . setState ( { priceFailed : true } ) ; } } else { this . setState ( { priceFailed : true } ) ; } } public async componentDidMount ( ) { this . createEthereumSubscription ( this . props . civil ) ; await this . divinePrice ( this . props . estimateFunctions ) ; } public async componentWillUnmount ( ) { this . unbscribeEthereum ( ) ; } public createEthereumSubscription ( civil ? ) { this . unbscribeEthereum ( ) ; let subscription ; if ( civil ) { subscription = civil . accountStream . subscribe ( currentAccount => this . setState ( { currentAccount } ) ) . add ( civil . networkNameStream . subscribe ( currentNetwork => this . setState ( { currentNetwork } ) ) ) ; } this . setState ( { ethereumUpdates : subscription , } ) ; } public unbscribeEthereum ( ) { if ( this . state . ethereumUpdates ) { this . state . ethereumUpdates . unsubscribe ( ) ; } } public render ( ) { const TransactionButtonComponent = this . props . noModal ? TransactionButtonNoModal : TransactionButton ; return ( < Wrapper > { this . renderDetails ( ) } < ButtonWrapper > < TransactionButtonComponent preExecuteTransactions = { this . props . preExecuteTransactions } disabled = { this . isDisabled ( ) } transactions = { this . props . transactions } Button = { this . props . Button } > { this . props . children } < /TransactionButtonComponent> < TransactionPopUpWarning > This will open a new window to confirm and process your transaction . < /TransactionPopUpWarning> < /ButtonWrapper> < /Wrapper>); } public isDisabled ( ) { const onRequiredNetwork = ! this . props . requiredNetwork || this . props . requiredNetwork . includes ( this . state . currentNetwork ) ; return this . props . disabled || ! this . props . civil || ! this . state . currentAccount || ! onRequiredNetwork ; } public renderNoMetaMask ( ) { return ( < DetailSection > < SmallHeader > Set up your MetaMask wallet < /SmallHeader> < SmallText > Download the MetaMask browser plugin and follow the instructions to set up your wallet . < /SmallText> < Link href = "https://metamask.io/" target = "_blank" > Get MetaMask < /Link> < /DetailSection>); } public renderMetaMaskLocked ( ) { return ( < DetailSection > < SmallHeader > MetaMask is locked < /SmallHeader> < SmallText > Please unlock MetaMask to create a newsroom contract < /SmallText> < /DetailSection>); } public renderWrongNetwork ( ) { return ( < DetailSection > < SmallHeader > MetaMask is on the wrong network < /SmallHeader> < SmallText > Please change your network to the { this . props . requiredNetwork ! . replace ( /^\w/ , c => c . toUpperCase ( ) ) } Network before proceeding < /SmallText> < /DetailSection>); } public renderCostsEstimates ( ) { if ( this . state . price === 0 && ! this . state . priceFailed ) { return < SmallText > Estimating cost . < /SmallText>; } else if ( this . state . priceFailed ) { return < SmallText > Could not estimate cost . < /SmallText>; } else { return < SmallText > ETH : { this . state . price . toFixed ( 5 ) } < /SmallText>; } } public renderTransactionDetails ( ) { return ( < DetailSection > < SmallText > Estimated Cost { " " } < QuestionToolTip explainerText = { < > Current Prices based on { " " } < ToolTipLink href = "https://ethgasstation.info/" target = "_blank" > { "https://ethgasstation.info/" } < /ToolTipLink> < />}/ > < /SmallText> { this . renderCostsEstimates ( ) } < /DetailSection>); } public renderDetails ( ) { if ( ! this . props . civil ) { return this . renderNoMetaMask ( ) ; } else if ( ! this . state . currentAccount ) { return this . renderMetaMaskLocked ( ) ; } else if ( this . props . requiredNetwork && this . props . requiredNetwork !== this . state . currentNetwork ) { return this . renderWrongNetwork ( ) ; } else { return this . renderTransactionDetails ( ) ; } } }