From 8f90806ee98cc9d7e7eee9f21264149a967dd7a2 Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Wed, 20 Nov 2024 19:45:42 -0800 Subject: [PATCH 1/5] Fix wallet selection logic Specifically for wallets created from this scene --- src/components/scenes/Staking/EarnScene.tsx | 89 ++++++++++++++++++--- 1 file changed, 78 insertions(+), 11 deletions(-) diff --git a/src/components/scenes/Staking/EarnScene.tsx b/src/components/scenes/Staking/EarnScene.tsx index 6ccf322c8e5..c230509ba0f 100644 --- a/src/components/scenes/Staking/EarnScene.tsx +++ b/src/components/scenes/Staking/EarnScene.tsx @@ -1,3 +1,4 @@ +import { useIsFocused } from '@react-navigation/native' import { EdgeCurrencyInfo, EdgeCurrencyWallet } from 'edge-core-js' import * as React from 'react' import { ActivityIndicator } from 'react-native' @@ -64,13 +65,50 @@ export const EarnScene = (props: Props) => { const wallets = Object.values(currencyWallets) const [isPortfolioSelected, setIsPortfolioSelected] = React.useState(false) - const [isLoading, setIsLoading] = React.useState(true) - - const [updateCounter, setUpdateCounter] = React.useState(0) + const [isLoadingDiscover, setIsLoadingDiscover] = React.useState(true) + const [isLoadingPortfolio, setIsLoadingPortfolio] = React.useState(true) + const [isPrevFocused, setIsPrevFocused] = React.useState(true) 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)) { @@ -106,15 +144,34 @@ export const EarnScene = (props: Props) => { stakePolicy, walletStakeInfos: walletStakePositions }) - // Trigger re-render - setUpdateCounter(prevCounter => prevCounter + 1) } } } - setIsLoading(false) + setIsLoadingPortfolio(false) + setIsLoadingDiscover(false) }, - [updateCounter], - 'EarnScene' + [], + 'EarnScene Initialize STAKE_POLICY_MAP' + ) + + // Refresh stake positions when re-entering the scene + useAsyncEffect( + async () => { + if (isFocused && !isPrevFocused) { + setIsLoadingPortfolio(true) + + for (const pluginId of Object.keys(currencyConfigMap)) { + const newDisplayStakeInfos = await refreshStakePositions(pluginId) + STAKE_POLICY_MAP[pluginId] = newDisplayStakeInfos + } + + setIsLoadingPortfolio(false) + } + + setIsPrevFocused(isFocused) + }, + [isFocused], + 'EarnScene Refresh Stake Positions' ) const renderStakeItems = (displayStakeInfo: DisplayStakeInfo, currencyInfo: EdgeCurrencyInfo) => { @@ -130,10 +187,18 @@ export const EarnScene = (props: Props) => { let walletId: string | undefined let stakePosition - if (walletStakeInfos.length === 1 || (isPortfolioSelected && openStakePositions.length === 1)) { + 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] + 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 + } walletId = wallet.id stakePosition = existingStakePosition @@ -190,7 +255,9 @@ export const EarnScene = (props: Props) => { {Object.keys(STAKE_POLICY_MAP).map(pluginId => STAKE_POLICY_MAP[pluginId].map(displayStakeInfo => renderStakeItems(displayStakeInfo, currencyConfigMap[pluginId].currencyInfo)) )} - {isLoading && } + {((isLoadingDiscover && !isPortfolioSelected) || (isLoadingPortfolio && isPortfolioSelected)) && ( + + )} ) } From 970f8620f617118d23a2e033bed8a86dacbc64d6 Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Fri, 22 Nov 2024 10:15:19 -0800 Subject: [PATCH 2/5] Filter deprecated stake policies From Discover tab only, in case they have an open stake position already. --- src/components/scenes/Staking/EarnScene.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/scenes/Staking/EarnScene.tsx b/src/components/scenes/Staking/EarnScene.tsx index c230509ba0f..85eefe6dbd0 100644 --- a/src/components/scenes/Staking/EarnScene.tsx +++ b/src/components/scenes/Staking/EarnScene.tsx @@ -121,7 +121,7 @@ export const EarnScene = (props: Props) => { const matchingWallets = wallets.filter((wallet: EdgeCurrencyWallet) => wallet.currencyInfo.pluginId === pluginId) for (const stakePlugin of stakePlugins) { - const stakePolicies = stakePlugin.getPolicies({ pluginId }) + const stakePolicies = stakePlugin.getPolicies({ pluginId }).filter(stakePolicy => !stakePolicy.deprecated) for (const stakePolicy of stakePolicies) { const walletStakePositions = [] From 7a39881332ef19149215b888a3c2664c111cc1cf Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Tue, 26 Nov 2024 17:49:50 -0800 Subject: [PATCH 3/5] 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)) && ( )} From f8aa16eeaea2a820091b3c73ad3dd33c07cf7bd5 Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Tue, 26 Nov 2024 18:56:20 -0800 Subject: [PATCH 4/5] fixup! Split data+logic for Portfolio vs Discover --- src/components/scenes/Staking/EarnScene.tsx | 160 +++++++++++++------- 1 file changed, 107 insertions(+), 53 deletions(-) diff --git a/src/components/scenes/Staking/EarnScene.tsx b/src/components/scenes/Staking/EarnScene.tsx index 4205c94355e..034d7311ca3 100644 --- a/src/components/scenes/Staking/EarnScene.tsx +++ b/src/components/scenes/Staking/EarnScene.tsx @@ -27,11 +27,10 @@ import { cacheStyles, Theme, useTheme } from '../../services/ThemeContext' interface Props extends EdgeAppSceneProps<'earnScene'> {} let USERNAME: string | undefined -let DISCOVER_ITEMS: DiscoverStakeInfo[] = [] -let PORTFOLIO_ITEMS: PortfolioStakeInfo[] = [] +let DISCOVER_MAP: DiscoverStakeMap = {} +let PORTFOLIO_MAP: PortfolioStakeMap = {} interface DiscoverStakeInfo { - pluginId: string stakePlugin: StakePlugin stakePolicy: StakePolicy } @@ -40,23 +39,49 @@ interface PortfolioStakeInfo extends DiscoverStakeInfo { walletStakeInfos: WalletStakeInfo[] } +interface DiscoverStakeMap { + [stakePolicyId: string]: DiscoverStakeInfo +} + +interface PortfolioStakeMap { + [stakePolicyId: string]: PortfolioStakeInfo +} + interface WalletStakeInfo { wallet: EdgeCurrencyWallet - isPositionOpen: boolean stakePosition: StakePosition } +/** Hook to ensure the UI updates on map changes, while retaining cached data + * functionality */ +const useStakeMaps = () => { + const [, forceUpdate] = React.useReducer(x => x + 1, 0) + + const updateMaps = React.useCallback((updates: () => void) => { + updates() + forceUpdate() + }, []) + + return { + discoverMap: DISCOVER_MAP, + portfolioMap: PORTFOLIO_MAP, + updateMaps + } +} + export const EarnScene = (props: Props) => { const { navigation } = props const theme = useTheme() const styles = getStyles(theme) + const { discoverMap, portfolioMap, updateMaps } = useStakeMaps() + const account = useSelector(state => state.core.account) if (USERNAME !== account.username) { // Reset local variable if user changes USERNAME = account.username - DISCOVER_ITEMS = [] - PORTFOLIO_ITEMS = [] + DISCOVER_MAP = {} + PORTFOLIO_MAP = {} } const currencyConfigMap = useSelector(state => state.core.account.currencyConfig) @@ -76,27 +101,32 @@ export const EarnScene = (props: Props) => { useAsyncEffect( async () => { const pluginIds = Object.keys(currencyConfigMap) - DISCOVER_ITEMS = [] for (const pluginId of pluginIds) { + setIsLoadingDiscover(true) + const isStakingSupported = SPECIAL_CURRENCY_INFO[pluginId]?.isStakingSupported === true && ENV.ENABLE_STAKING if (!isStakingSupported) continue const stakePlugins = await getStakePlugins(pluginId) - for (const stakePlugin of stakePlugins) { - const stakePolicies = stakePlugin.getPolicies({ pluginId }).filter(stakePolicy => !stakePolicy.deprecated) - - for (const stakePolicy of stakePolicies) { - DISCOVER_ITEMS.push({ - pluginId, - stakePlugin, - stakePolicy - }) + updateMaps(() => { + for (const stakePlugin of stakePlugins) { + for (const stakePolicy of stakePlugin.getPolicies({ pluginId }).filter(stakePolicy => !stakePolicy.deprecated)) { + DISCOVER_MAP[stakePolicy.stakePolicyId] = { + stakePlugin, + stakePolicy + } + } } - } + }) + + console.debug('getStakePlugins', pluginId, 'complete') + setIsLoadingDiscover(false) } + setIsLoadingDiscover(false) + return () => {} }, [], 'EarnScene Initialize Discover Items' @@ -107,46 +137,69 @@ export const EarnScene = (props: Props) => { async () => { 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 }) + const controller = new AbortController() + const signal = controller.signal + + try { + const stakePolicyIds = Object.keys(discoverMap) + for (const stakePolicyId of stakePolicyIds) { + if (signal.aborted) break + + const discoverInfo = discoverMap[stakePolicyId] + const { stakePlugin, stakePolicy } = discoverInfo + + // Find matching wallets based on the first stake asset's pluginId + const pluginId = stakePolicy.stakeAssets[0].pluginId + const matchingWallets = wallets.filter((wallet: EdgeCurrencyWallet) => wallet.currencyInfo.pluginId === pluginId) + + const walletStakeInfoPromises = matchingWallets.map(async wallet => { + if (signal.aborted) return null + 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) { + return { wallet, stakePosition } + } + } catch (e) { + showDevError(e) } - } catch (e) { - showDevError(e) + return null + }) + + if (!signal.aborted) { + const walletStakeInfos = (await Promise.all(walletStakeInfoPromises)).filter( + (info: WalletStakeInfo | null): info is WalletStakeInfo => info != null + ) + + updateMaps(() => { + PORTFOLIO_MAP[stakePolicyId] = { + ...discoverInfo, + walletStakeInfos + } + }) } } - - if (walletStakeInfos.length > 0) { - PORTFOLIO_ITEMS.push({ - ...discoverInfo, - walletStakeInfos - }) + } finally { + if (!signal.aborted) { + setIsLoadingPortfolio(false) + setIsPrevFocused(isFocused) } } - setIsLoadingPortfolio(false) + return () => { + controller.abort() + } } - setIsPrevFocused(isFocused) }, - [isFocused, isLoadingDiscover], + [isFocused, isLoadingDiscover, updateMaps], 'EarnScene Refresh Portfolio Data' ) @@ -202,6 +255,7 @@ export const EarnScene = (props: Props) => { const renderPortfolioItem = (portfolioStakeInfo: PortfolioStakeInfo, currencyInfo: EdgeCurrencyInfo) => { const { stakePlugin, stakePolicy, walletStakeInfos } = portfolioStakeInfo + if (walletStakeInfos.length === 0) return null const handlePress = async () => { let walletId: string | undefined @@ -215,9 +269,7 @@ export const EarnScene = (props: Props) => { stakePosition = existingStakePosition } else { // Select from wallets that have an open position - const allowedWalletIds = walletStakeInfos - .filter(walletStakeInfo => walletStakeInfo.isPositionOpen) - .map(walletStakePosition => walletStakePosition.wallet.id) + const allowedWalletIds = walletStakeInfos.map(walletStakePosition => walletStakePosition.wallet.id) const result = await Airship.show(bridge => ( { - {isPortfolioSelected && PORTFOLIO_ITEMS.map(info => renderPortfolioItem(info, currencyConfigMap[info.pluginId].currencyInfo))} - {!isPortfolioSelected && DISCOVER_ITEMS.map(info => renderDiscoverItem(info, currencyConfigMap[info.pluginId].currencyInfo))} + {isPortfolioSelected && + Object.values(portfolioMap).map(info => renderPortfolioItem(info, currencyConfigMap[info.stakePolicy.stakeAssets[0].pluginId].currencyInfo))} + {!isPortfolioSelected && + Object.values(discoverMap).map(info => renderDiscoverItem(info, currencyConfigMap[info.stakePolicy.stakeAssets[0].pluginId].currencyInfo))} {((isLoadingDiscover && !isPortfolioSelected) || (isLoadingPortfolio && isPortfolioSelected)) && ( )} From 8a7aae69ff1980aa5a9acfc69d793264cfe3551b Mon Sep 17 00:00:00 2001 From: Jon Tzeng Date: Wed, 27 Nov 2024 13:00:27 -0800 Subject: [PATCH 5/5] fixup! fixup! Split data+logic for Portfolio vs Discover --- src/components/scenes/Staking/EarnScene.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/scenes/Staking/EarnScene.tsx b/src/components/scenes/Staking/EarnScene.tsx index 034d7311ca3..757857ff055 100644 --- a/src/components/scenes/Staking/EarnScene.tsx +++ b/src/components/scenes/Staking/EarnScene.tsx @@ -26,6 +26,8 @@ import { cacheStyles, Theme, useTheme } from '../../services/ThemeContext' interface Props extends EdgeAppSceneProps<'earnScene'> {} +export interface EarnSceneParams {} + let USERNAME: string | undefined let DISCOVER_MAP: DiscoverStakeMap = {} let PORTFOLIO_MAP: PortfolioStakeMap = {}