Merge pull request #41 from nimbus-gui/hn.fix-validator-onboarding
Hn.fix validator onboarding
This commit is contained in:
commit
9d28bf19c1
41
src/App.tsx
41
src/App.tsx
|
@ -21,6 +21,18 @@ import LogsPage from './pages/LogsPage/LogsPage'
|
|||
import { ethereumRopsten, wcV2InitOptions, apiKey } from './constants'
|
||||
import './App.css'
|
||||
|
||||
//ValidatorOnboarding sub-routes
|
||||
import Overview from './pages/ValidatorOnboarding/Overview/Overview'
|
||||
import Advisories from './pages/ValidatorOnboarding/Advisories/Advisories'
|
||||
import ValidatorSetup from './pages/ValidatorOnboarding/ValidatorSetup/ValidatorSetup/ValidatorSetup'
|
||||
import ValidatorSetupInstall from './pages/ValidatorOnboarding/ValidatorSetup/ValidatorInstalling/ValidatorInstall'
|
||||
import ConsensusSelection from './pages/ValidatorOnboarding/ValidatorSetup/ConsensusClient/ConsensusSelection'
|
||||
import ActivationValidatorSetup from './pages/ValidatorOnboarding/ValidatorSetup/ValidatorActivation/ActivationValidatorSetup'
|
||||
import ClientSetup from './pages/ValidatorOnboarding/ClientSetup/ClientSetup'
|
||||
import KeyGeneration from './pages/ValidatorOnboarding/KeyGeneration/KeyGeneration'
|
||||
import Deposit from './pages/ValidatorOnboarding/Deposit/Deposit'
|
||||
import Activation from './pages/ValidatorOnboarding/Activation/Activation'
|
||||
|
||||
const injected = injectedModule()
|
||||
const walletConnect = walletConnectModule(wcV2InitOptions)
|
||||
|
||||
|
@ -55,10 +67,35 @@ const router = createBrowserRouter([
|
|||
element: <PairDevice />,
|
||||
},
|
||||
{ path: '/create-local-node', element: <CreateLocalNode /> },
|
||||
{ path: '/validator-onboarding', element: <ValidatorOnboarding /> },
|
||||
{
|
||||
path: '/validator-onboarding',
|
||||
children: [
|
||||
{ path: '', element: <Overview /> },
|
||||
{ path: 'advisories', element: <Advisories /> },
|
||||
{ path: 'validator-setup', element: <ValidatorSetup /> },
|
||||
{ path: 'validator-setup-install', element: <ValidatorSetupInstall /> },
|
||||
{ path: 'consensus-selection', element: <ConsensusSelection /> },
|
||||
{
|
||||
path: 'activation-validator-setup',
|
||||
element: <ActivationValidatorSetup />,
|
||||
},
|
||||
{ path: 'client-setup', element: <ClientSetup /> },
|
||||
{ path: 'key-generation', element: <KeyGeneration /> },
|
||||
{ path: 'deposit', element: <Deposit /> },
|
||||
{
|
||||
path: 'activation',
|
||||
element: <Activation />,
|
||||
},
|
||||
],
|
||||
element: <ValidatorOnboarding />,
|
||||
},
|
||||
{ path: '/dashboard', element: <Dashboard /> },
|
||||
{ path: '/logs', element: <LogsPage /> },
|
||||
{ path: '/validator-management', element: <ValidatorManagement /> },
|
||||
{
|
||||
path: '/validator-management',
|
||||
|
||||
element: <ValidatorManagement />,
|
||||
},
|
||||
])
|
||||
|
||||
function App() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import ReactMarkdown from 'react-markdown'
|
||||
|
||||
import styles from './../../../pages/ValidatorOnboarding/ValidatorSetup/ValidatorInstalling/InstallLayout.module.css'
|
||||
import MarkdownLink from './MarkdownLink'
|
||||
|
||||
type MarkdownProps = {
|
||||
|
@ -7,7 +7,13 @@ type MarkdownProps = {
|
|||
}
|
||||
|
||||
const Markdown = ({ children }: MarkdownProps) => {
|
||||
return <ReactMarkdown children={children} components={{ a: MarkdownLink }} />
|
||||
return (
|
||||
<ReactMarkdown
|
||||
children={children}
|
||||
components={{ a: MarkdownLink }}
|
||||
className={styles['markdown-text']}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default Markdown
|
||||
|
|
|
@ -5,38 +5,11 @@ import Confetti from 'react-confetti'
|
|||
|
||||
import ActivationCard from './ActivationCard'
|
||||
import LinkWithArrow from '../../../components/General/LinkWithArrow'
|
||||
import { useWindowSize } from '../../../hooks/useWindowSize'
|
||||
|
||||
type ActivationProps = {
|
||||
validatorsValue: string
|
||||
executionSyncStatus1: {
|
||||
text: string
|
||||
isGaugeIncluded: boolean
|
||||
gaugeColor: string
|
||||
gaugeSynced: number
|
||||
gaugeTotal: number
|
||||
}
|
||||
executionSyncStatus2: {
|
||||
text: string
|
||||
isGaugeIncluded: boolean
|
||||
gaugeColor: string
|
||||
gaugeSynced: number
|
||||
gaugeTotal: number
|
||||
}
|
||||
currentAPRValue: string
|
||||
estimatedActivationTimeValue: string
|
||||
validatorQueueValue: string
|
||||
}
|
||||
|
||||
const Activation = ({
|
||||
validatorsValue,
|
||||
executionSyncStatus1,
|
||||
executionSyncStatus2,
|
||||
currentAPRValue,
|
||||
estimatedActivationTimeValue,
|
||||
validatorQueueValue,
|
||||
}: ActivationProps) => {
|
||||
const Activation = () => {
|
||||
const [showConfetti, setShowConfetti] = useState(true)
|
||||
|
||||
const windowSize = useWindowSize()
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
setShowConfetti(false)
|
||||
|
@ -61,22 +34,36 @@ const Activation = ({
|
|||
Validators and are currently syncing your nodes.
|
||||
</Text>
|
||||
</Stack>
|
||||
<YStack space={'$3'} marginTop={'25px'} width={'33%'}>
|
||||
<XStack space={'$3'} justifyContent={'space-between'}>
|
||||
<ActivationCard text="Validators" value={validatorsValue} />
|
||||
<ActivationCard {...executionSyncStatus1} />
|
||||
<ActivationCard {...executionSyncStatus2} />
|
||||
<YStack marginTop={'25px'}>
|
||||
<XStack
|
||||
width="100%"
|
||||
flexWrap={windowSize.width < 780 ? 'wrap' : 'nowrap'}
|
||||
>
|
||||
<ActivationCard
|
||||
text="Execution Sync Status"
|
||||
isGaugeIncluded={true}
|
||||
gaugeColor={'#2a4af5'}
|
||||
gaugeSynced={123.524}
|
||||
gaugeTotal={172.503}
|
||||
/>
|
||||
<ActivationCard
|
||||
text="Execution Sync Status"
|
||||
isGaugeIncluded={true}
|
||||
gaugeColor={'#EB5757'}
|
||||
gaugeSynced={123.524}
|
||||
gaugeTotal={172.503}
|
||||
/>
|
||||
</XStack>
|
||||
<XStack space={'$3'}>
|
||||
<ActivationCard text="Current APR" value={currentAPRValue} />
|
||||
<XStack
|
||||
flexWrap={windowSize.width < 780 ? 'wrap' : 'nowrap'}
|
||||
width="100%"
|
||||
>
|
||||
<ActivationCard text="Validator Queue" value="92603" />
|
||||
<ActivationCard
|
||||
text="Estimated Activation Time"
|
||||
value={estimatedActivationTimeValue}
|
||||
/>
|
||||
<ActivationCard
|
||||
text="Validator Queue"
|
||||
value={validatorQueueValue}
|
||||
value="32 Days"
|
||||
/>
|
||||
<ActivationCard text="Current APR" value="4.40%" />
|
||||
</XStack>
|
||||
</YStack>
|
||||
</YStack>
|
||||
|
|
|
@ -26,7 +26,8 @@ const ActivationCard = ({
|
|||
border: '1px solid rgba(0, 0, 0, 0.15)',
|
||||
padding: '12px 16px',
|
||||
backgroundColor: '#FFF',
|
||||
width: '100%',
|
||||
flex: 1,
|
||||
margin: '8px',
|
||||
}}
|
||||
>
|
||||
{!isGaugeIncluded && (
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Text } from '@status-im/components'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Stack, YStack } from 'tamagui'
|
||||
import { useWindowSize } from '../../../hooks/useWindowSize'
|
||||
|
||||
type AdvisoriesContentProps = {
|
||||
title: string
|
||||
|
@ -8,8 +9,9 @@ type AdvisoriesContentProps = {
|
|||
}
|
||||
|
||||
const AdvisoriesContent = ({ title, content }: AdvisoriesContentProps) => {
|
||||
const windowSize = useWindowSize()
|
||||
return (
|
||||
<YStack space={'$1'} style={{ width: '70%' }}>
|
||||
<YStack space={'$1'} width={windowSize.width < 780 ? '100%' : '70%'}>
|
||||
<Stack style={{ marginBottom: '5%' }}>
|
||||
<Text size={27} weight={400}>
|
||||
{title}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
display: flex;
|
||||
padding: 30px;
|
||||
justify-content: space-between;
|
||||
width: auto;
|
||||
}
|
||||
.advisories-nav {
|
||||
display: flex;
|
||||
|
|
|
@ -8,18 +8,17 @@ import { useNavigate } from 'react-router-dom'
|
|||
|
||||
import { RootState } from '../../redux/store'
|
||||
import LinkWithArrow from '../../components/General/LinkWithArrow'
|
||||
import {
|
||||
setActiveStep,
|
||||
setSubStepValidatorSetup,
|
||||
} from '../../redux/ValidatorOnboarding/slice'
|
||||
import { setActiveStep } from '../../redux/ValidatorOnboarding/slice'
|
||||
import { KEYSTORE_FILES } from '../../constants'
|
||||
import {
|
||||
setIsConfirmPhraseStage,
|
||||
setIsCopyPastedPhrase,
|
||||
setValidWords,
|
||||
} from '../../redux/ValidatorOnboarding/KeyGeneration/slice'
|
||||
import { useWindowSize } from '../../hooks/useWindowSize'
|
||||
|
||||
const ContinueButton = () => {
|
||||
const windowSize = useWindowSize()
|
||||
const [isDisabled, setIsDisabled] = useState(false)
|
||||
const {
|
||||
isCopyPastedPhrase,
|
||||
|
@ -33,24 +32,44 @@ const ContinueButton = () => {
|
|||
const { activeStep, subStepValidatorSetup } = useSelector(
|
||||
(state: RootState) => state.validatorOnboarding,
|
||||
)
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const pathToStepMap = {
|
||||
'/validator-onboarding/': 0,
|
||||
'/validator-onboarding/advisories': 1,
|
||||
'/validator-onboarding/validator-setup': 2,
|
||||
'/validator-onboarding/validator-setup-install': 3,
|
||||
'/validator-onboarding/consensus-selection': 4,
|
||||
'/validator-onboarding/activation-validator-setup': 5,
|
||||
'/validator-onboarding/client-setup': 6,
|
||||
'/validator-onboarding/key-generation': 7,
|
||||
'/validator-onboarding/deposit': 8,
|
||||
'/validator-onboarding/activation': 9,
|
||||
}
|
||||
dispatch(
|
||||
setActiveStep(
|
||||
pathToStepMap[location.pathname as keyof typeof pathToStepMap] || 0,
|
||||
),
|
||||
)
|
||||
|
||||
const { isValidatorSet } = useSelector(
|
||||
(state: RootState) => state.validatorSetup,
|
||||
)
|
||||
|
||||
const dispatch = useDispatch()
|
||||
const navigate = useNavigate()
|
||||
const isActivationValScreen = activeStep === 3 && subStepValidatorSetup === 3
|
||||
|
||||
const isActivationValScreen = activeStep === 5
|
||||
|
||||
useEffect(() => {
|
||||
const getDisabledButton = () => {
|
||||
if (activeStep === 4 && isConfirmPhraseStage) {
|
||||
if (activeStep === 7 && isConfirmPhraseStage) {
|
||||
if (
|
||||
validWords.some(w => w === false) ||
|
||||
generatedMnemonic.some((w, i) => w !== mnemonic[i])
|
||||
) {
|
||||
return true
|
||||
}
|
||||
} else if (activeStep === 3 && !isValidatorSet) {
|
||||
} else if (activeStep === 6 && !isValidatorSet) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
@ -66,22 +85,10 @@ const ContinueButton = () => {
|
|||
isValidatorSet,
|
||||
])
|
||||
|
||||
const handleStep1 = () => {
|
||||
dispatch(setActiveStep(activeStep + 1))
|
||||
}
|
||||
|
||||
const handleStep2 = () => {
|
||||
if (subStepValidatorSetup === 3) {
|
||||
return dispatch(setActiveStep(activeStep + 1))
|
||||
}
|
||||
dispatch(setSubStepValidatorSetup(subStepValidatorSetup + 1))
|
||||
}
|
||||
|
||||
const handleStep4 = () => {
|
||||
const handleRecoveryMechanism = () => {
|
||||
if (!isConfirmPhraseStage && recoveryMechanism === KEYSTORE_FILES) {
|
||||
return dispatch(setActiveStep(activeStep + 1))
|
||||
}
|
||||
|
||||
if (!isConfirmPhraseStage) {
|
||||
return dispatch(setIsConfirmPhraseStage(true))
|
||||
}
|
||||
|
@ -102,26 +109,43 @@ const ContinueButton = () => {
|
|||
}
|
||||
|
||||
const continueHandler = () => {
|
||||
if (activeStep === 1) {
|
||||
handleStep1()
|
||||
} else if (activeStep === 2) {
|
||||
handleStep2()
|
||||
} else if (activeStep === 4) {
|
||||
handleStep4()
|
||||
} else {
|
||||
if (activeStep < 6) {
|
||||
dispatch(setActiveStep(activeStep + 1))
|
||||
let nextPath
|
||||
if (activeStep === 0) nextPath = '/validator-onboarding/advisories'
|
||||
else if (activeStep === 1)
|
||||
nextPath = '/validator-onboarding/validator-setup'
|
||||
else if (activeStep === 2)
|
||||
nextPath = '/validator-onboarding/validator-setup-install'
|
||||
else if (activeStep === 3)
|
||||
nextPath = '/validator-onboarding/consensus-selection'
|
||||
else if (activeStep === 4)
|
||||
nextPath = '/validator-onboarding/activation-validator-setup'
|
||||
else if (activeStep === 5) {
|
||||
nextPath = '/validator-onboarding/client-setup'
|
||||
} else if (activeStep === 6) {
|
||||
nextPath = '/validator-onboarding/key-generation'
|
||||
} else if (activeStep === 7) {
|
||||
if (isConfirmPhraseStage) {
|
||||
nextPath = '/validator-onboarding/deposit'
|
||||
} else {
|
||||
navigate('/dashboard')
|
||||
nextPath = '/validator-onboarding/key-generation'
|
||||
}
|
||||
}
|
||||
handleRecoveryMechanism()
|
||||
} else if (activeStep === 8) nextPath = '/validator-onboarding/activation'
|
||||
else if (activeStep === 9) nextPath = '/dashboard'
|
||||
else nextPath = '/validator-onboarding/'
|
||||
|
||||
navigate(nextPath)
|
||||
}
|
||||
|
||||
return (
|
||||
<XStack
|
||||
style={{
|
||||
width: '100%',
|
||||
justifyContent: isActivationValScreen ? 'space-between' : 'end',
|
||||
justifyContent: isActivationValScreen
|
||||
? 'space-between'
|
||||
: windowSize.width < 560
|
||||
? 'start'
|
||||
: 'end',
|
||||
alignItems: 'center',
|
||||
zIndex: 1000,
|
||||
marginTop: '21px',
|
||||
|
@ -145,7 +169,7 @@ const ContinueButton = () => {
|
|||
/>
|
||||
)}
|
||||
<Button onPress={continueHandler} size={40} disabled={isDisabled}>
|
||||
{activeStep < 6 ? 'Continue' : 'Continue to Dashboard'}
|
||||
{activeStep < 9 ? 'Continue' : 'Continue to Dashboard'}
|
||||
</Button>
|
||||
</XStack>
|
||||
)
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
import type { Meta, StoryObj } from '@storybook/react'
|
||||
|
||||
import FormStepper from './FormStepper'
|
||||
|
||||
const meta = {
|
||||
title: 'ValidatorOnboarding/FormStepper',
|
||||
component: FormStepper,
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
},
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
activeStep: {
|
||||
options: [0, 1, 2, 3, 4, 5],
|
||||
control: { type: 'radio' },
|
||||
defaultValue: 0,
|
||||
},
|
||||
},
|
||||
} satisfies Meta<typeof FormStepper>
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
export const OverviewActive: Story = {
|
||||
args: {
|
||||
activeStep: 0,
|
||||
},
|
||||
}
|
||||
|
||||
export const AdvisoriesActive: Story = {
|
||||
args: {
|
||||
activeStep: 1,
|
||||
},
|
||||
}
|
||||
|
||||
export const ClientSetupActive: Story = {
|
||||
args: {
|
||||
activeStep: 2,
|
||||
},
|
||||
}
|
||||
|
||||
export const ValidatorSetupActive: Story = {
|
||||
args: {
|
||||
activeStep: 3,
|
||||
},
|
||||
}
|
||||
|
||||
export const KeyGenerationActive: Story = {
|
||||
args: {
|
||||
activeStep: 4,
|
||||
},
|
||||
}
|
||||
|
||||
export const ActivationActive: Story = {
|
||||
args: {
|
||||
activeStep: 5,
|
||||
},
|
||||
}
|
||||
|
||||
export const NoActiveStep: Story = {
|
||||
args: {
|
||||
activeStep: -1,
|
||||
},
|
||||
}
|
|
@ -1,7 +1,5 @@
|
|||
import { Stepper, Step } from 'react-form-stepper'
|
||||
import { useDispatch } from 'react-redux'
|
||||
|
||||
import { setActiveStep } from '../../../redux/ValidatorOnboarding/slice'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { FORM_STEPS } from '../../../constants'
|
||||
import { useWindowSize } from '../../../hooks/useWindowSize'
|
||||
import './FormStepper.css'
|
||||
|
@ -11,9 +9,22 @@ type FormStepperProps = {
|
|||
}
|
||||
|
||||
const FormStepper = ({ activeStep }: FormStepperProps) => {
|
||||
const dispatch = useDispatch()
|
||||
const navigate = useNavigate()
|
||||
const windowSize = useWindowSize()
|
||||
|
||||
const stepToUrlMap = [
|
||||
'/validator-onboarding/',
|
||||
'/validator-onboarding/advisories',
|
||||
'/validator-onboarding/validator-setup',
|
||||
'/validator-onboarding/validator-setup-install',
|
||||
'/validator-onboarding/consensus-selection',
|
||||
'/validator-onboarding/activation-validator-setup',
|
||||
'/validator-onboarding/client-setup',
|
||||
'/validator-onboarding/key-generation',
|
||||
'/validator-onboarding/deposit',
|
||||
'/validator-onboarding/activation',
|
||||
]
|
||||
|
||||
const getIsStepVisible = (
|
||||
index: number,
|
||||
stepsBefore: number,
|
||||
|
@ -23,7 +34,6 @@ const FormStepper = ({ activeStep }: FormStepperProps) => {
|
|||
let start = activeStep - stepsBefore
|
||||
let end = activeStep + stepsAfter
|
||||
|
||||
// active step is near the start or end
|
||||
if (start < 0) {
|
||||
end -= start
|
||||
start = 0
|
||||
|
@ -41,23 +51,27 @@ const FormStepper = ({ activeStep }: FormStepperProps) => {
|
|||
|
||||
const isStepVisible = (index: number) => {
|
||||
if (windowSize.width < 774) {
|
||||
return getIsStepVisible(index, 1, 1) // 3 steps (1 before, 1 after)
|
||||
return getIsStepVisible(index, 1, 1)
|
||||
} else if (windowSize.width < 963) {
|
||||
return getIsStepVisible(index, 1, 2) // 4 steps
|
||||
return getIsStepVisible(index, 1, 2)
|
||||
} else if (windowSize.width < 1183) {
|
||||
return getIsStepVisible(index, 1, 3) // 5 steps
|
||||
return getIsStepVisible(index, 1, 3)
|
||||
} else if (windowSize.width < 1300) {
|
||||
return getIsStepVisible(index, 2, 3) // 6 steps
|
||||
return getIsStepVisible(index, 2, 3)
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
const changeStepOnClickHandler = (index: number) => {
|
||||
if (activeStep > index) {
|
||||
dispatch(setActiveStep(index))
|
||||
const path = stepToUrlMap[index]
|
||||
if (path && index < activeStep) {
|
||||
navigate(path)
|
||||
}
|
||||
}
|
||||
if (activeStep > 1) {
|
||||
activeStep = activeStep < 6 ? 2 : activeStep - 3
|
||||
}
|
||||
|
||||
return (
|
||||
<Stepper
|
||||
|
@ -85,12 +99,18 @@ const FormStepper = ({ activeStep }: FormStepperProps) => {
|
|||
completed={activeStep > originalIndex - 1}
|
||||
data-subtitle={step.subtitle}
|
||||
data-step={step.label}
|
||||
style={
|
||||
originalIndex === activeStep
|
||||
? { backgroundColor: stepStyle.currentBgColor }
|
||||
: {}
|
||||
}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</Stepper>
|
||||
)
|
||||
}
|
||||
|
||||
const stepStyle = {
|
||||
// For default dots:
|
||||
inactiveBgColor: '#FFFFFF',
|
||||
|
@ -107,9 +127,10 @@ const stepStyle = {
|
|||
inactiveTextColor: '#000000',
|
||||
size: '28px',
|
||||
circleFontSize: '0px',
|
||||
labelFontSize: '14px',
|
||||
labelFontSize: '13px',
|
||||
borderRadius: '50%',
|
||||
fontWeight: 700,
|
||||
currentBgColor: '#808080',
|
||||
}
|
||||
|
||||
const customConnectorStyle = {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { YStack } from 'tamagui'
|
||||
import { YStack, Stack } from 'tamagui'
|
||||
import { Text } from '@status-im/components'
|
||||
|
||||
import OverviewCard from './OverviewCard'
|
||||
|
@ -12,10 +12,17 @@ const Overview = () => {
|
|||
imgHeight="250%"
|
||||
rightImageSrc="./background-images/sync-status-background.png"
|
||||
>
|
||||
<YStack space={'$5'} marginTop={'2rem'} width="100%">
|
||||
<Text size={27} weight={'semibold'}>
|
||||
Overview
|
||||
</Text>
|
||||
<YStack
|
||||
space={'$5'}
|
||||
marginTop={'2rem'}
|
||||
marginBottom={'2rem'}
|
||||
width="100%"
|
||||
>
|
||||
<Stack marginBottom={'$2'}>
|
||||
<Text size={27} weight={'semibold'}>
|
||||
Overview
|
||||
</Text>
|
||||
</Stack>
|
||||
<Text size={19}>
|
||||
Becoming a validator is a big responsibility with important
|
||||
preparation steps. Only start the deposit process when you're ready.
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
flex-wrap: wrap;
|
||||
width: 250%;
|
||||
}
|
||||
|
||||
.overviewCard {
|
||||
border-radius: 16px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.15);
|
||||
|
@ -19,6 +20,5 @@
|
|||
}
|
||||
.overviewCard {
|
||||
width: 35%;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,23 +4,15 @@ import { useSelector } from 'react-redux'
|
|||
import { RootState } from '../../redux/store'
|
||||
import FormStepper from './FormStepper/FormStepper'
|
||||
import Titles from '../../components/General/Titles'
|
||||
import Overview from './Overview/Overview'
|
||||
import KeyGeneration from './KeyGeneration/KeyGeneration'
|
||||
import Activation from './Activation/Activation'
|
||||
import ValidatorBoxWrapper from './ValidatorBoxWrapper/ValidatorBoxWrapper'
|
||||
import ClientSetup from './ClientSetup/ClientSetup'
|
||||
import ConsensusSelection from './ValidatorSetup/ConsensusClient/ConsensusSelection'
|
||||
import Advisories from './Advisories/Advisories'
|
||||
import ValidatorSetup from './ValidatorSetup/ValidatorSetup/ValidatorSetup'
|
||||
import ValidatorSetupInstall from './ValidatorSetup/ValidatorInstalling/ValidatorInstall'
|
||||
import ContinueButton from './ContinueButton'
|
||||
import ActivationValidatorSetup from './ValidatorSetup/ValidatorActivation/ActivationValidatorSetup'
|
||||
import Deposit from './Deposit/Deposit'
|
||||
|
||||
import { useWindowSize } from '../../hooks/useWindowSize'
|
||||
import styles from './layoutGradient.module.css'
|
||||
import { Outlet } from 'react-router-dom'
|
||||
|
||||
const ValidatorOnboarding = () => {
|
||||
const { activeStep, subStepValidatorSetup } = useSelector(
|
||||
const { activeStep } = useSelector(
|
||||
(state: RootState) => state.validatorOnboarding,
|
||||
)
|
||||
const windowSize = useWindowSize()
|
||||
|
@ -42,45 +34,7 @@ const ValidatorOnboarding = () => {
|
|||
/>
|
||||
<FormStepper activeStep={activeStep} />
|
||||
<ValidatorBoxWrapper>
|
||||
{activeStep === 0 && <Overview />}
|
||||
{activeStep === 1 && <Advisories />}
|
||||
{activeStep === 2 && subStepValidatorSetup === 0 && (
|
||||
<ValidatorSetup />
|
||||
)}
|
||||
{activeStep === 2 && subStepValidatorSetup === 1 && (
|
||||
<ValidatorSetupInstall />
|
||||
)}
|
||||
{activeStep === 2 && subStepValidatorSetup === 2 && (
|
||||
<ConsensusSelection />
|
||||
)}
|
||||
{activeStep === 2 && subStepValidatorSetup === 3 && (
|
||||
<ActivationValidatorSetup />
|
||||
)}
|
||||
{activeStep === 3 && <ClientSetup />}
|
||||
{activeStep === 4 && <KeyGeneration />}
|
||||
{activeStep === 5 && <Deposit />}
|
||||
{activeStep === 6 && (
|
||||
<Activation
|
||||
validatorsValue="4"
|
||||
executionSyncStatus1={{
|
||||
text: 'Execution Sync Status',
|
||||
isGaugeIncluded: true,
|
||||
gaugeColor: '$blue',
|
||||
gaugeSynced: 123.524,
|
||||
gaugeTotal: 172.503,
|
||||
}}
|
||||
executionSyncStatus2={{
|
||||
text: 'Execution Sync Status',
|
||||
isGaugeIncluded: true,
|
||||
gaugeColor: '$red',
|
||||
gaugeSynced: 123.524,
|
||||
gaugeTotal: 172.503,
|
||||
}}
|
||||
currentAPRValue="4.40%"
|
||||
estimatedActivationTimeValue="32 Days"
|
||||
validatorQueueValue="92603"
|
||||
/>
|
||||
)}
|
||||
<Outlet />
|
||||
</ValidatorBoxWrapper>
|
||||
<ContinueButton />
|
||||
</YStack>
|
||||
|
|
|
@ -2,6 +2,7 @@ import { Stack, YStack } from 'tamagui'
|
|||
import { Text } from '@status-im/components'
|
||||
|
||||
import Icon from '../../../../components/General/Icon'
|
||||
import { useWindowSize } from '../../../../hooks/useWindowSize'
|
||||
|
||||
type ConsensusClientCardProps = {
|
||||
name: string
|
||||
|
@ -9,6 +10,7 @@ type ConsensusClientCardProps = {
|
|||
}
|
||||
|
||||
const ConsensusClientCard = ({ name, icon }: ConsensusClientCardProps) => {
|
||||
const windowSize = useWindowSize()
|
||||
return (
|
||||
<YStack
|
||||
style={{
|
||||
|
@ -16,8 +18,9 @@ const ConsensusClientCard = ({ name, icon }: ConsensusClientCardProps) => {
|
|||
border: '1px solid #2A4AF5',
|
||||
borderRadius: '16px',
|
||||
padding: '12px 16px',
|
||||
width: '29%',
|
||||
cursor: 'pointer',
|
||||
minWidth: '250px',
|
||||
width: windowSize.width < 780 ? '100%' : 'auto',
|
||||
}}
|
||||
space={'$8'}
|
||||
>
|
||||
|
|
|
@ -3,8 +3,8 @@ import { InfoBadgeIcon } from '@status-im/icons'
|
|||
import { Text } from '@status-im/components'
|
||||
|
||||
import StandardGauge from '../../../../components/Charts/StandardGauge'
|
||||
import BorderBox from '../../../../components/General/BorderBox'
|
||||
import { formatNumbersWithComa } from '../../../../utilities'
|
||||
import { useWindowSize } from '../../../../hooks/useWindowSize'
|
||||
|
||||
type ConsensusGaugeCardProps = {
|
||||
synced: number
|
||||
|
@ -19,43 +19,56 @@ const ConsensusGaugeCard = ({
|
|||
title,
|
||||
color,
|
||||
}: ConsensusGaugeCardProps) => {
|
||||
const windowSize = useWindowSize()
|
||||
return (
|
||||
<BorderBox style={{ borderRadius: '10.1px', borderWidth: '0.5px' }}>
|
||||
<XStack space={'$2'} alignItems="center">
|
||||
<Stack
|
||||
style={{
|
||||
height: '35px',
|
||||
width: '35px',
|
||||
}}
|
||||
>
|
||||
<StandardGauge
|
||||
data={[
|
||||
{
|
||||
id: title,
|
||||
label: title,
|
||||
value: synced,
|
||||
color: color,
|
||||
},
|
||||
{
|
||||
id: 'free',
|
||||
label: 'free',
|
||||
value: total - synced || 1,
|
||||
color: '#E7EAEE',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Stack>
|
||||
<YStack>
|
||||
<Text size={11} color="#84888e" weight={'semibold'}>
|
||||
{title}
|
||||
</Text>
|
||||
<Text size={15} weight={'semibold'}>
|
||||
{formatNumbersWithComa(synced)} / {formatNumbersWithComa(total)}
|
||||
</Text>
|
||||
</YStack>
|
||||
<Stack
|
||||
style={{
|
||||
border: '1px solid #DCE0E5',
|
||||
borderRadius: '10px',
|
||||
padding: '6px 12px',
|
||||
borderWidth: '0.5px',
|
||||
width: windowSize.width < 580 ? '100%' : 'auto',
|
||||
height: '100%',
|
||||
}}
|
||||
>
|
||||
<XStack space={'$2'} alignItems="center" justifyContent="space-between">
|
||||
<XStack space={'$3'}>
|
||||
<Stack
|
||||
style={{
|
||||
height: '35px',
|
||||
width: '35px',
|
||||
}}
|
||||
space={'$2'}
|
||||
>
|
||||
<StandardGauge
|
||||
data={[
|
||||
{
|
||||
id: title,
|
||||
label: title,
|
||||
value: synced,
|
||||
color: color,
|
||||
},
|
||||
{
|
||||
id: 'free',
|
||||
label: 'free',
|
||||
value: total - synced || 1,
|
||||
color: '#E7EAEE',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Stack>
|
||||
<YStack>
|
||||
<Text size={11} color="#84888e" weight={'semibold'}>
|
||||
{title}
|
||||
</Text>
|
||||
<Text size={15} weight={'semibold'}>
|
||||
{formatNumbersWithComa(synced)} / {formatNumbersWithComa(total)}
|
||||
</Text>
|
||||
</YStack>
|
||||
</XStack>
|
||||
<InfoBadgeIcon size={20} color="#A1ABBD" />
|
||||
</XStack>
|
||||
</BorderBox>
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import ConsensusGaugeCard from './ConsensusGaugeCard'
|
|||
import ConsensusClientCard from './ConsensusClientCard'
|
||||
import LinkWithArrow from '../../../../components/General/LinkWithArrow'
|
||||
import { RootState } from '../../../../redux/store'
|
||||
import { useWindowSize } from '../../../../hooks/useWindowSize'
|
||||
|
||||
const clientIcons = {
|
||||
Nethermind: '/icons/nethermind-circle.png',
|
||||
|
@ -17,6 +18,7 @@ const clientIcons = {
|
|||
}
|
||||
|
||||
const ConsensusSelection = () => {
|
||||
const windowSize = useWindowSize()
|
||||
const selectedClient = useSelector(
|
||||
(state: RootState) => state.execClient.selectedClient,
|
||||
) as 'Nethermind' | 'Besu' | 'Geth' | 'Erigon' | 'Nimbus'
|
||||
|
@ -37,9 +39,14 @@ const ConsensusSelection = () => {
|
|||
flexWrap="wrap"
|
||||
>
|
||||
<Text size={27} weight={'semibold'}>
|
||||
Validator Setup
|
||||
Client Setup
|
||||
</Text>
|
||||
<XStack space={'$2'} flexWrap="wrap">
|
||||
<XStack
|
||||
space={'$2'}
|
||||
flexWrap={windowSize.width < 735 ? 'wrap' : 'nowrap'}
|
||||
marginTop={windowSize.width < 735 ? '20px' : 0}
|
||||
width={windowSize.width < 580 ? '100%' : 'auto'}
|
||||
>
|
||||
<PairedDeviceCard />
|
||||
|
||||
<ConsensusGaugeCard
|
||||
|
@ -76,7 +83,7 @@ const ConsensusSelection = () => {
|
|||
|
||||
<XStack space={'$8'} flexWrap="wrap">
|
||||
<ConsensusClientCard name={clients[0].name} icon={clients[0].icon} />
|
||||
<YStack width={'67%'} maxWidth="550px" space={'$4'}>
|
||||
<YStack width={windowSize.width < 780 ? '100%' : '70%'} space={'$4'}>
|
||||
<Text size={19}>The resource efficient Ethereum Clients.</Text>
|
||||
<Text size={15}>
|
||||
{selectedClient} is a client implementation for both execution and
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { XStack, YStack } from 'tamagui'
|
||||
import { InfoBadgeIcon } from '@status-im/icons'
|
||||
import { Avatar, Text } from '@status-im/components'
|
||||
import { useWindowSize } from '../../../../hooks/useWindowSize'
|
||||
|
||||
const PairedDeviceCard = () => {
|
||||
const windowSize = useWindowSize()
|
||||
return (
|
||||
<XStack
|
||||
space={'$7'}
|
||||
|
@ -10,10 +12,13 @@ const PairedDeviceCard = () => {
|
|||
padding: '6px 12px',
|
||||
border: '1px solid #DCE0E5',
|
||||
borderRadius: '10px',
|
||||
marginBottom: '20px',
|
||||
width: windowSize.width < 580 ? '100%' : 'auto',
|
||||
}}
|
||||
justifyContent="space-between"
|
||||
alignItems={'center'}
|
||||
>
|
||||
<XStack space={'$3'} alignItems={'center'}>
|
||||
<XStack space={'$3'}>
|
||||
<Avatar
|
||||
backgroundColor="pink"
|
||||
type="icon"
|
||||
|
|
|
@ -4,10 +4,11 @@ import { Text } from '@status-im/components'
|
|||
import Confetti from 'react-confetti'
|
||||
|
||||
import ActivationCard from '../../Activation/ActivationCard'
|
||||
import { useWindowSize } from '../../../../hooks/useWindowSize'
|
||||
|
||||
const ActivationValidatorSetup = () => {
|
||||
const [showConfetti, setShowConfetti] = useState(true)
|
||||
|
||||
const windowSize = useWindowSize()
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
setShowConfetti(false)
|
||||
|
@ -19,7 +20,7 @@ const ActivationValidatorSetup = () => {
|
|||
}, [])
|
||||
|
||||
return (
|
||||
<Stack style={styles.confettiContainer} width={'100%'} minHeight={'65vh'}>
|
||||
<Stack style={styles.confettiContainer} minHeight={'65vh'}>
|
||||
{showConfetti && <Confetti style={styles.confettiCanvas} />}
|
||||
<YStack style={{ padding: '26px 32px' }}>
|
||||
<YStack space={'$5'}>
|
||||
|
@ -34,8 +35,11 @@ const ActivationValidatorSetup = () => {
|
|||
making a deposit.
|
||||
</Text>
|
||||
</Stack>
|
||||
<YStack space={'$3'} marginTop={'25px'} width={'33%'}>
|
||||
<XStack width={'151%'} space={'$3'}>
|
||||
<YStack marginTop={'25px'}>
|
||||
<XStack
|
||||
width="100%"
|
||||
flexWrap={windowSize.width < 780 ? 'wrap' : 'nowrap'}
|
||||
>
|
||||
<ActivationCard
|
||||
text="Execution Sync Status"
|
||||
isGaugeIncluded={true}
|
||||
|
@ -51,7 +55,10 @@ const ActivationValidatorSetup = () => {
|
|||
gaugeTotal={172.503}
|
||||
/>
|
||||
</XStack>
|
||||
<XStack space={'$3'}>
|
||||
<XStack
|
||||
flexWrap={windowSize.width < 780 ? 'wrap' : 'nowrap'}
|
||||
width="100%"
|
||||
>
|
||||
<ActivationCard text="Validator Queue" value="92603" />
|
||||
<ActivationCard
|
||||
text="Estimated Activation Time"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.osCardsContainer {
|
||||
.os-cards-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
display: grid;
|
||||
|
@ -7,36 +7,45 @@
|
|||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
.osCard {
|
||||
.os-card {
|
||||
border: 1px solid rgba(0, 0, 0, 0.15);
|
||||
border-radius: 16px;
|
||||
padding: 12px 16px;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.osCardSelected {
|
||||
.markdown-text {
|
||||
overflow-x: auto;
|
||||
width: 100%;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
.os-card-selected {
|
||||
background-color: #2a4af50d;
|
||||
border: 1px solid #2a4af566;
|
||||
border-radius: 16px;
|
||||
padding: 12px 16px;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media (max-width: 1000px) {
|
||||
.osCardsContainer {
|
||||
.os-cards-container {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
.osCard:nth-child(3) {
|
||||
.os-card:nth-child(3) {
|
||||
width: 205%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 750px) {
|
||||
.osCardsContainer {
|
||||
@media (max-width: 850px) {
|
||||
.os-cards-container {
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
}
|
||||
.osCard {
|
||||
.os-card {
|
||||
width: 100%;
|
||||
}
|
||||
.osCard:nth-child(3) {
|
||||
.os-card:nth-child(3) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,13 +14,15 @@ type OSCardsProps = {
|
|||
}
|
||||
const OSCards = ({ selectedOS, handleOSCardClick }: OSCardsProps) => {
|
||||
return (
|
||||
<div className={styles.osCardsContainer}>
|
||||
<div className={styles['os-cards-container']}>
|
||||
{cards.map(card => (
|
||||
<div
|
||||
key={card.name}
|
||||
className={`${styles.osCard} ${
|
||||
selectedOS === card.name ? styles.osCardSelected : ''
|
||||
}`}
|
||||
className={
|
||||
selectedOS === card.name
|
||||
? styles['os-card-selected']
|
||||
: styles['os-card']
|
||||
}
|
||||
onClick={() => handleOSCardClick(card.name)}
|
||||
>
|
||||
<OSCard key={card.name} icon={card.icon} name={card.name} />
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Stack, YStack } from 'tamagui'
|
||||
import { Text } from '@status-im/components'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { useState } from 'react'
|
||||
|
||||
import { RootState } from '../../../../redux/store'
|
||||
|
@ -8,21 +8,56 @@ import { DOCUMENTATIONS } from './documentations'
|
|||
import { MAC } from '../../../../constants'
|
||||
import OSCards from './OSCards'
|
||||
import Markdown from '../../../../components/General/Markdown/Markdown'
|
||||
import { setPinnedMessage } from '../../../../redux/PinnedMessage/slice'
|
||||
|
||||
function extractBashCommands(documentation: any) {
|
||||
const bashCommandRegex = /```bash\n([\s\S]*?)\n```/g
|
||||
const matches = []
|
||||
let match
|
||||
|
||||
while ((match = bashCommandRegex.exec(documentation)) !== null) {
|
||||
matches.push(match[1])
|
||||
}
|
||||
return matches.join('\n\n')
|
||||
}
|
||||
|
||||
const ValidatorSetupInstall = () => {
|
||||
const dispatch = useDispatch()
|
||||
const [selectedOS, setSelectedOS] = useState(MAC)
|
||||
const selectedClient = useSelector(
|
||||
(state: RootState) => state.execClient.selectedClient,
|
||||
)
|
||||
const docText = DOCUMENTATIONS[selectedClient].documentation[selectedOS]
|
||||
const bashCommands = extractBashCommands(docText)
|
||||
|
||||
const handleOSCardClick = (os: string) => {
|
||||
setSelectedOS(os)
|
||||
}
|
||||
|
||||
const copyCommands = () => {
|
||||
navigator.clipboard.writeText(bashCommands)
|
||||
dispatch(
|
||||
setPinnedMessage({
|
||||
id: '123',
|
||||
text: 'You have successfully copied the commands to your clipboard.',
|
||||
pinned: true,
|
||||
}),
|
||||
)
|
||||
setTimeout(() => {
|
||||
dispatch(
|
||||
setPinnedMessage({
|
||||
id: '123',
|
||||
text: 'You have successfully copied the commands to your clipboard.',
|
||||
pinned: false,
|
||||
}),
|
||||
)
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
return (
|
||||
<YStack style={{ padding: '26px 32px', width: 'fit-content' }}>
|
||||
<YStack style={{ padding: '26px 32px', width: '100%' }}>
|
||||
<Text size={27} weight={'semibold'}>
|
||||
Validator Setup
|
||||
Client Setup
|
||||
</Text>
|
||||
<YStack
|
||||
style={{
|
||||
|
@ -43,9 +78,13 @@ const ValidatorSetupInstall = () => {
|
|||
selectedOS={selectedOS}
|
||||
handleOSCardClick={handleOSCardClick}
|
||||
/>
|
||||
<Markdown
|
||||
children={DOCUMENTATIONS[selectedClient].documentation[selectedOS]}
|
||||
/>
|
||||
<Stack onPress={() => copyCommands()}>
|
||||
<Markdown
|
||||
children={
|
||||
DOCUMENTATIONS[selectedClient].documentation[selectedOS]
|
||||
}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</YStack>
|
||||
</YStack>
|
||||
|
|
|
@ -19,7 +19,7 @@ const ValidatorSetup = () => {
|
|||
space={'$8'}
|
||||
>
|
||||
<Text size={27} weight={'semibold'}>
|
||||
Validator Setup
|
||||
Client Setup
|
||||
</Text>
|
||||
<PairedDeviceCard />
|
||||
</XStack>
|
||||
|
|
Loading…
Reference in New Issue