From a2016ae785b9c49b81a81dde84a77f7eebfa2e6c Mon Sep 17 00:00:00 2001 From: aeddaqqa Date: Mon, 18 Mar 2024 15:22:23 +0000 Subject: [PATCH 1/3] Added a badge to the validator partners. --- src/@types/partners.ts | 31 +++++++++++++++++++ src/components/Partners/PartnerCard.tsx | 36 +++++++++++++++------- src/components/Partners/PartnersFilter.tsx | 10 +++--- src/hooks/useWallet.ts | 9 +++++- src/layout/MainLayout.tsx | 20 ++++++++---- src/redux/services/partners.ts | 2 +- src/redux/slices/app-config.ts | 10 +++++- src/redux/slices/utils.ts | 8 +++++ src/theme/typography.ts | 2 +- src/views/partners/ListPartners.tsx | 4 +++ 10 files changed, 105 insertions(+), 27 deletions(-) diff --git a/src/@types/partners.ts b/src/@types/partners.ts index 4be89d19..23123e40 100644 --- a/src/@types/partners.ts +++ b/src/@types/partners.ts @@ -11,6 +11,7 @@ export interface AttributesType { contactFirstname?: string contactLastname?: string contactPhone?: string + pChainAddress?: string companyShortDescription?: string companyLongDescription?: string isConsortiumMember?: boolean @@ -146,3 +147,33 @@ export interface PaginationType { pageCount: number total: number } + +type RewardOwner = { + locktime: string + threshold: string + addresses: string[] +} + +type RewardInfo = { + locktime: string + threshold: string + addresses: string[] +} + +export type Validator = { + txID: string + startTime: string + endTime: string + weight: string + nodeID: string + stakeAmount: string + rewardOwner: RewardOwner + validationRewardOwner: RewardInfo + delegationRewardOwner: RewardInfo + potentialReward: string + delegationFee: string + uptime: string + connected: boolean + delegatorCount: string + delegatorWeight: string +} diff --git a/src/components/Partners/PartnerCard.tsx b/src/components/Partners/PartnerCard.tsx index 477a205a..34da1523 100644 --- a/src/components/Partners/PartnerCard.tsx +++ b/src/components/Partners/PartnerCard.tsx @@ -1,7 +1,9 @@ import { Box, Typography } from '@mui/material' -import React from 'react' -import { PartnerDataType } from '../../@types/partners' +import React, { useEffect, useState } from 'react' +import { PartnerDataType, Validator } from '../../@types/partners' +import { useEffectOnce } from '../../hooks/useEffectOnce' +import useWallet from '../../hooks/useWallet' import PartnerBusinessFields from './PartnerBusinessFields' import PartnerFlag from './PartnerFlag' import PartnerLogo from './PartnerLogo' @@ -11,21 +13,33 @@ interface PartnerCardProps { clickable: boolean partner: PartnerDataType index: number + validators: Validator[] } -const PartnerCard: React.FC = ({ partner, clickable, onClick }) => { +const PartnerCard: React.FC = ({ partner, clickable, onClick, validators }) => { + useEffectOnce(() => {}) + const { getRegisteredNode } = useWallet() + const [isValidator, setIsValidator] = useState(false) + + const chackValidatorStatus = async (address: string) => { + if (!pChainAddress) setIsValidator(false) + let nodeID = await getRegisteredNode(address) + setIsValidator(!!validators.find(v => v.nodeID === nodeID)) + } const { attributes: { - isConsortiumMember, companyName, companyShortDescription, business_fields, companyLogoColor, country_flag, logoBox, + pChainAddress, }, } = partner - + useEffect(() => { + chackValidatorStatus(pChainAddress) + }, [partner]) return ( = ({ partner, clickable, onClick } overflow: 'hidden', }} > - {/* - // this value is incorrect, it should be based on the pchain addres but its not yet added to the api - {!!isConsortiumMember && ( + {!!isValidator && ( theme.palette.background.gradient, + background: theme => theme.palette.blue[50], padding: '10px 14px 8px 12px', display: 'flex', alignItems: 'center', @@ -61,9 +73,11 @@ const PartnerCard: React.FC = ({ partner, clickable, onClick } top: '0', }} > - Validator + theme.palette.grey[950] }} variant="overline"> + Validator + - )} */} + )} {!!companyLogoColor && !!companyName && ( = ({ state, dispatchPartners - {/* - disabled for now because we don't have the pchain address for the partner in strapi Only Validators} control={ = ({ state, dispatchPartners /> } /> - */} + ) } diff --git a/src/hooks/useWallet.ts b/src/hooks/useWallet.ts index de5e5967..8de5f7f8 100644 --- a/src/hooks/useWallet.ts +++ b/src/hooks/useWallet.ts @@ -1,3 +1,4 @@ +import { ava as caminoClient } from 'wallet/caminoClient' import { getNameOfWallet, getPchainAddress } from '../helpers/walletStore' import { updatePchainAddress } from '../redux/slices/app-config' import { useAppDispatch } from './reduxHooks' @@ -5,6 +6,12 @@ import { useAppDispatch } from './reduxHooks' const useWallet = () => { const dispatch = useAppDispatch() + async function getCurrentValidators() { + return caminoClient.PChain().getCurrentValidators() + } + async function getRegisteredNode(address: string): Promise { + return await caminoClient.PChain().getRegisteredShortIDLink(address) + } const updateStore = (type, params) => { switch (type) { case 'updateName': @@ -16,7 +23,7 @@ const useWallet = () => { ) } } - return { updateStore } + return { updateStore, getRegisteredNode, getCurrentValidators } } export default useWallet diff --git a/src/layout/MainLayout.tsx b/src/layout/MainLayout.tsx index fd55188b..d04cdef6 100644 --- a/src/layout/MainLayout.tsx +++ b/src/layout/MainLayout.tsx @@ -1,5 +1,12 @@ -import { Backdrop, CircularProgress, Paper, Typography } from '@mui/material' -import { Box, Toolbar, useTheme } from '@mui/material' +import { + Backdrop, + Box, + CircularProgress, + Paper, + Toolbar, + Typography, + useTheme, +} from '@mui/material' import React, { useState } from 'react' import { addNetworks, @@ -8,21 +15,22 @@ import { changeNetworkStatus, } from '../redux/slices/network' +import { Status } from '../@types' import Footer from '../components/Footer' import Navbar from '../components/Navbar' import Notifications from '../components/Notification' -import { Status } from '../@types' import { changeActiveApp } from '../redux/slices/app-config' import { matchNetworkStatus } from '../utils/componentsUtils' // @ts-ignore +import { useEffect } from 'react' +import { useLocation } from 'react-router-dom' import store from 'wallet/store' import { useAppDispatch } from '../hooks/reduxHooks' -import { useEffect } from 'react' import { useEffectOnce } from '../hooks/useEffectOnce' -import { useLocation } from 'react-router-dom' import useNetwork from '../hooks/useNetwork' // @ts-ignore import { useStore } from 'Explorer/useStore' +import { getCurrentValidators } from '../redux/slices/utils' const MainLayout = ({ children }) => { const [loadNetworks, setLoadNetworks] = useState(true) @@ -59,7 +67,7 @@ const MainLayout = ({ children }) => { useEffectOnce(() => { init() }) - + dispatch(getCurrentValidators()) useEffect(() => { const html = document.documentElement if (loading || loadNetworks) html.style.overflow = 'hidden' diff --git a/src/redux/services/partners.ts b/src/redux/services/partners.ts index ec1506af..50a9de24 100644 --- a/src/redux/services/partners.ts +++ b/src/redux/services/partners.ts @@ -28,7 +28,7 @@ export const partnersApi = createApi({ query += `&filters[companyName][$contains]=${companyName}` } if (validators) { - query += `&filters[isConsortiumMember][$eq]=true` + query += `&filters[pChainAddress][$ne]=null` } return query diff --git a/src/redux/slices/app-config.ts b/src/redux/slices/app-config.ts index 4fa3c190..8ee28f53 100644 --- a/src/redux/slices/app-config.ts +++ b/src/redux/slices/app-config.ts @@ -1,9 +1,10 @@ import { createSlice } from '@reduxjs/toolkit' import store from 'wallet/store' import { Status, SuitePlatforms } from '../../@types' +import { Validator } from '../../@types/partners' import { APPS_CONSTS } from '../../constants/apps-consts' import { RootState } from '../store' -import { updateAuthStatus } from './utils' +import { getCurrentValidators, updateAuthStatus } from './utils' type NotificationSeverityType = 'success' | 'warning' | 'info' | 'error' interface InitialStateAppConfigType { @@ -19,6 +20,7 @@ interface InitialStateAppConfigType { account: any showButton: boolean pChainAddress: string + validators: Validator[] } let initialState: InitialStateAppConfigType = { @@ -34,6 +36,7 @@ let initialState: InitialStateAppConfigType = { notificationMessage: '', account: null, showButton: false, + validators: [], } const appConfigSlice = createSlice({ @@ -94,6 +97,9 @@ const appConfigSlice = createSlice({ builder.addCase(updateAuthStatus.rejected, (state, { payload }) => { state.isAuth = false }) + builder.addCase(getCurrentValidators.fulfilled, (state, { payload }) => { + state.validators = payload + }) }, }) @@ -130,6 +136,8 @@ export const getPChainAddress = (state: RootState) => state.appConfig.pChainAddr // getWalletName export const getWalletName = (state: RootState) => state.appConfig.walletName +export const selectValidators = (state: RootState) => state.appConfig.validators + export const { changeActiveApp, updateValues, diff --git a/src/redux/slices/utils.ts b/src/redux/slices/utils.ts index 5626f794..c3791846 100644 --- a/src/redux/slices/utils.ts +++ b/src/redux/slices/utils.ts @@ -1,4 +1,5 @@ import { createAsyncThunk } from '@reduxjs/toolkit' +import useWallet from '../../hooks/useWallet' export const updateAuthStatus = createAsyncThunk( 'appConfig/updateAuthStatus', @@ -9,3 +10,10 @@ export const updateAuthStatus = createAsyncThunk( return false }, ) + +export const getCurrentValidators = createAsyncThunk('appConfig/getCurrentValidators', async () => { + const { getCurrentValidators } = useWallet() + try { + return (await getCurrentValidators()).validators + } catch (e) {} +}) diff --git a/src/theme/typography.ts b/src/theme/typography.ts index 33693359..d83f4804 100644 --- a/src/theme/typography.ts +++ b/src/theme/typography.ts @@ -140,7 +140,7 @@ const typography = { }, overline: { fontFamily: FONT, - fontWeight: 700, + fontWeight: 600, lineHeight: '18px', fontSize: pxToRem(12), letterSpacing: '-1.1%', diff --git a/src/views/partners/ListPartners.tsx b/src/views/partners/ListPartners.tsx index 825a5655..7860d37b 100644 --- a/src/views/partners/ListPartners.tsx +++ b/src/views/partners/ListPartners.tsx @@ -4,6 +4,8 @@ import { Box } from '@mui/material' import React from 'react' import { useNavigate } from 'react-router' import PartnerCard from '../../components/Partners/PartnerCard' +import { useAppSelector } from '../../hooks/reduxHooks' +import { selectValidators } from '../../redux/slices/app-config' interface ListPartnersProps { partners: PartnersResponseType @@ -11,6 +13,7 @@ interface ListPartnersProps { const ListPartners: React.FC = ({ partners }) => { const navigate = useNavigate() + const validators = useAppSelector(selectValidators) return ( = ({ partners }) => { > {partners.data.map((partner, index) => ( { if ( !!( From 9a2716c2fab053ad9e8357acc76518d94ab99433 Mon Sep 17 00:00:00 2001 From: aeddaqqa Date: Mon, 8 Apr 2024 00:33:27 +0000 Subject: [PATCH 2/3] feat(partners) : add error page when partners api is down --- package.json | 6 ++--- src/views/partners/index.tsx | 48 +++++++++++++++++++++++++++++++++--- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 61aecc4e..9a649184 100644 --- a/package.json +++ b/package.json @@ -32,11 +32,11 @@ "@emotion/styled": "^11.10.5", "@mdi/js": "^7.0.96", "@mdi/react": "^1.6.1", - "@mui/icons-material": "5.10.14", - "@mui/material": "5.10.14", - "@mui/system": "5.10.14", "@reduxjs/toolkit": "^1.8.1", "@testing-library/dom": "^8.19.0", + "@mui/icons-material": "^5.15.11", + "@mui/material": "^5.15.11", + "@mui/system": "^5.15.11", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.0.1", "@testing-library/user-event": "^14.1.1", diff --git a/src/views/partners/index.tsx b/src/views/partners/index.tsx index cfd73960..028ddaa1 100644 --- a/src/views/partners/index.tsx +++ b/src/views/partners/index.tsx @@ -1,4 +1,11 @@ -import { Box, CircularProgress, Pagination, PaginationItem, Typography } from '@mui/material' +import { + Box, + CircularProgress, + Pagination, + PaginationItem, + Typography, + useTheme, +} from '@mui/material' import React, { ReactNode, useReducer } from 'react' import { initialStatePartners, @@ -6,6 +13,8 @@ import { partnersReducer, } from '../../helpers/partnersReducer' +import { mdiAccessPointNetwork } from '@mdi/js' +import Icon from '@mdi/react' import ArrowBackIcon from '@mui/icons-material/ArrowBack' import ArrowForwardIcon from '@mui/icons-material/ArrowForward' import PartnersFilter from '../../components/Partners/PartnersFilter' @@ -41,14 +50,46 @@ const PartnersListWrapper: React.FC = ({ const Partners = () => { const [state, dispatchPartnersActions] = useReducer(partnersReducer, initialStatePartners) - const { data: partners, isLoading, isFetching } = useListPartnersQuery(state) + const { data: partners, isLoading, isFetching, error } = useListPartnersQuery(state) const handleChange = (event: React.ChangeEvent, value: number) => { dispatchPartnersActions({ type: partnersActions.NEXT_PAGE, payload: value }) } - + const theme = useTheme() + if (error) { + return ( + + + + Something went wrong + + We have encountered an unexpected issue with our current system. + + + + ) + } if (!partners?.data) { return } + const content = ( <> @@ -72,6 +113,7 @@ const Partners = () => { ) + return ( Date: Mon, 8 Apr 2024 00:42:21 +0000 Subject: [PATCH 3/3] feat(partners): add routing to partners when partner route is not found --- src/views/partners/Partner.tsx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/views/partners/Partner.tsx b/src/views/partners/Partner.tsx index 0cd291b6..f8138f72 100644 --- a/src/views/partners/Partner.tsx +++ b/src/views/partners/Partner.tsx @@ -47,10 +47,27 @@ const Partner = () => { data: partner, isLoading, isFetching, + error, } = useFetchPartnerDataQuery({ companyName: partnerID, }) const navigate = useNavigate() + if ( + error || + !partner || + !!( + partner.attributes.companyName && + partner.attributes.companyLongDescription && + partner.attributes.companyWebsite && + partner.attributes.contactEmail && + partner.attributes.contactFirstname && + partner.attributes.contactLastname && + partner.attributes.contactPhone + ) + ) { + navigate('/partners') + return null + } if (isLoading || isFetching) return <> return (