diff --git a/packages/ui/src/common/components/forms/FilterBox/FilterSearchBox.tsx b/packages/ui/src/common/components/forms/FilterBox/FilterSearchBox.tsx index a9b20766ea..8191fa5bd0 100644 --- a/packages/ui/src/common/components/forms/FilterBox/FilterSearchBox.tsx +++ b/packages/ui/src/common/components/forms/FilterBox/FilterSearchBox.tsx @@ -41,9 +41,16 @@ interface SearchBoxProps extends ControlProps { } export const SearchBox = React.memo(({ value, onApply, onChange, label, displayReset }: SearchBoxProps) => { const change = onChange && (({ target }: ChangeEvent) => onChange(target.value)) - const isValid = () => !value || value.length === 0 || value.length > 2 - const keyDown = - !isValid() || !value || !onApply ? undefined : ({ key }: React.KeyboardEvent) => key === 'Enter' && onApply() + const isValid = !value || value.length === 0 || value.length > 2 + const [showInvalid, setShowInvalid] = useState(false) + useEffect(() => { + if (isValid) setShowInvalid(false) + }, [isValid]) + const keyDown = ({ key }: React.KeyboardEvent) => { + if (key !== 'Enter') return + if (!isValid) return setShowInvalid(true) + onApply?.() + } const reset = onChange && onApply && @@ -56,8 +63,8 @@ export const SearchBox = React.memo(({ value, onApply, onChange, label, displayR {label} {displayReset && value && ( diff --git a/packages/ui/src/common/utils/object.ts b/packages/ui/src/common/utils/object.ts index 65b3fce5d8..4e9f1a086b 100644 --- a/packages/ui/src/common/utils/object.ts +++ b/packages/ui/src/common/utils/object.ts @@ -22,3 +22,6 @@ const mapEntries = ( transform: (x: any, key?: string | number, context?: AnyObject) => any, context?: AnyObject ): [any, any][] => entries.map(([key, value]) => [key, transform(value, key, context)]) + +export const has = >(key: K, o: T): key is Extract => + key in o diff --git a/packages/ui/src/memberships/components/MemberProfile/MemberDetails.tsx b/packages/ui/src/memberships/components/MemberProfile/MemberDetails.tsx index 57030ff329..73691375de 100644 --- a/packages/ui/src/memberships/components/MemberProfile/MemberDetails.tsx +++ b/packages/ui/src/memberships/components/MemberProfile/MemberDetails.tsx @@ -13,6 +13,8 @@ import { SidePaneLabel, EmptyBody, } from '@/common/components/SidePane' +import { has } from '@/common/utils/object' +import { ExternalResourceLink } from '@/memberships/constants' import { useIsMyMembership } from '@/memberships/hooks/useIsMyMembership' import { useMemberExtraInfo } from '@/memberships/hooks/useMemberExtraInfo' @@ -37,15 +39,6 @@ export const MemberDetails = React.memo(({ member }: Props) => { initiatingLeaving = '-', } = useMemberExtraInfo(member) - const externalResourceLink: any = { - TELEGRAM: 'https://web.telegram.org/k/#@', - TWITTER: 'https://twitter.com/', - FACEBOOK: 'https://facebook.com/', - YOUTUBE: 'https://youtube.com/user/', - LINKEDIN: 'https://www.linkedin.com/in/', - GITHUB: 'https://github.com/', - } - if (isLoading || !memberDetails) { return ( @@ -143,9 +136,9 @@ export const MemberDetails = React.memo(({ member }: Props) => { - {externalResourceLink[externalResource.source] ? ( + {has(externalResource.source, ExternalResourceLink) ? ( {externalResource.value} diff --git a/packages/ui/src/memberships/constants/externalResourceLink.ts b/packages/ui/src/memberships/constants/externalResourceLink.ts new file mode 100644 index 0000000000..ac2740193c --- /dev/null +++ b/packages/ui/src/memberships/constants/externalResourceLink.ts @@ -0,0 +1,8 @@ +export const ExternalResourceLink = { + TELEGRAM: 'https://t.me/', + TWITTER: 'https://twitter.com/', + FACEBOOK: 'https://facebook.com/', + YOUTUBE: 'https://youtube.com/user/', + LINKEDIN: 'https://www.linkedin.com/in/', + GITHUB: 'https://github.com/', +} as const diff --git a/packages/ui/src/memberships/constants/index.ts b/packages/ui/src/memberships/constants/index.ts index 856f210744..d21e69ee07 100644 --- a/packages/ui/src/memberships/constants/index.ts +++ b/packages/ui/src/memberships/constants/index.ts @@ -1 +1,2 @@ export * from './email' +export * from './externalResourceLink' diff --git a/packages/ui/src/validators/components/ValidatorInfo.tsx b/packages/ui/src/validators/components/ValidatorInfo.tsx index 7a419e19b6..2a97a731dc 100644 --- a/packages/ui/src/validators/components/ValidatorInfo.tsx +++ b/packages/ui/src/validators/components/ValidatorInfo.tsx @@ -4,12 +4,14 @@ import styled from 'styled-components' import { CopyComponent } from '@/common/components/CopyComponent' import { DiscordIcon, TelegramIcon, TwitterIcon } from '@/common/components/icons/socials' +import { Link } from '@/common/components/Link' import { DefaultTooltip, Tooltip } from '@/common/components/Tooltip' import { BorderRad, Colors, Transitions } from '@/common/constants' import { shortenAddress } from '@/common/model/formatters' import { Address } from '@/common/types' import { MemberIcons } from '@/memberships/components' import { Avatar } from '@/memberships/components/Avatar' +import { ExternalResourceLink } from '@/memberships/constants' import { MemberWithDetails } from '@/memberships/types' interface ValidatorInfoProps { @@ -39,18 +41,18 @@ export const ValidatorInfo = React.memo(({ address, member, size = 's' }: Valida {(twitter || telegram || discord) && ( {twitter && ( - + e.stopPropagation()} href={`${ExternalResourceLink.TWITTER}${twitter.value}`}> - + )} {telegram && ( - + e.stopPropagation()} href={`${ExternalResourceLink.TELEGRAM}${telegram.value}`}> - + )} {discord && ( diff --git a/packages/ui/src/validators/components/ValidatorsFilter.tsx b/packages/ui/src/validators/components/ValidatorsFilter.tsx index 5772a80b38..7b046d63ec 100644 --- a/packages/ui/src/validators/components/ValidatorsFilter.tsx +++ b/packages/ui/src/validators/components/ValidatorsFilter.tsx @@ -1,7 +1,8 @@ import React, { useEffect, useState } from 'react' import styled from 'styled-components' -import { FilterBox } from '@/common/components/forms/FilterBox' +import { InputNotification } from '@/common/components/forms' +import { Fields, FilterBox } from '@/common/components/forms/FilterBox' import { SearchBox } from '@/common/components/forms/FilterBox/FilterSearchBox' import { FilterSelect } from '@/common/components/selects' @@ -24,8 +25,8 @@ export const ValidatorsFilter = ({ filter }: ValidatorFilterProps) => { const display = () => filter.setSearch(search) const { isVerified, isActive } = filter - const verificationValue = isVerified === true ? 'verified' : isVerified === false ? 'unverified' : undefined - const stateValue = isActive === true ? 'active' : isActive === false ? 'waiting' : undefined + const verificationValue = isVerified === true ? 'verified' : isVerified === false ? 'unverified' : null + const stateValue = isActive === true ? 'active' : isActive === false ? 'waiting' : null const clear = filter.search || verificationValue || stateValue @@ -37,8 +38,8 @@ export const ValidatorsFilter = ({ filter }: ValidatorFilterProps) => { : undefined return ( - - + + { /> - - + + ) } +const ValidatorFilterBox = styled(FilterBox)` + ${Fields} { + padding-bottom: 22px; + } +` + const SelectFields = styled.div` display: flex; justify-content: flex-start; @@ -74,11 +81,16 @@ const SelectFields = styled.div` } } ` -const Fields = styled.div` +const ResponsiveWrapper = styled.div` display: flex; justify-content: space-between; gap: 8px; + ${InputNotification} { + top: unset; + bottom: 2px; + } + @media (max-width: 767px) { flex-direction: column; } diff --git a/packages/ui/src/validators/components/ValidatorsList.tsx b/packages/ui/src/validators/components/ValidatorsList.tsx index 0f9ef40cda..a45eeaa3b4 100644 --- a/packages/ui/src/validators/components/ValidatorsList.tsx +++ b/packages/ui/src/validators/components/ValidatorsList.tsx @@ -65,7 +65,7 @@ export const ValidatorsList = ({ validators, eraIndex, order, pagination }: Vali The APR is subject to the amount staked and have a diminishing return for higher token amounts. This is calculated as follow:
- Yearly Reward * Commission / Stake + Yearly Reward * (1 - Commission) / Stake
Reward:
Average reward generated (during the last 30 days) extrapolated over a year.
@@ -88,6 +88,9 @@ export const ValidatorsList = ({ validators, eraIndex, order, pagination }: Vali isDescending={order.isDescending} > Commission + The validator commission on the nominators rewards

}> + +
{!validators ? ( diff --git a/packages/ui/src/validators/components/statistics/Era.tsx b/packages/ui/src/validators/components/statistics/Era.tsx index 4babce7c28..10f52d7304 100644 --- a/packages/ui/src/validators/components/statistics/Era.tsx +++ b/packages/ui/src/validators/components/statistics/Era.tsx @@ -12,7 +12,7 @@ import { import { DurationValue } from '@/common/components/typography/DurationValue' import { ERA_DURATION } from '@/common/constants' import { MILLISECONDS_PER_BLOCK } from '@/common/model/formatters' -import { whenDefined } from '@/common/utils' +import { isDefined, whenDefined } from '@/common/utils' interface EraProps { eraStartedOn: number | undefined @@ -44,23 +44,23 @@ export const Era = ({ eraStartedOn }: EraProps) => { tooltipText="One era consists of 6 epochs with 1 hour duration each." tooltipTitle="Era" tooltipLinkText="What is an era" - actionElement={} + actionElement={isDefined(percentage) && } > Next Reward -
- -
+
{isDefined(nextReward) && }
Blocks / Points -
- {blocks && ( - + + {blocks ? ( + <> {blocks} / {blocks * POINTS_PER_BLOCK} - + + ) : ( + '- / -' )} -
+
) diff --git a/packages/ui/src/validators/components/statistics/Rewards.tsx b/packages/ui/src/validators/components/statistics/Rewards.tsx index 7e66d5b6dc..94d0d97c5b 100644 --- a/packages/ui/src/validators/components/statistics/Rewards.tsx +++ b/packages/ui/src/validators/components/statistics/Rewards.tsx @@ -23,7 +23,7 @@ export const Rewards = ({ totalRewards, lastRewards }: RewardsProps) => { - Last + Last Era diff --git a/packages/ui/src/validators/components/statistics/Staking.tsx b/packages/ui/src/validators/components/statistics/Staking.tsx index 319a042c3b..204771cac8 100644 --- a/packages/ui/src/validators/components/statistics/Staking.tsx +++ b/packages/ui/src/validators/components/statistics/Staking.tsx @@ -20,10 +20,10 @@ export const Staking = ({ idealStaking, currentStaking, stakingPercentage }: Sta return ( diff --git a/packages/ui/src/validators/components/statistics/ValidatorsState.tsx b/packages/ui/src/validators/components/statistics/ValidatorsState.tsx index f35be07583..2abc3e5d90 100644 --- a/packages/ui/src/validators/components/statistics/ValidatorsState.tsx +++ b/packages/ui/src/validators/components/statistics/ValidatorsState.tsx @@ -16,23 +16,21 @@ export const ValidatorsState = ({ allNominatorsCount, }: ValidatorsStateProps) => { return ( - + Validator (Active / Waiting) - {activeValidatorsCount} / {allValidatorsCount - activeValidatorsCount} + {`${activeValidatorsCount > 0 ? activeValidatorsCount : '-'} / ${ + allValidatorsCount && activeValidatorsCount ? allValidatorsCount - activeValidatorsCount : '-' + }`} Nominator (Active / Total) - {activeNominatorsCount} / {allNominatorsCount} + {`${activeNominatorsCount > 0 ? activeValidatorsCount : '-'} / ${ + activeNominatorsCount && allNominatorsCount ? allNominatorsCount : '-' + }`} diff --git a/packages/ui/src/validators/hooks/useValidatorsList.tsx b/packages/ui/src/validators/hooks/useValidatorsList.tsx index d5d23f79eb..8a5ae7b717 100644 --- a/packages/ui/src/validators/hooks/useValidatorsList.tsx +++ b/packages/ui/src/validators/hooks/useValidatorsList.tsx @@ -4,7 +4,7 @@ import { ValidatorsContext } from '../providers/context' import { ValidatorDetailsOrder } from '../types' const VALIDATOR_PER_PAGE = 7 -const DESCENDING_KEYS: ValidatorDetailsOrder['key'][] = ['apr', 'commission'] +const DESCENDING_KEYS: ValidatorDetailsOrder['key'][] = ['apr'] export const useValidatorsList = () => { const [search, setSearch] = useState('') diff --git a/packages/ui/src/validators/modals/NominatingRedirectModal/NominatingRedirectModal.tsx b/packages/ui/src/validators/modals/NominatingRedirectModal/NominatingRedirectModal.tsx index dd058515e7..e5c13617bc 100644 --- a/packages/ui/src/validators/modals/NominatingRedirectModal/NominatingRedirectModal.tsx +++ b/packages/ui/src/validators/modals/NominatingRedirectModal/NominatingRedirectModal.tsx @@ -29,7 +29,7 @@ export const NominatingRedirectModal = () => { size="medium" href="https://polkadot.js.org/apps/?rpc=wss://rpc.joystream.org:9944#/staking/actions" > - Norminate + Nominate diff --git a/packages/ui/src/validators/modals/validatorCard/Nominators.tsx b/packages/ui/src/validators/modals/validatorCard/Nominators.tsx index d7c9df0f6d..acaa3689fe 100644 --- a/packages/ui/src/validators/modals/validatorCard/Nominators.tsx +++ b/packages/ui/src/validators/modals/validatorCard/Nominators.tsx @@ -29,10 +29,10 @@ export const Nominators = ({ validator }: Props) => { {validator.staking?.nominators?.map(({ address, staking }, index) => ( - + - + ))} @@ -51,10 +51,9 @@ const Title = styled.h4` const NominatorList = styled(List)` gap: 8px; ` -const ValidatorItemWrap = styled.div` +const NominatorItemWrap = styled.div` display: grid; grid-template-columns: 1fr 1fr; - grid-template-rows: 1fr; justify-content: space-between; justify-items: end; align-items: center; @@ -65,6 +64,11 @@ const ValidatorItemWrap = styled.div` cursor: pointer; transition: ${Transitions.all}; ${TableListItemAsLinkHover} + + @media (max-width: 424px) { + grid-gap: 8px; + grid-template-columns: 1fr; + } ` const ListHeaders = styled.div` display: grid; diff --git a/packages/ui/src/validators/modals/validatorCard/ValidatorDetail.tsx b/packages/ui/src/validators/modals/validatorCard/ValidatorDetail.tsx index 15fbb8bba6..e46b788ce1 100644 --- a/packages/ui/src/validators/modals/validatorCard/ValidatorDetail.tsx +++ b/packages/ui/src/validators/modals/validatorCard/ValidatorDetail.tsx @@ -68,7 +68,9 @@ export const ValidatorDetail = ({ validator, eraIndex, hideModal }: Props) => {
Era points
- +
+ +
)} @@ -115,6 +117,14 @@ const Details = styled(RowGapBlock)` const ModalStatistics = styled(StatisticsThreeColumns)` grid-gap: 10px; + + @media (max-width: 767px) { + grid-template-columns: 1fr 1fr; + } + + @media (max-width: 424px) { + grid-template-columns: 1fr; + } ` const Stat = styled(NumericValueStat)` @@ -123,5 +133,10 @@ const Stat = styled(NumericValueStat)` const RewardPointsChartWrapper = styled.div` width: 100%; - height: 200px; + overflow: auto; + + > div { + min-width: 500px; + height: 200px; + } ` diff --git a/packages/ui/src/validators/providers/utils.ts b/packages/ui/src/validators/providers/utils.ts index 59cb67132a..00c4349b5f 100644 --- a/packages/ui/src/validators/providers/utils.ts +++ b/packages/ui/src/validators/providers/utils.ts @@ -130,7 +130,14 @@ export const getValidatorInfo = ( const commission = validator.commission const averageReward = rewards.reduce((sum, reward) => sum.add(reward.eraReward), BN_ZERO).divn(rewards.length) - const apr = Number(averageReward.muln(ERAS_PER_YEAR).muln(commission).div(staking.total)) + const apr = + Number( + averageReward + .muln(ERAS_PER_YEAR) + .muln(100 - commission) + .muln(100) + .div(staking.total) + ) / 100 return { APR: apr } }) )