Last active
May 18, 2019 21:46
-
-
Save PaulRBerg/fbce504b2bed835501b69bcef57991ff to your computer and use it in GitHub Desktop.
Parent component for StackOverflow question
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { Component } from "react"; | |
import classnames from "classnames"; | |
import PropTypes from "prop-types"; | |
import ReactGA from "react-ga"; | |
import ReactTooltip from "react-tooltip"; | |
import { connect } from "react-redux"; | |
import { push } from "connected-react-router"; | |
import { Query } from "react-apollo"; | |
import { withTranslation } from "react-i18next"; | |
import CustomCircularProgressBar from "../../components/CustomCircularProgressBar"; | |
import FaArrowCircleDown from "../../assets/images/fa-arrow-circle-down.svg"; | |
import FaArrowCircleUp from "../../assets/images/fa-arrow-circle-up.svg"; | |
import FaCalendarAlt from "../../assets/images/fa-calendar-alt.svg"; | |
import FaCalendarCheck from "../../assets/images/fa-calendar-check.svg"; | |
import FaEnvelope from "../../assets/images/fa-envelope.svg"; | |
import FaHeartRate from "../../assets/images/fa-heart-rate.svg"; | |
import FaInboxOut from "../../assets/images/fa-inbox-out.svg"; | |
import FaInfoCircle from "../../assets/images/fa-info-circle.svg"; | |
import FaPaste from "../../assets/images/fa-paste.svg"; | |
import FaPlus from "../../assets/images/fa-plus.svg"; | |
import FaStopwatch from "../../assets/images/fa-stopwatch.svg"; | |
import FaTachometer from "../../assets/images/fa-tachometer.svg"; | |
import FaTwitterBlack from "../../assets/images/fa-twitter-black.svg"; | |
import links from "../../constants/links"; | |
import PrimaryButton from "../../components/PrimaryButton"; | |
import SablierABI from "../../abi/sablier"; | |
import StopModal from "./StopModal"; | |
import TokenLogo from "../../components/TokenLogo"; | |
import WithdrawModal from "./WithdrawModal"; | |
import { addPendingTx, selectors } from "../../redux/ducks/web3connect"; | |
import { GET_STREAM } from "../../apollo/queries"; | |
import { Parser } from "../../classes/parser"; | |
import { StreamFlow } from "../../classes/stream"; | |
import "react-circular-progressbar/dist/styles.css"; | |
import "./stream.scss"; | |
class Stream extends Component { | |
static propTypes = { | |
account: PropTypes.string, | |
addPendingTx: PropTypes.func.isRequired, | |
balances: PropTypes.object.isRequired, | |
blockNumber: PropTypes.number.isRequired, | |
push: PropTypes.func.isRequired, | |
sablierAddress: PropTypes.string, | |
selectors: PropTypes.func.isRequired, | |
web3: PropTypes.object.isRequired, | |
}; | |
state = { | |
showStopModal: false, | |
showWithdrawModal: false, | |
}; | |
componentDidMount() { | |
ReactGA.pageview(window.location.pathname + window.location.search); | |
} | |
goToDashboard() { | |
this.props.push("/dashboard"); | |
} | |
goToNewStream() { | |
this.props.push("/"); | |
} | |
onClickCopyLink() { | |
const { match } = this.props; | |
const link = `${links.share.sablier}${match.url}`; | |
navigator.clipboard.writeText(link); | |
} | |
onClickGoToDashboard() { | |
this.goToDashboard(); | |
} | |
onClickInviteYourFriends() { | |
const { t } = this.props; | |
const subject = encodeURIComponent(t("mailto.subject")); | |
const body = [t("heya"), "👋", "\r\n\r\n", encodeURIComponent(t("mailto.body"))].join(""); | |
const mailto = `mailto:?subject=${subject}&body=${body}`; | |
window.open(mailto); | |
} | |
onClickStopAndWithdraw() { | |
this.setState({ showStopModal: true }); | |
} | |
onClickTweetAboutSablier() { | |
const { t } = this.props; | |
const intent = `https://twitter.com/intent/tweet?url=${links.share.sablier}&text=${t("shareTwitterText")}`; | |
window.open(intent, "_blank"); | |
} | |
onSubmitWithdraw(amount) { | |
const { account, addPendingTx, sablierAddress, web3 } = this.props; | |
new web3.eth.Contract(SablierABI, sablierAddress).methods | |
.withdrawFromStream(, 10).send({ from: recipient }), | |
.createStream(account, recipient, token, startBlock, stopBlock, paymentWei, blockDelta) | |
.send({ from: account }) | |
.once("transactionHash", (transactionHash) => { | |
addPendingTx(transactionHash); | |
}) | |
.once("confirmation", (confirmationNumber, receipt) => { | |
this.goToStream(receipt); | |
}) | |
.once("error", (err) => { | |
this.onSubmitError(err); | |
}); | |
} | |
renderFunds(stream) { | |
const { t } = this.props; | |
return ( | |
<div className="stream__funds-container"> | |
<div className="stream__funds-item"> | |
<div className="stream__funds-item__separator" /> | |
<div className="stream__funds-item__label-container"> | |
<span className="stream__funds-item__title-label"> | |
{stream.flow === StreamFlow.IN.name ? t("earned") : t("paid")} {t("soFar")} | |
</span> | |
<div className="stream__funds-item__value-container"> | |
<span className="stream__funds-item__value-label"> | |
{stream.funds.paid.toLocaleString()} {stream.token.symbol} | |
</span> | |
<TokenLogo className="stream__funds-item__token-logo" address={stream.token.address} size="32px" /> | |
</div> | |
</div> | |
</div> | |
<div className="stream__funds-item" style={{ marginTop: "48px" }}> | |
<div className="stream__funds-item__separator" /> | |
<div className="stream__funds-item__label-container"> | |
<span className="stream__funds-item__title-label">{t("totalDeposit")}</span> | |
<div className="stream__funds-item__value-container"> | |
<span className="stream__funds-item__value-label"> | |
{stream.funds.deposit.toLocaleString() || "0"} {stream.token.symbol} | |
</span> | |
<TokenLogo className="stream__funds-item__token-logo" address={stream.token.address} size="32px" /> | |
</div> | |
</div> | |
</div> | |
</div> | |
); | |
} | |
renderStat(id, value, icon, tooltip) { | |
const { t } = this.props; | |
return ( | |
<div className="stream__stats-item"> | |
<div className="stream__stats-item__icon-container"> | |
<img className="stream__stats-item__icon" alt={t(id)} src={icon} /> | |
</div> | |
<div className="stream__stats-item__label-container"> | |
<span className="stream__stats-item__title-label">{t(id)}</span> | |
<span className="stream__stats-item__value-label">{value}</span> | |
</div> | |
<div className="spacer" /> | |
<div className="stream__stats-item__info-icon-container" data-tip={tooltip}> | |
<img className="stream__stats-item__info-icon" alt={t("info")} src={FaInfoCircle} /> | |
<ReactTooltip className="stream__stats-item__tooltip" effect="solid" multiline={true} type="dark" /> | |
</div> | |
</div> | |
); | |
} | |
renderStream(stream) { | |
const { t } = this.props; | |
return ( | |
<div className="stream__left-container"> | |
<div className="stream__stream-container"> | |
<div className="stream__circular-progress-bar-container"> | |
<CustomCircularProgressBar | |
className="stream__circular-progress-bar" | |
percentage={stream.funds.ratio} | |
strokeWidth={6} | |
> | |
<span className="stream__circular-progress-bar-percentage-label">{stream.funds.ratio}%</span> | |
<span className="stream__circular-progress-bar-label">{t("streamed")}</span> | |
</CustomCircularProgressBar> | |
</div> | |
{this.renderFunds(stream)} | |
</div> | |
<div className="stream__separator" /> | |
<div className="stream__stats-container"> | |
{stream.flow === StreamFlow.IN.name | |
? this.renderStat("flow", t("incoming"), FaArrowCircleDown, t("tooltip.flow")) | |
: this.renderStat("flow", t("outgoing"), FaArrowCircleUp, t("tooltip.flow"))} | |
{this.renderStat("rate", stream.rate, FaHeartRate, t("tooltip.rate"))} | |
{this.renderStat("startTime", stream.startTime, FaCalendarAlt, t("tooltip.age"))} | |
{this.renderStat("stopTime", stream.stopTime, FaCalendarCheck, t("tooltip.remaining"))} | |
</div> | |
</div> | |
); | |
} | |
renderActions(stream) { | |
const { t } = this.props; | |
return ( | |
<div className="stream__action-container"> | |
<span className="stream__title-label">{t("action", { count: 3 })}</span> | |
<div className="stream__button-container"> | |
<PrimaryButton | |
className={classnames(["stream__button", "stream__action-container-button", "primary-button--white"])} | |
icon={FaTachometer} | |
label={t("goDashboard")} | |
labelClassName={classnames("primary-button__label--black")} | |
onClick={() => this.onClickGoToDashboard()} | |
/> | |
{stream.flow === StreamFlow.IN.name ? ( | |
<PrimaryButton | |
className={classnames(["stream__button", "stream__action-container-button"])} | |
icon={FaInboxOut} | |
label={t("withdraw")} | |
onClick={() => this.setState({ showWithdrawModal: true })} | |
/> | |
) : ( | |
<PrimaryButton | |
className={classnames(["stream__button", "stream__action-container-button"])} | |
icon={FaPlus} | |
label={t("newStream")} | |
onClick={() => this.goToNewStream()} | |
/> | |
)} | |
<PrimaryButton | |
className={classnames(["stream__button", "stream__action-container-button", "primary-button--yellow"])} | |
icon={FaStopwatch} | |
label={t("stopAndWithdraw")} | |
onClick={() => this.setState({ showStopModal: true })} | |
/> | |
</div> | |
</div> | |
); | |
} | |
renderShare() { | |
const { t } = this.props; | |
return ( | |
<div className="stream__share-container"> | |
<span className="stream__title-label">{t("share")}</span> | |
<div className="stream__button-container" data-tip="Hello There"> | |
<PrimaryButton | |
className={classnames(["stream__button", "stream__action-container-button", "primary-button--white"])} | |
icon={FaPaste} | |
label={t("copyLink")} | |
labelClassName={classnames("primary-button__label--black")} | |
onClick={() => this.onClickCopyLink()} | |
/> | |
<PrimaryButton | |
className={classnames(["stream__button", "stream__action-container-button", "primary-button--white"])} | |
icon={FaTwitterBlack} | |
label={t("tweetAboutSablier")} | |
labelClassName={classnames("primary-button__label--black")} | |
onClick={() => this.onClickTweetAboutSablier()} | |
/> | |
<PrimaryButton | |
className={classnames(["stream__button", "stream__action-container-button", "primary-button--white"])} | |
icon={FaEnvelope} | |
label={t("inviteYourFriends")} | |
labelClassName={classnames("primary-button__label--black")} | |
onClick={() => this.onClickInviteYourFriends()} | |
/> | |
</div> | |
</div> | |
); | |
} | |
render() { | |
const { account, blockNumber, rawStreamId, t } = this.props; | |
const { showStopModal, showWithdrawModal } = this.state; | |
const streamId = `${account.toLowerCase()}/${rawStreamId}`; | |
return ( | |
<Query query={GET_STREAM} variables={{ streamId }}> | |
{({ loading, error, data }) => { | |
if (loading) return <div className="stream__loader" />; | |
if (error) return <div className="stream__no-data">{error.message}</div>; | |
if (!data.stream) return <div className="stream__no-data">{t("noData")}</div>; | |
const parser = new Parser(data.stream, account, blockNumber, t); | |
const stream = parser.parse(); | |
return ( | |
<div className="stream"> | |
{this.renderStream(stream)} | |
<div className="stream__right-container"> | |
{this.renderActions(stream)} | |
{this.renderShare()} | |
</div> | |
{!showWithdrawModal ? null : ( | |
<WithdrawModal | |
onClose={() => this.setState({ showWithdrawModal: false })} | |
paid={stream.funds.paid} | |
tokenSymbol={stream.token.symbol} | |
withdrawable={stream.funds.withdrawable} | |
withdrawn={stream.funds.withdrawn} | |
/> | |
)} | |
{!showStopModal ? null : ( | |
<StopModal | |
deposit={stream.funds.deposit} | |
onClose={() => this.setState({ showStopModal: false })} | |
tokenSymbol={stream.token.symbol} | |
withdrawable={stream.funds.withdrawable} | |
/> | |
)} | |
</div> | |
); | |
}} | |
</Query> | |
); | |
} | |
} | |
export default connect( | |
(state) => ({ | |
account: state.web3connect.account, | |
blockNumber: state.web3connect.blockNumber, | |
balances: state.web3connect.balances, | |
// eslint-disable-next-line eqeqeq | |
isConnected: !!state.web3connect.account && state.web3connect.networkId == (process.env.REACT_APP_NETWORK_ID || 1), | |
rawStreamId: | |
sablierAddress: state.addresses.sablierAddress, | |
web3: state.web3connect.web3, | |
}), | |
(dispatch) => ({ | |
addPendingTx: (id) => dispatch(addPendingTx(id)), | |
push: (path) => dispatch(push(path)), | |
selectors: () => dispatch(selectors()), | |
}), | |
)(withTranslation()(Stream)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment