Skip to content

Commit

Permalink
feat: allow to manage user addresses
Browse files Browse the repository at this point in the history
Add more addresses not recognized by KYC
Allow to hide/show user addresses
  • Loading branch information
jycssu-com committed Dec 4, 2023
1 parent 3c040a3 commit 901ac6b
Show file tree
Hide file tree
Showing 12 changed files with 416 additions and 29 deletions.
4 changes: 2 additions & 2 deletions src/components/assetPage/assetPageTransfersTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
RealTokenTransfer,
TransferOrigin,
} from 'src/repositories/transferts.repository'
import { selectAddressList } from 'src/store/features/settings/settingsSelector'
import { selectUserAddressList } from 'src/store/features/settings/settingsSelector'
import { UserRealtoken } from 'src/store/features/wallets/walletsSelector'

function getTransferTitle(item: RealTokenTransfer) {
Expand Down Expand Up @@ -80,7 +80,7 @@ const TransferRow: FC<{ item: RealTokenTransfer }> = ({ item }) => {
export const AssetPageTransfersTab: FC<{ data: UserRealtoken }> = ({
data,
}) => {
const addressList = useSelector(selectAddressList)
const addressList = useSelector(selectUserAddressList)
const [transfers, setTransfers] = useState<RealTokenTransfer[]>([])

useEffect(() => {
Expand Down
4 changes: 2 additions & 2 deletions src/components/layouts/MainLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { MediaQuery, createStyles } from '@mantine/core'
import { useModals } from '@mantine/modals'

import {
selectCleanedAddressList,
selectIsInitialized,
selectUserAddressList,
} from 'src/store/features/settings/settingsSelector'

import { Footer } from './Footer'
Expand Down Expand Up @@ -43,7 +43,7 @@ export const MainLayout: FC<MainLayoutProps> = ({ children }) => {
const { classes } = useStyles()

const isInitialized = useSelector(selectIsInitialized)
const addressList = useSelector(selectCleanedAddressList)
const addressList = useSelector(selectUserAddressList)
const modals = useModals()

useEffect(() => {
Expand Down
50 changes: 43 additions & 7 deletions src/components/layouts/WalletMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
selectIsInitialized,
selectUser,
} from 'src/store/features/settings/settingsSelector'
import { setUserAddress } from 'src/store/features/settings/settingsSlice'
import { User, setUserAddress } from 'src/store/features/settings/settingsSlice'

import { IntegerField, StringField } from '../commons'

Expand All @@ -33,7 +33,7 @@ interface WalletItemProps {

const useStyles = createStyles({
address: {
span: { fontFamily: 'monospace', fontSize: '12px' },
span: { fontFamily: 'monospace', fontSize: '11px' },
},
})

Expand All @@ -53,13 +53,45 @@ const WalletItem: FC<WalletItemProps> = (props) => {
}
WalletItem.displayName = 'WalletItem'

const WalletItemList: FC<{ addressList: string[] }> = (props) => {
const AddWalletButton: FC<{ onClick?: () => void }> = (props) => {
const modals = useModals()
const { t } = useTranslation('common', { keyPrefix: 'manageWalletModal' })

function openModal() {
props.onClick?.()
modals.openContextModal('manageWalletModal', {
innerProps: {},
closeOnClickOutside: false,
closeOnEscape: false,
})
}

return (
<>
<Button
onClick={openModal}
size={'xs'}
compact={true}
variant={'outline'}
>
{t('open')}
</Button>
</>
)
}
AddWalletButton.displayName = 'AddWalletButton'

const WalletItemList: FC<{ user: User }> = (props) => {
const addresses = [
...(props.user?.addressList ?? []),
...(props.user?.customAddressList ?? []),
]
return (
<Flex direction={'column'} gap={'xs'}>
{props.addressList
{addresses
.filter((item) => item)
.map((address) => (
<WalletItem key={address} address={address} />
.map((item) => (
<WalletItem key={item} address={item} />
))}
</Flex>
)
Expand Down Expand Up @@ -140,7 +172,11 @@ export const WalletMenu: FC = () => {

<Menu.Divider my={'xs'} />

<WalletItemList addressList={user.addressList} />
<WalletItemList user={user} />

<Box ta={'center'} mb={'xs'} mt={'sm'}>
<AddWalletButton onClick={handlers.close} />
</Box>

<Menu.Divider mt={'xs'} />
</>
Expand Down
242 changes: 242 additions & 0 deletions src/components/modals/ManageWalletModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
import { FC, useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'

import {
Button,
Flex,
Modal,
Stack,
TextInput,
createStyles,
} from '@mantine/core'
import { useDisclosure } from '@mantine/hooks'
import { ContextModalProps } from '@mantine/modals'

import { utils as EthersUtils } from 'ethers'

import { useAppDispatch } from 'src/hooks/react-hooks'
import { selectUser } from 'src/store/features/settings/settingsSelector'
import {
setCustomAddressList,
setHiddenAddressList,
} from 'src/store/features/settings/settingsSlice'

const AddAddressButton: FC<{
onChange: (value: string) => void
addresses: string[]
}> = (props) => {
const { t } = useTranslation('common', { keyPrefix: 'addCustomAddress' })

const [opened, { open, close }] = useDisclosure(false)
const [address, setAddress] = useState('')
const [isDirty, setIsDirty] = useState(false)

const isInvalidAddress = !EthersUtils.isAddress(address)
const isAddressAlreadyAdded = props.addresses
.map((address) => address.toLowerCase())
.includes(address.toLowerCase())

const currentError = isInvalidAddress
? t('invalidAddress')
: isAddressAlreadyAdded
? t('addressAlreadyAdded')
: undefined

function resetAndClose() {
setAddress('')
setIsDirty(false)
close()
}

function submit() {
if (currentError) {
return setIsDirty(true)
}
props.onChange(address)
resetAndClose()
}

return (
<>
<Modal
opened={opened}
onClose={close}
title={t('title')}
zIndex={300}
centered={true}
withCloseButton={false}
>
<TextInput
style={{ width: '100%' }}
size={'xs'}
mt={'xs'}
value={address}
error={currentError && isDirty ? currentError : undefined}
onChange={(event) => setAddress(event.currentTarget.value)}
/>

<Flex justify={'flex-end'} gap={'sm'} mt={'lg'}>
<Button onClick={resetAndClose} variant={'subtle'}>
{t('close')}
</Button>
<Button onClick={submit}>{t('submit')}</Button>
</Flex>
</Modal>

<Button onClick={open} size={'xs'} variant={'outline'}>
{t('open')}
</Button>
</>
)
}
AddAddressButton.displayName = 'AddAddressButton'

const useStyles = createStyles({
address: {
border: '1px solid rgb(92, 95, 102)',
borderRadius: '10px',
fontFamily: 'monospace',
fontSize: '13px',
display: 'inline-block',
padding: '4px 8px',
maxWidth: '100%',
overflowWrap: 'break-word',
},
})

const WalletItem: FC<{
address: string
isVisible: boolean
onToggle: (address: string) => void
onRemove?: (address: string) => void
}> = (props) => {
const { classes } = useStyles()
const { t } = useTranslation('common', {
keyPrefix: 'manageWalletModal.item',
})

const address = EthersUtils.getAddress(props.address)

return (
<div
className={classes.address}
style={{
opacity: props.isVisible ? 1 : 0.5,
transition: 'opacity 0.2s',
}}
>
<div>{address}</div>
<Flex
gap={'sm'}
justify={'space-between'}
direction={'row-reverse'}
mt={'3px'}
mb={'2px'}
>
<Button
onClick={() => props.onToggle(props.address)}
size={'xs'}
compact={true}
variant={'outline'}
>
{props.isVisible ? t('hide') : t('show')}
</Button>
{props.onRemove ? (
<Button
onClick={() => props.onRemove?.(props.address)}
size={'xs'}
compact={true}
variant={'outline'}
color={'red'}
>
{t('remove')}
</Button>
) : undefined}
</Flex>
</div>
)
}
WalletItem.displayName = 'WalletItem'

export const ManageWalletModal: FC<ContextModalProps> = ({ context, id }) => {
const { t } = useTranslation('common', { keyPrefix: 'manageWalletModal' })
const dispatch = useAppDispatch()

const onClose = useCallback(() => {
context.closeModal(id)
}, [context, id])

const user = useSelector(selectUser)
const [customAddresses, setCustomAddresses] = useState(
user?.customAddressList ?? []
)
const [hiddenAddresses, setHiddenAddresses] = useState(
user?.hiddenAddressList ?? []
)

const addresses = [...(user?.addressList ?? []), ...(customAddresses ?? [])]

function addAddress(address: string) {
setCustomAddresses([...customAddresses, address])
}

function removeAddress(address: string) {
setCustomAddresses(customAddresses.filter((item) => item !== address))
}

function toggleAddressVisibility(address: string) {
if (hiddenAddresses.includes(address)) {
setHiddenAddresses(hiddenAddresses.filter((item) => item !== address))
} else {
setHiddenAddresses([...hiddenAddresses, address])
}
}

const onSubmit = async () => {
dispatch(setCustomAddressList(customAddresses))
dispatch(setHiddenAddressList(hiddenAddresses))
onClose()
}

return (
<Stack
justify={'space-between'}
style={{ width: '500px', maxWidth: '100%', padding: 'none' }}
>
<Flex direction={'column'} gap={'xs'} align={'center'}>
<div style={{ margin: '5px 0' }}>{t('open')}</div>
{user?.addressList.map((address) => (
<WalletItem
key={address}
address={address}
isVisible={!hiddenAddresses.includes(address)}
onToggle={toggleAddressVisibility}
/>
))}
{customAddresses.map((address) => (
<WalletItem
key={address}
address={address}
isVisible={!hiddenAddresses.includes(address)}
onRemove={removeAddress}
onToggle={toggleAddressVisibility}
/>
))}
<AddAddressButton onChange={addAddress} addresses={addresses} />
</Flex>

<Flex justify={'space-between'}>
<div />
<div>
<Flex gap={'sm'}>
<Button onClick={onClose} variant={'subtle'}>
{t('close')}
</Button>
<Button onClick={onSubmit}>{t('submit')}</Button>
</Flex>
</div>
</Flex>
</Stack>
)
}
2 changes: 2 additions & 0 deletions src/components/modals/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { WalletModal } from '@realtoken/realt-commons'

import { AssetsViewFilterModal } from '../assetsView/filters/AssetsViewFilterModal'
import { ManageWalletModal } from './ManageWalletModal'

export const modals = {
web3Wallets: WalletModal,
assetsViewFilterModal: AssetsViewFilterModal,
manageWalletModal: ManageWalletModal,
}
Loading

0 comments on commit 901ac6b

Please sign in to comment.