Skip to content

Commit

Permalink
Split data+logic for Portfolio vs Discover
Browse files Browse the repository at this point in the history
  • Loading branch information
Jon-edge committed Nov 27, 2024
1 parent 970f862 commit 7a39881
Showing 1 changed file with 119 additions and 115 deletions.
234 changes: 119 additions & 115 deletions src/components/scenes/Staking/EarnScene.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand All @@ -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<boolean>()

const handleSelectEarn = useHandler(() => setIsPortfolioSelected(false))
const handleSelectPortfolio = useHandler(() => setIsPortfolioSelected(true))

const isFocused = useIsFocused()

const refreshStakePositions = async (pluginId: string): Promise<DisplayStakeInfo[]> => {
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<WalletListResult>(bridge => (
<WalletListModal
bridge={bridge}
allowedAssets={allowedAssets}
headerTitle={lstrings.select_wallet}
showCreateWallet
navigation={navigation as NavigationBase}
/>
))

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 (
<EdgeAnim key={stakePolicy.stakePolicyId} enter={fadeInUp20}>
<EarnOptionCard currencyInfo={currencyInfo} stakePolicy={stakePolicy} isOpenPosition={false} onPress={handlePress} />
</EdgeAnim>
)
}

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<WalletListResult>(bridge => (
<WalletListModal
bridge={bridge}
allowedAssets={allowedAssets}
allowedWalletIds={!isPortfolioSelected ? undefined : allowedPortfolioWalletIds}
allowedWalletIds={allowedWalletIds}
headerTitle={lstrings.select_wallet}
// Only allow wallet creation on the Discover tab
showCreateWallet={!isPortfolioSelected}
showCreateWallet={false}
navigation={navigation as NavigationBase}
/>
))
Expand All @@ -243,7 +248,7 @@ export const EarnScene = (props: Props) => {

return (
<EdgeAnim key={stakePolicy.stakePolicyId} enter={fadeInUp20}>
<EarnOptionCard currencyInfo={currencyInfo} stakePolicy={stakePolicy} isOpenPosition={isPortfolioSelected} onPress={handlePress} />
<EarnOptionCard currencyInfo={currencyInfo} stakePolicy={stakePolicy} isOpenPosition onPress={handlePress} />
</EdgeAnim>
)
}
Expand All @@ -252,9 +257,8 @@ export const EarnScene = (props: Props) => {
<SceneWrapper scroll padding={theme.rem(0.5)}>
<EdgeSwitch labelA={lstrings.staking_discover} labelB={lstrings.staking_portfolio} onSelectA={handleSelectEarn} onSelectB={handleSelectPortfolio} />
<SectionHeader leftTitle={lstrings.staking_earning_pools} />
{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)) && (
<ActivityIndicator style={styles.loader} size="large" color={theme.primaryText} />
)}
Expand Down

0 comments on commit 7a39881

Please sign in to comment.