Skip to content

Commit

Permalink
pulling in positions and voting power
Browse files Browse the repository at this point in the history
  • Loading branch information
bryzettler committed Nov 8, 2023
1 parent 873584f commit 1665757
Show file tree
Hide file tree
Showing 14 changed files with 517 additions and 278 deletions.
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,11 @@
"typescript": "4.9.4"
},
"resolutions": {
"@types/react": "17"
"@types/react": "17",
"@helium/account-fetch-cache-hooks": "^0.4.6",
"@helium/helium-react-hooks": "^0.4.6",
"@helium/voter-stake-registry-hooks": "^0.4.6",
"@helium/modular-governance-hooks": "^0.0.7"
},
"jest": {
"preset": "react-native",
Expand Down
35 changes: 19 additions & 16 deletions src/features/governance/GovernanceNavigator.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React, { memo } from 'react'
import {
StackNavigationOptions,
createStackNavigator,
} from '@react-navigation/stack'
import React, { memo } from 'react'
import { GovernanceProvider } from '@storage/GovernanceProvider'
import GovernanceScreen from './GovernanceScreen'
import VotingPowerScreen from './VotingPowerScreen'
import ProposalScreen from './ProposalScreen'
import VotingPowerScreen from './VotingPowerScreen'

const GovernanceStack = createStackNavigator()
const screenOptions: StackNavigationOptions = {
Expand All @@ -14,20 +15,22 @@ const screenOptions: StackNavigationOptions = {

const GovernanceStackScreen = () => {
return (
<GovernanceStack.Navigator screenOptions={screenOptions}>
<GovernanceStack.Screen
name="GovernanceScreen"
component={GovernanceScreen}
/>
<GovernanceStack.Screen
name="VotingPowerScreen"
component={VotingPowerScreen}
/>
<GovernanceStack.Screen
name="ProposalScreen"
component={ProposalScreen}
/>
</GovernanceStack.Navigator>
<GovernanceProvider>
<GovernanceStack.Navigator screenOptions={screenOptions}>
<GovernanceStack.Screen
name="GovernanceScreen"
component={GovernanceScreen}
/>
<GovernanceStack.Screen
name="VotingPowerScreen"
component={VotingPowerScreen}
/>
<GovernanceStack.Screen
name="ProposalScreen"
component={ProposalScreen}
/>
</GovernanceStack.Navigator>
</GovernanceProvider>
)
}

Expand Down
23 changes: 12 additions & 11 deletions src/features/governance/GovernanceScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,20 @@ import TokenPill from '@components/TokenPill'
import { HNT_MINT, IOT_MINT, MOBILE_MINT } from '@helium/spl-utils'
import { useNavigation } from '@react-navigation/native'
import globalStyles from '@theme/globalStyles'
import React, { useMemo, useState } from 'react'
import React, { useMemo } from 'react'
import { ScrollView } from 'react-native'
import { Edge } from 'react-native-safe-area-context'
import { useGovernance } from '@storage/GovernanceProvider'
import { ProposalsList } from './ProposalsList'
import { VotingPowerCard } from './VotingPowerCard'
import { GovernanceNavigationProp } from './governanceTypes'

const GovMints = [HNT_MINT, MOBILE_MINT, IOT_MINT]

export const GovernanceScreen = () => {
const navigation = useNavigation<GovernanceNavigationProp>()
const safeEdges = useMemo(() => ['top'] as Edge[], [])
const [selectedMint, setSelectedMint] = useState(HNT_MINT)
const { mint, setMint } = useGovernance()

return (
<ReAnimatedBox entering={DelayedFadeIn} style={globalStyles.container}>
Expand All @@ -32,25 +34,24 @@ export const GovernanceScreen = () => {
justifyContent="space-between"
marginVertical="xl"
>
{GovMints.map((mint) => (
{GovMints.map((m) => (
<TokenPill
key={mint.toBase58()}
mint={mint}
isActive={selectedMint.equals(mint)}
onPress={() => setSelectedMint(mint)}
key={m.toBase58()}
mint={m}
isActive={mint.equals(m)}
onPress={() => setMint(m)}
activeColor="secondaryBackground"
/>
))}
</Box>
<VotingPowerCard
mint={selectedMint}
onPress={async (mint) => {
onPress={async (m) => {
navigation.push('VotingPowerScreen', {
mint: mint.toBase58(),
mint: m.toBase58(),
})
}}
/>
<ProposalsList mint={selectedMint} />
<ProposalsList />
</ScrollView>
</SafeAreaBox>
</ReAnimatedBox>
Expand Down
62 changes: 25 additions & 37 deletions src/features/governance/ProposalCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,6 @@ import Box from '@components/Box'
import Text from '@components/Text'
import TouchableOpacityBox from '@components/TouchableOpacityBox'
import { useMint } from '@helium/helium-react-hooks'
import {
useProposal,
useProposalConfig,
} from '@helium/modular-governance-hooks'
import { useRegistrar } from '@helium/voter-stake-registry-hooks'
import { BoxProps } from '@shopify/restyle'
import { PublicKey } from '@solana/web3.js'
import { Color, Theme } from '@theme/theme'
Expand All @@ -16,30 +11,35 @@ import BN from 'bn.js'
import MarkdownIt from 'markdown-it'
import React, { useCallback, useEffect, useMemo } from 'react'
import { useAsync } from 'react-async-hook'
import { ProposalFilter } from './governanceTypes'
import { useGovernance } from '@storage/GovernanceProvider'
import { ProposalFilter, ProposalV0 } from './governanceTypes'

interface IProposalCardProps extends BoxProps<Theme> {
filter: ProposalFilter
proposal: ProposalV0
proposalKey: PublicKey
onPress?: (proposal: PublicKey) => Promise<void>
}

// TODO (gov): add you voted
const markdownParser = MarkdownIt()
export const ProposalCardSkeleton = (boxProps: BoxProps<Theme>) => (
<Box
backgroundColor="secondaryBackground"
borderRadius="l"
padding="xxl"
{...boxProps}
/>
)
export const ProposalCard = ({
filter,
proposal,
proposalKey,
onPress,
...boxProps
}: IProposalCardProps) => {
const {
error: proposalError,
loading: proposalLoading,
info: proposal,
} = useProposal(proposalKey)

const { info: proposalConfig } = useProposalConfig(proposal?.proposalConfig)
const { info: registrar } = useRegistrar(proposalConfig?.voteController)
const decimals = useMint(registrar?.votingMints[0].mint)?.info?.decimals
const { mint } = useGovernance()
const decimals = useMint(mint)?.info?.decimals

const {
error: descError,
Expand All @@ -56,11 +56,10 @@ export const ProposalCard = ({
}
}, [proposal])

// TODO: Remove this
// TODO (gov): Add better error handling
useEffect(() => {
if (proposalError) console.error(proposalError)
if (descError) console.error(descError)
}, [proposalError, descError])
}, [descError])

const votingResults = useMemo(() => {
const totalVotes: BN = [...(proposal?.choices || [])].reduce(
Expand All @@ -77,8 +76,9 @@ export const ProposalCard = ({
: (r.weight.toNumber() / totalVotes.toNumber()) * 100,
}))
.sort((a, b) => b.percent - a.percent)

return { results, totalVotes }
}, [proposal?.choices])
}, [proposal])

const derivedState: Omit<ProposalFilter, 'all'> | undefined = useMemo(() => {
if (proposal?.state && proposal?.choices) {
Expand All @@ -102,10 +102,7 @@ export const ProposalCard = ({
}
}, [proposal?.state, proposal?.choices])

const isLoading = useMemo(
() => proposalLoading || descLoading,
[proposalLoading, descLoading],
)
const isLoading = useMemo(() => descLoading, [descLoading])

const isVisible = useMemo(() => {
if (!isLoading) {
Expand All @@ -119,20 +116,10 @@ export const ProposalCard = ({
if (onPress) await onPress(proposalKey)
}, [proposalKey, onPress])

// todo: add total votes
// todo: add you voted

if (!isVisible) return null
if (isLoading) {
// todo: add spinner or skeleton pulse
return (
<Box
backgroundColor="secondaryBackground"
borderRadius="l"
padding="xxl"
{...boxProps}
/>
)
// TODO (gov): add spinner or skeleton pulse
return <ProposalCardSkeleton {...boxProps} />
}

return (
Expand All @@ -157,9 +144,10 @@ export const ProposalCard = ({
{proposal?.name}
</Text>
</Box>
<Box flexDirection="row" marginLeft="s" alignItems="flex-start">
<Box flexDirection="row" marginLeft="s">
{proposal?.tags.map((tag, idx) => (
<Box
key={tag}
padding="s"
marginLeft={idx > 0 ? 's' : 'none'}
backgroundColor={
Expand Down Expand Up @@ -241,7 +229,7 @@ export const ProposalCard = ({
Votes
</Text>
<Text variant="body2" color="primaryText" textAlign="right">
{humanReadable(votingResults?.totalVotes, decimals)}
{humanReadable(votingResults?.totalVotes, decimals) || 'None'}
</Text>
</Box>
</Box>
Expand Down
66 changes: 28 additions & 38 deletions src/features/governance/ProposalsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,23 @@ import ListItem from '@components/ListItem'
import Text from '@components/Text'
import TouchableOpacityBox from '@components/TouchableOpacityBox'
import { BoxProps } from '@shopify/restyle'
import { PublicKey } from '@solana/web3.js'
import { Theme } from '@theme/theme'
import React, { useCallback, useState, useMemo } from 'react'
import { useNavigation } from '@react-navigation/native'
import { useGovNetwork } from '@hooks/useGovNetwork'
import { organizationKey, proposalKey } from '@helium/organization-sdk'
import { useOrganization } from '@helium/modular-governance-hooks'
import { ProposalCard } from './ProposalCard'
import { GovernanceNavigationProp, ProposalFilter } from './governanceTypes'
import React, { useCallback, useState } from 'react'
// import { useNavigation } from '@react-navigation/native'
import { useGovernance } from '@storage/GovernanceProvider'
import { ProposalCard, ProposalCardSkeleton } from './ProposalCard'
import {
// GovernanceNavigationProp,
ProposalFilter,
ProposalV0,
} from './governanceTypes'

interface IProposalsListProps extends BoxProps<Theme> {
mint: PublicKey
}

export const ProposalsList = ({ mint, ...boxProps }: IProposalsListProps) => {
const navigation = useNavigation<GovernanceNavigationProp>()
type IProposalsListProps = BoxProps<Theme>
export const ProposalsList = ({ ...boxProps }: IProposalsListProps) => {
// const navigation = useNavigation<GovernanceNavigationProp>()
const { proposals, loading } = useGovernance()
const [filter, setFilter] = useState<ProposalFilter>('all')
const [filtersOpen, setFiltersOpen] = useState(false)
const { network } = useGovNetwork(mint)
const [orgKey] = organizationKey(network)
const { info: organization } = useOrganization(orgKey)
const proposalKeys = useMemo(
() =>
Array(organization?.numProposals)
.fill(0)
.map((_, index) => proposalKey(orgKey, index)[0])
.reverse(),
[organization?.numProposals, orgKey],
)

const handleFilterPress = (f: ProposalFilter) => () => {
setFilter(f)
Expand Down Expand Up @@ -88,19 +76,21 @@ export const ProposalsList = ({ mint, ...boxProps }: IProposalsListProps) => {
{`Proposals: ${filter.charAt(0).toUpperCase() + filter.slice(1)}`}
</Text>
</TouchableOpacityBox>
{proposalKeys?.map((pKey, idx) => (
<ProposalCard
key={pKey.toBase58()}
filter={filter}
proposalKey={pKey}
marginTop={idx > 0 ? 'm' : undefined}
onPress={async (proposal) => {
navigation.push('ProposalScreen', {
proposal: proposal.toBase58(),
})
}}
/>
))}
{loading &&
Array(3).map((_, idx) => (
// eslint-disable-next-line react/no-array-index-key
<ProposalCardSkeleton key={`prop-skeleton-${idx}`} />
))}
{!loading &&
proposals?.map((proposal, idx) => (
<ProposalCard
key={`${proposal.publicKey.toBase58()}`}
filter={filter}
proposal={proposal.info as ProposalV0}
proposalKey={proposal.publicKey}
marginTop={idx > 0 ? 'm' : undefined}
/>
))}
</Box>
<BlurActionSheet
title="Filter Proposals"
Expand Down
Loading

0 comments on commit 1665757

Please sign in to comment.