-
- Show your supporters a pop-up with a message and a link
- after they receive an NFT. You can use this to direct
- supporters to your project's website, a Discord server,
- or somewhere else.
-
-
-
- The message that will be shown to supporters after
- they receive an NFT.
-
- }
- />
- }
- >
-
-
-
- The text on the button at the bottom of the
- pop-up. You can preview this below.
-
- }
- />
- }
- >
-
-
-
- Supporters will be sent to this page if they click
- the button on your pop-up. You can preview this
- below.
-
- }
- />
- }
- extra={t`If you leave this blank, the button will close the pop-up.`}
- >
-
-
- }
- onClick={postPayModal.open}
- >
- Preview
-
-
-
- {postPayModalData && (
-
- )}
>
)
}
diff --git a/src/components/Create/components/pages/NftRewards/NftAdvancedFormItems.tsx b/src/components/Create/components/pages/NftRewards/NftAdvancedFormItems.tsx
new file mode 100644
index 0000000000..e035f83fae
--- /dev/null
+++ b/src/components/Create/components/pages/NftRewards/NftAdvancedFormItems.tsx
@@ -0,0 +1,26 @@
+import { t } from '@lingui/macro'
+import { Form } from 'antd'
+import { JuiceSwitch } from 'components/inputs/JuiceSwitch'
+import {
+ PREVENT_OVERSPENDING_EXPLANATION,
+ USE_DATASOURCE_FOR_REDEEM_EXPLANATION,
+} from 'components/strings'
+
+export function NftAdvancedFormItems() {
+ return (
+ <>
+
+
+
+
+
+
+ >
+ )
+}
diff --git a/src/components/Create/components/pages/NftRewards/NftPaymentSuccessFormItems.tsx b/src/components/Create/components/pages/NftRewards/NftPaymentSuccessFormItems.tsx
new file mode 100644
index 0000000000..b22f35cab1
--- /dev/null
+++ b/src/components/Create/components/pages/NftRewards/NftPaymentSuccessFormItems.tsx
@@ -0,0 +1,100 @@
+import { EyeOutlined } from '@ant-design/icons'
+import { Trans, t } from '@lingui/macro'
+import { Form } from 'antd'
+import { NftPostPayModal } from 'components/NftRewards/NftPostPayModal'
+import TooltipLabel from 'components/TooltipLabel'
+import { CreateButton } from 'components/buttons/CreateButton'
+import { JuiceTextArea } from 'components/inputs/JuiceTextArea'
+import { JuiceInput } from 'components/inputs/JuiceTextInput'
+import { useModal } from 'hooks/useModal'
+import { useAppSelector } from 'redux/hooks/useAppSelector'
+
+export function NftPaymentSuccessFormItems({
+ hidePreview,
+}: {
+ hidePreview?: boolean
+}) {
+ const postPayModal = useModal()
+ const postPayModalData = useAppSelector(
+ state => state.editingV2Project.nftRewards.postPayModal,
+ )
+ return (
+ <>
+
+
+
+ Show your supporters a pop-up with a message and a link after they
+ receive an NFT. You can use this to direct supporters to your
+ project's website, a Discord server, or somewhere else.
+
+
+
+ The message that will be shown to supporters after they
+ receive an NFT.
+
+ }
+ />
+ }
+ >
+
+
+
+ The text on the button at the bottom of the pop-up. You can
+ preview this below.
+
+ }
+ />
+ }
+ >
+
+
+
+ Supporters will be sent to this page if they click the button
+ on your pop-up. You can preview this below.
+
+ }
+ />
+ }
+ extra={t`If you leave this blank, the button will close the pop-up.`}
+ >
+
+
+ {hidePreview ? null : (
+ }
+ onClick={postPayModal.open}
+ >
+ Preview
+
+ )}
+
+ {postPayModalData && (
+
+ )}
+ >
+ )
+}
diff --git a/src/components/Create/components/pages/NftRewards/index.ts b/src/components/Create/components/pages/NftRewards/index.ts
index 5f5f642e3c..b915789057 100644
--- a/src/components/Create/components/pages/NftRewards/index.ts
+++ b/src/components/Create/components/pages/NftRewards/index.ts
@@ -1 +1 @@
-export * from './NftRewardsPage'
+export * from './AddNftCollectionForm'
diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/ProjectSettingsContent.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/ProjectSettingsContent.tsx
index 45d6fd2e0f..fd98432b22 100644
--- a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/ProjectSettingsContent.tsx
+++ b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/ProjectSettingsContent.tsx
@@ -1,11 +1,13 @@
+import * as constants from '@ethersproject/constants'
import { ChevronRightIcon } from '@heroicons/react/20/solid'
import { ArrowLeftIcon } from '@heroicons/react/24/outline'
import { Trans, t } from '@lingui/macro'
import { Button, Layout } from 'antd'
import { V2V3SettingsPageKey } from 'components/v2v3/V2V3Project/V2V3ProjectSettings/ProjectSettingsDashboard'
import { FEATURE_FLAGS } from 'constants/featureFlags'
+import { V2V3ProjectContext } from 'contexts/v2v3/Project/V2V3ProjectContext'
import Link from 'next/link'
-import { useMemo } from 'react'
+import { useContext, useMemo } from 'react'
import { twJoin } from 'tailwind-merge'
import { featureFlagEnabled } from 'utils/featureFlags'
import { ProjectSettingsLayout } from './ProjectSettingsLayout'
@@ -14,6 +16,7 @@ import { ArchiveProjectSettingsPage } from './pages/ArchiveProjectSettingsPage'
import { EditNftsPage } from './pages/EditNftsPage'
import { GovernanceSettingsPage } from './pages/GovernanceSettingsPage'
import { EditCyclePage } from './pages/NewEditCyclePage/EditCyclePage'
+import { EditNftsPage as NewEditNftsPage } from './pages/NewEditNftsPage/EditNftsPage'
import { PayoutsSettingsPage } from './pages/PayoutsSettingsPage'
import { ProjectDetailsSettingsPage } from './pages/ProjectDetailsSettingsPage/ProjectDetailsSettingsPage'
import { ProjectHandleSettingsPage } from './pages/ProjectHandleSettingsPage'
@@ -32,7 +35,9 @@ const SettingsPageComponents: {
cycle: featureFlagEnabled(FEATURE_FLAGS.NEW_CYCLE_CONFIG_PAGE)
? EditCyclePage
: ReconfigureFundingCycleSettingsPage,
- nfts: EditNftsPage,
+ nfts: featureFlagEnabled(FEATURE_FLAGS.NEW_EDIT_NFTS)
+ ? NewEditNftsPage
+ : EditNftsPage,
payouts: PayoutsSettingsPage,
reservedtokens: ReservedTokensSettingsPage,
tokenmigration: V1V2TokenMigrationSettingsPage,
@@ -43,7 +48,9 @@ const SettingsPageComponents: {
projectnft: ProjectNftSettingsPage,
}
-const V2V3SettingsPageKeyTitleMap = (): {
+const V2V3SettingsPageKeyTitleMap = (
+ hasExistingNfts: boolean,
+): {
[k in V2V3SettingsPageKey]: string
} => ({
general: t`General`,
@@ -51,7 +58,7 @@ const V2V3SettingsPageKeyTitleMap = (): {
cycle: t`Cycle configuration`,
payouts: t`Payouts`,
reservedtokens: t`Reserved token recipients`,
- nfts: t`Edit NFT collection`,
+ nfts: hasExistingNfts ? t`Edit NFT collection` : t`Launch New NFT Collection`,
tokenmigration: t`Token migration`,
transferownership: t`Transfer ownership`,
archiveproject: t`Archive project`,
@@ -99,12 +106,17 @@ export function ProjectSettingsContent({
}: {
settingsPageKey: V2V3SettingsPageKey
}) {
+ const { fundingCycleMetadata } = useContext(V2V3ProjectContext)
+ const hasExistingNfts =
+ fundingCycleMetadata?.dataSource !== constants.AddressZero
+
const ActiveSettingsPage = useMemo(
() => SettingsPageComponents[settingsPageKey],
[settingsPageKey],
)
- const pageTitle = V2V3SettingsPageKeyTitleMap()[settingsPageKey]
+ const pageTitle =
+ V2V3SettingsPageKeyTitleMap(hasExistingNfts)[settingsPageKey]
return (
diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/EditNftsPage.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/EditNftsPage.tsx
index 8c801cbc61..095b962219 100644
--- a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/EditNftsPage.tsx
+++ b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/EditNftsPage.tsx
@@ -1,18 +1,14 @@
-import { t, Trans } from '@lingui/macro'
-import { Button, Empty, Tabs } from 'antd'
+import { Trans } from '@lingui/macro'
+import { Button, Empty } from 'antd'
import Loading from 'components/Loading'
-import { FEATURE_FLAGS } from 'constants/featureFlags'
import { ProjectMetadataContext } from 'contexts/shared/ProjectMetadataContext'
import { V2V3ProjectContext } from 'contexts/v2v3/Project/V2V3ProjectContext'
import { useHasNftRewards } from 'hooks/JB721Delegate/useHasNftRewards'
import Image from 'next/image'
import Link from 'next/link'
import { useContext } from 'react'
-import { featureFlagEnabled } from 'utils/featureFlags'
import { settingsPagePath } from 'utils/routes'
-import { EditCollectionDetailsSection } from './EditCollectionDetailsSection'
-import { EditNftsSection } from './EditNftsSection'
-import { NewEditNftsSection } from './NewEditNftsSection'
+import { UpdateNftsPage } from '../NewEditNftsPage/UpdateNftsPage/UpdateNftsPage'
import blueberry from '/public/assets/images/blueberry-ol.png'
export function EditNftsPage() {
@@ -20,23 +16,10 @@ export function EditNftsPage() {
const { projectId } = useContext(ProjectMetadataContext)
const { handle } = useContext(V2V3ProjectContext)
- const items = [
- { label: t`NFTs`, key: 'nfts', children: },
- {
- label: t`Collection details`,
- key: 'collection',
- children: ,
- },
- ]
-
if (hasNftsLoading) {
return
}
- if (featureFlagEnabled(FEATURE_FLAGS.NEW_EDIT_NFTS)) {
- return
- }
-
if (!hasExistingNfts) {
return (
@@ -78,5 +61,5 @@ export function EditNftsPage() {
)
}
- return
+ return
}
diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/EnableNftsCard.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/EnableNftsCard.tsx
deleted file mode 100644
index 9d90224c72..0000000000
--- a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/EnableNftsCard.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import { Trans } from '@lingui/macro'
-import { Button } from 'antd'
-import { ExternalLinkWithIcon } from 'components/ProjectDashboard/components/ui/ExternalLinkWithIcon'
-import { useState } from 'react'
-import { helpPagePath } from 'utils/routes'
-import { EditCycleHeader } from '../NewEditCyclePage/EditCycleHeader'
-import { EnableNftsModal } from './EnableNftsModal'
-
-export function EnableNftsCard() {
- const [enableNftsModalOpen, setEnableNftsModalOpen] = useState(false)
- return (
- <>
-
- Enable NFTs for your project}
- description={
-
- Juicebox mints and provides extended functionality for you to sell
- or reward contributors with NFTs for your project.{' '}
-
- Learn more about NFTs
-
-
- }
- />
-
-
- setEnableNftsModalOpen(false)}
- />
- >
- )
-}
diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/NewEditNftsSection.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/NewEditNftsSection.tsx
deleted file mode 100644
index d99d5ab3de..0000000000
--- a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/NewEditNftsSection.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import { Trans } from '@lingui/macro'
-import { Form } from 'antd'
-import TooltipLabel from 'components/TooltipLabel'
-import { JuiceSwitch } from 'components/inputs/JuiceSwitch'
-import { ProjectMetadataContext } from 'contexts/shared/ProjectMetadataContext'
-import { V2V3ProjectContext } from 'contexts/v2v3/Project/V2V3ProjectContext'
-import { useNftDeployerCanReconfigure } from 'hooks/JB721Delegate/contractReader/useNftDeployerCanReconfigure'
-import { useContext } from 'react'
-import { AdvancedDropdown } from '../NewEditCyclePage/AdvancedDropdown'
-import { EnableNftsCard } from './EnableNftsCard'
-import { NftsTable } from './NftsTable'
-
-export function NewEditNftsSection() {
- const { projectOwnerAddress } = useContext(V2V3ProjectContext)
- const { projectId } = useContext(ProjectMetadataContext)
-
- const nftDeployerCanReconfigure = useNftDeployerCanReconfigure({
- projectId,
- projectOwnerAddress,
- })
-
- return (
-
- {nftDeployerCanReconfigure ? : }
-
-
- Use NFTs for redemption}
- tip={
-
- Contributors will redeem from the project's treasury with
- their NFTs as opposed to standard project tokens.
-
- }
- />
- }
- />
-
-
-
- )
-}
diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/NftsTable.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/NftsTable.tsx
deleted file mode 100644
index f6d0335fc5..0000000000
--- a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/NftsTable.tsx
+++ /dev/null
@@ -1,3 +0,0 @@
-export function NftsTable() {
- return <>NFTs table>
-}
diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/hooks/useEditingNfts.tsx.ts b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/hooks/useEditingNfts.ts
similarity index 100%
rename from src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/hooks/useEditingNfts.tsx.ts
rename to src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/hooks/useEditingNfts.ts
diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditCyclePage/ReviewConfirmModal/ReviewConfirmModal.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditCyclePage/ReviewConfirmModal/ReviewConfirmModal.tsx
index 81e4897b5f..bd8ea39420 100644
--- a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditCyclePage/ReviewConfirmModal/ReviewConfirmModal.tsx
+++ b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditCyclePage/ReviewConfirmModal/ReviewConfirmModal.tsx
@@ -57,7 +57,7 @@ export function ReviewConfirmModal({
open={open}
title={Review & confirm}
destroyOnClose
- onOk={reconfigureFundingCycle}
+ onOk={() => reconfigureFundingCycle()}
okText={Deploy changes}
okButtonProps={{ disabled: !formHasChanges }}
cancelButtonProps={{ hidden: true }}
diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/EditNftsPage.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/EditNftsPage.tsx
new file mode 100644
index 0000000000..a5a08d8031
--- /dev/null
+++ b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/EditNftsPage.tsx
@@ -0,0 +1,29 @@
+import * as constants from '@ethersproject/constants'
+import { ProjectMetadataContext } from 'contexts/shared/ProjectMetadataContext'
+import { V2V3ProjectContext } from 'contexts/v2v3/Project/V2V3ProjectContext'
+import { useNftDeployerCanReconfigure } from 'hooks/JB721Delegate/contractReader/useNftDeployerCanReconfigure'
+import { useContext } from 'react'
+import { EnableNftsCard } from './LaunchNftCollection/EnableNftsCard'
+import { LaunchNftsPage } from './LaunchNftCollection/LaunchNftsCollection'
+import { UpdateNftsPage } from './UpdateNftsPage/UpdateNftsPage'
+
+export function EditNftsPage() {
+ const { projectId } = useContext(ProjectMetadataContext)
+ const { projectOwnerAddress, fundingCycleMetadata } =
+ useContext(V2V3ProjectContext)
+ const hasExistingNfts =
+ fundingCycleMetadata?.dataSource !== constants.AddressZero
+
+ const nftDeployerCanReconfigure = useNftDeployerCanReconfigure({
+ projectId,
+ projectOwnerAddress,
+ })
+
+ if (hasExistingNfts) {
+ return
+ } else if (!nftDeployerCanReconfigure) {
+ return
+ } else {
+ return
+ }
+}
diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/LaunchNftCollection/EnableNftsCard.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/LaunchNftCollection/EnableNftsCard.tsx
new file mode 100644
index 0000000000..c8ab9b2eed
--- /dev/null
+++ b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/LaunchNftCollection/EnableNftsCard.tsx
@@ -0,0 +1,39 @@
+import { Trans } from '@lingui/macro'
+import { Button } from 'antd'
+import Image from 'next/image'
+import { useState } from 'react'
+import { EnableNftsModal } from './EnableNftsModal'
+import noNftsImage from '/public/assets/images/settings/no-nfts.webp'
+
+export function EnableNftsCard() {
+ const [enableNftsModalOpen, setEnableNftsModalOpen] = useState(false)
+ return (
+ <>
+
+
+
+ You haven't launched an NFT collection yet.
+
+
+
+
+ setEnableNftsModalOpen(false)}
+ />
+ >
+ )
+}
diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/EnableNftsModal.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/LaunchNftCollection/EnableNftsModal.tsx
similarity index 89%
rename from src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/EnableNftsModal.tsx
rename to src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/LaunchNftCollection/EnableNftsModal.tsx
index 8e21603606..5ce4dcd1d9 100644
--- a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/EnableNftsModal.tsx
+++ b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/LaunchNftCollection/EnableNftsModal.tsx
@@ -4,6 +4,7 @@ import TooltipIcon from 'components/TooltipIcon'
import TransactionModal from 'components/modals/TransactionModal'
import { useSetNftOperatorPermissionsTx } from 'hooks/JB721Delegate/transactor/useSetNftOperatorPermissionsTx'
import { useState } from 'react'
+import { reloadWindow } from 'utils/windowUtils'
export function EnableNftsModal({
open,
@@ -21,7 +22,7 @@ export function EnableNftsModal({
await setNftOperatorPermissionsTx(undefined, {
onConfirmed: () => {
setLoading(false)
- onClose()
+ reloadWindow()
},
onDone() {
setLoading(false)
@@ -43,8 +44,8 @@ export function EnableNftsModal({
confirmLoading={loading}
>
- To add NFTs to your cycle. You'll need to{' '}
- grant NFT permissions before launching your new cycle.
+ To add NFTs to your next cycle, you'll need to{' '}
+ grant NFT permissions.
{' '}
}
+ />
+ )
+}
diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/LaunchNftCollection/UploadAndLaunchNftsButton.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/LaunchNftCollection/UploadAndLaunchNftsButton.tsx
new file mode 100644
index 0000000000..a416fd5115
--- /dev/null
+++ b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/LaunchNftCollection/UploadAndLaunchNftsButton.tsx
@@ -0,0 +1,95 @@
+import { Trans } from '@lingui/macro'
+import { Button } from 'antd'
+import { useState } from 'react'
+import { useAppSelector } from 'redux/hooks/useAppSelector'
+import { pinNftCollectionMetadata, pinNftRewards } from 'utils/nftRewards'
+import {
+ EditingFundingCycleConfig,
+ useEditingFundingCycleConfig,
+} from '../../ReconfigureFundingCycleSettingsPage/hooks/useEditingFundingCycleConfig'
+import { useReconfigureFundingCycle } from '../../ReconfigureFundingCycleSettingsPage/hooks/useReconfigureFundingCycle'
+
+export function UploadAndLaunchNftsButton({
+ className,
+}: {
+ className?: string
+}) {
+ const [ipfsUploading, setIpfsUploading] = useState(false)
+
+ const {
+ projectMetadata: { logoUri },
+ } = useAppSelector(state => state.editingV2Project)
+
+ const editingFundingCycleConfig = useEditingFundingCycleConfig()
+ const { reconfigureLoading, reconfigureFundingCycle } =
+ useReconfigureFundingCycle({
+ editingFundingCycleConfig,
+ memo: 'First NFT collection',
+ launchedNewNfts: true,
+ })
+
+ const {
+ editingPayoutGroupedSplits,
+ editingReservedTokensGroupedSplits,
+ editingFundingCycleMetadata,
+ editingFundingCycleData,
+ editingFundAccessConstraints,
+ editingNftRewards,
+ editingMustStartAtOrAfter,
+ } = editingFundingCycleConfig
+
+ const uploadNftsToIpfs = async () => {
+ setIpfsUploading(true)
+ const newRewardTiers =
+ editingFundingCycleConfig.editingNftRewards?.rewardTiers
+ const collectionName =
+ editingFundingCycleConfig.editingNftRewards?.collectionMetadata.name ?? ''
+ const collectionDescription =
+ editingFundingCycleConfig.editingNftRewards?.collectionMetadata
+ .description ?? ''
+ const collectionLogoUri = logoUri ?? ''
+ const collectionInfoUri =
+ editingFundingCycleConfig.editingNftRewards?.collectionMetadata.uri ?? ''
+
+ const [rewardTiersCIDs, nftCollectionMetadataUri] = await Promise.all([
+ newRewardTiers ? pinNftRewards(newRewardTiers) : [],
+ pinNftCollectionMetadata({
+ collectionName,
+ collectionDescription,
+ collectionLogoUri,
+ collectionInfoUri,
+ }),
+ ])
+ const latestEditingData: EditingFundingCycleConfig = {
+ editingPayoutGroupedSplits,
+ editingReservedTokensGroupedSplits,
+ editingFundingCycleMetadata,
+ editingFundingCycleData,
+ editingFundAccessConstraints,
+ editingNftRewards: editingNftRewards
+ ? {
+ ...editingNftRewards,
+ collectionMetadata: {
+ ...editingNftRewards.collectionMetadata,
+ uri: nftCollectionMetadataUri,
+ },
+ CIDs: rewardTiersCIDs,
+ }
+ : undefined,
+ editingMustStartAtOrAfter,
+ }
+ reconfigureFundingCycle(latestEditingData)
+ setIpfsUploading(false)
+ }
+
+ return (
+
+ )
+}
diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/LaunchNftCollection/index.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/LaunchNftCollection/index.tsx
new file mode 100644
index 0000000000..ac2a006b48
--- /dev/null
+++ b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/LaunchNftCollection/index.tsx
@@ -0,0 +1 @@
+export * from './LaunchNftsCollection'
diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/EditCollectionDetailsSection.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/UpdateNftsPage/EditCollectionDetailsSection.tsx
similarity index 93%
rename from src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/EditCollectionDetailsSection.tsx
rename to src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/UpdateNftsPage/EditCollectionDetailsSection.tsx
index f2d0e4330b..85c058d5aa 100644
--- a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/EditCollectionDetailsSection.tsx
+++ b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/UpdateNftsPage/EditCollectionDetailsSection.tsx
@@ -8,8 +8,8 @@ import { useNftCollectionMetadata } from 'hooks/JB721Delegate/useNftCollectionMe
import { useReconfigureNftCollectionMetadata } from 'hooks/v2v3/transactor/useReconfigureNftCollectionMetadata'
import { NftCollectionMetadata } from 'models/nftRewards'
import { useCallback, useContext, useEffect, useMemo, useState } from 'react'
-import { NftCollectionDetailsFormItems } from '../../../../shared/FundingCycleConfigurationDrawers/NftDrawer/shared/NftCollectionDetailsFormItems'
-import { MarketplaceFormFields } from '../../../../shared/FundingCycleConfigurationDrawers/NftDrawer/shared/formFields'
+import { NftCollectionDetailsFormItems } from '../../../../../shared/FundingCycleConfigurationDrawers/NftDrawer/shared/NftCollectionDetailsFormItems'
+import { MarketplaceFormFields } from '../../../../../shared/FundingCycleConfigurationDrawers/NftDrawer/shared/formFields'
const useCollectionDetailsForm = () => {
const [form] = useForm()
diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/UpdateNftsPage/EditNftsPostPaySection/EditNftsPostPaySection.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/UpdateNftsPage/EditNftsPostPaySection/EditNftsPostPaySection.tsx
new file mode 100644
index 0000000000..0e89354040
--- /dev/null
+++ b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/UpdateNftsPage/EditNftsPostPaySection/EditNftsPostPaySection.tsx
@@ -0,0 +1,91 @@
+import { Trans } from '@lingui/macro'
+import { Button, Form } from 'antd'
+import { NftPaymentSuccessFormItems } from 'components/Create/components/pages/NftRewards/NftPaymentSuccessFormItems'
+import { PV_V2 } from 'constants/pv'
+import { ProjectMetadataContext } from 'contexts/shared/ProjectMetadataContext'
+import { useEditProjectDetailsTx } from 'hooks/v2v3/transactor/useEditProjectDetailsTx'
+import { uploadProjectMetadata } from 'lib/api/ipfs'
+import { revalidateProject } from 'lib/api/nextjs'
+import { useCallback, useContext, useState } from 'react'
+import { emitInfoNotification } from 'utils/notifications'
+import {
+ EditNftsPostPayFormFields,
+ useEditNftsPostPayForm,
+} from './useEditNftsPostPayForm'
+
+export function EditNftsPostPaySection() {
+ const [loading, setLoading] = useState()
+ const { projectMetadata, refetchProjectMetadata, projectId } = useContext(
+ ProjectMetadataContext,
+ )
+ const { form, initialValues } = useEditNftsPostPayForm()
+
+ const editProjectDetailsTx = useEditProjectDetailsTx()
+
+ const onProjectFormSaved = useCallback(async () => {
+ setLoading(true)
+
+ const fields: EditNftsPostPayFormFields = form.getFieldsValue(true)
+ const uploadedMetadata = await uploadProjectMetadata({
+ ...projectMetadata,
+ nftPaymentSuccessModal: {
+ ctaText: fields.postPayButtonText,
+ ctaLink: fields.postPayButtonLink,
+ content: fields.postPayMessage,
+ },
+ })
+
+ if (!uploadedMetadata.Hash) {
+ setLoading(false)
+ return
+ }
+
+ const txSuccess = await editProjectDetailsTx(
+ {
+ cid: uploadedMetadata.Hash,
+ },
+ {
+ onConfirmed: async () => {
+ setLoading(false)
+
+ emitInfoNotification('NFT post-pay data changed', {
+ description: 'Your new NFT post-pay has been saved.',
+ })
+
+ if (projectId) {
+ await revalidateProject({
+ pv: PV_V2,
+ projectId: String(projectId),
+ })
+ }
+ refetchProjectMetadata()
+ },
+ onError: () => {
+ setLoading(false)
+ },
+ onCancelled: () => {
+ setLoading(false)
+ },
+ },
+ )
+
+ if (!txSuccess) {
+ setLoading(false)
+ }
+ }, [
+ editProjectDetailsTx,
+ form,
+ projectMetadata,
+ projectId,
+ refetchProjectMetadata,
+ ])
+
+ return (
+
+ )
+}
diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/UpdateNftsPage/EditNftsPostPaySection/useEditNftsPostPayForm.ts b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/UpdateNftsPage/EditNftsPostPaySection/useEditNftsPostPayForm.ts
new file mode 100644
index 0000000000..b921ea3e23
--- /dev/null
+++ b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/UpdateNftsPage/EditNftsPostPaySection/useEditNftsPostPayForm.ts
@@ -0,0 +1,24 @@
+import { Form } from 'antd'
+import { ProjectMetadataContext } from 'contexts/shared/ProjectMetadataContext'
+import { useContext } from 'react'
+
+export interface EditNftsPostPayFormFields {
+ postPayMessage: string | undefined
+ postPayButtonText: string | undefined
+ postPayButtonLink: string | undefined
+}
+
+export const useEditNftsPostPayForm = () => {
+ const { projectMetadata } = useContext(ProjectMetadataContext)
+ const [form] = Form.useForm()
+
+ const initialValues: EditNftsPostPayFormFields = {
+ postPayMessage: projectMetadata?.nftPaymentSuccessModal?.content,
+ postPayButtonText: projectMetadata?.nftPaymentSuccessModal?.ctaText,
+ postPayButtonLink: projectMetadata?.nftPaymentSuccessModal?.ctaLink,
+ }
+ return {
+ form,
+ initialValues,
+ }
+}
diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/EditNftsSection.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/UpdateNftsPage/EditNftsSection.tsx
similarity index 88%
rename from src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/EditNftsSection.tsx
rename to src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/UpdateNftsPage/EditNftsSection.tsx
index d0e4b9ee46..200aa44f15 100644
--- a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/EditNftsPage/EditNftsSection.tsx
+++ b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/UpdateNftsPage/EditNftsSection.tsx
@@ -7,7 +7,7 @@ import { useUpdateCurrentCollection } from 'components/v2v3/V2V3Project/V2V3Proj
import { useHasNftRewards } from 'hooks/JB721Delegate/useHasNftRewards'
import { useCallback, useState } from 'react'
-import { useEditingNfts } from './hooks/useEditingNfts.tsx'
+import { useEditingNfts } from '../../EditNftsPage/hooks/useEditingNfts'
export function EditNftsSection() {
const [submitLoading, setSubmitLoading] = useState(false)
@@ -27,7 +27,11 @@ export function EditNftsSection() {
setSubmitLoading(false)
}, [rewardTiers, updateExistingCollection])
- if (loading) return
+ // this component only renders when data source is not 0x000..
+ // so if there are no rewardTiers here, it's safe to assume they're still loading
+ const noTiers = !rewardTiers || rewardTiers.length === 0
+
+ if (loading || noTiers) return
return (
<>
diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/UpdateNftsPage/UpdateNftsPage.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/UpdateNftsPage/UpdateNftsPage.tsx
new file mode 100644
index 0000000000..07b8545546
--- /dev/null
+++ b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/UpdateNftsPage/UpdateNftsPage.tsx
@@ -0,0 +1,23 @@
+import { t } from '@lingui/macro'
+import { Tabs } from 'antd'
+import { EditCollectionDetailsSection } from './EditCollectionDetailsSection'
+import { EditNftsPostPaySection } from './EditNftsPostPaySection/EditNftsPostPaySection'
+import { EditNftsSection } from './EditNftsSection'
+
+export function UpdateNftsPage() {
+ const items = [
+ { label: t`NFTs`, key: 'nfts', children: },
+ {
+ label: t`Collection details`,
+ key: 'collection',
+ children: ,
+ },
+ {
+ label: t`Post-pay popup`,
+ key: 'post-pay',
+ children: ,
+ },
+ ]
+
+ return
+}
diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/UpdateNftsPage/index.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/UpdateNftsPage/index.tsx
new file mode 100644
index 0000000000..511e1182d5
--- /dev/null
+++ b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/UpdateNftsPage/index.tsx
@@ -0,0 +1 @@
+export * from './UpdateNftsPage'
diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ReconfigureFundingCycleSettingsPage/hooks/useReconfigureFundingCycle.ts b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ReconfigureFundingCycleSettingsPage/hooks/useReconfigureFundingCycle.ts
index 07d039787c..d1ced0c89a 100644
--- a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ReconfigureFundingCycleSettingsPage/hooks/useReconfigureFundingCycle.ts
+++ b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ReconfigureFundingCycleSettingsPage/hooks/useReconfigureFundingCycle.ts
@@ -65,111 +65,109 @@ export const useReconfigureFundingCycle = ({
const reconfigureV2V3FundingCycleWithNftsTx =
useReconfigureV2V3FundingCycleWithNftsTx()
- const {
- editingPayoutGroupedSplits,
- editingReservedTokensGroupedSplits,
- editingFundingCycleMetadata,
- editingFundingCycleData,
- editingFundAccessConstraints,
- editingNftRewards,
- editingMustStartAtOrAfter,
- } = editingFundingCycleConfig
-
- const reconfigureFundingCycle = useCallback(async () => {
- setReconfigureTxLoading(true)
- if (
- !(
- fundingCycle &&
- editingFundingCycleData &&
- editingFundingCycleMetadata &&
- editingFundAccessConstraints
- )
- ) {
- setReconfigureTxLoading(false)
- throw new Error('Error deploying project.')
- }
-
- // Projects with NFT rewards need useDataSourceForPay to be true for NFT rewards to work
- const fundingCycleMetadata = nftRewardsCids?.length
- ? {
- ...editingFundingCycleMetadata,
- ...NFT_FUNDING_CYCLE_METADATA_OVERRIDES,
- }
- : editingFundingCycleMetadata
-
- const weight = getWeightArgument({
- currentFundingCycleWeight: fundingCycle.weight,
- newFundingCycleWeight: editingFundingCycleData.weight,
- })
-
- const reconfigureFundingCycleData: ReconfigureFundingCycleTxParams = {
- fundingCycleData: {
- ...editingFundingCycleData,
- weight,
- },
- fundingCycleMetadata,
- fundAccessConstraints: editingFundAccessConstraints,
- groupedSplits: [
+ // If given a latestEditingData, will use that. Else, will use redux store
+ const reconfigureFundingCycle = useCallback(
+ async (latestEditingData?: EditingFundingCycleConfig) => {
+ const {
editingPayoutGroupedSplits,
editingReservedTokensGroupedSplits,
- ],
- memo,
- mustStartAtOrAfter: editingMustStartAtOrAfter,
- }
+ editingFundingCycleMetadata,
+ editingFundingCycleData,
+ editingFundAccessConstraints,
+ editingNftRewards,
+ editingMustStartAtOrAfter,
+ } = latestEditingData ?? editingFundingCycleConfig
- const txOpts = {
- async onConfirmed() {
- if (projectId) {
- await revalidateProject({
- pv: PV_V2,
- projectId: String(projectId),
- })
- }
+ setReconfigureTxLoading(true)
+ if (
+ !(
+ fundingCycle &&
+ editingFundingCycleData &&
+ editingFundingCycleMetadata &&
+ editingFundAccessConstraints
+ )
+ ) {
setReconfigureTxLoading(false)
- if (onComplete) {
- onComplete()
- } else {
- reloadWindow()
- }
- },
- }
+ throw new Error('Error deploying project.')
+ }
+
+ // Projects with NFT rewards need useDataSourceForPay to be true for NFT rewards to work
+ const fundingCycleMetadata = nftRewardsCids?.length
+ ? {
+ ...editingFundingCycleMetadata,
+ ...NFT_FUNDING_CYCLE_METADATA_OVERRIDES,
+ }
+ : editingFundingCycleMetadata
- let txSuccessful: boolean
- if (launchedNewNfts && editingNftRewards?.rewardTiers) {
- txSuccessful = await reconfigureV2V3FundingCycleWithNftsTx(
- {
- reconfigureData: reconfigureFundingCycleData,
- tiered721DelegateData: editingNftRewards,
+ const weight = getWeightArgument({
+ currentFundingCycleWeight: fundingCycle.weight,
+ newFundingCycleWeight: editingFundingCycleData.weight,
+ })
+
+ const reconfigureFundingCycleData: ReconfigureFundingCycleTxParams = {
+ fundingCycleData: {
+ ...editingFundingCycleData,
+ weight,
+ },
+ fundingCycleMetadata,
+ fundAccessConstraints: editingFundAccessConstraints,
+ groupedSplits: [
+ editingPayoutGroupedSplits,
+ editingReservedTokensGroupedSplits,
+ ],
+ memo,
+ mustStartAtOrAfter: editingMustStartAtOrAfter,
+ }
+
+ const txOpts = {
+ async onConfirmed() {
+ if (projectId) {
+ await revalidateProject({
+ pv: PV_V2,
+ projectId: String(projectId),
+ })
+ }
+ setReconfigureTxLoading(false)
+ if (onComplete) {
+ onComplete()
+ } else {
+ reloadWindow()
+ }
},
- txOpts,
- )
- } else {
- txSuccessful = await reconfigureV2V3FundingCycleTx(
- reconfigureFundingCycleData,
- txOpts,
- )
- }
+ }
- if (!txSuccessful) {
- setReconfigureTxLoading(false)
- }
- }, [
- editingFundingCycleData,
- editingFundingCycleMetadata,
- editingFundAccessConstraints,
- reconfigureV2V3FundingCycleTx,
- reconfigureV2V3FundingCycleWithNftsTx,
- launchedNewNfts,
- editingNftRewards,
- editingPayoutGroupedSplits,
- editingReservedTokensGroupedSplits,
- editingMustStartAtOrAfter,
- nftRewardsCids,
- fundingCycle,
- memo,
- onComplete,
- projectId,
- ])
+ let txSuccessful: boolean
+ if (launchedNewNfts && editingNftRewards?.rewardTiers) {
+ txSuccessful = await reconfigureV2V3FundingCycleWithNftsTx(
+ {
+ reconfigureData: reconfigureFundingCycleData,
+ tiered721DelegateData: editingNftRewards,
+ },
+ txOpts,
+ )
+ } else {
+ txSuccessful = await reconfigureV2V3FundingCycleTx(
+ reconfigureFundingCycleData,
+ txOpts,
+ )
+ }
+
+ if (!txSuccessful) {
+ setReconfigureTxLoading(false)
+ }
+ },
+ [
+ editingFundingCycleConfig,
+ reconfigureV2V3FundingCycleTx,
+ reconfigureV2V3FundingCycleWithNftsTx,
+ launchedNewNfts,
+ nftRewardsCids,
+ fundingCycle,
+ memo,
+ onComplete,
+ projectId,
+ ],
+ )
return { reconfigureLoading: reconfigureTxLoading, reconfigureFundingCycle }
}
diff --git a/src/locales/messages.pot b/src/locales/messages.pot
index 816f2e4a88..60d8f2429a 100644
--- a/src/locales/messages.pot
+++ b/src/locales/messages.pot
@@ -152,9 +152,6 @@ msgstr ""
msgid "Data from current cycle"
msgstr ""
-msgid "Contributors will redeem from the project's treasury with their NFTs as opposed to standard project tokens."
-msgstr ""
-
msgid "You're about to add NFTs to your cycle. You'll need to <0>grant NFT permissions0> before deploying the new cycle"
msgstr ""
@@ -308,9 +305,6 @@ msgstr ""
msgid "Contributions"
msgstr ""
-msgid "Enable NFTs for your project"
-msgstr ""
-
msgid "Attach an image"
msgstr ""
@@ -1022,6 +1016,9 @@ msgstr ""
msgid "<0><1>Peel1> manages this website — the juicebox.money frontend interface. You can reach out to Peel through the <2>Peel Discord2>.0><3><4>JuiceboxDAO4> builds and governs the Juicebox fundraising protocol and other community resources. You can reach out to JuiceboxDAO through the <5>Juicebox Discord5>.3>"
msgstr ""
+msgid "To add NFTs to your next cycle, you'll need to <0>grant NFT permissions0>."
+msgstr ""
+
msgid "Email address"
msgstr ""
@@ -1514,6 +1511,9 @@ msgstr ""
msgid "Current"
msgstr ""
+msgid "Save post-pay popup"
+msgstr ""
+
msgid "Next cycle, the project will issue {0} tokens per 1 ETH. The cycle after that, the project will issue {1} tokens per 1 ETH."
msgstr ""
@@ -3017,9 +3017,6 @@ msgstr ""
msgid "Danger Zone"
msgstr ""
-msgid "Juicebox mints and provides extended functionality for you to sell or reward contributors with NFTs for your project. <0>Learn more about NFTs0>"
-msgstr ""
-
msgid "Create an ERC-20 token (optional)"
msgstr ""
@@ -3401,6 +3398,9 @@ msgstr ""
msgid "Project tags"
msgstr ""
+msgid "Deploy NFT collection"
+msgstr ""
+
msgid "Configure how your project will collect and spend ETH."
msgstr ""
@@ -3539,9 +3539,6 @@ msgstr ""
msgid "Prevent NFT overspending"
msgstr ""
-msgid "To add NFTs to your cycle. You'll need to <0>grant NFT permissions0> before launching your new cycle."
-msgstr ""
-
msgid "Pay this project to receive NFTs."
msgstr ""
@@ -3581,9 +3578,6 @@ msgstr ""
msgid "Burn your {tokensLabel}. You won't receive ETH in return because this project has no ETH, or is using all of its ETH for payouts."
msgstr ""
-msgid "Use NFTs for redemption"
-msgstr ""
-
msgid "JB vs. Kickstarter"
msgstr ""
@@ -4487,6 +4481,9 @@ msgstr ""
msgid "Percentage to reserve"
msgstr ""
+msgid "Post-pay popup"
+msgstr ""
+
msgid "All assets"
msgstr ""
@@ -4559,12 +4556,18 @@ msgstr ""
msgid "For fundraising"
msgstr ""
+msgid "Launch New NFT Collection"
+msgstr ""
+
msgid "Status"
msgstr ""
msgid "Redemption rate:"
msgstr ""
+msgid "You haven't launched an NFT collection yet."
+msgstr ""
+
msgid "Project Token"
msgstr ""