Skip to content

Commit

Permalink
feat: add fox page governance section (#7952)
Browse files Browse the repository at this point in the history
  • Loading branch information
NeOMakinG authored Oct 21, 2024
1 parent 149f550 commit b226bd2
Show file tree
Hide file tree
Showing 14 changed files with 374 additions and 9 deletions.
1 change: 1 addition & 0 deletions .env.base
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ REACT_APP_FEATURE_FOX_PAGE=false
REACT_APP_FEATURE_FOX_PAGE_RFOX=false
REACT_APP_FEATURE_FOX_PAGE_FOX_SECTION=true
REACT_APP_FEATURE_FOX_PAGE_FOX_FARMING_SECTION=false
REACT_APP_FEATURE_FOX_PAGE_GOVERNANCE=false
REACT_APP_FEATURE_PHANTOM_WALLET=true

# absolute URL prefix
Expand Down
1 change: 1 addition & 0 deletions .env.dev
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ REACT_APP_FEATURE_FOX_PAGE=true
REACT_APP_FEATURE_FOX_PAGE_RFOX=true
REACT_APP_FEATURE_FOX_PAGE_FOX_SECTION=true
REACT_APP_FEATURE_FOX_PAGE_FOX_FARMING_SECTION=true
REACT_APP_FEATURE_FOX_PAGE_GOVERNANCE=true
REACT_APP_FEATURE_PHANTOM_WALLET=true

# logging
Expand Down
12 changes: 12 additions & 0 deletions src/assets/translations/en/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"done": "Done",
"cancel": "Cancel",
"close": "Close",
"closed": "Closed",
"loadMore": "Load More",
"saveChanges": "Save Changes",
"new": "New",
Expand Down Expand Up @@ -2645,6 +2646,17 @@
"totalClaimableRewards": "Total Claimable Rewards",
"totalStakingValue": "Total Staking Value",
"nextEpoch": "Next Epoch"
},
"governance": {
"title": "Governance",
"description": {
"0": "Join us on the forum",
"1": "on the forum",
"2": "and shape the future of the DAO."
},
"totalVotingPower": "Total Voting Power",
"noActiveProposals": "No active proposals available",
"noClosedProposals": "No closed proposals available"
}
}
}
1 change: 1 addition & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ const validators = {
REACT_APP_FEATURE_FOX_PAGE_RFOX: bool({ default: false }),
REACT_APP_FEATURE_FOX_PAGE_FOX_SECTION: bool({ default: true }),
REACT_APP_FEATURE_FOX_PAGE_FOX_FARMING_SECTION: bool({ default: false }),
REACT_APP_FEATURE_FOX_PAGE_GOVERNANCE: bool({ default: false }),
REACT_APP_FEATURE_LIMIT_ORDERS: bool({ default: false }),
}

Expand Down
2 changes: 2 additions & 0 deletions src/pages/Fox/FoxPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Main } from 'components/Layout/Main'
import { SEO } from 'components/Layout/Seo'

import { FoxFarming } from './components/FoxFarming'
import { FoxGovernance } from './components/FoxGovernance'
import { FoxHeader } from './components/FoxHeader'
import { FoxToken } from './components/FoxToken'
import { RFOXSection } from './components/RFOXSection'
Expand All @@ -20,6 +21,7 @@ export const FoxPage = () => {
<FoxToken />
<RFOXSection />
<FoxFarming />
<FoxGovernance />
</Main>
</FoxPageProvider>
)
Expand Down
178 changes: 178 additions & 0 deletions src/pages/Fox/components/FoxGovernance.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import type { FlexProps } from '@chakra-ui/react'
import {
Box,
Card,
CardBody,
Divider,
Flex,
Heading,
Link,
Skeleton,
Tab,
TabList,
TabPanel,
TabPanels,
Tabs,
Text as CText,
} from '@chakra-ui/react'
import { foxAssetId } from '@shapeshiftoss/caip'
import { useCallback, useEffect } from 'react'
import { useTranslate } from 'react-polyglot'
import { useDispatch } from 'react-redux'
import { Amount } from 'components/Amount/Amount'
import { Text } from 'components/Text'
import { useFeatureFlag } from 'hooks/useFeatureFlag/useFeatureFlag'
import { selectIsSnapshotApiQueriesPending, selectVotingPower } from 'state/apis/snapshot/selectors'
import { snapshotApi, useGetProposalsQuery } from 'state/apis/snapshot/snapshot'
import { selectAssetById, selectWalletAccountIds } from 'state/slices/selectors'
import { useAppSelector } from 'state/store'

import { FoxGovernanceProposal } from './FoxGovernanceProposal'

const containerPaddingX = { base: 4, xl: 0 }
const headerTitleMb = { base: 4, md: 0 }
const headerTitleMaxWidth = { base: '100%', md: '50%' }
const headerSx: FlexProps['sx'] = {
alignItems: { base: 'flex-start', md: 'center' },
justifyContent: 'space-between',
mb: 8,
flexDir: {
base: 'column',
md: 'row',
},
}

const flexPropsMd1 = { base: '1 0 auto', md: 1 }
const flexPropsMdAuto = { base: 1, md: 'auto' }
const widthBaseFull = { base: 'full' }
const widthMdAuto = { base: 'full', md: 'auto' }
const tabListPaddingLeft = { base: 6, md: 0 }

export const FoxGovernance = () => {
const translate = useTranslate()
const isFoxGovernanceEnabled = useFeatureFlag('FoxPageGovernance')
const dispatch = useDispatch()
const {
data: { activeProposals, closedProposals } = { activeProposals: [], closedProposals: [] },
} = useGetProposalsQuery()

const accountIds = useAppSelector(selectWalletAccountIds)

const foxEthAsset = useAppSelector(state => selectAssetById(state, foxAssetId))

const votingPower = useAppSelector(state => selectVotingPower(state, { feeModel: 'SWAPPER' }))
const isVotingPowerQueriesPending = useAppSelector(selectIsSnapshotApiQueriesPending)

useEffect(() => {
dispatch(
snapshotApi.endpoints.getVotingPower.initiate({ model: 'SWAPPER' }, { forceRefetch: true }),
)
}, [dispatch, accountIds])

const ActiveProposals = useCallback(() => {
if (!activeProposals.length)
return <Text color='text.subtle' translation='foxPage.governance.noActiveProposals' />

return (
<>
{activeProposals.map(proposal => (
<FoxGovernanceProposal {...proposal} />
))}
</>
)
}, [activeProposals])

const ClosedProposals = useCallback(() => {
if (!closedProposals.length)
return <Text color='text.subtle' translation='foxPage.governance.noClosedProposals' />

return (
<>
{closedProposals.map(proposal => (
<FoxGovernanceProposal {...proposal} />
))}
</>
)
}, [closedProposals])

if (!isFoxGovernanceEnabled) return null
if (!foxEthAsset) return null

return (
<>
<Divider mb={4} />
<Box py={4} px={containerPaddingX}>
<Flex sx={headerSx}>
<Box mb={headerTitleMb} maxWidth={headerTitleMaxWidth}>
<Heading as='h2' fontSize='2xl' display='flex' alignItems='center'>
<CText as='span' me={2}>
🏛️
</CText>
{translate('foxPage.governance.title')}
</Heading>
<Flex alignItems='center'>
<Text
fontSize='md'
color='text.subtle'
translation='foxPage.governance.description.0'
/>
<Link
colorScheme='blue'
color='blue.300'
href='https://forum.shapeshift.com/'
isExternal
mx={1}
>
{translate('foxPage.governance.description.1')}
</Link>
<Text
fontSize='md'
color='text.subtle'
translation='foxPage.governance.description.2'
/>
</Flex>
</Box>

<Card width='100%' maxWidth='400px'>
<CardBody py={4} px={4}>
<Flex alignItems='center' justifyContent='space-between'>
<Box width='100%'>
<Text
fontSize='md'
color='text.subtle'
translation='foxPage.governance.totalVotingPower'
/>

<Skeleton
isLoaded={Boolean(votingPower !== undefined && !isVotingPowerQueriesPending)}
>
<Amount.Crypto value={votingPower ?? '0'} symbol={foxEthAsset.symbol ?? ''} />
</Skeleton>
</Box>
</Flex>
</CardBody>
</Card>
</Flex>

<Tabs isLazy lazyBehavior='keepMounted' variant='soft-rounded' size='sm'>
<Flex flex={flexPropsMd1} width={widthBaseFull}>
<TabList m={0} width={widthMdAuto} pl={tabListPaddingLeft}>
<Tab flex={flexPropsMdAuto} me={2}>
{translate('common.active')}
</Tab>
<Tab flex={flexPropsMdAuto}>{translate('common.closed')}</Tab>
</TabList>
</Flex>
<TabPanels>
<TabPanel px={0}>
<ActiveProposals />
</TabPanel>
<TabPanel px={0}>
<ClosedProposals />
</TabPanel>
</TabPanels>
</Tabs>
</Box>
</>
)
}
72 changes: 72 additions & 0 deletions src/pages/Fox/components/FoxGovernanceProposal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Box, Card, CardBody, Flex, Link, Progress, Tag, Text } from '@chakra-ui/react'
import { foxAssetId } from '@shapeshiftoss/caip'
import { Amount } from 'components/Amount/Amount'
import { bnOrZero } from 'lib/bignumber/bignumber'
import type { Proposal } from 'state/apis/snapshot/validators'
import { selectAssetById } from 'state/slices/selectors'
import { useAppSelector } from 'state/store'

const hoverProps = {
opacity: '0.6',
transition: 'opacity 0.2s',
}

export const FoxGovernanceProposal: React.FC<Proposal> = ({
title,
body,
choices,
scores,
scores_total,
link,
}) => {
const foxAsset = useAppSelector(state => selectAssetById(state, foxAssetId))

return (
<Link href={link} isExternal={true} _hover={hoverProps}>
<Card width='100%' my={2}>
<CardBody py={4} px={4}>
<Text fontSize='md' fontWeight='bold'>
{title}
</Text>
<Text
fontSize='md'
color='text.subtle'
whiteSpace='nowrap'
textOverflow='ellipsis'
overflow='hidden'
mb={4}
>
{body}
</Text>
{choices.map((option, index) => {
const percent = bnOrZero(scores?.[index])
.div(scores_total ?? 1)
.times(100)
.toNumber()

return (
<Box key={option} justifyContent='space-between' alignItems='center' my={4}>
<Flex alignItems='center' justifyContent='space-between' mb={2}>
<Text fontSize='md'>{option}</Text>
<Flex fontSize='md' ml={2} alignItems='center'>
<Amount.Crypto
value={bnOrZero(scores?.[index]).toFixed(0)}
symbol={foxAsset?.symbol ?? ''}
me={2}
/>
<Tag colorScheme='blue'>
<Amount.Percent
value={bnOrZero(percent).div(100).toFixed(4)}
></Amount.Percent>
</Tag>
</Flex>
</Flex>
<Progress borderRadius='30' colorScheme='green' size='sm' value={percent} />
</Box>
)
})}
</CardBody>
</Card>
</Link>
)
}
5 changes: 3 additions & 2 deletions src/pages/Fox/components/FoxTokenFilterButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ export const FoxTokenFilterButton = ({
onFilterClick,
isSelected,
}: FoxTokenFilterButtonProps) => {
const buttonsBgColor = useColorModeValue('grey.500', 'white')
const buttonsBgColor = useColorModeValue('gray.100', 'white')
const buttonsColor = useColorModeValue('gray.500', 'white')
const feeAsset = useAppSelector(state => selectFeeAssetByChainId(state, filter.chainId ?? ''))

const iconSrc = feeAsset?.networkIcon
Expand All @@ -45,7 +46,7 @@ export const FoxTokenFilterButton = ({
_hover={buttonsHover}
variant={isSelected ? 'solid' : 'outline'}
backgroundColor={isSelected ? buttonsBgColor : 'transparent'}
color={isSelected ? 'gray.900' : 'white'}
color={isSelected ? 'gray.900' : buttonsColor}
// eslint-disable-next-line react-memo/require-usememo
onClick={() => onFilterClick(filter)}
leftIcon={filter.assetId ? networkIcon : undefined}
Expand Down
7 changes: 2 additions & 5 deletions src/pages/RFOX/hooks/useRfoxContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,8 @@ export const RFOXProvider: React.FC<React.PropsWithChildren<{ stakingAssetId: As
const [stakingAssetAccountId, setStakingAssetAccountId] = useState<AccountId | undefined>()

const filter = useMemo(
() =>
stakingAssetAccountId && stakingAssetId
? { assetId: stakingAssetAccountId, accountId: stakingAssetAccountId }
: undefined,
[stakingAssetAccountId, stakingAssetId],
() => (stakingAssetAccountId ? { accountId: stakingAssetAccountId } : undefined),
[stakingAssetAccountId],
)

const stakingAssetAccountNumber = useAppSelector(state =>
Expand Down
2 changes: 2 additions & 0 deletions src/state/apis/snapshot/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,5 @@ export const selectVotingPower = createSelector(
return votingPowerByModel[feeModel!]
},
)

export const selectProposals = (state: ReduxState) => state.snapshot.proposals
Loading

0 comments on commit b226bd2

Please sign in to comment.