Skip to content

Commit

Permalink
add ability to add identity
Browse files Browse the repository at this point in the history
  • Loading branch information
Tbaut committed Oct 16, 2023
1 parent 40be892 commit 438e273
Show file tree
Hide file tree
Showing 7 changed files with 243 additions and 58 deletions.
45 changes: 14 additions & 31 deletions packages/ui/src/components/AccountDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import { AccountBadge, IconSizeVariant } from '../types'
import { getDisplayAddress } from '../utils'
import IdenticonBadge from './IdenticonBadge'
import { useApi } from '../contexts/ApiContext'
import { DeriveAccountInfo, DeriveAccountRegistration } from '@polkadot/api-derive/types'
import IdentityIcon from './IdentityIcon'
import Balance from './library/Balance'
import { useGetEncodedAddress } from '../hooks/useGetEncodedAddress'
import { useIdentity } from '../hooks/useIdentity'

interface Props {
address: string
Expand All @@ -30,44 +30,27 @@ const AccountDisplay = ({
}: Props) => {
const { getNamesWithExtension } = useAccountNames()
const localName = useMemo(() => getNamesWithExtension(address), [address, getNamesWithExtension])
const [identity, setIdentity] = useState<DeriveAccountRegistration | null>(null)
const { api } = useApi()
const [mainDisplay, setMainDisplay] = useState<string>('')
const [sub, setSub] = useState<string | null>(null)
const getEncodedAddress = useGetEncodedAddress()
const encodedAddress = useMemo(() => getEncodedAddress(address), [address, getEncodedAddress])
const identity = useIdentity(address)

useEffect(() => {
if (!api) {
return
}

let unsubscribe: () => void

api.derive.accounts
.info(address, (info: DeriveAccountInfo) => {
setIdentity(info.identity)
if (!identity) return

if (info.identity.displayParent && info.identity.display) {
// when an identity is a sub identity `displayParent` is set
// and `display` get the sub identity
setMainDisplay(info.identity.displayParent)
setSub(info.identity.display)
} else {
// There should not be a `displayParent` without a `display`
// but we can't be too sure.
setMainDisplay(
info.identity.displayParent || info.identity.display || info.nickname || ''
)
}
})
.then((unsub) => {
unsubscribe = unsub
})
.catch((e) => console.error(e))

return () => unsubscribe && unsubscribe()
}, [address, api])
if (identity.displayParent && identity.display) {
// when an identity is a sub identity `displayParent` is set
// and `display` get the sub identity
setMainDisplay(identity.displayParent)
setSub(identity.display)
} else {
// There should not be a `displayParent` without a `display`
// but we can't be too sure.
setMainDisplay(identity.displayParent || identity.display || '')
}
}, [address, api, identity])

return (
<div className={className}>
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/components/EasySetup/BalancesTransfer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { styled } from '@mui/material/styles'
import { SubmittableExtrinsic } from '@polkadot/api/types'
import { ISubmittableResult } from '@polkadot/types/types'
import GenericAccountSelection, { AccountBaseInfo } from '../select/GenericAccountSelection'
import React, { useCallback, useEffect, useMemo, useState } from 'react'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { useApi } from '../../contexts/ApiContext'
import { useCheckBalance } from '../../hooks/useCheckBalance'
import BN from 'bn.js'
Expand Down
127 changes: 127 additions & 0 deletions packages/ui/src/components/EasySetup/IdentitySetIdentity.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { Grid } from '@mui/material'
import { styled } from '@mui/material/styles'
import { SubmittableExtrinsic } from '@polkadot/api/types'
import { ISubmittableResult } from '@polkadot/types/types'
import { useCallback, useEffect, useState } from 'react'
import { useApi } from '../../contexts/ApiContext'
import { TextFieldStyled } from '../library'
import { useIdentity } from '../../hooks/useIdentity'

interface Props {
className?: string
from: string
onSetExtrinsic: (ext?: SubmittableExtrinsic<'promise', ISubmittableResult>) => void
}

interface IdentityFields {
display: string | undefined
legal: string | undefined
web: string | undefined
riot: string | undefined
email: string | undefined
image: string | undefined
twitter: string | undefined
}

const getRawOrNone = (val: string | undefined) => {
return val
? {
Raw: val
}
: { none: null }
}
const getExtrinsicsArgs = (identity: IdentityFields) => {
const { legal, display, email, image, riot, twitter, web } = identity

return {
additional: [],
display: getRawOrNone(display),
legal: getRawOrNone(legal),
web: getRawOrNone(web),
riot: getRawOrNone(riot),
email: getRawOrNone(email),
pgpFingerprint: null,
image: getRawOrNone(image),
twitter: getRawOrNone(twitter)
}
}

const IdentitySetIdentity = ({ className, onSetExtrinsic, from }: Props) => {
const { api, chainInfo } = useApi()
const [identity, setIdentity] = useState<IdentityFields | undefined>()
const chainIdentity = useIdentity(from)

useEffect(() => {
if (chainIdentity) {
const { display, email, legal, web, riot, twitter, image } = chainIdentity
setIdentity({
display,
legal,
web,
riot,
email,
image,
twitter
})
} else {
setIdentity({
display: undefined,
legal: undefined,
web: undefined,
riot: undefined,
email: undefined,
image: undefined,
twitter: undefined
})
}
}, [chainIdentity])

useEffect(() => {
if (!api) {
onSetExtrinsic(undefined)
return
}

if (!identity) {
onSetExtrinsic(undefined)
return
}

const extrinsicsArgs = getExtrinsicsArgs(identity)
onSetExtrinsic(api.tx.identity.setIdentity(extrinsicsArgs))
}, [api, chainInfo, identity, onSetExtrinsic])

const onChangeField = useCallback((field: keyof IdentityFields, value: string) => {
setIdentity((prev) => (prev ? { ...prev, [field]: value } : undefined))
}, [])

return (
<Grid
className={className}
container
spacing={1}
>
{identity &&
Object.entries(identity).map(([fieldName, value]) => (
<Grid
item
xs={12}
sm={6}
md={6}
alignItems="center"
>
<TextFieldStyled
data-cy={`${fieldName}-amount`}
label={fieldName}
onChange={(val) => onChangeField(fieldName as keyof IdentityFields, val.target.value)}
value={value || ''}
/>
</Grid>
))}
</Grid>
)
}

export default styled(IdentitySetIdentity)`
margin-top: 0.5rem;
`
51 changes: 36 additions & 15 deletions packages/ui/src/components/modals/Send.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,27 @@ import FromCallData from '../EasySetup/FromCallData'
import { ModalCloseButton } from '../library/ModalCloseButton'
import { formatBnBalance } from '../../utils/formatBnBalance'
import { useGetMultisigTx } from '../../hooks/useGetMultisigTx'
import IdentitySetIdentity from '../EasySetup/IdentitySetIdentity'

const SEND_TOKEN_MENU = 'Send tokens'
const FROM_CALL_DATA_MENU = 'From call data'
const MANUEL_EXTRINSIC_MENU = 'Manual extrinsic'
export const easyTransferTitle = [
'Send tokens',
'From call data',
'Manual extrinsic',
'Set identity'
] as const

export type EasyTransferTitle = (typeof easyTransferTitle)[number]
export const DEFAULT_EASY_SETUP_SELECTION: EasyTransferTitle = 'Send tokens'

interface Props {
preselected: EasyTransferTitle
onClose: () => void
className?: string
onSuccess?: () => void
onFinalized?: () => void
}

const Send = ({ onClose, className, onSuccess, onFinalized }: Props) => {
const Send = ({ onClose, className, onSuccess, onFinalized, preselected }: Props) => {
const { getSubscanExtrinsicLink } = useGetSubscanLinks()
const { api, chainInfo } = useApi()
const [isSubmitting, setIsSubmitting] = useState(false)
Expand Down Expand Up @@ -66,7 +74,9 @@ const Send = ({ onClose, className, onSuccess, onFinalized }: Props) => {
const [extrinsicToCall, setExtrinsicToCall] = useState<
SubmittableExtrinsic<'promise', ISubmittableResult> | undefined
>()
const [selectedEasyOption, setSelectedEasyOption] = useState(SEND_TOKEN_MENU)
const [selectedEasyOption, setSelectedEasyOption] = useState<EasyTransferTitle>(
preselected || DEFAULT_EASY_SETUP_SELECTION
)
const multisigTx = useGetMultisigTx({
selectedMultisig,
extrinsicToCall,
Expand Down Expand Up @@ -119,24 +129,30 @@ const Send = ({ onClose, className, onSuccess, onFinalized }: Props) => {
[getMultisigByAddress, selectedMultiProxy]
)

const easySetupOptions: { [index: string]: ReactNode } = useMemo(() => {
const easySetupOptions: Record<EasyTransferTitle, ReactNode> = useMemo(() => {
return {
[SEND_TOKEN_MENU]: (
'Send tokens': (
<BalancesTransfer
from={selectedOrigin.address}
onSetExtrinsic={setExtrinsicToCall}
onSetErrorMessage={setEasyOptionErrorMessage}
/>
),
[MANUEL_EXTRINSIC_MENU]: (
'Set identity': (
<IdentitySetIdentity
from={selectedOrigin.address}
onSetExtrinsic={setExtrinsicToCall}
/>
),
'Manual extrinsic': (
<ManualExtrinsic
onSetExtrinsic={setExtrinsicToCall}
onSetErrorMessage={setEasyOptionErrorMessage}
onSelectFromCallData={() => setSelectedEasyOption(FROM_CALL_DATA_MENU)}
onSelectFromCallData={() => setSelectedEasyOption('From call data')}
hasErrorMessage={!!easyOptionErrorMessage}
/>
),
[FROM_CALL_DATA_MENU]: (
'From call data': (
<FromCallData
onSetExtrinsic={setExtrinsicToCall}
onSetErrorMessage={setEasyOptionErrorMessage}
Expand Down Expand Up @@ -218,16 +234,21 @@ const Send = ({ onClose, className, onSuccess, onFinalized }: Props) => {
getSubscanExtrinsicLink
])

const onChangeEasySetupOption = useCallback(
({ target: { value } }: SelectChangeEvent<unknown>) => {
if (typeof value !== 'string') {
console.error('Unexpected network value, expect string but received', value)
const onChangeEasySetupOption: (event: SelectChangeEvent<unknown>) => void = useCallback(
({ target: { value } }) => {
if (typeof value !== 'string' && !easyTransferTitle.includes(value as EasyTransferTitle)) {
console.error(
'Unexpected selection, expect one of',
easyTransferTitle,
'but received',
value
)
return
}

setErrorMessage('')
setEasyOptionErrorMessage('')
setSelectedEasyOption(value)
setSelectedEasyOption(value as EasyTransferTitle)
},
[]
)
Expand Down
23 changes: 19 additions & 4 deletions packages/ui/src/contexts/ModalsContext.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useState, useContext, createContext, useCallback } from 'react'
import ChangeMultisig from '../components/modals/ChangeMultisig'
import EditNames from '../components/modals/EditNames'
import Send from '../components/modals/Send'
import Send, { DEFAULT_EASY_SETUP_SELECTION, EasyTransferTitle } from '../components/modals/Send'
import { usePendingTx } from '../hooks/usePendingTx'
import { SignClientTypes } from '@walletconnect/types'
import WCSessionProposal from '../components/modals/WalletConnectSessionProposal'
Expand All @@ -11,7 +11,8 @@ import WalletConnectSigning from '../components/modals/WalletConnectSigning'
interface ModalsContextProps {
setIsEditModalOpen: (isOpen: boolean) => void
setIsChangeMultiModalOpen: (isOpen: boolean) => void
setIsSendModalOpen: (isOpen: boolean) => void
onOpenSendModal: (preselected?: EasyTransferTitle) => void
onCloseSendModal: () => void
openWalletConnectSessionModal: ({ sessionProposal }: OpenWCModalParams) => void
onOpenSigningModal: (info: SigningInfo) => void
onOpenWalletConnectSigning: (request: SignClientTypes.EventArguments['session_request']) => void
Expand All @@ -33,6 +34,9 @@ const ModalsContextProvider: React.FC<React.PropsWithChildren> = ({ children })
const [isSigningModalOpen, setIsSigningModalOpen] = useState(false)
const [signingModalInfo, setSigningModalInfo] = useState<SigningInfo | undefined>()
const [isOpenWalletConnectSigning, setIsOpenWalletConnectSigning] = useState(false)
const [sendModalPreselection, setSendModalPreselection] = useState<EasyTransferTitle>(
DEFAULT_EASY_SETUP_SELECTION
)
const [walletConnectRequest, setWalletConnectRequest] = useState<
SignClientTypes.EventArguments['session_request'] | undefined
>()
Expand All @@ -41,7 +45,6 @@ const ModalsContextProvider: React.FC<React.PropsWithChildren> = ({ children })
OpenWCModalParams['sessionProposal'] | undefined
>()
const { refresh } = usePendingTx()
const onCloseSendModal = useCallback(() => setIsSendModalOpen(false), [setIsSendModalOpen])
const onCloseEditModal = useCallback(() => setIsEditModalOpen(false), [setIsEditModalOpen])
const onCloseChangeMultiModal = useCallback(
() => setIsChangeMultiModalOpen(false),
Expand All @@ -52,6 +55,16 @@ const ModalsContextProvider: React.FC<React.PropsWithChildren> = ({ children })
setSigningModalInfo(undefined)
}, [])

const onOpenSendModal = useCallback((preselection?: EasyTransferTitle) => {
preselection && setSendModalPreselection(preselection)
setIsSendModalOpen(true)
}, [])

const onCloseSendModal = useCallback(() => {
setIsSendModalOpen(false)
setSendModalPreselection(DEFAULT_EASY_SETUP_SELECTION)
}, [])

const onSuccessSendModal = useCallback(() => {
onCloseSendModal()
refresh()
Expand Down Expand Up @@ -93,7 +106,8 @@ const ModalsContextProvider: React.FC<React.PropsWithChildren> = ({ children })
value={{
setIsEditModalOpen,
setIsChangeMultiModalOpen,
setIsSendModalOpen,
onOpenSendModal,
onCloseSendModal,
openWalletConnectSessionModal,
onOpenSigningModal,
onOpenWalletConnectSigning
Expand All @@ -102,6 +116,7 @@ const ModalsContextProvider: React.FC<React.PropsWithChildren> = ({ children })
{children}
{isSendModalOpen && (
<Send
preselected={sendModalPreselection}
onClose={onCloseSendModal}
onSuccess={onSuccessSendModal}
onFinalized={onFinalizedSendModal}
Expand Down
Loading

0 comments on commit 438e273

Please sign in to comment.