From 7a39881332ef19149215b888a3c2664c111cc1cf Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Tue, 26 Nov 2024 17:49:50 -0800 Subject: [PATCH] Split data+logic for Portfolio vs Discover --- src/components/scenes/Staking/EarnScene.tsx | 234 ++++++++++---------- 1 file changed, 119 insertions(+), 115 deletions(-) diff --git a/src/components/scenes/Staking/EarnScene.tsx b/src/components/scenes/Staking/EarnScene.tsx index 85eefe6dbd0..4205c94355e 100644 --- a/src/components/scenes/Staking/EarnScene.tsx +++ b/src/components/scenes/Staking/EarnScene.tsx @@ -27,24 +27,23 @@ import { cacheStyles, Theme, useTheme } from '../../services/ThemeContext' interface Props extends EdgeAppSceneProps<'earnScene'> {} let USERNAME: string | undefined -let STAKE_POLICY_MAP: StakePolicyMap = {} +let DISCOVER_ITEMS: DiscoverStakeInfo[] = [] +let PORTFOLIO_ITEMS: PortfolioStakeInfo[] = [] -export interface EarnSceneParams {} - -interface WalletStakeInfo { - wallet: EdgeCurrencyWallet - isPositionOpen: boolean - stakePosition: StakePosition -} - -interface DisplayStakeInfo { +interface DiscoverStakeInfo { + pluginId: string stakePlugin: StakePlugin stakePolicy: StakePolicy +} + +interface PortfolioStakeInfo extends DiscoverStakeInfo { walletStakeInfos: WalletStakeInfo[] } -interface StakePolicyMap { - [pluginId: string]: DisplayStakeInfo[] +interface WalletStakeInfo { + wallet: EdgeCurrencyWallet + isPositionOpen: boolean + stakePosition: StakePosition } export const EarnScene = (props: Props) => { @@ -56,170 +55,176 @@ export const EarnScene = (props: Props) => { if (USERNAME !== account.username) { // Reset local variable if user changes USERNAME = account.username - STAKE_POLICY_MAP = {} + DISCOVER_ITEMS = [] + PORTFOLIO_ITEMS = [] } const currencyConfigMap = useSelector(state => state.core.account.currencyConfig) - const currencyWallets = useWatch(account, 'currencyWallets') const wallets = Object.values(currencyWallets) const [isPortfolioSelected, setIsPortfolioSelected] = React.useState(false) const [isLoadingDiscover, setIsLoadingDiscover] = React.useState(true) const [isLoadingPortfolio, setIsLoadingPortfolio] = React.useState(true) - const [isPrevFocused, setIsPrevFocused] = React.useState(true) + const [isPrevFocused, setIsPrevFocused] = React.useState() const handleSelectEarn = useHandler(() => setIsPortfolioSelected(false)) const handleSelectPortfolio = useHandler(() => setIsPortfolioSelected(true)) const isFocused = useIsFocused() - const refreshStakePositions = async (pluginId: string): Promise => { - const isStakingSupported = SPECIAL_CURRENCY_INFO[pluginId]?.isStakingSupported === true && ENV.ENABLE_STAKING - if (!isStakingSupported || STAKE_POLICY_MAP[pluginId] == null) return [] - - const matchingWallets = wallets.filter((wallet: EdgeCurrencyWallet) => wallet.currencyInfo.pluginId === pluginId) - const updatedDisplayStakeInfos = [] - for (const displayStakeInfo of STAKE_POLICY_MAP[pluginId]) { - const { stakePlugin, stakePolicy } = displayStakeInfo - - const walletStakePositions = [] - for (const wallet of matchingWallets) { - try { - // Determine if a wallet matching this policy has an open position - const stakePosition = await stakePlugin.fetchStakePosition({ stakePolicyId: stakePolicy.stakePolicyId, wallet, account }) - const allocations = getPositionAllocations(stakePosition) - const { staked, earned, unstaked } = allocations - const isPositionOpen = [...staked, ...earned, ...unstaked].some(positionAllocation => !zeroString(positionAllocation.nativeAmount)) - - walletStakePositions.push({ wallet, isPositionOpen, stakePosition }) - } catch (e) { - showDevError(e) - } - } - - // Create a new displayStakeInfo object - updatedDisplayStakeInfos.push({ - stakePlugin, - stakePolicy, - walletStakeInfos: walletStakePositions - }) - } - - return updatedDisplayStakeInfos - } - useAsyncEffect( async () => { - for (const pluginId of Object.keys(currencyConfigMap)) { + const pluginIds = Object.keys(currencyConfigMap) + DISCOVER_ITEMS = [] + + for (const pluginId of pluginIds) { const isStakingSupported = SPECIAL_CURRENCY_INFO[pluginId]?.isStakingSupported === true && ENV.ENABLE_STAKING - if (STAKE_POLICY_MAP[pluginId] != null || !isStakingSupported) continue + if (!isStakingSupported) continue - // Initialize stake policy const stakePlugins = await getStakePlugins(pluginId) - STAKE_POLICY_MAP[pluginId] = [] - const matchingWallets = wallets.filter((wallet: EdgeCurrencyWallet) => wallet.currencyInfo.pluginId === pluginId) for (const stakePlugin of stakePlugins) { const stakePolicies = stakePlugin.getPolicies({ pluginId }).filter(stakePolicy => !stakePolicy.deprecated) for (const stakePolicy of stakePolicies) { - const walletStakePositions = [] - for (const wallet of matchingWallets) { - try { - // Determine if a wallet matching this policy has an open position - const stakePosition = await stakePlugin.fetchStakePosition({ stakePolicyId: stakePolicy.stakePolicyId, wallet, account }) - const allocations = getPositionAllocations(stakePosition) - const { staked, earned, unstaked } = allocations - const isPositionOpen = [...staked, ...earned, ...unstaked].some(positionAllocation => !zeroString(positionAllocation.nativeAmount)) - - walletStakePositions.push({ wallet, isPositionOpen, stakePosition }) - } catch (e) { - showDevError(e) - } - } - - STAKE_POLICY_MAP[pluginId].push({ + DISCOVER_ITEMS.push({ + pluginId, stakePlugin, - stakePolicy, - walletStakeInfos: walletStakePositions + stakePolicy }) } } } - setIsLoadingPortfolio(false) setIsLoadingDiscover(false) }, [], - 'EarnScene Initialize STAKE_POLICY_MAP' + 'EarnScene Initialize Discover Items' ) - // Refresh stake positions when re-entering the scene + // Refresh stake positions when re-entering the scene or on initial load useAsyncEffect( async () => { - if (isFocused && !isPrevFocused) { + if (!isLoadingDiscover || (isFocused && !isPrevFocused)) { setIsLoadingPortfolio(true) + PORTFOLIO_ITEMS = [] + + for (const discoverInfo of DISCOVER_ITEMS) { + const { pluginId, stakePlugin, stakePolicy } = discoverInfo + console.debug(`refreshing stake positions for ${pluginId} ${stakePolicy.stakePolicyId}`) + const matchingWallets = wallets.filter((wallet: EdgeCurrencyWallet) => wallet.currencyInfo.pluginId === pluginId) + const walletStakeInfos = [] + + for (const wallet of matchingWallets) { + try { + const stakePosition = await stakePlugin.fetchStakePosition({ + stakePolicyId: stakePolicy.stakePolicyId, + wallet, + account + }) + const allocations = getPositionAllocations(stakePosition) + const { staked, earned, unstaked } = allocations + const isPositionOpen = [...staked, ...earned, ...unstaked].some(positionAllocation => !zeroString(positionAllocation.nativeAmount)) + + if (isPositionOpen) { + walletStakeInfos.push({ wallet, isPositionOpen, stakePosition }) + } + } catch (e) { + showDevError(e) + } + } - for (const pluginId of Object.keys(currencyConfigMap)) { - const newDisplayStakeInfos = await refreshStakePositions(pluginId) - STAKE_POLICY_MAP[pluginId] = newDisplayStakeInfos + if (walletStakeInfos.length > 0) { + PORTFOLIO_ITEMS.push({ + ...discoverInfo, + walletStakeInfos + }) + } } setIsLoadingPortfolio(false) } - setIsPrevFocused(isFocused) }, - [isFocused], - 'EarnScene Refresh Stake Positions' + [isFocused, isLoadingDiscover], + 'EarnScene Refresh Portfolio Data' ) - const renderStakeItems = (displayStakeInfo: DisplayStakeInfo, currencyInfo: EdgeCurrencyInfo) => { - const { stakePlugin, stakePolicy, walletStakeInfos } = displayStakeInfo + const renderDiscoverItem = (discoverStakeInfo: DiscoverStakeInfo, currencyInfo: EdgeCurrencyInfo) => { + const { stakePlugin, stakePolicy } = discoverStakeInfo - const openStakePositions = walletStakeInfos.filter(walletStakeInfo => walletStakeInfo.isPositionOpen) + const handlePress = async () => { + let walletId: string | undefined - if (isPortfolioSelected && openStakePositions.length === 0) { - return null + const matchingWallets = wallets.filter((wallet: EdgeCurrencyWallet) => wallet.currencyInfo.pluginId === currencyInfo.pluginId) + if (matchingWallets.length === 1) { + // Only one compatible wallet, auto-select it + const wallet = matchingWallets[0] + walletId = wallet.id + } else { + // Select an existing wallet that matches this policy or create a new one + const allowedAssets = stakePolicy.stakeAssets.map(stakeAsset => ({ pluginId: stakeAsset.pluginId, tokenId: null })) + + const result = await Airship.show(bridge => ( + + )) + + if (result?.type === 'wallet') { + walletId = result.walletId + } + } + + // User backed out of the WalletListModal + if (walletId == null) return + + navigation.push('stakeOverview', { + walletId, + stakePlugin, + stakePolicy, + // 'stakeOverview' scene will fetch the position if one exists. + // No need to know if a position exists at this point. + stakePosition: undefined + }) } + return ( + + + + ) + } + + const renderPortfolioItem = (portfolioStakeInfo: PortfolioStakeInfo, currencyInfo: EdgeCurrencyInfo) => { + const { stakePlugin, stakePolicy, walletStakeInfos } = portfolioStakeInfo + const handlePress = async () => { let walletId: string | undefined let stakePosition const matchingWallets = wallets.filter((wallet: EdgeCurrencyWallet) => wallet.currencyInfo.pluginId === currencyInfo.pluginId) if (matchingWallets.length === 1) { - // Only one compatible wallet if on "Discover", or only one open - // position on "Portfolio." Auto-select the wallet. - const { wallet, stakePosition: existingStakePosition } = walletStakeInfos[0] ?? { - // It's possible that the wallet was created on this scene previously, - // and when re-navigating back here, the STAKE_POLICY_MAP has not - // finished updating. The `StakeOverviewScene` will handle refreshing - // the position, if any. - wallet: matchingWallets[0], - stakePosition: undefined - } - + // Only one wallet with an open position, auto-select it + const { wallet, stakePosition: existingStakePosition } = walletStakeInfos[0] walletId = wallet.id stakePosition = existingStakePosition } else { - // Select an existing wallet that matches this policy or create a new one - const allowedAssets = stakePolicy.stakeAssets.map(stakeAsset => ({ pluginId: stakeAsset.pluginId, tokenId: null })) - - // Filter for wallets that have an open position if "Portfolio" is - // selected - const allowedPortfolioWalletIds = isPortfolioSelected - ? walletStakeInfos.filter(walletStakeInfo => walletStakeInfo.isPositionOpen).map(walletStakePosition => walletStakePosition.wallet.id) - : undefined + // Select from wallets that have an open position + const allowedWalletIds = walletStakeInfos + .filter(walletStakeInfo => walletStakeInfo.isPositionOpen) + .map(walletStakePosition => walletStakePosition.wallet.id) const result = await Airship.show(bridge => ( )) @@ -243,7 +248,7 @@ export const EarnScene = (props: Props) => { return ( - + ) } @@ -252,9 +257,8 @@ export const EarnScene = (props: Props) => { - {Object.keys(STAKE_POLICY_MAP).map(pluginId => - STAKE_POLICY_MAP[pluginId].map(displayStakeInfo => renderStakeItems(displayStakeInfo, currencyConfigMap[pluginId].currencyInfo)) - )} + {isPortfolioSelected && PORTFOLIO_ITEMS.map(info => renderPortfolioItem(info, currencyConfigMap[info.pluginId].currencyInfo))} + {!isPortfolioSelected && DISCOVER_ITEMS.map(info => renderDiscoverItem(info, currencyConfigMap[info.pluginId].currencyInfo))} {((isLoadingDiscover && !isPortfolioSelected) || (isLoadingPortfolio && isPortfolioSelected)) && ( )}