@@ -34,16 +34,19 @@ import { DropdownSelect } from '@buildeross/ui/DropdownSelect'
3434import { isChainIdSupportedByCoining } from '@buildeross/utils/coining'
3535import { isChainIdSupportedByDroposal } from '@buildeross/utils/droposal'
3636import { isChainIdSupportedByEAS } from '@buildeross/utils/eas'
37+ import { getEnsAddress } from '@buildeross/utils/ens'
38+ import { getProvider } from '@buildeross/utils/provider'
3739import { isChainIdSupportedBySablier } from '@buildeross/utils/sablier/constants'
3840import { Box , Button , Flex , Icon , Stack , Text } from '@buildeross/zord'
41+ import { Formik , FormikProps } from 'formik'
3942import { GetServerSideProps } from 'next'
4043import { useRouter } from 'next/router'
4144import React , { useCallback , useEffect , useMemo } from 'react'
4245import { getDaoLayout } from 'src/layouts/DaoLayout'
4346import { NextPageWithLayout } from 'src/pages/_app'
4447import { notFoundWrap } from 'src/styles/404.css'
4548import * as styles from 'src/styles/create.css'
46- import { isAddress , isAddressEqual } from 'viem'
49+ import { getAddress , isAddress , isAddressEqual } from 'viem'
4750import { useAccount , useReadContract } from 'wagmi'
4851
4952const createSelectOption = ( type : TransactionType ) => ( {
@@ -130,6 +133,14 @@ const validateDiscussionUrl = (value?: string | null): string | undefined => {
130133 return undefined
131134}
132135
136+ type DraftFormValues = {
137+ title : string
138+ summary : string
139+ representedAddress : string
140+ discussionUrl : string
141+ representedAddressEnabled : boolean
142+ }
143+
133144const CreateProposalPage : NextPageWithLayout = ( ) => {
134145 const { query, push } = useRouter ( )
135146 const addresses = useDaoStore ( ( x ) => x . addresses )
@@ -164,10 +175,6 @@ const CreateProposalPage: NextPageWithLayout = () => {
164175 ? 'transactions'
165176 : 'draft'
166177 )
167- const [ titleTouched , setTitleTouched ] = React . useState ( false )
168- const [ summaryTouched , setSummaryTouched ] = React . useState ( false )
169- const [ representedAddressTouched , setRepresentedAddressTouched ] = React . useState ( false )
170- const [ discussionUrlTouched , setDiscussionUrlTouched ] = React . useState ( false )
171178
172179 const { data : paused } = useReadContract ( {
173180 abi : auctionAbi ,
@@ -314,26 +321,6 @@ const CreateProposalPage: NextPageWithLayout = () => {
314321 return requirements
315322 } , [ title , summary , representedAddress , representedAddressEnabled , discussionUrl ] )
316323
317- const titleError = useMemo ( ( ) => {
318- if ( ! titleTouched ) return undefined
319- return validateTitle ( title )
320- } , [ title , titleTouched ] )
321-
322- const summaryError = useMemo ( ( ) => {
323- if ( ! summaryTouched ) return undefined
324- return validateSummary ( summary )
325- } , [ summary , summaryTouched ] )
326-
327- const representedAddressError = useMemo ( ( ) => {
328- if ( ! representedAddressTouched ) return undefined
329- return validateRepresentedAddress ( representedAddress , representedAddressEnabled )
330- } , [ representedAddress , representedAddressEnabled , representedAddressTouched ] )
331-
332- const discussionUrlError = useMemo ( ( ) => {
333- if ( ! discussionUrlTouched ) return undefined
334- return validateDiscussionUrl ( discussionUrl )
335- } , [ discussionUrl , discussionUrlTouched ] )
336-
337324 const missingReviewRequirements = useMemo ( ( ) => {
338325 const requirements = [ ...missingDraftRequirements ]
339326
@@ -458,14 +445,51 @@ const CreateProposalPage: NextPageWithLayout = () => {
458445
459446 const onResetProposal = useCallback ( ( ) => {
460447 clearProposal ( )
461- setTitleTouched ( false )
462- setSummaryTouched ( false )
463- setRepresentedAddressTouched ( false )
464- setDiscussionUrlTouched ( false )
465448 setCreateStage ( 'draft' )
466449 setFurthestStage ( 'draft' )
467450 } , [ clearProposal ] )
468451
452+ const resolveAndStoreRepresentedAddress = useCallback (
453+ async ( formik : FormikProps < DraftFormValues > ) => {
454+ if ( ! formik . values . representedAddressEnabled ) {
455+ setRepresentedAddress ( undefined )
456+ return true
457+ }
458+
459+ const rawValue = ( formik . values . representedAddress || '' ) . trim ( )
460+
461+ if ( ! rawValue ) {
462+ setRepresentedAddress ( undefined )
463+ return true
464+ }
465+
466+ try {
467+ const resolved = await getEnsAddress ( rawValue , getProvider ( chain . id ) )
468+ if ( ! resolved || ! isAddress ( resolved , { strict : false } ) ) {
469+ void formik . setFieldError (
470+ 'representedAddress' ,
471+ PROPOSAL_REPRESENTED_ADDRESS_FORMAT_ERROR
472+ )
473+ return false
474+ }
475+
476+ const normalizedAddress = getAddress ( resolved )
477+ if ( normalizedAddress !== formik . values . representedAddress ) {
478+ void formik . setFieldValue ( 'representedAddress' , normalizedAddress )
479+ }
480+ setRepresentedAddress ( normalizedAddress )
481+ return true
482+ } catch {
483+ void formik . setFieldError (
484+ 'representedAddress' ,
485+ PROPOSAL_REPRESENTED_ADDRESS_FORMAT_ERROR
486+ )
487+ return false
488+ }
489+ } ,
490+ [ chain . id , setRepresentedAddress ]
491+ )
492+
469493 const onStageSelect = useCallback (
470494 ( stage : 'draft' | 'transactions' | 'review' ) => {
471495 if ( stage === 'draft' ) {
@@ -585,32 +609,69 @@ const CreateProposalPage: NextPageWithLayout = () => {
585609 borderRadius = { 'curved' }
586610 gap = { 'x2' }
587611 >
588- < ProposalDraftForm
589- title = { title || '' }
590- summary = { summary || '' }
591- representedAddress = { representedAddress || '' }
592- discussionUrl = { discussionUrl || '' }
593- representedAddressEnabled = { representedAddressEnabled }
594- onTitleChange = { setTitle }
595- onSummaryChange = { setSummary }
596- onRepresentedAddressChange = { setRepresentedAddress }
597- onDiscussionUrlChange = { setDiscussionUrl }
598- onRepresentedAddressEnabledChange = { ( value ) => {
599- setRepresentedAddressEnabled ( value )
600- setRepresentedAddressTouched ( true )
601- if ( ! value ) {
602- setRepresentedAddress ( undefined )
612+ < Formik < DraftFormValues >
613+ initialValues = { {
614+ title : title || '' ,
615+ summary : summary || '' ,
616+ representedAddress : representedAddress || '' ,
617+ discussionUrl : discussionUrl || '' ,
618+ representedAddressEnabled,
619+ } }
620+ enableReinitialize
621+ validateOnBlur
622+ validateOnChange
623+ validate = { ( values ) => {
624+ const errors : Partial < Record < keyof DraftFormValues , string > > = { }
625+ const titleValidationError = validateTitle ( values . title )
626+ if ( titleValidationError ) errors . title = titleValidationError
627+
628+ const summaryValidationError = validateSummary ( values . summary )
629+ if ( summaryValidationError ) errors . summary = summaryValidationError
630+
631+ const representedAddressValidationError = validateRepresentedAddress (
632+ values . representedAddress ,
633+ values . representedAddressEnabled
634+ )
635+ if ( representedAddressValidationError ) {
636+ errors . representedAddress = representedAddressValidationError
637+ }
638+
639+ const discussionUrlValidationError = validateDiscussionUrl (
640+ values . discussionUrl
641+ )
642+ if ( discussionUrlValidationError ) {
643+ errors . discussionUrl = discussionUrlValidationError
603644 }
645+
646+ return errors
604647 } }
605- onTitleBlur = { ( ) => setTitleTouched ( true ) }
606- onSummaryBlur = { ( ) => setSummaryTouched ( true ) }
607- onRepresentedAddressBlur = { ( ) => setRepresentedAddressTouched ( true ) }
608- onDiscussionUrlBlur = { ( ) => setDiscussionUrlTouched ( true ) }
609- titleError = { titleError }
610- summaryError = { summaryError }
611- representedAddressError = { representedAddressError }
612- discussionUrlError = { discussionUrlError }
613- />
648+ onSubmit = { ( ) => undefined }
649+ >
650+ { ( formik ) => (
651+ < ProposalDraftForm
652+ formik = { formik }
653+ onTitleChange = { ( value ) => {
654+ setTitle ( value )
655+ } }
656+ onSummaryChange = { ( value ) => {
657+ setSummary ( value )
658+ } }
659+ onDiscussionUrlChange = { ( value ) => {
660+ setDiscussionUrl ( value )
661+ } }
662+ onRepresentedAddressEnabledChange = { ( value ) => {
663+ setRepresentedAddressEnabled ( value )
664+ if ( ! value ) {
665+ setRepresentedAddress ( undefined )
666+ void formik . setFieldValue ( 'representedAddress' , '' )
667+ }
668+ } }
669+ onRepresentedAddressBlur = { async ( ) => {
670+ await resolveAndStoreRepresentedAddress ( formik )
671+ } }
672+ />
673+ ) }
674+ </ Formik >
614675 </ Stack >
615676 ) : (
616677 < TwoColumnLayout
0 commit comments