Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

4330 create validator profile #4428

Merged
merged 48 commits into from
Dec 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
0f3f878
Compoenents Created
eshark9312 Jun 5, 2023
e614115
RowInline created
eshark9312 Jun 5, 2023
66e224e
create profile modal complete
eshark9312 Jun 7, 2023
e1d6bee
profile modification modal complete
eshark9312 Jun 7, 2023
f38ad8a
change BuyMembershipModal to multiTransaction modal
eshark9312 Jun 12, 2023
ccbf067
add Bonding Validator Account Transaction,fix the machine
eshark9312 Jun 12, 2023
9feb498
fix the bug in machine
eshark9312 Jun 12, 2023
f0aa60f
fix the bug
eshark9312 Jun 12, 2023
d35dd27
complete the UI,but did not implement transaction for binding validat…
eshark9312 Jun 12, 2023
26a13a5
lint:fix
eshark9312 Jun 13, 2023
bc7cb69
add bondValidatorAcc tx
eshark9312 Sep 14, 2023
d74e18b
update storybook
eshark9312 Sep 14, 2023
a621b78
fix mistake
eshark9312 Sep 14, 2023
c8c8f7d
lint fix
eshark9312 Sep 14, 2023
bb0ae93
fix storybook
eshark9312 Sep 14, 2023
20fb31f
add storybook for update validator membership
eshark9312 Sep 15, 2023
7c76ee6
Revert "fix storybook"
eshark9312 Sep 15, 2023
12faeaa
add some happy and failure case
eshark9312 Sep 15, 2023
d25976f
Metadata to BYTES for UpdateProfile Tx
eshark9312 Nov 6, 2023
04cdc44
buyValidatorMembership flow draft
eshark9312 Nov 21, 2023
18b468b
update machine, modal flow
eshark9312 Nov 21, 2023
979f7c4
fix machine
eshark9312 Nov 21, 2023
7a2254e
address merge conflicts
eshark9312 Nov 21, 2023
83a93a1
lint --fix
eshark9312 Nov 21, 2023
06d5006
update MembershipForm, UpdateMembershipForm
eshark9312 Nov 22, 2023
e6a1e66
update interaction test
eshark9312 Nov 22, 2023
b336907
fix machine self transition condition, correct PlusIcon import
eshark9312 Nov 22, 2023
1c6cea5
fix signer, update test
eshark9312 Dec 8, 2023
3346b38
revert changes on UpdateMembership
eshark9312 Dec 8, 2023
a0a4157
Merge branch 'dev' into 4330-CreateValidatorProfile
eshark9312 Dec 8, 2023
8fbb7aa
fix
eshark9312 Dec 8, 2023
d3182d2
update app.stories.tsx with validator provider mocking
eshark9312 Dec 18, 2023
1f44b0d
Revert "update app.stories.tsx with validator provider mocking"
eshark9312 Dec 18, 2023
ff449b6
update storybook comment
eshark9312 Dec 18, 2023
699b6d9
update buymembership machine
eshark9312 Dec 20, 2023
5e37fa0
update buyMembership machine
eshark9312 Dec 20, 2023
f28995d
Merge branch '4330-CreateValidatorProfile' of https://github.com/esha…
eshark9312 Dec 20, 2023
884ddaf
Merge remote-tracking branch 'origin/dev' into 4330-CreateValidatorPr…
eshark9312 Dec 20, 2023
1df9b06
fix
eshark9312 Dec 20, 2023
b5ac65c
Fix the duplicated transaction signing
thesan Dec 22, 2023
bae6040
Improve binding tests
thesan Dec 22, 2023
c1a0b4b
Change the select validator account function
thesan Dec 22, 2023
732d7da
add validatorProvider
eshark9312 Dec 18, 2023
dc4337a
Merge remote-tracking branch 'origin/dev' into 4330-CreateValidatorPr…
eshark9312 Dec 22, 2023
afa815c
check validator account
eshark9312 Dec 24, 2023
64b6ed0
Count the membership's root/controller account into the validator mem…
eshark9312 Dec 24, 2023
ac96134
fix
eshark9312 Dec 27, 2023
6e750c4
Skip validator query until it's needed
thesan Dec 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions packages/ui/.storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { TransactionStatusProvider } from '../src/common/providers/transactionSt
import { MockProvidersDecorator, MockRouterDecorator } from '../src/mocks/providers'
import { i18next } from '../src/services/i18n'
import { KeyringContext } from '../src/common/providers/keyring/context'
import { ValidatorContextProvider } from '../src/validators/providers/provider'
import { Keyring } from '@polkadot/ui-keyring'

configure({ testIdAttribute: 'id' })
Expand Down Expand Up @@ -56,11 +57,13 @@ const ModalDecorator: Decorator = (Story) => (
<TransactionStatusProvider>
<ModalContextProvider>
<OnBoardingProvider>
<Story />
<GlobalModals />
<NotificationsHolder>
<TransactionStatus />
</NotificationsHolder>
<ValidatorContextProvider>
<Story />
<GlobalModals />
<NotificationsHolder>
<TransactionStatus />
</NotificationsHolder>
</ValidatorContextProvider>
</OnBoardingProvider>
</ModalContextProvider>
</TransactionStatusProvider>
Expand Down
545 changes: 540 additions & 5 deletions packages/ui/src/app/App.stories.tsx

Large diffs are not rendered by default.

35 changes: 19 additions & 16 deletions packages/ui/src/app/Providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { OnBoardingProvider } from '@/common/providers/onboarding/provider'
import { ResponsiveProvider } from '@/common/providers/responsive/provider'
import { TransactionStatusProvider } from '@/common/providers/transactionStatus/provider'
import { MembershipContextProvider } from '@/memberships/providers/membership/provider'
import { ValidatorContextProvider } from '@/validators/providers/provider'

import { BackendProvider } from './providers/backend/provider'
import { GlobalStyle } from './providers/GlobalStyle'
Expand All @@ -31,22 +32,24 @@ export const Providers = ({ children }: Props) => (
<TransactionStatusProvider>
<BalancesContextProvider>
<MembershipContextProvider>
<HashRouter>
<RouteActions>
<ModalContextProvider>
<BackendProvider>
<OnBoardingProvider>
<ImageReportProvider>
<ResponsiveProvider>
<GlobalStyle />
{children}
</ResponsiveProvider>
</ImageReportProvider>
</OnBoardingProvider>
</BackendProvider>
</ModalContextProvider>
</RouteActions>
</HashRouter>
<ValidatorContextProvider>
<HashRouter>
<RouteActions>
<ModalContextProvider>
<BackendProvider>
<OnBoardingProvider>
<ImageReportProvider>
<ResponsiveProvider>
<GlobalStyle />
{children}
</ResponsiveProvider>
</ImageReportProvider>
</OnBoardingProvider>
</BackendProvider>
</ModalContextProvider>
</RouteActions>
</HashRouter>
</ValidatorContextProvider>
</MembershipContextProvider>
</BalancesContextProvider>
</TransactionStatusProvider>
Expand Down
10 changes: 10 additions & 0 deletions packages/ui/src/common/components/Modal/Modals.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ export const Row = styled.div`
height: auto;
`

export const RowInline = styled.div<{ gap?: number; top?: number }>`
display: flex;
flex-direction: row;
width: 100%;
height: auto;
align-items: center;
gap: ${({ gap }) => gap ?? 16}px;
margin-top: ${({ top }) => top ?? 0}px;
`

export const AccountRow = styled.div`
display: grid;
grid-template-columns: 1fr 1fr;
Expand Down
3 changes: 2 additions & 1 deletion packages/ui/src/common/components/forms/Label.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Colors } from '../../constants'
import { TooltipContainer } from '../Tooltip'

interface LabelProps {
noMargin?: boolean
isRequired?: boolean
className?: string
}
Expand All @@ -13,7 +14,7 @@ export const Label = styled.label<LabelProps>`
align-items: center;
align-content: center;
width: fit-content;
margin-bottom: 4px;
margin-bottom: ${({ noMargin }) => (noMargin ? '0px' : '4px')};
font-size: 14px;
line-height: 20px;
font-weight: 700;
Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/common/components/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ export * from './ApplicationIcon'
export * from './CouncilMemberIcon'
export * from './VerifiedMemberIcon'
export * from './MenuIcon'
export * from './PlusIcon'
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { SubmittableExtrinsic } from '@polkadot/api/types'
import { ISubmittableResult } from '@polkadot/types/types'
import React from 'react'
import { ActorRef } from 'xstate'

import { Account } from '@/accounts/types'
import { TextMedium } from '@/common/components/typography'
import { SignTransactionModal } from '@/common/modals/SignTransactionModal/SignTransactionModal'

interface SignProps {
transaction: SubmittableExtrinsic<'rxjs', ISubmittableResult> | undefined
signer: Account
service: ActorRef<any>
}

export const AddStakingAccCandidateModal = ({ transaction, signer, service }: SignProps) => (
<SignTransactionModal
buttonText="Sign and Bond"
transaction={transaction}
signer={signer.address}
skipQueryNode
service={service}
useMultiTransaction={{
steps: [
{ title: 'Create Membership' },
{ title: 'Bind Validator Account' },
{ title: 'Confirm Validator Account' },
],
active: 1,
}}
>
<TextMedium>You are intending to bond your validator account with your membership.</TextMedium>
</SignTransactionModal>
)
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import HCaptcha from '@hcaptcha/react-hcaptcha'
import { BalanceOf } from '@polkadot/types/interfaces/runtime'
import React, { useEffect, useState } from 'react'
import React, { useEffect, useMemo, useState } from 'react'
import { FormProvider, useForm } from 'react-hook-form'
import styled from 'styled-components'
import * as Yup from 'yup'

import { SelectAccount } from '@/accounts/components/SelectAccount'
import { SelectAccount, SelectedAccount } from '@/accounts/components/SelectAccount'
import { useMyAccounts } from '@/accounts/hooks/useMyAccounts'
import { accountOrNamed } from '@/accounts/model/accountOrNamed'
import { encodeAddress } from '@/accounts/model/encodeAddress'
import { Account } from '@/accounts/types'
import { TermsRoutes } from '@/app/constants/routes'
import { ButtonGhost, ButtonPrimary } from '@/common/components/buttons'
Expand All @@ -20,27 +22,30 @@ import {
LabelLink,
ToggleCheckbox,
} from '@/common/components/forms'
import { Arrow } from '@/common/components/icons'
import { Arrow, CrossIcon, PlusIcon } from '@/common/components/icons'
import { AlertSymbol } from '@/common/components/icons/symbols'
import { Loading } from '@/common/components/Loading'
import {
ModalFooter,
ModalFooterGroup,
ModalHeader,
Row,
RowInline,
ScrolledModal,
ScrolledModalBody,
ScrolledModalContainer,
TransactionInfoContainer,
} from '@/common/components/Modal'
import { Tooltip, TooltipDefault } from '@/common/components/Tooltip'
import { TransactionInfo } from '@/common/components/TransactionInfo'
import { TextMedium } from '@/common/components/typography'
import { TextMedium, TextSmall } from '@/common/components/typography'
import { definedValues } from '@/common/utils'
import { useYupValidationResolver } from '@/common/utils/validation'
import { AvatarInput } from '@/memberships/components/AvatarInput'
import { SocialMediaSelector } from '@/memberships/components/SocialMediaSelector/SocialMediaSelector'
import { useUploadAvatarAndSubmit } from '@/memberships/hooks/useUploadAvatarAndSubmit'
import { useGetMembersCountQuery } from '@/memberships/queries'
import { useValidators } from '@/validators/hooks/useValidators'

import { SelectMember } from '../../components/SelectMember'
import {
Expand Down Expand Up @@ -76,6 +81,7 @@ const CreateMemberSchema = Yup.object().shape({
),
hasTerms: Yup.boolean().required().oneOf([true]),
isReferred: Yup.boolean(),
isValidator: Yup.boolean(),
referrer: ReferrerSchema,
externalResources: ExternalResourcesSchema,
})
Expand All @@ -88,6 +94,9 @@ export interface MemberFormFields {
about: string
avatarUri: File | string | null
isReferred?: boolean
isValidator?: boolean
validatorAccountCandidate?: Account
validatorAccounts?: Account[]
referrer?: Member
hasTerms?: boolean
invitor?: Member
Expand All @@ -101,6 +110,7 @@ const formDefaultValues = {
about: '',
avatarUri: null,
isReferred: false,
isValidator: false,
referrer: undefined,
hasTerms: false,
externalResources: {},
Expand Down Expand Up @@ -135,7 +145,30 @@ export const BuyMembershipForm = ({
},
})

const [handle, isReferred, referrer, captchaToken] = form.watch(['handle', 'isReferred', 'referrer', 'captchaToken'])
const [handle, isReferred, isValidator, referrer, captchaToken, validatorAccountCandidate] = form.watch([
'handle',
'isReferred',
'isValidator',
'referrer',
'captchaToken',
'validatorAccountCandidate',
])

const { allValidators, allValidatorsWithCtrlAcc } = useValidators({ skip: isValidator ?? true })
const [validatorAccounts, setValidatorAccounts] = useState<Account[]>([])
const validatorAddresses = useMemo(() => {
if (!allValidatorsWithCtrlAcc || !allValidators) return
return (
[...allValidatorsWithCtrlAcc, ...allValidators.map(({ address }) => address)].filter(
(element) => !!element
) as string[]
).map(encodeAddress)
}, [allValidators, allValidatorsWithCtrlAcc])

const isValidValidatorAccount = useMemo(
thesan marked this conversation as resolved.
Show resolved Hide resolved
() => validatorAccountCandidate && validatorAddresses?.includes(encodeAddress(validatorAccountCandidate.address)),
[allValidators, allValidatorsWithCtrlAcc, validatorAddresses, validatorAccountCandidate]
)

useEffect(() => {
if (handle) {
Expand All @@ -149,10 +182,21 @@ export const BuyMembershipForm = ({
}
}, [data?.membershipsConnection.totalCount])

const isFormValid = !isUploading && form.formState.isValid
const isFormValid = !isUploading && form.formState.isValid && (!isValidator || validatorAccounts?.length)
const isDisabled =
type === 'onBoarding' && process.env.REACT_APP_CAPTCHA_SITE_KEY ? !captchaToken || !isFormValid : !isFormValid

const addValidatorAccount = () => {
if (validatorAccountCandidate && isValidValidatorAccount) {
setValidatorAccounts([...new Set([...validatorAccounts, validatorAccountCandidate])])
form?.setValue('validatorAccountCandidate' as keyof MemberFormFields, undefined)
}
}

const removeValidatorAccount = (index: number) => {
setValidatorAccounts([...validatorAccounts.slice(0, index), ...validatorAccounts.slice(index + 1)])
}

return (
<>
<ScrolledModalBody>
Expand Down Expand Up @@ -234,6 +278,79 @@ export const BuyMembershipForm = ({

<SocialMediaSelector />

{type === 'general' && (
<>
<RowInline top={16}>
<Label>I am a validator: </Label>
<ToggleCheckbox trueLabel="Yes" falseLabel="No" name="isValidator" />
</RowInline>
{isValidator && (
<>
<SelectValidatorAccountWrapper>
<RowInline gap={4}>
<Label noMargin>Add validator controller account or validator stash account</Label>
<Tooltip tooltipText="This is the status which indicates the selected account is actually a validator account.">
<TooltipDefault />
</Tooltip>
<TextSmall dark>*</TextSmall>
</RowInline>
<TextMedium dark>
If your validator account is not in your signer wallet, paste the account address to the field
below:
</TextMedium>
<RowInline>
<InputComponent id="select-validatorAccount" inputSize="l">
<SelectAccount
id="select-validatorAccount"
name="validatorAccountCandidate"
filter={(account) => !!validatorAddresses?.includes(encodeAddress(account.address))}
/>
</InputComponent>
<ButtonPrimary
square
size="large"
onClick={addValidatorAccount}
disabled={!isValidValidatorAccount}
className="add-button"
>
<PlusIcon />
</ButtonPrimary>
</RowInline>
{validatorAccountCandidate && !isValidValidatorAccount && (
<RowInline gap={2}>
<TextSmall error>
<InputNotificationIcon>
<AlertSymbol />
</InputNotificationIcon>
</TextSmall>
<TextSmall error>
This account is neither a validator controller account nor a validator stash account.
</TextSmall>
</RowInline>
)}
</SelectValidatorAccountWrapper>

{validatorAccounts.map((account, index) => (
<Row>
<RowInline>
<SelectedAccount account={account as Account} key={'selected' + index} />
<ButtonGhost
square
size="large"
onClick={() => {
removeValidatorAccount(index)
}}
>
<CrossIcon />
</ButtonGhost>
</RowInline>
</Row>
))}
</>
)}
</>
)}

{process.env.REACT_APP_CAPTCHA_SITE_KEY && type === 'onBoarding' && (
<Row>
<HCaptcha
Expand Down Expand Up @@ -287,6 +404,10 @@ export const BuyMembershipForm = ({
<ButtonPrimary
size="medium"
onClick={() => {
validatorAccounts?.map((account, index) => {
form?.register(('validatorAccounts[' + index + ']') as keyof MemberFormFields)
form?.setValue(('validatorAccounts[' + index + ']') as keyof MemberFormFields, account)
})
const values = form.getValues()
uploadAvatarAndSubmit({ ...values, externalResources: { ...definedValues(values.externalResources) } })
}}
Expand All @@ -308,3 +429,25 @@ export const BuyMembershipFormModal = ({ onClose, onSubmit, membershipPrice }: B
</ScrolledModal>
)
}

const SelectValidatorAccountWrapper = styled.div`
margin-top: -4px;
display: flex;
flex-direction: column;
gap: 8px;
`

const InputNotificationIcon = styled.div`
display: flex;
justify-content: center;
align-items: center;
width: 12px;
height: 12px;
color: inherit;
padding-right: 2px;

.blackPart,
.primaryPart {
fill: currentColor;
}
`
Loading