Skip to content

Commit

Permalink
Display the votes on the network proposal details page
Browse files Browse the repository at this point in the history
  • Loading branch information
csillag committed Mar 31, 2024
1 parent 9f5b604 commit c445593
Show file tree
Hide file tree
Showing 13 changed files with 696 additions and 13 deletions.
1 change: 1 addition & 0 deletions .changelog/1356.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Display the votes on the network proposal details page
77 changes: 77 additions & 0 deletions src/app/components/Proposals/ProposalVoteIndicator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { FC } from 'react'
import { TFunction } from 'i18next'
import { useTranslation } from 'react-i18next'
import Box from '@mui/material/Box'
import CheckCircleIcon from '@mui/icons-material/CheckCircle'
import CancelIcon from '@mui/icons-material/Cancel'
import RemoveCircleIcon from '@mui/icons-material/RemoveCircle'
import { styled } from '@mui/material/styles'
import { COLORS } from '../../../styles/theme/colors'
import { ProposalVoteValue } from '../../../types/vote'

const StyledBox = styled(Box)(({ theme }) => ({
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'space-between',
gap: 3,
flex: 1,
borderRadius: 10,
padding: theme.spacing(2, 2, 2, '10px'),
fontSize: '12px',
minWidth: '85px',
}))

const StyledIcon = styled(Box)({
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
fontSize: '18px',
})

const getStatuses = (t: TFunction) => ({
[ProposalVoteValue.abstain]: {
backgroundColor: COLORS.lightSilver,
icon: RemoveCircleIcon,
iconColor: COLORS.grayMedium,
label: t('networkProposal.vote.abstain'),
textColor: COLORS.grayExtraDark,
},
[ProposalVoteValue.yes]: {
backgroundColor: COLORS.honeydew,
icon: CheckCircleIcon,
iconColor: COLORS.eucalyptus,
label: t('networkProposal.vote.yes'),
textColor: COLORS.grayExtraDark,
},
[ProposalVoteValue.no]: {
backgroundColor: COLORS.linen,
icon: CancelIcon,
iconColor: COLORS.errorIndicatorBackground,
label: t('networkProposal.vote.no'),
textColor: COLORS.grayExtraDark,
},
})

type ProposalVoteIndicatorProps = {
vote: ProposalVoteValue
}

export const ProposalVoteIndicator: FC<ProposalVoteIndicatorProps> = ({ vote }) => {
const { t } = useTranslation()

if (!ProposalVoteValue[vote]) {
return null
}

const statusConfig = getStatuses(t)[vote]
const IconComponent = statusConfig.icon

return (
<StyledBox sx={{ backgroundColor: statusConfig.backgroundColor, color: statusConfig.textColor }}>
{statusConfig.label}
<StyledIcon sx={{ color: statusConfig.iconColor }}>
<IconComponent color="inherit" fontSize="inherit" />
</StyledIcon>
</StyledBox>
)
}
64 changes: 64 additions & 0 deletions src/app/components/Proposals/VoteTypePills.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import Box from '@mui/material/Box'
import Chip from '@mui/material/Chip'
import Typography from '@mui/material/Typography'
import { COLORS } from '../../../styles/theme/colors'
import { ProposalVoteValue, VoteType } from '../../../types/vote'

type VoteTypePillsProps = {
handleChange: (voteType: VoteType) => void
value?: VoteType
}

export const VoteTypePills: FC<VoteTypePillsProps> = ({ handleChange, value }) => {
const { t } = useTranslation()
const options: { label: string; value: VoteType }[] = [
{
label: t('networkProposal.vote.all'),
value: 'any',
},
{
label: t('networkProposal.vote.yes'),
value: ProposalVoteValue.yes,
},
{
label: t('networkProposal.vote.abstain'),
value: ProposalVoteValue.abstain,
},
{
label: t('networkProposal.vote.no'),
value: ProposalVoteValue.no,
},
]

return (
<>
{options.map(option => {
const selected = option.value === value
return (
<Chip
key={option.value}
onClick={() => handleChange(option.value)}
clickable
color="secondary"
label={
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<Typography component="span" sx={{ fontSize: 16 }}>
{option.label}
</Typography>
</Box>
}
sx={{
mr: 3,
borderColor: COLORS.brandMedium,
backgroundColor: selected ? COLORS.brandMedium : COLORS.brandMedium15,
color: selected ? COLORS.white : COLORS.grayExtraDark,
}}
variant={selected ? 'outlined-selected' : 'outlined'}
/>
)
})}
</>
)
}
70 changes: 70 additions & 0 deletions src/app/components/Proposals/VoterSearchBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { FC } from 'react'
import TextField from '@mui/material/TextField'
import SearchIcon from '@mui/icons-material/Search'
import HighlightOffIcon from '@mui/icons-material/HighlightOff'
import InputAdornment from '@mui/material/InputAdornment'
import { COLORS } from '../../../styles/theme/colors'
import { SearchVariant } from '../Search'
import { useTranslation } from 'react-i18next'
import IconButton from '@mui/material/IconButton'

export interface SearchBarProps {
variant: SearchVariant
value: string
onChange: (value: string) => void
}

export const VoterSearchBar: FC<SearchBarProps> = ({ variant, value, onChange }) => {
const { t } = useTranslation()
const startAdornment = variant === 'button' && (
<InputAdornment
position="start"
disablePointerEvents // Pass clicks through, so it focuses the input
>
<SearchIcon sx={{ color: COLORS.grayDark }} />
</InputAdornment>
)

const onClearValue = () => onChange('')

const endAdornment = (
<InputAdornment position="end">
<>
{value && (
<IconButton color="inherit" onClick={onClearValue}>
<HighlightOffIcon />
</IconButton>
)}
</>
</InputAdornment>
)

return (
<>
<TextField
value={value}
onChange={e => onChange(e.target.value)}
InputProps={{
inputProps: {
sx: {
p: 0,
marginRight: 2,
},
},
startAdornment,
endAdornment,
}}
placeholder={t('networkProposal.searchForVoters')}
// FormHelperTextProps={{
// component: 'div', // replace p with div tag
// sx: {
// marginTop: 0,
// marginBottom: 0,
// marginLeft: variant === 'button' ? '48px' : '17px',
// marginRight: variant === 'button' ? '48px' : '17px',
// },
// }}
/>
</>
)
}
28 changes: 28 additions & 0 deletions src/app/components/Validators/DeferredValidatorLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { FC } from 'react'
import { Network } from '../../../types/network'
import { Layer, Validator } from '../../../oasis-nexus/api'
import { SearchScope } from '../../../types/searchScope'
import { ValidatorLink } from './ValidatorLink'

export const DeferredValidatorLink: FC<{
network: Network
address: string
validator: Validator | undefined
isError: boolean
highlightedPart?: string | undefined
}> = ({ network, address, validator, isError, highlightedPart }) => {
const scope: SearchScope = { network, layer: Layer.consensus }

if (isError) {
console.log('Warning: failed to look up validators!')
}

return (
<ValidatorLink
address={address}
network={scope.network}
name={validator?.media?.name}
highlightedPart={highlightedPart}
/>
)
}
12 changes: 11 additions & 1 deletion src/app/components/Validators/ValidatorImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import ImageNotSupportedIcon from '@mui/icons-material/ImageNotSupported'
import { hasValidProtocol } from '../../utils/url'
import { COLORS } from 'styles/theme/colors'
import { Circle } from '../Circle'
import { HighlightedText } from '../HighlightedText'
import Box from '@mui/material/Box'

const StyledImage = styled('img')({
width: '28px',
Expand All @@ -15,9 +17,10 @@ type ValidatorImageProps = {
address: string
name: string | undefined
logotype: string | undefined
highlightedPart?: string | undefined
}

export const ValidatorImage: FC<ValidatorImageProps> = ({ address, name, logotype }) => {
export const ValidatorImage: FC<ValidatorImageProps> = ({ address, name, logotype, highlightedPart }) => {
return (
<>
{logotype && hasValidProtocol(logotype) ? (
Expand All @@ -27,6 +30,13 @@ export const ValidatorImage: FC<ValidatorImageProps> = ({ address, name, logotyp
<ImageNotSupportedIcon sx={{ color: COLORS.grayMedium, fontSize: 18 }} />
</Circle>
)}
{name ? (
<Box sx={{ display: 'inline' }}>
<HighlightedText text={name} pattern={highlightedPart} />
</Box>
) : (
address
)}{' '}
</>
)
}
31 changes: 26 additions & 5 deletions src/app/components/Validators/ValidatorLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,37 @@ import { RouteUtils } from '../../utils/route-utils'
import Typography from '@mui/material/Typography'
import { COLORS } from '../../../styles/theme/colors'
import { Network } from '../../../types/network'
import { HighlightedText } from '../HighlightedText'

type ValidatorLinkProps = {
address: string
name?: string
network: Network
alwaysTrim?: boolean
highlightedPart?: string
}

export const ValidatorLink: FC<ValidatorLinkProps> = ({ address, name, network, alwaysTrim }) => {
export const ValidatorLink: FC<ValidatorLinkProps> = ({
address,
name,
network,
alwaysTrim,
highlightedPart,
}) => {
const { isTablet } = useScreenSize()
const to = RouteUtils.getValidatorRoute(network, address)
return (
<Typography variant="mono" component="span" sx={{ color: COLORS.brandDark, fontWeight: 700 }}>
{isTablet ? (
<TabletValidatorLink address={address} name={name} to={to} />
<TabletValidatorLink address={address} name={name} to={to} highlightedPart={highlightedPart} />
) : (
<DesktopValidatorLink address={address} alwaysTrim={alwaysTrim} name={name} to={to} />
<DesktopValidatorLink
address={address}
alwaysTrim={alwaysTrim}
name={name}
to={to}
highlightedPart={highlightedPart}
/>
)}
</Typography>
)
Expand All @@ -42,6 +56,7 @@ type TabletValidatorLinkProps = {
address: string
name?: string
to: string
highlightedPart?: string // TODO: add support for highlighting on tablet
}

const TabletValidatorLink: FC<TabletValidatorLinkProps> = ({ address, name, to }) => {
Expand All @@ -55,13 +70,19 @@ type DesktopValidatorLinkProps = TabletValidatorLinkProps & {
alwaysTrim?: boolean
}

const DesktopValidatorLink: FC<DesktopValidatorLinkProps> = ({ address, name, to, alwaysTrim }) => {
const DesktopValidatorLink: FC<DesktopValidatorLinkProps> = ({
address,
name,
to,
alwaysTrim,
highlightedPart,
}) => {
if (alwaysTrim) {
return <TrimLinkLabel label={address} to={to} />
}
return (
<Link component={RouterLink} to={to}>
{name || address}
{name ? <HighlightedText text={name} pattern={highlightedPart} /> : address}
</Link>
)
}
Loading

0 comments on commit c445593

Please sign in to comment.