import React, { useState, useContext } from 'react';
import PropTypes from 'prop-types';
import { useSelector, useDispatch } from 'react-redux';
import { chain } from 'lodash';
import {
addImportedTokens,
ignoreTokens,
setNewTokensImported,
} from '../../../store/actions';
import {
getCurrentChainId,
getDetectedTokensInCurrentNetwork,
} from '../../../selectors';
import { MetaMetricsContext } from '../../../contexts/metametrics';
import {
AssetType,
TokenStandard,
} from '../../../../shared/constants/transaction';
import {
MetaMetricsEventCategory,
MetaMetricsEventLocation,
MetaMetricsEventName,
MetaMetricsTokenEventSource,
} from '../../../../shared/constants/metametrics';
import DetectedTokenSelectionPopover from './detected-token-selection-popover/detected-token-selection-popover';
import DetectedTokenIgnoredPopover from './detected-token-ignored-popover/detected-token-ignored-popover';
const sortingBasedOnTokenSelection = (tokensDetected) => {
return (
chain(tokensDetected)
// get the values
.values()
// create a new object with keys 'selected', 'deselected' and group the tokens
.groupBy((token) => (token.selected ? 'selected' : 'deselected'))
// ditch the 'selected' property and get just the tokens'
.mapValues((group) =>
group.map(({ token }) => {
const { address, symbol, decimals, aggregators } = token;
return { address, symbol, decimals, aggregators };
}),
)
// Exit the chain and get the underlying value, an object.
.value()
);
};
const DetectedToken = ({ setShowDetectedTokens }) => {
const dispatch = useDispatch();
const trackEvent = useContext(MetaMetricsContext);
const chainId = useSelector(getCurrentChainId);
const detectedTokens = useSelector(getDetectedTokensInCurrentNetwork);
const [tokensListDetected, setTokensListDetected] = useState(() =>
detectedTokens.reduce((tokenObj, token) => {
tokenObj[token.address] = { token, selected: true };
return tokenObj;
}, {}),
);
const [showDetectedTokenIgnoredPopover, setShowDetectedTokenIgnoredPopover] =
useState(false);
const [partiallyIgnoreDetectedTokens, setPartiallyIgnoreDetectedTokens] =
useState(false);
const importSelectedTokens = async (selectedTokens) => {
selectedTokens.forEach((importedToken) => {
trackEvent({
event: MetaMetricsEventName.TokenAdded,
category: MetaMetricsEventCategory.Wallet,
sensitiveProperties: {
token_symbol: importedToken.symbol,
token_contract_address: importedToken.address,
token_decimal_precision: importedToken.decimals,
source_connection_method: MetaMetricsTokenEventSource.Detected,
token_standard: TokenStandard.ERC20,
asset_type: AssetType.token,
token_added_type: 'detected',
chain_id: chainId,
},
});
});
await dispatch(addImportedTokens(selectedTokens));
const tokenSymbols = selectedTokens.map(({ symbol }) => symbol);
dispatch(setNewTokensImported(tokenSymbols.join(', ')));
};
const handleClearTokensSelection = async () => {
const { selected: selectedTokens = [], deselected: deSelectedTokens = [] } =
sortingBasedOnTokenSelection(tokensListDetected);
if (deSelectedTokens.length < detectedTokens.length) {
await importSelectedTokens(selectedTokens);
}
const tokensDetailsList = deSelectedTokens.map(
({ symbol, address }) => `${symbol} - ${address}`,
);
trackEvent({
event: MetaMetricsEventName.TokenHidden,
category: MetaMetricsEventCategory.Wallet,
sensitiveProperties: {
tokens: tokensDetailsList,
location: MetaMetricsEventLocation.TokenDetection,
token_standard: TokenStandard.ERC20,
asset_type: AssetType.token,
},
});
const deSelectedTokensAddresses = deSelectedTokens.map(
({ address }) => address,
);
await dispatch(
ignoreTokens({
tokensToIgnore: deSelectedTokensAddresses,
dontShowLoadingIndicator: true,
}),
);
setShowDetectedTokens(false);
setPartiallyIgnoreDetectedTokens(false);
};
const handleTokenSelection = (token) => {
setTokensListDetected((prevState) => ({
...prevState,
[token.address]: {
...prevState[token.address],
selected: !prevState[token.address].selected,
},
}));
};
const onImport = async () => {
const { selected: selectedTokens = [] } =
sortingBasedOnTokenSelection(tokensListDetected);
if (selectedTokens.length < detectedTokens.length) {
setShowDetectedTokenIgnoredPopover(true);
setPartiallyIgnoreDetectedTokens(true);
} else {
await importSelectedTokens(selectedTokens);
setShowDetectedTokens(false);
}
};
const onIgnoreAll = () => {
const newTokensListDetected = { ...tokensListDetected };
for (const tokenAddress of Object.keys(tokensListDetected)) {
newTokensListDetected[tokenAddress].selected = false;
}
setTokensListDetected(newTokensListDetected);
setShowDetectedTokenIgnoredPopover(true);
};
const onCancelIgnore = () => {
setShowDetectedTokenIgnoredPopover(false);
setPartiallyIgnoreDetectedTokens(false);
};
return (
<>
{showDetectedTokenIgnoredPopover && (
)}
{detectedTokens.length > 0 && (
)}
>
);
};
DetectedToken.propTypes = {
setShowDetectedTokens: PropTypes.func.isRequired,
};
export default DetectedToken;