diff --git a/src/components/TraceConversationAction.jsx b/src/components/TraceConversationAction.jsx
index 1f2809f0c..18acd3414 100644
--- a/src/components/TraceConversationAction.jsx
+++ b/src/components/TraceConversationAction.jsx
@@ -1,5 +1,5 @@
/* eslint-disable react/prefer-stateless-function */
-// @dev: not prefering stateless here because functionality will be extended
+// @dev: not preferring stateless here because functionality will be extended
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
@@ -16,7 +16,7 @@ import LPTrace from '../models/LPTrace';
class TraceConversationAction extends Component {
render() {
- const { messageContext, trace, isAmountEnoughForWithdraw } = this.props;
+ const { messageContext, trace, isAmountEnoughForWithdraw, withdrawalTokens } = this.props;
switch (messageContext) {
case 'proposed':
@@ -38,6 +38,7 @@ class TraceConversationAction extends Component {
);
@@ -68,6 +69,11 @@ TraceConversationAction.propTypes = {
).isRequired,
messageContext: PropTypes.string.isRequired,
isAmountEnoughForWithdraw: PropTypes.bool.isRequired,
+ withdrawalTokens: PropTypes.arrayOf(PropTypes.shape({})),
+};
+
+TraceConversationAction.defaultProps = {
+ withdrawalTokens: [],
};
export default React.memo(TraceConversationAction);
diff --git a/src/components/TraceConversationItem.jsx b/src/components/TraceConversationItem.jsx
index 63a7ab96b..2f6558bca 100644
--- a/src/components/TraceConversationItem.jsx
+++ b/src/components/TraceConversationItem.jsx
@@ -143,7 +143,12 @@ const getEtherScanUrl = ({ messageContext }) =>
? config.homeEtherscan
: config.etherscan;
-function TraceConversationItem({ conversation, trace, isAmountEnoughForWithdraw }) {
+function TraceConversationItem({
+ conversation,
+ trace,
+ isAmountEnoughForWithdraw,
+ withdrawalTokens,
+}) {
if (!conversation) return null;
const { txHash, messageContext, message, performedByRole, createdAt, owner } = conversation;
@@ -180,6 +185,7 @@ function TraceConversationItem({ conversation, trace, isAmountEnoughForWithdraw
messageContext={messageContext}
trace={trace}
isAmountEnoughForWithdraw={isAmountEnoughForWithdraw}
+ withdrawalTokens={withdrawalTokens}
/>
@@ -195,6 +201,11 @@ TraceConversationItem.propTypes = {
).isRequired,
conversation: PropTypes.instanceOf(Object).isRequired,
isAmountEnoughForWithdraw: PropTypes.bool.isRequired,
+ withdrawalTokens: PropTypes.arrayOf(PropTypes.shape({})),
+};
+
+TraceConversationItem.defaultProps = {
+ withdrawalTokens: [],
};
export default React.memo(TraceConversationItem);
diff --git a/src/components/TraceConversations.jsx b/src/components/TraceConversations.jsx
index 50fa16268..bdb4ee1b4 100644
--- a/src/components/TraceConversations.jsx
+++ b/src/components/TraceConversations.jsx
@@ -12,7 +12,7 @@ import BridgedTrace from '../models/BridgedTrace';
import LPPCappedTrace from '../models/LPPCappedTrace';
import LPTrace from '../models/LPTrace';
-const TraceConversations = ({ trace, maxHeight, isAmountEnoughForWithdraw }) => {
+const TraceConversations = ({ trace, maxHeight, isAmountEnoughForWithdraw, withdrawalTokens }) => {
const conversationsNumPerLoad = 5;
const [conversations, setConversations] = useState({});
const [isLoading, setLoading] = useState(true);
@@ -66,6 +66,7 @@ const TraceConversations = ({ trace, maxHeight, isAmountEnoughForWithdraw }) =>
conversation={conversation}
trace={trace}
isAmountEnoughForWithdraw={isAmountEnoughForWithdraw}
+ withdrawalTokens={withdrawalTokens}
/>
))}
@@ -85,10 +86,12 @@ TraceConversations.propTypes = {
).isRequired,
maxHeight: PropTypes.string,
isAmountEnoughForWithdraw: PropTypes.bool.isRequired,
+ withdrawalTokens: PropTypes.arrayOf(PropTypes.shape({})),
};
TraceConversations.defaultProps = {
maxHeight: 'unset',
+ withdrawalTokens: [],
};
export default React.memo(TraceConversations);
diff --git a/src/components/ViewTraceAlerts.jsx b/src/components/ViewTraceAlerts.jsx
index ca76570a8..e06bbea85 100644
--- a/src/components/ViewTraceAlerts.jsx
+++ b/src/components/ViewTraceAlerts.jsx
@@ -18,7 +18,7 @@ import BridgedTrace from '../models/BridgedTrace';
import LPPCappedTrace from '../models/LPPCappedTrace';
import LPTrace from '../models/LPTrace';
-const ViewTraceAlerts = ({ trace, campaign, isAmountEnoughForWithdraw }) => {
+const ViewTraceAlerts = ({ trace, campaign, isAmountEnoughForWithdraw, withdrawalTokens }) => {
const {
state: { currentUser, userIsCommunityOwner },
} = useContext(UserContext);
@@ -92,6 +92,7 @@ const ViewTraceAlerts = ({ trace, campaign, isAmountEnoughForWithdraw }) => {
)}
@@ -105,6 +106,11 @@ ViewTraceAlerts.propTypes = {
).isRequired,
campaign: PropTypes.instanceOf(Campaign).isRequired,
isAmountEnoughForWithdraw: PropTypes.bool.isRequired,
+ withdrawalTokens: PropTypes.arrayOf(PropTypes.shape({})),
+};
+
+ViewTraceAlerts.defaultProps = {
+ withdrawalTokens: [],
};
export default React.memo(ViewTraceAlerts);
diff --git a/src/components/WithdrawTraceFundsButton.jsx b/src/components/WithdrawTraceFundsButton.jsx
index 606166924..8c552fd48 100644
--- a/src/components/WithdrawTraceFundsButton.jsx
+++ b/src/components/WithdrawTraceFundsButton.jsx
@@ -1,16 +1,16 @@
-import React, { Fragment, useContext } from 'react';
+import React, { Fragment, useContext, useRef } from 'react';
import PropTypes from 'prop-types';
+import { Modal, Select } from 'antd';
import TraceService from 'services/TraceService';
import Trace from 'models/Trace';
import { authenticateUser, checkBalance } from 'lib/middleware';
-import { Modal } from 'antd';
import { Context as Web3Context } from '../contextProviders/Web3Provider';
import { Context as NotificationContext } from '../contextProviders/NotificationModalProvider';
+import { Context as UserContext } from '../contextProviders/UserProvider';
import DonationBlockchainService from '../services/DonationBlockchainService';
import LPTrace from '../models/LPTrace';
import config from '../configuration';
-import { Context as UserContext } from '../contextProviders/UserProvider';
import ErrorHandler from '../lib/ErrorHandler';
import BridgedTrace from '../models/BridgedTrace';
import LPPCappedTrace from '../models/LPPCappedTrace';
@@ -21,7 +21,7 @@ import {
} from '../services/ConversionRateService';
import { displayTransactionError, txNotification } from '../lib/helpers';
-const WithdrawTraceFundsButton = ({ trace, isAmountEnoughForWithdraw }) => {
+const WithdrawTraceFundsButton = ({ trace, isAmountEnoughForWithdraw, withdrawalTokens }) => {
const {
state: { currentUser },
} = useContext(UserContext);
@@ -33,6 +33,8 @@ const WithdrawTraceFundsButton = ({ trace, isAmountEnoughForWithdraw }) => {
actions: { minPayoutWarningInWithdraw },
} = useContext(NotificationContext);
+ const selectedTokens = useRef([]);
+
async function sendWithdrawAnalyticsEvent(txUrl) {
const donationsCounters = trace.donationCounters.filter(dc => dc.currentBalance.gt(0));
// eslint-disable-next-line no-restricted-syntax
@@ -85,6 +87,15 @@ const WithdrawTraceFundsButton = ({ trace, isAmountEnoughForWithdraw }) => {
minPayoutWarningInWithdraw();
return;
}
+
+ let defaultValue;
+ if (withdrawalTokens.length === 1) {
+ const token = withdrawalTokens[0];
+ // eslint-disable-next-line react/prop-types
+ defaultValue = token.address;
+ selectedTokens.current = defaultValue;
+ }
+
Modal.confirm({
title: isRecipient ? 'Withdrawal Funds to Wallet' : 'Disburse Funds to Recipient',
content: (
@@ -111,16 +122,39 @@ const WithdrawTraceFundsButton = ({ trace, isAmountEnoughForWithdraw }) => {
{isRecipient ? 'your' : "the recipient's"} wallet.
)}
+
+
Select tokens to withdraw:
+
),
cancelText: 'Cancel',
- okText: 'Yes, withdrawal',
+ okText: 'Withdrawal',
centered: true,
width: 500,
onOk: () =>
TraceService.withdraw({
+ web3,
trace,
from: userAddress,
+ selectedTokens: selectedTokens.current,
onTxHash: txUrl => {
sendWithdrawAnalyticsEvent(txUrl);
txNotification('Initiating withdrawal from Trace...', txUrl, true);
@@ -137,7 +171,6 @@ const WithdrawTraceFundsButton = ({ trace, isAmountEnoughForWithdraw }) => {
// TODO: need to update feathers to reset the donations to previous state as this
else displayTransactionError(txUrl);
},
- web3,
}),
});
})
@@ -170,6 +203,11 @@ WithdrawTraceFundsButton.propTypes = {
[Trace, BridgedTrace, LPPCappedTrace, LPTrace].map(PropTypes.instanceOf),
).isRequired,
isAmountEnoughForWithdraw: PropTypes.bool.isRequired,
+ withdrawalTokens: PropTypes.arrayOf(PropTypes.shape({})),
+};
+
+WithdrawTraceFundsButton.defaultProps = {
+ withdrawalTokens: [],
};
export default React.memo(WithdrawTraceFundsButton);
diff --git a/src/components/views/ViewTrace.jsx b/src/components/views/ViewTrace.jsx
index 9981578ab..d4bdb67b9 100644
--- a/src/components/views/ViewTrace.jsx
+++ b/src/components/views/ViewTrace.jsx
@@ -71,15 +71,16 @@ const ViewTrace = props => {
const [trace, setTrace] = useState({});
const [communityTitle, setCommunityTitle] = useState('');
const [notFound, setNotFound] = useState(false);
- const [isAmountEnoughForWithdraw, setIsAmountEnoughForWithdraw] = useState(true);
const [currency, setCurrency] = useState(null);
const [currentBalanceValue, setCurrentBalanceValue] = useState(0);
const [currentBalanceUsdValue, setCurrentBalanceUsdValue] = useState(0);
+ const [withdrawalTokens, setWithdrawalTokens] = useState([]);
const donationsObserver = useRef();
const traceSubscription = useRef();
const newDonations = useRef(0);
const donationsPerBatch = 50;
+ const isAmountEnoughForWithdraw = withdrawalTokens.length > 0;
const getCommunityTitle = async communityId => {
if (communityId === 0) return;
@@ -196,16 +197,20 @@ const ViewTrace = props => {
if (!currentBalanceUsdValue) {
return;
}
+ const _withdrawalTokens = [];
// eslint-disable-next-line no-restricted-syntax
for (const currencyUsdValue of currentBalanceUsdValue) {
- // if usdValue is zero we should not set setIsAmountEnoughForWithdraw(false) because we check
- // minimumPayoutUsdValue comparison when usdValue for a currency is not zero
- if (currencyUsdValue.usdValue && currencyUsdValue.usdValue < minimumPayoutUsdValue) {
- setIsAmountEnoughForWithdraw(false);
- return;
+ if (currencyUsdValue.usdValue >= minimumPayoutUsdValue) {
+ const token = activeTokenWhitelist.find(
+ _token => _token.symbol === currencyUsdValue.currency,
+ );
+ _withdrawalTokens.push(token);
}
}
- setIsAmountEnoughForWithdraw(true);
+
+ if (_withdrawalTokens.length) {
+ setWithdrawalTokens(_withdrawalTokens);
+ }
}, [currentBalanceUsdValue]);
const isActiveTrace = () => {
@@ -380,6 +385,7 @@ const ViewTrace = props => {
trace={trace}
campaign={campaign}
isAmountEnoughForWithdraw={isAmountEnoughForWithdraw}
+ withdrawalTokens={withdrawalTokens}
/>
@@ -624,6 +630,7 @@ const ViewTrace = props => {
trace={trace}
isAmountEnoughForWithdraw={isAmountEnoughForWithdraw}
maxHeight={`${detailsCardHeight}px`}
+ withdrawalTokens={withdrawalTokens}
/>
diff --git a/src/components/views/verification/Main.jsx b/src/components/views/verification/Main.jsx
index 00c3d20a4..aff8f12f2 100644
--- a/src/components/views/verification/Main.jsx
+++ b/src/components/views/verification/Main.jsx
@@ -82,8 +82,7 @@ const Verification = props => {
setStep(step + 1);
})
.catch(err => {
- if (err.message) ErrorHandler(err, err.message);
- else ErrorHandler(err, 'Something went wrong!');
+ ErrorHandler(err, err.message);
})
.finally(() => setIsSaving(false));
};
diff --git a/src/services/DonationBlockchainService.jsx b/src/services/DonationBlockchainService.jsx
index 595bf1a80..59f01c2e2 100644
--- a/src/services/DonationBlockchainService.jsx
+++ b/src/services/DonationBlockchainService.jsx
@@ -839,7 +839,7 @@ class DonationBlockchainService {
.then(({ total }) => total);
}
- static async getTraceDonations(traceId) {
+ static async getTraceDonations(traceId, selectedTokens) {
const service = feathersClient.service('/donations');
let data = [];
let total;
@@ -857,6 +857,11 @@ class DonationBlockchainService {
$limit: spare || 1,
$sort: { tokenAddress: 1, pledgeId: 1 }, // group by token
};
+
+ if (selectedTokens) {
+ query.tokenAddress = { $in: selectedTokens };
+ }
+
// eslint-disable-next-line no-await-in-loop
const resp = await service.find({ query });
diff --git a/src/styles/_antOverrides.scss b/src/styles/_antOverrides.scss
index 7eff2ec2f..6b3f2e3c1 100644
--- a/src/styles/_antOverrides.scss
+++ b/src/styles/_antOverrides.scss
@@ -146,6 +146,17 @@
}
}
+//Ant custom multi select with ant-select-custom-multiple class
+.ant-select-custom-multiple {
+ .ant-select-selection-overflow {
+ margin-top: -3px;
+ }
+ .ant-select-selection-item {
+ line-height: 30px !important;
+ height: 30px;
+ }
+}
+
.ant-picker {
width: 100%;
border: 2px solid #dfdae8;