src/modules/portfolio/components/market-portfolio-card/market-portfolio-card.jsx
import React, { Component } from "react";
import classNames from "classnames";
import PropTypes from "prop-types";
import getValue from "utils/get-value";
import MarketPositionsListOrphanedOrder from "modules/market/components/market-positions-list--orphaned-order/market-positions-list--orphaned-order";
import MarketPositionsListPosition from "modules/market/components/market-positions-list--position/market-positions-list--position";
import MarketPositionsListOrder from "modules/market/components/market-positions-list--order/market-positions-list--order";
import ChevronFlip from "modules/common/components/chevron-flip/chevron-flip";
import MarketLink from "modules/market/components/market-link/market-link";
import {
TYPE_CLAIM_PROCEEDS,
TYPE_FINALIZE_MARKET
} from "modules/markets/constants/link-types";
import { dateHasPassed } from "utils/format-date";
import CommonStyles from "modules/market/components/common/market-common.styles";
import PositionStyles from "modules/market/components/market-positions-list/market-positions-list.styles";
import Styles from "modules/portfolio/components/market-portfolio-card/market-portfolio-card.styles";
import MarketPortfolioCardFooter from "modules/portfolio/components/market-portfolio-card/market-portfolio-card-footer";
import {
AWAITING_SIGNATURE,
PENDING
} from "modules/transactions/constants/statuses";
import CutoffFlag from "modules/market/components/cutoff-flag/cutoff-flag";
export default class MarketPortfolioCard extends Component {
static propTypes = {
claimTradingProceeds: PropTypes.func.isRequired,
currentTimestamp: PropTypes.number.isRequired,
isMobile: PropTypes.bool.isRequired,
linkType: PropTypes.string,
market: PropTypes.object.isRequired,
positionsDefault: PropTypes.bool,
finalizeMarket: PropTypes.func.isRequired,
getWinningBalances: PropTypes.func.isRequired,
orphanedOrders: PropTypes.array.isRequired,
transactionsStatus: PropTypes.object.isRequired,
cancelOrphanedOrder: PropTypes.func.isRequired,
sellCompleteSets: PropTypes.func.isRequired
};
static defaultProps = {
positionsDefault: true,
linkType: null
};
constructor(props) {
super(props);
const { positionsDefault, orphanedOrders } = props;
this.state = {
tableOpen: {
myPositions: positionsDefault,
openOrders: orphanedOrders.length > 0 // open if orphaned orders are present
},
claimClicked: false,
disableFinalize: false
};
}
componentWillMount() {
const { market, getWinningBalances } = this.props;
getWinningBalances([market.id]);
}
toggleTable(tableKey) {
const { tableOpen } = this.state;
this.setState({
tableOpen: {
...tableOpen,
[tableKey]: !tableOpen[tableKey]
}
});
}
finalizeMarket = () => {
const { finalizeMarket, market } = this.props;
this.setState({ disableFinalize: true });
finalizeMarket(market.id, err => {
if (err) this.setState({ disableFinalize: false });
});
};
claimProceeds = () => {
const { claimTradingProceeds, market } = this.props;
this.setState({ claimClicked: true });
claimTradingProceeds(market.id, err => {
if (err) this.setState({ claimClicked: false });
});
};
render() {
const {
currentTimestamp,
isMobile,
linkType,
market,
orphanedOrders,
cancelOrphanedOrder,
sellCompleteSets,
transactionsStatus
} = this.props;
const { tableOpen, claimClicked, disableFinalize } = this.state;
const myPositionsSummary = getValue(market, "myPositionsSummary");
const myPositionOutcomes = getValue(market, "outcomes");
const numCompleteSets = getValue(myPositionsSummary, "numCompleteSets");
let localButtonText;
let buttonAction;
let disabled = false;
switch (linkType) {
case TYPE_CLAIM_PROCEEDS:
localButtonText = "Claim Proceeds";
buttonAction = this.claimProceeds;
disabled = claimClicked;
break;
case TYPE_FINALIZE_MARKET:
localButtonText = "Finalize Market";
buttonAction = this.finalizeMarket;
disabled = disableFinalize;
break;
default:
localButtonText = "View";
}
const pendingCompleteSetsHash = `pending-${market.id}-${numCompleteSets &&
numCompleteSets.fullPrecision}`;
const pendingCompleteSetsInfo = transactionsStatus[pendingCompleteSetsHash];
const status = pendingCompleteSetsInfo && pendingCompleteSetsInfo.status;
let completeSetButtonText = "Sell Complete Sets";
switch (status) {
case AWAITING_SIGNATURE:
completeSetButtonText = "Awaiting Signature...";
break;
case PENDING:
completeSetButtonText = "Pending transaction...";
break;
default:
completeSetButtonText = "Sell Complete Sets";
break;
}
return (
<article className={CommonStyles.MarketCommon__container}>
<section
className={classNames(
CommonStyles.MarketCommon__topcontent,
Styles.MarketCard__topcontent
)}
>
<div
className={classNames(
CommonStyles.MarketCommon__header,
Styles.MarketCard__header
)}
>
<div className={Styles.MarketCard__headertext}>
<span>
<CutoffFlag
passesInitialREPFilter={market.passesInitialREPFilter}
/>
</span>
<span className={Styles["MarketCard__expiration--mobile"]}>
{dateHasPassed(
currentTimestamp,
(market.endTime || {}).timestamp
)
? "Reporting Started "
: "Reporting Starts "}
{isMobile
? (market.endTime || {}).formattedLocalShort
: (market.endTime || {}).formattedLocal}
</span>
<h1 className={CommonStyles.MarketCommon__description}>
<MarketLink id={market.id}>{market.description}</MarketLink>
</h1>
</div>
</div>
<div className={Styles.MarketCard__topstats}>
<div className={Styles.MarketCard__leftstats}>
<div className={Styles.MarketCard__stat}>
<span className={Styles.MarketCard__statlabel}>
Realized P/L
</span>
<span className={Styles.MarketCard__statvalue}>
{getValue(myPositionsSummary, "realizedNet.formatted") || "0"}
</span>
<span className={Styles.MarketCard__statunit}>ETH</span>
</div>
<div className={Styles.MarketCard__stat}>
<span className={Styles.MarketCard__statlabel}>
Unrealized P/L
</span>
<span className={Styles.MarketCard__statvalue}>
{getValue(myPositionsSummary, "unrealizedNet.formatted") ||
"0"}
</span>
<span className={Styles.MarketCard__statunit}>ETH</span>
</div>
<div className={Styles.MarketCard__stat}>
<span className={Styles.MarketCard__statlabel}>Total P/L</span>
<span className={Styles.MarketCard__statvalue}>
{getValue(myPositionsSummary, "totalNet.formatted") || "0"}
</span>
<span className={Styles.MarketCard__statunit}>ETH</span>
</div>
</div>
<span className={Styles.MarketCard__expiration}>
<span className={Styles.MarketCard__expirationlabel}>
{market.endTimeLabel}
</span>
<span className={Styles.MarketCard__expirationvalue}>
{getValue(market, "endTime.formattedLocal")}
</span>
</span>
</div>
</section>
<section className={Styles.MarketCard__tablesection}>
{(myPositionOutcomes || []).filter(outcome => outcome.position)
.length > 0 && (
<button
className={Styles.MarketCard__headingcontainer}
onClick={() => this.toggleTable("myPositions")}
>
<h1 className={Styles.MarketCard__tableheading}>My Positions</h1>
<div className={Styles.MarketCard__tabletoggle}>
<ChevronFlip pointDown={!tableOpen.myPositions} />
</div>
</button>
)}
<div className={PositionStyles.MarketPositionsList__table}>
{tableOpen.myPositions &&
(myPositionOutcomes || []).filter(outcome => outcome.position)
.length > 0 && (
<ul
className={classNames(
PositionStyles["MarketPositionsList__table-header"],
Styles["MarketCard__table-header"]
)}
>
<li>Outcome</li>
{isMobile ? (
<li>
<span>Net Qty</span>
</li>
) : (
<li>
<span>Net Quantity</span>
</li>
)}
{isMobile ? (
<li>
<span>Qty</span>
</li>
) : (
<li>
<span>Quantity</span>
</li>
)}
{isMobile ? (
<li>
<span>Avg</span>
</li>
) : (
<li>
<span>Avg Price</span>
</li>
)}
{!isMobile && (
<li>
<span>Last Price</span>
</li>
)}
{!isMobile && (
<li>
<span>
Unrealized <span />
P/L
</span>
</li>
)}
{!isMobile && (
<li>
<span>
Realized <span />
P/L
</span>
</li>
)}
<li>
<span>
Total <span />
P/L
</span>
</li>
</ul>
)}
<div className={PositionStyles["MarketPositionsList__table-body"]}>
{tableOpen.myPositions &&
(myPositionOutcomes || [])
.filter(outcome => outcome.position)
.map(outcome => (
<MarketPositionsListPosition
key={`${outcome.id}${outcome.marketId}`}
outcomeName={outcome.outcome || outcome.name}
position={outcome.position}
openOrders={
outcome.userOpenOrders
? outcome.userOpenOrders.filter(
order =>
order.id === outcome.position.id &&
order.pending
)
: []
}
isExtendedDisplay
isMobile={isMobile}
outcome={outcome}
/>
))}
</div>
</div>
{tableOpen.myPositions &&
numCompleteSets &&
numCompleteSets.value > 0 && (
<div
className={PositionStyles.MarketPositionsList__completeSets}
style={{ paddingLeft: "0.5rem" }}
>
<span>{`You currently have ${
numCompleteSets.full
} of all outcomes.`}</span>
<button
onClick={e => {
sellCompleteSets(market.id, numCompleteSets, () => {});
}}
disabled={!!pendingCompleteSetsInfo}
>
{completeSetButtonText}
</button>
</div>
)}
</section>
<section className={Styles.MarketCard__tablesection}>
<div className={PositionStyles.MarketPositionsList__table}>
{((myPositionOutcomes || []).filter(
outcome => outcome.userOpenOrders.length > 0
).length > 0 ||
orphanedOrders.length > 0) && (
<button
className={Styles.MarketCard__headingcontainer}
onClick={() => this.toggleTable("openOrders")}
>
<h1 className={Styles.MarketCard__tableheading}>Open Orders</h1>
<div className={Styles.MarketCard__tabletoggle}>
<ChevronFlip pointDown={!tableOpen.openOrders} />
</div>
</button>
)}
<div className={PositionStyles.MarketPositionsList__table}>
{tableOpen.openOrders &&
((myPositionOutcomes || []).filter(
outcome => outcome.userOpenOrders.length > 0
).length > 0 ||
orphanedOrders.length > 0) && (
<ul
className={classNames(
PositionStyles["MarketPositionsList__table-header"],
Styles["MarketCard__table-header"]
)}
>
<li>Outcome</li>
<li />
{isMobile ? (
<li>
<span>Qty</span>
</li>
) : (
<li>
<span>Quantity</span>
</li>
)}
{isMobile ? (
<li>
<span>Avg</span>
</li>
) : (
<li>
<span>Avg Price</span>
</li>
)}
{!isMobile && (
<li>
<span>Last Price</span>
</li>
)}
{!isMobile && (
<li>
<span>Escrowed ETH</span>
</li>
)}
{!isMobile && (
<li>
<span>Escrowed Shares</span>
</li>
)}
<li className={Styles.MarketCard__hide}>
<span>
Total <span />
P/L
</span>
</li>
<li>
<span>Action</span>
</li>
</ul>
)}
<div
className={PositionStyles["MarketPositionsList__table-body"]}
>
{tableOpen.openOrders &&
(myPositionOutcomes || [])
.filter(outcome => outcome.userOpenOrders)
.map(outcome =>
outcome.userOpenOrders.map((order, i) => (
<MarketPositionsListOrder
key={order.id}
outcomeName={outcome.name}
order={order}
pending={order.pending}
isExtendedDisplay
isMobile={isMobile}
outcome={outcome}
/>
))
)}
{tableOpen.openOrders &&
(orphanedOrders || []).map(order => (
<MarketPositionsListOrphanedOrder
key={order.orderId}
outcomeName={order.outcomeName}
order={order}
pending={false}
isExtendedDisplay
isMobile={isMobile}
outcome={order}
isOrphaned
cancelOrphanedOrder={cancelOrphanedOrder}
/>
))}
</div>
</div>
</div>
</section>
{linkType &&
((linkType === TYPE_CLAIM_PROCEEDS && market.outstandingReturns) ||
linkType === TYPE_FINALIZE_MARKET) && (
<MarketPortfolioCardFooter
linkType={linkType}
localButtonText={localButtonText}
buttonAction={buttonAction}
outstandingReturns={market.outstandingReturns}
finalizationTime={market.finalizationTime}
currentTimestamp={currentTimestamp}
marketId={market.id}
claimClicked={linkType === TYPE_CLAIM_PROCEEDS && claimClicked}
disabled={disabled}
/>
)}
</article>
);
}
}