import React, {
useEffect,
useRef,
useState,
useCallback,
useContext,
} from 'react';
import {
RefreshControl,
ScrollView,
InteractionManager,
ActivityIndicator,
StyleSheet,
View,
} from 'react-native';
import { useSelector } from 'react-redux';
import ScrollableTabView from 'react-native-scrollable-tab-view';
import DefaultTabBar from 'react-native-scrollable-tab-view/DefaultTabBar';
import { fontStyles, baseStyles } from '../../../styles/common';
import AccountOverview from '../../UI/AccountOverview';
import Tokens from '../../UI/Tokens';
import { getWalletNavbarOptions } from '../../UI/Navbar';
import { strings } from '../../../../locales/i18n';
import { renderFromWei, weiToFiat, hexToBN } from '../../../util/number';
import Engine from '../../../core/Engine';
import CollectibleContracts from '../../UI/CollectibleContracts';
import Analytics from '../../../core/Analytics/Analytics';
import { ANALYTICS_EVENT_OPTS } from '../../../util/analytics';
import { getTicker } from '../../../util/transactions';
import OnboardingWizard from '../../UI/OnboardingWizard';
import ErrorBoundary from '../ErrorBoundary';
import { DrawerContext } from '../../Nav/Main/MainNavigator';
import { useAppThemeFromContext, mockTheme } from '../../../util/theme';
import { shouldShowWhatsNewModal } from '../../../util/onboarding';
import Logger from '../../../util/Logger';
import Routes from '../../../constants/navigation/Routes';
const createStyles = (colors: any) =>
StyleSheet.create({
wrapper: {
flex: 1,
backgroundColor: colors.background.default,
},
tabUnderlineStyle: {
height: 2,
backgroundColor: colors.primary.default,
},
tabStyle: {
paddingBottom: 0,
},
tabBar: {
borderColor: colors.border.muted,
},
textStyle: {
fontSize: 12,
letterSpacing: 0.5,
...(fontStyles.bold as any),
},
loader: {
backgroundColor: colors.background.default,
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
/**
* Main view for the wallet
*/
const Wallet = ({ navigation }: any) => {
const { drawerRef } = useContext(DrawerContext);
const [refreshing, setRefreshing] = useState(false);
const accountOverviewRef = useRef(null);
const { colors } = useAppThemeFromContext() || mockTheme;
const styles = createStyles(colors);
/**
* Map of accounts to information objects including balances
*/
const accounts = useSelector(
(state: any) =>
state.engine.backgroundState.AccountTrackerController.accounts,
);
/**
* ETH to current currency conversion rate
*/
const conversionRate = useSelector(
(state: any) =>
state.engine.backgroundState.CurrencyRateController.conversionRate,
);
/**
* Currency code of the currently-active currency
*/
const currentCurrency = useSelector(
(state: any) =>
state.engine.backgroundState.CurrencyRateController.currentCurrency,
);
/**
* An object containing each identity in the format address => account
*/
const identities = useSelector(
(state: any) =>
state.engine.backgroundState.PreferencesController.identities,
);
/**
* A string that represents the selected address
*/
const selectedAddress = useSelector(
(state: any) =>
state.engine.backgroundState.PreferencesController.selectedAddress,
);
/**
* An array that represents the user tokens
*/
const tokens = useSelector(
(state: any) => state.engine.backgroundState.TokensController.tokens,
);
/**
* Current provider ticker
*/
const ticker = useSelector(
(state: any) =>
state.engine.backgroundState.NetworkController.provider.ticker,
);
/**
* Current onboarding wizard step
*/
const wizardStep = useSelector((state: any) => state.wizard.step);
const { colors: themeColors } = useAppThemeFromContext() || mockTheme;
/**
* Check to see if we need to show What's New modal
*/
useEffect(() => {
if (wizardStep > 0) {
// Do not check since it will conflict with the onboarding wizard
return;
}
const checkWhatsNewModal = async () => {
try {
const shouldShowWhatsNew = await shouldShowWhatsNewModal();
if (shouldShowWhatsNew) {
navigation.navigate(Routes.MODAL.ROOT_MODAL_FLOW, {
screen: Routes.MODAL.WHATS_NEW,
});
}
} catch (error) {
Logger.log(error, "Error while checking What's New modal!");
}
};
checkWhatsNewModal();
}, [wizardStep, navigation]);
useEffect(
() => {
requestAnimationFrame(async () => {
const {
TokenDetectionController,
CollectibleDetectionController,
AccountTrackerController,
} = Engine.context as any;
TokenDetectionController.detectTokens();
CollectibleDetectionController.detectCollectibles();
AccountTrackerController.refresh();
});
},
/* eslint-disable-next-line */
[navigation],
);
useEffect(() => {
navigation.setOptions(
getWalletNavbarOptions(
'wallet.title',
navigation,
drawerRef,
themeColors,
),
);
/* eslint-disable-next-line */
}, [navigation, themeColors]);
const onRefresh = useCallback(async () => {
requestAnimationFrame(async () => {
setRefreshing(true);
const {
TokenDetectionController,
CollectibleDetectionController,
AccountTrackerController,
CurrencyRateController,
TokenRatesController,
} = Engine.context as any;
const actions = [
TokenDetectionController.detectTokens(),
CollectibleDetectionController.detectCollectibles(),
AccountTrackerController.refresh(),
CurrencyRateController.start(),
TokenRatesController.poll(),
];
await Promise.all(actions);
setRefreshing(false);
});
}, [setRefreshing]);
const renderTabBar = useCallback(
() => (
),
[styles, colors],
);
const onChangeTab = useCallback((obj) => {
InteractionManager.runAfterInteractions(() => {
if (obj.ref.props.tabLabel === strings('wallet.tokens')) {
Analytics.trackEvent(ANALYTICS_EVENT_OPTS.WALLET_TOKENS);
} else {
Analytics.trackEvent(ANALYTICS_EVENT_OPTS.WALLET_COLLECTIBLES);
}
});
}, []);
const onRef = useCallback((ref) => {
accountOverviewRef.current = ref;
}, []);
const renderContent = useCallback(() => {
let balance: any = 0;
let assets = tokens;
if (accounts[selectedAddress]) {
balance = renderFromWei(accounts[selectedAddress].balance);
assets = [
{
name: 'Ether', // FIXME: use 'Ether' for mainnet only, what should it be for custom networks?
symbol: getTicker(ticker),
isETH: true,
balance,
balanceFiat: weiToFiat(
hexToBN(accounts[selectedAddress].balance) as any,
conversionRate,
currentCurrency,
),
logo: '../images/eth-logo.png',
},
...(tokens || []),
];
} else {
assets = tokens;
}
const account = {
address: selectedAddress,
...identities[selectedAddress],
...accounts[selectedAddress],
};
return (
);
}, [
renderTabBar,
accounts,
conversionRate,
currentCurrency,
identities,
navigation,
onChangeTab,
onRef,
selectedAddress,
ticker,
tokens,
styles,
]);
const renderLoader = useCallback(
() => (
),
[styles],
);
/**
* Return current step of onboarding wizard if not step 5 nor 0
*/
const renderOnboardingWizard = useCallback(
() =>
[1, 2, 3, 4].includes(wizardStep) && (
),
[navigation, wizardStep],
);
return (
}
>
{selectedAddress ? renderContent() : renderLoader()}
{renderOnboardingWizard()}
);
};
export default Wallet;