Skip to content

Commit

Permalink
Proposal creation page cleanup and simulation feature (#1505)
Browse files Browse the repository at this point in the history
* Added ability to simulate proposal manually.

* Remove duplicate code in new proposal components.
  • Loading branch information
NoahSaso authored Dec 7, 2023
1 parent 074e2ee commit 539d22b
Show file tree
Hide file tree
Showing 19 changed files with 755 additions and 1,090 deletions.
3 changes: 3 additions & 0 deletions packages/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@
"showInstantiateMessage": "Show Instantiate Message",
"showQrCode": "Show QR code",
"showRawData": "Show raw data",
"simulate": "Simulate",
"spend": "Spend",
"stake": "Stake",
"stakeAllButProposalDeposit": "Stake all but the {{proposalDeposit}} ${{tokenSymbol}} proposal deposit",
Expand Down Expand Up @@ -361,6 +362,7 @@
"needsVote": "You must select a vote.",
"nftCollectionNotChosen": "You must create an NFT collection or enter the address of an existing collection before using this action.",
"nftMetadataNotUploaded": "You must upload the NFT metadata before you can mint.",
"noActionsToSimulate": "No actions to simulate.",
"noCancellableVestingContracts": "There are no vesting contracts that can be cancelled.",
"noClaimsAvailable": "No claims available.",
"noCw20Tokens": "There are no CW20 tokens displayed in the treasury.",
Expand Down Expand Up @@ -1159,6 +1161,7 @@
"proposalClosed": "Closed successfully.",
"proposalCreatedCompleteCompensationCycle": "Proposal published. Complete the compensation cycle by saving the proposal ID.",
"proposalExecuted": "Executed. Refreshing page...",
"proposalSimulation": "Proposal simulated successfully. If executed right now, it would not fail.",
"ratingsSubmitted": "Ratings submitted.",
"restaked": "Restaked successfully.",
"saved": "Saved successfully.",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { FlagOutlined, Timelapse } from '@mui/icons-material'
import { useState } from 'react'
import { useCallback, useState } from 'react'
import { useFormContext } from 'react-hook-form'
import toast from 'react-hot-toast'
import { useTranslation } from 'react-i18next'
import { useRecoilCallback, useRecoilValue } from 'recoil'
import { useRecoilCallback, useRecoilValueLoadable } from 'recoil'

import { DaoCoreV2Selectors, blocksPerYearSelector } from '@dao-dao/state'
import {
NewProposalTitleDescriptionHeader,
NewProposal as StatelessNewProposal,
NewProposalProps as StatelessNewProposalProps,
useCachedLoadable,
useChain,
useDaoInfoContext,
Expand All @@ -15,24 +19,26 @@ import {
IProposalModuleAdapterCommonOptions,
} from '@dao-dao/types'
import {
MAX_NUM_PROPOSAL_CHOICES,
convertActionsToMessages,
convertExpirationToDate,
dateToWdhms,
processError,
} from '@dao-dao/utils'

import { useLoadedActionsAndCategories } from '../../../../../../actions'
import { EntityDisplay } from '../../../../../../components/EntityDisplay'
import { SuspenseLoader } from '../../../../../../components/SuspenseLoader'
import { useMembership, useWallet } from '../../../../../../hooks'
import { proposalSelector } from '../../../contracts/DaoProposalMultiple.recoil'
import { makeGetProposalInfo } from '../../../functions'
import { useLoadedActionsAndCategories } from '../../../../../actions'
import { useMembership, useWallet } from '../../../../../hooks'
import { proposalSelector } from '../../contracts/DaoProposalMultiple.recoil'
import { makeGetProposalInfo } from '../../functions'
import {
NewProposalData,
NewProposalForm,
SimulateProposal,
UsePublishProposal,
} from '../../../types'
import { useProcessQ } from '../../hooks'
import { NewProposal as StatelessNewProposal } from './NewProposal'
} from '../../types'
import { useProcessQ } from '../hooks'
import { NewProposalMain } from './NewProposalMain'
import { NewProposalPreview } from './NewProposalPreview'

export type NewProposalProps = BaseNewProposalProps<NewProposalForm> & {
options: IProposalModuleAdapterCommonOptions
Expand All @@ -54,9 +60,12 @@ export const NewProposal = ({
isActive,
activeThreshold,
} = useDaoInfoContext()
const { isWalletConnected, getStargateClient } = useWallet()
const { isWalletConnecting, isWalletConnected, getStargateClient } =
useWallet()

const { loadedActions, categories } = useLoadedActionsAndCategories()
const { watch } = useFormContext<NewProposalForm>()
const proposalTitle = watch('title')
const choices = watch('choices') ?? []

const { isMember = false, loading: membershipLoading } = useMembership({
coreAddress,
Expand All @@ -80,19 +89,33 @@ export const NewProposal = ({

const processQ = useProcessQ()

const blocksPerYear = useRecoilValue(
const blocksPerYearLoadable = useRecoilValueLoadable(
blocksPerYearSelector({
chainId,
})
)

const {
simulateProposal: _simulateProposal,
publishProposal,
anyoneCanPropose,
depositUnsatisfied,
simulationBypassExpiration,
} = usePublishProposal()

const [simulating, setSimulating] = useState(false)
const simulateProposal: SimulateProposal = useCallback(
async (...params) => {
setSimulating(true)
try {
await _simulateProposal(...params)
} finally {
setSimulating(false)
}
},
[_simulateProposal]
)

const createProposal = useRecoilCallback(
({ snapshot }) =>
async (newProposalData: NewProposalData) => {
Expand All @@ -101,6 +124,12 @@ export const NewProposal = ({
return
}

if (blocksPerYearLoadable.state !== 'hasValue') {
toast.error(t('error.loadingData'))
return
}
const blocksPerYear = blocksPerYearLoadable.contents

setLoading(true)
try {
const { proposalNumber, proposalId } = await publishProposal(
Expand Down Expand Up @@ -193,7 +222,7 @@ export const NewProposal = ({
t,
publishProposal,
options,
blocksPerYear,
blocksPerYearLoadable,
getStargateClient,
chainId,
processQ,
Expand All @@ -204,25 +233,56 @@ export const NewProposal = ({
]
)

const { loadedActions } = useLoadedActionsAndCategories()

const getProposalDataFromFormData: StatelessNewProposalProps<
NewProposalForm,
NewProposalData
>['getProposalDataFromFormData'] = ({ title, description, choices }) => ({
title,
description,
choices: {
options: choices.map((option) => ({
title: option.title,
description: option.description,
msgs: convertActionsToMessages(loadedActions, option.actionData),
})),
},
})

return (
<StatelessNewProposal
EntityDisplay={EntityDisplay}
SuspenseLoader={SuspenseLoader}
<StatelessNewProposal<NewProposalForm, NewProposalData>
activeThreshold={activeThreshold}
additionalSubmitError={
choices.length < 2
? t('error.tooFewChoices')
: choices.length > MAX_NUM_PROPOSAL_CHOICES
? t('error.tooManyChoices', {
count: MAX_NUM_PROPOSAL_CHOICES,
})
: undefined
}
anyoneCanPropose={anyoneCanPropose}
categories={categories}
connected={isWalletConnected}
content={{
Header: NewProposalTitleDescriptionHeader,
Main: NewProposalMain,
Preview: NewProposalPreview,
}}
createProposal={createProposal}
depositUnsatisfied={depositUnsatisfied}
getProposalDataFromFormData={getProposalDataFromFormData}
isActive={isActive}
isMember={
membershipLoading
? { loading: true }
: { loading: false, data: isMember }
}
isPaused={isPaused}
loadedActions={loadedActions}
loading={loading}
isWalletConnecting={isWalletConnecting}
loading={loading || simulating}
proposalTitle={proposalTitle}
simulateProposal={simulateProposal}
simulationBypassExpiration={simulationBypassExpiration}
{...props}
/>
Expand Down

This file was deleted.

Loading

2 comments on commit 539d22b

@vercel
Copy link

@vercel vercel bot commented on 539d22b Dec 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on 539d22b Dec 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.