From fa66271a96bac0c62e428fc4452f4810477f62ed Mon Sep 17 00:00:00 2001 From: Manuel Gellfart Date: Thu, 27 Oct 2022 15:16:46 +0200 Subject: [PATCH] [Safe Creation] Owner step (#989) * feat: basic step 2 implementation Co-authored-by: iamacook --- src/components/common/NameInput/index.tsx | 2 +- src/components/create-safe/steps/OwnerRow.tsx | 2 +- src/components/new-safe/CreateSafe/index.tsx | 2 + .../new-safe/steps/Step2/OwnerRow.tsx | 114 ++++++++++++ src/components/new-safe/steps/Step2/index.tsx | 167 ++++++++++++++++++ 5 files changed, 285 insertions(+), 2 deletions(-) create mode 100644 src/components/new-safe/steps/Step2/OwnerRow.tsx create mode 100644 src/components/new-safe/steps/Step2/index.tsx diff --git a/src/components/common/NameInput/index.tsx b/src/components/common/NameInput/index.tsx index a86a743e10..4b64d181a9 100644 --- a/src/components/common/NameInput/index.tsx +++ b/src/components/common/NameInput/index.tsx @@ -9,7 +9,7 @@ const NameInput = ({ validate, required = false, ...props -}: Omit & { +}: Omit & { name: string validate?: Validate required?: boolean diff --git a/src/components/create-safe/steps/OwnerRow.tsx b/src/components/create-safe/steps/OwnerRow.tsx index 7a2c959bc9..a7f96ae1b4 100644 --- a/src/components/create-safe/steps/OwnerRow.tsx +++ b/src/components/create-safe/steps/OwnerRow.tsx @@ -94,7 +94,7 @@ export const OwnerRow = ({ {index > 0 && ( <> remove?.(index)} size="small"> - + )} diff --git a/src/components/new-safe/CreateSafe/index.tsx b/src/components/new-safe/CreateSafe/index.tsx index b0ff33ddf7..cb94da9aaf 100644 --- a/src/components/new-safe/CreateSafe/index.tsx +++ b/src/components/new-safe/CreateSafe/index.tsx @@ -7,6 +7,7 @@ import { useCurrentChain } from '@/hooks/useChains' import useWallet from '@/hooks/wallets/useWallet' import OverviewWidget from '../OverviewWidget' import CreateSafeStep1 from '../steps/Step1' +import CreateSafeStep2 from '../steps/Step2' const CreateSafe = () => { const router = useRouter() @@ -40,6 +41,7 @@ const CreateSafe = () => { + diff --git a/src/components/new-safe/steps/Step2/OwnerRow.tsx b/src/components/new-safe/steps/Step2/OwnerRow.tsx new file mode 100644 index 0000000000..5d1384deb8 --- /dev/null +++ b/src/components/new-safe/steps/Step2/OwnerRow.tsx @@ -0,0 +1,114 @@ +import { useCallback, useEffect, useMemo } from 'react' +import { CircularProgress, FormControl, Grid, IconButton, SvgIcon } from '@mui/material' +import NameInput from '@/components/common/NameInput' +import InputAdornment from '@mui/material/InputAdornment' +import AddressBookInput from '@/components/common/AddressBookInput' +import DeleteIcon from '@/public/images/common/delete.svg' +import { useFormContext, useWatch } from 'react-hook-form' +import { useAddressResolver } from '@/hooks/useAddressResolver' +import EthHashInfo from '@/components/common/EthHashInfo' +import type { NamedAddress } from '@/components/create-safe/types' +import useWallet from '@/hooks/wallets/useWallet' + +/** + * TODO: this is a slightly modified copy of the old /create-safe/OwnerRow.tsx + * Once we remove the old safe creation flow we should remove the old file. + */ +export const OwnerRow = ({ + index, + groupName, + removable = true, + remove, + readOnly = false, +}: { + index: number + removable?: boolean + groupName: string + remove?: (index: number) => void + readOnly?: boolean +}) => { + const wallet = useWallet() + const fieldName = `${groupName}.${index}` + const { control, getValues, setValue } = useFormContext() + const owners = useWatch({ + control, + name: groupName, + }) + const owner = useWatch({ + control, + name: fieldName, + }) + + const deps = useMemo(() => { + return Array.from({ length: owners.length }, (_, i) => `${groupName}.${i}`) + }, [owners, groupName]) + + const validateSafeAddress = useCallback( + async (address: string) => { + if (owners.filter((owner: NamedAddress) => owner.address === address).length > 1) { + return 'Owner is already added' + } + }, + [owners], + ) + + const { ens, name, resolving } = useAddressResolver(owner.address) + + useEffect(() => { + if (ens) { + setValue(`${fieldName}.ens`, ens) + } + + if (name && !getValues(`${fieldName}.name`)) { + setValue(`${fieldName}.name`, name) + } + }, [ens, setValue, getValues, name, fieldName]) + + return ( + + + + + + + ) : null, + }} + /> + + + + {readOnly ? ( + + ) : ( + + + + )} + + {!readOnly && ( + + {removable && ( + <> + remove?.(index)}> + + + + )} + + )} + + ) +} diff --git a/src/components/new-safe/steps/Step2/index.tsx b/src/components/new-safe/steps/Step2/index.tsx new file mode 100644 index 0000000000..8476de3fef --- /dev/null +++ b/src/components/new-safe/steps/Step2/index.tsx @@ -0,0 +1,167 @@ +import { Button, Grid, SvgIcon, MenuItem, Select, Tooltip, Typography, Divider } from '@mui/material' +import { FormProvider, useFieldArray, useForm } from 'react-hook-form' +import type { ReactElement } from 'react' + +import useWallet from '@/hooks/wallets/useWallet' +import AddIcon from '@/public/images/common/add.svg' +import InfoIcon from '@/public/images/notifications/info.svg' +import StepCard from '../../StepCard' +import { OwnerRow } from './OwnerRow' +import useAddressBook from '@/hooks/useAddressBook' +import type { NamedAddress } from '@/components/create-safe/types' + +type Owner = { + name: string + address: string +} + +type CreateSafeStep2Form = { + owners: Owner[] + mobileOwners: Owner[] + threshold: number +} + +enum CreateSafeStep2Fields { + owners = 'owners', + mobileOwners = 'mobileOwners', + threshold = 'threshold', +} + +const STEP_2_FORM_ID = 'create-safe-step-2-form' + +const CreateSafeStep2 = (): ReactElement => { + const wallet = useWallet() + const addressBook = useAddressBook() + + const defaultOwnerAddressBookName = wallet?.address ? addressBook[wallet.address] : undefined + + const defaultOwner: NamedAddress = { + name: defaultOwnerAddressBookName || wallet?.ens || '', + address: wallet?.address || '', + } + const formMethods = useForm({ + mode: 'all', + defaultValues: { + [CreateSafeStep2Fields.owners]: [defaultOwner], + [CreateSafeStep2Fields.mobileOwners]: [], + [CreateSafeStep2Fields.threshold]: 1, + }, + }) + + const { register, handleSubmit, control } = formMethods + + const { + fields: ownerFields, + append: appendOwner, + remove: removeOwner, + } = useFieldArray({ control, name: 'owners', shouldUnregister: true }) + + const { + fields: mobileOwnerFields, + append: appendMobileOwner, + remove: removeMobileOwner, + } = useFieldArray({ control, name: 'mobileOwners', shouldUnregister: true }) + + const allOwners = [...ownerFields, ...mobileOwnerFields] + + const onSubmit = (data: CreateSafeStep2Form) => { + console.log(data) + } + + return ( + + + + + {ownerFields.map((field, i) => ( + 0} + groupName={CreateSafeStep2Fields.owners} + remove={removeOwner} + /> + ))} + + + + + Safe Mobile owner key (optional){' '} + + + + + + + + Add an extra layer of security and sign transactions with the Safe Mobile app. + + + + + {mobileOwnerFields.map((field, i) => ( + + ))} + + + + + + + Threshold + + + + + + + + Any transaction requires the confirmation of: + + {' '} + out of {allOwners.length} owner(s). + + + + + } + actions={ + <> + + + + } + /> + ) +} + +export default CreateSafeStep2