From 09ce68209ea056ccc8d0751a97e1189d04b44b95 Mon Sep 17 00:00:00 2001 From: Johnny D Date: Mon, 4 Sep 2023 14:06:18 +1000 Subject: [PATCH] Complete launch NFT collection form --- src/components/Create/Create.tsx | 4 +- ...wardsPage.tsx => AddNftCollectionForm.tsx} | 2 +- .../components/pages/NftRewards/index.ts | 2 +- .../ProjectSettingsContent.tsx | 2 +- .../ReviewConfirmModal/ReviewConfirmModal.tsx | 2 +- .../{NewEditNftsPage.tsx => EditNftsPage.tsx} | 6 +- .../pages/NewEditNftsPage/EnableNftsModal.tsx | 2 +- .../NewEditNftsPage/LaunchNftCollection.tsx | 71 ------- .../NewEditNftsPage/LaunchNftsAdvanced.tsx | 30 --- .../pages/NewEditNftsPage/LaunchNftsPage.tsx | 10 + .../UploadAndLaunchNftsButton.tsx | 95 +++++++++ .../hooks/useReconfigureFundingCycle.ts | 198 +++++++++--------- src/locales/messages.pot | 12 +- src/utils/nftRewards.ts | 1 + 14 files changed, 215 insertions(+), 222 deletions(-) rename src/components/Create/components/pages/NftRewards/{NftRewardsPage.tsx => AddNftCollectionForm.tsx} (99%) rename src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/{NewEditNftsPage.tsx => EditNftsPage.tsx} (74%) delete mode 100644 src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/LaunchNftCollection.tsx delete mode 100644 src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/LaunchNftsAdvanced.tsx create mode 100644 src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/LaunchNftsPage.tsx create mode 100644 src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/UploadAndLaunchNftsButton.tsx diff --git a/src/components/Create/Create.tsx b/src/components/Create/Create.tsx index 1741f23c2c..b778396280 100644 --- a/src/components/Create/Create.tsx +++ b/src/components/Create/Create.tsx @@ -12,8 +12,8 @@ import { NetworkName } from 'models/networkName' import Link from 'next/link' import { useRouter } from 'next/router' import { + AddNftCollectionForm, FundingCyclesPage, - NftRewardsPage, ProjectDetailsPage, ProjectTokenPage, ReconfigurationRulesPage, @@ -124,7 +124,7 @@ export function Create() { Reward your supporters with custom NFTs. } > - + 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/NewEditNftsPage.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/EditNftsPage.tsx similarity index 74% rename from src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/NewEditNftsPage.tsx rename to src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/EditNftsPage.tsx index 4bd74ba3f4..ee52d3b1c9 100644 --- a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/NewEditNftsPage.tsx +++ b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/EditNftsPage.tsx @@ -1,9 +1,9 @@ import Loading from 'components/Loading' import { useHasNftRewards } from 'hooks/JB721Delegate/useHasNftRewards' import { EditNftsTabs } from '../EditNftsPage/EditNftsTabs' -import { LaunchNftCollection } from './LaunchNftCollection' +import { LaunchNftsPage } from './LaunchNftsPage' -export function NewEditNftsPage() { +export function EditNftsPage() { const { value: hasExistingNfts, loading: hasNftsLoading } = useHasNftRewards() if (hasNftsLoading) { @@ -12,6 +12,6 @@ export function NewEditNftsPage() { if (hasExistingNfts) { return } else { - return + return } } diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/EnableNftsModal.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/EnableNftsModal.tsx index fc62ba6cb1..72a316d6e6 100644 --- a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/EnableNftsModal.tsx +++ b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/EnableNftsModal.tsx @@ -43,7 +43,7 @@ export function EnableNftsModal({ confirmLoading={loading} > - To add NFTs to your next cycle. You'll need to{' '} + To add NFTs to your next cycle, you'll need to{' '} grant NFT permissions. {' '} state.editingV2Project) - const dispatch = useDispatch() - - const editingFundingCycleConfig = useEditingFundingCycleConfig() - const { reconfigureLoading, reconfigureFundingCycle } = - useReconfigureFundingCycle({ - editingFundingCycleConfig, - memo: 'First NFT collection', - launchedNewNfts: true, - }) - - const onNftDeploy = async () => { - 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, - }), - ]) - console.info('rewardTiersCIDs: ', rewardTiersCIDs) - console.info('nftCollectionMetadataUri: ', nftCollectionMetadataUri) - dispatch(editingV2ProjectActions.setNftRewardsCIDs(rewardTiersCIDs)) - dispatch( - editingV2ProjectActions.setNftRewardsCollectionMetadataUri( - nftCollectionMetadataUri, - ), - ) - reconfigureFundingCycle() - } - - return ( - - Deploy NFT collection - - } - /> - ) -} diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/LaunchNftsAdvanced.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/LaunchNftsAdvanced.tsx deleted file mode 100644 index d285afd913..0000000000 --- a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/LaunchNftsAdvanced.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { Trans } from '@lingui/macro' -import { Form } from 'antd' -import TooltipLabel from 'components/TooltipLabel' -import { JuiceSwitch } from 'components/inputs/JuiceSwitch' -import { AdvancedDropdown } from '../NewEditCyclePage/AdvancedDropdown' - -export function LaunchNftsAdvanced() { - return ( - - {/* On-chain Governace (simple 2-option Select) */} - {/* Payment success message */} - - Use NFTs for redemption} - tip={ - - Contributors will redeem from the project's treasury with - their NFTs as opposed to standard project tokens. - - } - /> - } - /> - - {/* Prevent NFT overspending */} - - ) -} diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/LaunchNftsPage.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/LaunchNftsPage.tsx new file mode 100644 index 0000000000..ca5c19c30e --- /dev/null +++ b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/LaunchNftsPage.tsx @@ -0,0 +1,10 @@ +import { AddNftCollectionForm } from 'components/Create/components' +import { UploadAndLaunchNftsButton } from './UploadAndLaunchNftsButton' + +export function LaunchNftsPage() { + return ( + } + /> + ) +} diff --git a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/UploadAndLaunchNftsButton.tsx b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/UploadAndLaunchNftsButton.tsx new file mode 100644 index 0000000000..3b7f0bab9d --- /dev/null +++ b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/NewEditNftsPage/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/ReconfigureFundingCycleSettingsPage/hooks/useReconfigureFundingCycle.ts b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ReconfigureFundingCycleSettingsPage/hooks/useReconfigureFundingCycle.ts index 7ee6a2b5c6..d1ced0c89a 100644 --- a/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ReconfigureFundingCycleSettingsPage/hooks/useReconfigureFundingCycle.ts +++ b/src/components/v2v3/V2V3Project/V2V3ProjectSettings/pages/ReconfigureFundingCycleSettingsPage/hooks/useReconfigureFundingCycle.ts @@ -57,10 +57,6 @@ export const useReconfigureFundingCycle = ({ const { nftRewards: { CIDs: nftRewardsCids }, } = useContext(NftRewardsContext) - console.info( - 'TODO REMOVE: editingFundingCycleConfig: ', - editingFundingCycleConfig, - ) const [reconfigureTxLoading, setReconfigureTxLoading] = useState(false) @@ -69,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, - ) - } + } + + 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) - } - }, [ - editingFundingCycleData, - editingFundingCycleMetadata, - editingFundAccessConstraints, - reconfigureV2V3FundingCycleTx, - reconfigureV2V3FundingCycleWithNftsTx, - launchedNewNfts, - editingNftRewards, - editingPayoutGroupedSplits, - editingReservedTokensGroupedSplits, - editingMustStartAtOrAfter, - nftRewardsCids, - fundingCycle, - memo, - onComplete, - projectId, - ]) + 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 f6b0815014..f3bacdfd41 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 permissions before deploying the new cycle" msgstr "" @@ -497,9 +494,6 @@ msgstr "" msgid "Due to your <0>{0} contract, edits you make will not take effect until the first cycle to start at least <1>{1} from now." msgstr "" -msgid "To add NFTs to your next cycle. You'll need to <0>grant NFT permissions." -msgstr "" - msgid "Payer issuance rate" msgstr "" @@ -1022,6 +1016,9 @@ msgstr "" msgid "<0><1>Peel manages this website — the juicebox.money frontend interface. You can reach out to Peel through the <2>Peel Discord.<3><4>JuiceboxDAO builds and governs the Juicebox fundraising protocol and other community resources. You can reach out to JuiceboxDAO through the <5>Juicebox Discord." msgstr "" +msgid "To add NFTs to your next cycle, you'll need to <0>grant NFT permissions." +msgstr "" + msgid "Email address" msgstr "" @@ -3578,9 +3575,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 "" diff --git a/src/utils/nftRewards.ts b/src/utils/nftRewards.ts index 67ee9ce755..d795111266 100644 --- a/src/utils/nftRewards.ts +++ b/src/utils/nftRewards.ts @@ -175,6 +175,7 @@ export async function pinNftCollectionMetadata({ : 'https://juicebox.money', fee_recipient: undefined, } + const res = await pinJson(IPFSNftCollectionMetadata) return res.Hash }