Skip to content

Commit

Permalink
feat: chain selector in v4 create flow (#4567)
Browse files Browse the repository at this point in the history
  • Loading branch information
johnnyd-eth committed Jan 1, 2025
1 parent 1e9ba29 commit f8b085f
Show file tree
Hide file tree
Showing 19 changed files with 278 additions and 172 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Button, ButtonProps } from 'antd'

import { ExclamationTriangleIcon } from '@heroicons/react/24/outline'
import { Trans } from '@lingui/macro'
import { Button, ButtonProps } from 'antd'
import { useWallet } from 'hooks/Wallet'

export function ChangeNetworksButton(props: ButtonProps) {
Expand All @@ -9,7 +10,7 @@ export function ChangeNetworksButton(props: ButtonProps) {
return (
<Button
className="border border-warning-200 bg-warning-50 text-warning-800 dark:border-warning-500 dark:bg-warning-900 dark:text-warning-100"
onClick={changeNetworks}
onClick={() => changeNetworks()}
{...props}
>
<span>
Expand Down
3 changes: 2 additions & 1 deletion src/components/inputs/JuiceListbox.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Listbox, Transition } from '@headlessui/react'

import { ChevronDownIcon } from '@heroicons/react/24/outline'
import { Fragment } from 'react'
import { twMerge } from 'tailwind-merge'

interface JuiceListboxOption<T> {
export interface JuiceListboxOption<T> {
label: string
value: T
}
Expand Down
4 changes: 4 additions & 0 deletions src/constants/networks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ export const NETWORKS: Record<number, NetworkInfo> = {
},
}

export type SupportedChainId = keyof typeof NETWORKS;

export const NETWORKS_BY_NAME = Object.values(NETWORKS).reduce(
(acc, curr) => ({
...acc,
Expand All @@ -67,5 +69,7 @@ export const NETWORKS_BY_NAME = Object.values(NETWORKS).reduce(
{} as Record<NetworkName, NetworkInfo>,
)

export const DEFAULT_PROJECT_CHAIN_ID = NETWORKS_BY_NAME.mainnet.chainId

export const readNetwork =
NETWORKS_BY_NAME[process.env.NEXT_PUBLIC_INFURA_NETWORK]
10 changes: 5 additions & 5 deletions src/hooks/Wallet/hooks/useChangeNetworks.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { useSetChain } from '@web3-onboard/react'
import { useCallback } from 'react'
import { SupportedChainId, readNetwork } from 'constants/networks'

import { readNetwork } from 'constants/networks'
import { useCallback } from 'react'
import { useSetChain } from '@web3-onboard/react'

export function useChangeNetworks() {
const [{ chains }, setChain] = useSetChain()

const changeNetworks = useCallback(async () => {
const chain = chains.find(c => Number(c.id) === readNetwork.chainId)
const changeNetworks = useCallback(async (chainId?: SupportedChainId) => {
const chain = chains.find(c => Number(c.id) === (chainId ?? readNetwork.chainId))
if (!chain) {
console.error('FATAL: Chain not found')
throw new Error('FATAL: Chain not found')
Expand Down
3 changes: 3 additions & 0 deletions src/locales/messages.pot
Original file line number Diff line number Diff line change
Expand Up @@ -1196,6 +1196,9 @@ msgstr ""
msgid "Check your email for a confirmation link and retry subscribing."
msgstr ""

msgid "Project chain"
msgstr ""

msgid "Claim ERC-20 token"
msgstr ""

Expand Down
15 changes: 8 additions & 7 deletions src/packages/v2v3/components/Create/Create.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import { t, Trans } from '@lingui/macro'
import { DeployButtonText } from 'components/buttons/DeployProjectButtonText'
import Loading from 'components/Loading'
import {
CYCLE_EXPLANATION,
RECONFIG_RULES_EXPLANATION,
} from 'components/strings'
import { readNetwork } from 'constants/networks'
import { NetworkName } from 'models/networkName'
import { useRouter } from 'next/router'
import { Trans, t } from '@lingui/macro'

import { CreateBadge } from './components/CreateBadge'
import { DeployButtonText } from 'packages/v2v3/components/Create/components/DeployProjectButtonText'
import { DeploySuccess } from './components/pages/ReviewDeploy/components/DeploySuccess'
import { FundingCyclesPage } from './components/pages/FundingCycles/FundingCyclesPage'
import Loading from 'components/Loading'
import { NetworkName } from 'models/networkName'
import { NftRewardsPage } from './components/pages/NftRewards/NftRewardsPage'
import { PayoutsPage } from './components/pages/PayoutsPage/PayoutsPage'
import { ProjectDetailsPage } from './components/pages/ProjectDetails/ProjectDetailsPage'
import { ProjectTokenPage } from './components/pages/ProjectToken/ProjectTokenPage'
import { ReconfigurationRulesPage } from './components/pages/ReconfigurationRules/ReconfigurationRulesPage'
import { DeploySuccess } from './components/pages/ReviewDeploy/components/DeploySuccess'
import { ReviewDeployPage } from './components/pages/ReviewDeploy/ReviewDeployPage'
import { Wizard } from './components/Wizard/Wizard'
import { readNetwork } from 'constants/networks'
import { useLoadingInitialStateFromQuery } from './hooks/useLoadInitialStateFromQuery'
import { useRouter } from 'next/router'

export function Create() {
const router = useRouter()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import { ETH_TOKEN_ADDRESS } from 'constants/juiceboxTokens'
import isEqual from 'lodash/isEqual'
import { CreatePage } from 'models/createPage'
import { ProjectTokensSelection } from 'models/projectTokenSelection'
import { TreasurySelection } from 'models/treasurySelection'
import { useRouter } from 'next/router'
import { ballotStrategiesFn } from 'packages/v2v3/constants/ballotStrategies'
import { useDefaultJBETHPaymentTerminal } from 'packages/v2v3/hooks/defaultContracts/useDefaultJBETHPaymentTerminal'
import { MAX_DISTRIBUTION_LIMIT } from 'packages/v2v3/utils/math'
import { useEffect, useState } from 'react'
import { useDispatch } from 'react-redux'
import { CreateState, ProjectState } from 'redux/slices/v2v3/shared/v2ProjectTypes'
import {
DEFAULT_REDUX_STATE,
creatingV2ProjectActions,
} from 'redux/slices/v2v3/creatingV2Project'
import { useEffect, useState } from 'react'

import { CreatePage } from 'models/createPage'
import { DefaultSettings as DefaultTokenSettings } from '../components/pages/ProjectToken/hooks/useProjectTokenForm'
import { ETH_TOKEN_ADDRESS } from 'constants/juiceboxTokens'
import { INITIAL_REDUX_STATE } from 'redux/slices/v2v3/shared/v2ProjectInitialReduxState'
import { CreateState, ProjectState } from 'redux/slices/v2v3/shared/v2ProjectTypes'
import { MAX_DISTRIBUTION_LIMIT } from 'packages/v2v3/utils/math'
import { ProjectTokensSelection } from 'models/projectTokenSelection'
import { TreasurySelection } from 'models/treasurySelection'
import { ballotStrategiesFn } from 'packages/v2v3/constants/ballotStrategies'
import isEqual from 'lodash/isEqual'
import { isEqualAddress } from 'utils/address'
import { parseWad } from 'utils/format/formatNumber'
import { DefaultSettings as DefaultTokenSettings } from '../components/pages/ProjectToken/hooks/useProjectTokenForm'
import { projectTokenSettingsToReduxFormat } from '../utils/projectTokenSettingsToReduxFormat'
import { useDefaultJBETHPaymentTerminal } from 'packages/v2v3/hooks/defaultContracts/useDefaultJBETHPaymentTerminal'
import { useDispatch } from 'react-redux'
import { useRouter } from 'next/router'

const ReduxDefaultTokenSettings =
projectTokenSettingsToReduxFormat(DefaultTokenSettings)
Expand Down Expand Up @@ -100,6 +101,7 @@ const parseCreateFlowStateFromInitialState = (
}

return {
projectChainId: 0, // not necessary for v2v3
fundingCyclesPageSelection,
treasurySelection,
fundingTargetSelection: undefined, // TODO: Remove
Expand Down
16 changes: 8 additions & 8 deletions src/packages/v4/components/Create/Create.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import { Trans, t } from '@lingui/macro'
import {
DEADLINE_EXPLANATION,
RULESET_EXPLANATION
} from 'components/strings'
import { Trans, t } from '@lingui/macro'

import { Badge } from 'components/Badge'
import { DeployButtonText } from 'components/buttons/DeployProjectButtonText'
import Loading from 'components/Loading'
import { readNetwork } from 'constants/networks'
import { NetworkName } from 'models/networkName'
import { useRouter } from 'next/router'
import { CreateBadge } from './components/CreateBadge'
import { DeployProjectButtonText } from './DeployProjectButtonText'
import { DeploySuccess } from './components/pages/ReviewDeploy/components/DeploySuccess'
import { FundingCyclesPage } from './components/pages/FundingCycles/FundingCyclesPage'
import Loading from 'components/Loading'
import { NetworkName } from 'models/networkName'
import { NftRewardsPage } from './components/pages/NftRewards/NftRewardsPage'
import { PayoutsPage } from './components/pages/PayoutsPage/PayoutsPage'
import { ProjectDetailsPage } from './components/pages/ProjectDetails/ProjectDetailsPage'
import { ProjectTokenPage } from './components/pages/ProjectToken/ProjectTokenPage'
import { ReconfigurationRulesPage } from './components/pages/ReconfigurationRules/ReconfigurationRulesPage'
import { DeploySuccess } from './components/pages/ReviewDeploy/components/DeploySuccess'
import { ReviewDeployPage } from './components/pages/ReviewDeploy/ReviewDeployPage'
import { Wizard } from './components/Wizard/Wizard'
import { readNetwork } from 'constants/networks'
import { useLoadingInitialStateFromQuery } from './hooks/useLoadInitialStateFromQuery'
import { useRouter } from 'next/router'

export function Create() {
const router = useRouter()
Expand All @@ -44,7 +44,7 @@ export function Create() {
</h1>
{/* TODO: Remove wizard-create once form item css override is replaced */}
<div className="wizard-create">
<Wizard className="pb-28" doneText={<DeployButtonText />}>
<Wizard className="pb-28" doneText={<DeployProjectButtonText />}>
<Wizard.Page
name="projectDetails"
title={t`Project Details`}
Expand Down
38 changes: 38 additions & 0 deletions src/packages/v4/components/Create/DeployProjectButtonText.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { NETWORKS } from 'constants/networks'
import { Trans } from '@lingui/macro'
import { useAppSelector } from 'redux/hooks/useAppSelector'
import useMobile from 'hooks/useMobile'
import { useWallet } from 'hooks/Wallet'

export function DeployProjectButtonText() {
const isMobile = useMobile()
const { isConnected, chain } = useWallet()

const {
projectChainId,
} = useAppSelector(state => state.creatingV2Project)

const walletConnectedToWrongChain = chain?.id && projectChainId !== parseInt(chain.id)

if (!isConnected) {
return isMobile ? (
<Trans>Connect wallet</Trans>
) : (
<Trans>Connect wallet to deploy</Trans>
)
}

if (walletConnectedToWrongChain) {
return isMobile ? (
<Trans>Change network</Trans>
) : (
<Trans>Change networks to deploy</Trans>
)
}

if (chain?.name && !isMobile) {
return <Trans>Deploy project to {NETWORKS[projectChainId]?.label}</Trans>
}

return <Trans>Deploy project</Trans>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { DEFAULT_PROJECT_CHAIN_ID, NETWORKS, SupportedChainId } from "constants/networks"

import { JuiceListbox } from "components/inputs/JuiceListbox"

export const ProjectChainSelect: React.FC<
React.PropsWithChildren<{
value?: SupportedChainId
onChange?: (value: SupportedChainId) => void
}>
> = ({ value, onChange }) => {

const networkOptions = () => Object.entries(NETWORKS).map(
([chainId, networkInfo]) => ({
label: networkInfo.label,
value: parseInt(chainId),
})
)

return (
<JuiceListbox
value={{label: NETWORKS[value ?? DEFAULT_PROJECT_CHAIN_ID]?.label, value }}
onChange={({ value }) => {
if (!value) return
onChange?.(value)
}}
options={networkOptions()}
/>
)
}
Original file line number Diff line number Diff line change
@@ -1,34 +1,36 @@
import { RightOutlined } from '@ant-design/icons'
import { t, Trans } from '@lingui/macro'
import { Col, Form, Row } from 'antd'
import { Callout } from 'components/Callout/Callout'
import { FormItems } from 'components/formItems'
import { EthAddressInput } from 'components/inputs/EthAddressInput'
import FormattedNumberInput from 'components/inputs/FormattedNumberInput'
import { FormImageUploader } from 'components/inputs/FormImageUploader'
import { JuiceTextArea } from 'components/inputs/JuiceTextArea'
import { JuiceInput } from 'components/inputs/JuiceTextInput'
import { RichEditor } from 'components/RichEditor'
import { CREATE_FLOW } from 'constants/fathomEvents'
import { constants } from 'ethers'
import { useWallet } from 'hooks/Wallet'
import { trackFathomGoal } from 'lib/fathom'
import Link from 'next/link'
import { useLockPageRulesWrapper } from 'packages/v2v3/components/Create/hooks/useLockPageRulesWrapper'
import { V2V3CurrencyOption } from 'packages/v2v3/models/currencyOption'
import { Trans, t } from '@lingui/macro'
import {
V2V3_CURRENCY_ETH,
V2V3_CURRENCY_USD,
} from 'packages/v2v3/utils/currency'
import { useCallback, useContext, useMemo, useState } from 'react'
import { useSetCreateFurthestPageReached } from 'redux/hooks/v2v3/useEditingCreateFurthestPageReached'
import { inputMustBeEthAddressRule, inputMustExistRule } from 'utils/antdRules'
import { inputIsLengthRule } from 'utils/antdRules/inputIsLengthRule'
import { useCallback, useContext, useState } from 'react'

import { CREATE_FLOW } from 'constants/fathomEvents'
import { Callout } from 'components/Callout/Callout'
import { CreateCollapse } from '../../CreateCollapse/CreateCollapse'
import { EthAddressInput } from 'components/inputs/EthAddressInput'
import { FormImageUploader } from 'components/inputs/FormImageUploader'
import { FormItems } from 'components/formItems'
import FormattedNumberInput from 'components/inputs/FormattedNumberInput'
import { JuiceInput } from 'components/inputs/JuiceTextInput'
import { JuiceTextArea } from 'components/inputs/JuiceTextArea'
import Link from 'next/link'
import { OptionalHeader } from '../../OptionalHeader'
import { PageContext } from '../../Wizard/contexts/PageContext'
import { ProjectChainSelect } from './ProjectChainSelect'
import { RichEditor } from 'components/RichEditor'
import { RightOutlined } from '@ant-design/icons'
import { V2V3CurrencyOption } from 'packages/v2v3/models/currencyOption'
import { Wizard } from '../../Wizard/Wizard'
import { constants } from 'ethers'
import { inputIsLengthRule } from 'utils/antdRules/inputIsLengthRule'
import { trackFathomGoal } from 'lib/fathom'
import { useLockPageRulesWrapper } from 'packages/v2v3/components/Create/hooks/useLockPageRulesWrapper'
import { useProjectDetailsForm } from './hooks/useProjectDetailsForm'
import { useSetCreateFurthestPageReached } from 'redux/hooks/v2v3/useEditingCreateFurthestPageReached'
import { useWallet } from 'hooks/Wallet'

export const ProjectDetailsPage: React.FC<
React.PropsWithChildren<unknown>
Expand All @@ -45,32 +47,6 @@ export const ProjectDetailsPage: React.FC<
const projectOwnerDifferentThanWalletAddress =
inputWalletAddress && wallet.userAddress !== inputWalletAddress

const startTimestamp = Form.useWatch('startTimestamp', formProps.form)

// just for juicecrowd
const launchDate = useMemo(() => {
if (!startTimestamp) {
return null
}
const number = Number(startTimestamp)
if (isNaN(number)) {
return null
}

let date
if (number > 1000000000000) {
date = new Date(number)
} else {
date = new Date(number * 1000)
}

// format in local timezone
return {
local: date.toLocaleString(),
utc: date.toUTCString(),
}
}, [startTimestamp])

return (
<Form
{...formProps}
Expand All @@ -95,6 +71,14 @@ export const ProjectDetailsPage: React.FC<
<JuiceInput />
</Form.Item>

<Form.Item
name="projectChainId"
label={t`Project chain`}
required
>
<ProjectChainSelect />
</Form.Item>

<Form.Item
name="projectTagline"
label={t`Tagline`}
Expand Down
Loading

0 comments on commit f8b085f

Please sign in to comment.