Merge pull request #194 from public-awesome/export-import-collection-config
Export/Import collection creation configuration
This commit is contained in:
commit
1a9d7ac9c8
@ -1,4 +1,4 @@
|
||||
APP_VERSION=0.7.3
|
||||
APP_VERSION=0.7.4
|
||||
|
||||
NEXT_PUBLIC_PINATA_ENDPOINT_URL=https://api.pinata.cloud/pinning/pinFileToIPFS
|
||||
NEXT_PUBLIC_SG721_CODE_ID=2595
|
||||
|
@ -13,6 +13,8 @@ export interface MetadataInputProps {
|
||||
selectedAssetFile: File
|
||||
selectedMetadataFile: File
|
||||
updateMetadataToUpload: (metadataFile: File) => void
|
||||
onChange?: (metadata: any) => void
|
||||
importedMetadata?: any
|
||||
}
|
||||
|
||||
export const MetadataInput = (props: MetadataInputProps) => {
|
||||
@ -151,9 +153,12 @@ export const MetadataInput = (props: MetadataInputProps) => {
|
||||
|
||||
useEffect(() => {
|
||||
console.log(props.selectedMetadataFile?.name)
|
||||
if (props.selectedMetadataFile) void parseMetadata(props.selectedMetadataFile)
|
||||
else void parseMetadata(emptyMetadataFile)
|
||||
}, [props.selectedMetadataFile?.name])
|
||||
if (props.selectedMetadataFile) {
|
||||
void parseMetadata(props.selectedMetadataFile)
|
||||
} else if (!props.importedMetadata) {
|
||||
void parseMetadata(emptyMetadataFile)
|
||||
}
|
||||
}, [props.selectedMetadataFile?.name, props.importedMetadata])
|
||||
|
||||
const nameStateMemo = useMemo(() => nameState, [nameState.value])
|
||||
const descriptionStateMemo = useMemo(() => descriptionState, [descriptionState.value])
|
||||
@ -163,7 +168,10 @@ export const MetadataInput = (props: MetadataInputProps) => {
|
||||
|
||||
useEffect(() => {
|
||||
console.log('Update metadata')
|
||||
if (metadata) generateUpdatedMetadata()
|
||||
if (metadata) {
|
||||
generateUpdatedMetadata()
|
||||
if (props.onChange) props.onChange(metadata)
|
||||
}
|
||||
console.log(metadata)
|
||||
}, [
|
||||
nameStateMemo.value,
|
||||
@ -173,6 +181,33 @@ export const MetadataInput = (props: MetadataInputProps) => {
|
||||
attributesStateMemo.entries,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
if (props.importedMetadata) {
|
||||
void parseMetadata(emptyMetadataFile).then(() => {
|
||||
console.log('Imported metadata: ', props.importedMetadata)
|
||||
nameState.onChange(props.importedMetadata.name || '')
|
||||
descriptionState.onChange(props.importedMetadata.description || '')
|
||||
externalUrlState.onChange(props.importedMetadata.external_url || '')
|
||||
youtubeUrlState.onChange(props.importedMetadata.youtube_url || '')
|
||||
if (props.importedMetadata?.attributes && props.importedMetadata?.attributes?.length > 0) {
|
||||
attributesState.reset()
|
||||
props.importedMetadata?.attributes?.forEach((attribute: { trait_type: string; value: string }) => {
|
||||
attributesState.add({
|
||||
trait_type: attribute.trait_type,
|
||||
value: attribute.value,
|
||||
})
|
||||
})
|
||||
} else {
|
||||
attributesState.reset()
|
||||
attributesState.add({
|
||||
trait_type: '',
|
||||
value: '',
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}, [props.importedMetadata])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="grid grid-cols-2 mt-4 mr-4 ml-8 w-full max-w-6xl max-h-full no-scrollbar">
|
||||
|
@ -28,6 +28,7 @@ export interface MinterInfo {
|
||||
interface BaseMinterDetailsProps {
|
||||
onChange: (data: BaseMinterDetailsDataProps) => void
|
||||
minterType: MinterType
|
||||
importedBaseMinterDetails?: BaseMinterDetailsDataProps
|
||||
}
|
||||
|
||||
export interface BaseMinterDetailsDataProps {
|
||||
@ -37,7 +38,7 @@ export interface BaseMinterDetailsDataProps {
|
||||
collectionTokenCount: number | undefined
|
||||
}
|
||||
|
||||
export const BaseMinterDetails = ({ onChange, minterType }: BaseMinterDetailsProps) => {
|
||||
export const BaseMinterDetails = ({ onChange, minterType, importedBaseMinterDetails }: BaseMinterDetailsProps) => {
|
||||
const wallet = useWallet()
|
||||
|
||||
const [myBaseMinterContracts, setMyBaseMinterContracts] = useState<MinterInfo[]>([])
|
||||
@ -198,6 +199,15 @@ export const BaseMinterDetails = ({ onChange, minterType }: BaseMinterDetailsPro
|
||||
collectionTokenCount,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
if (importedBaseMinterDetails) {
|
||||
setBaseMinterAcquisitionMethod(importedBaseMinterDetails.baseMinterAcquisitionMethod)
|
||||
existingBaseMinterState.onChange(
|
||||
importedBaseMinterDetails.existingBaseMinter ? importedBaseMinterDetails.existingBaseMinter : '',
|
||||
)
|
||||
}
|
||||
}, [importedBaseMinterDetails])
|
||||
|
||||
return (
|
||||
<div className="mx-10 mb-4 rounded border-2 border-white/20">
|
||||
<div className="flex justify-center mb-2">
|
||||
|
@ -26,6 +26,7 @@ interface CollectionDetailsProps {
|
||||
uploadMethod: UploadMethod
|
||||
coverImageUrl: string
|
||||
minterType: MinterType
|
||||
importedCollectionDetails?: CollectionDetailsDataProps
|
||||
}
|
||||
|
||||
export interface CollectionDetailsDataProps {
|
||||
@ -39,7 +40,13 @@ export interface CollectionDetailsDataProps {
|
||||
updatable: boolean
|
||||
}
|
||||
|
||||
export const CollectionDetails = ({ onChange, uploadMethod, coverImageUrl, minterType }: CollectionDetailsProps) => {
|
||||
export const CollectionDetails = ({
|
||||
onChange,
|
||||
uploadMethod,
|
||||
coverImageUrl,
|
||||
minterType,
|
||||
importedCollectionDetails,
|
||||
}: CollectionDetailsProps) => {
|
||||
const [coverImage, setCoverImage] = useState<File | null>(null)
|
||||
const [timestamp, setTimestamp] = useState<Date | undefined>()
|
||||
const [explicit, setExplicit] = useState<boolean>(false)
|
||||
@ -105,6 +112,23 @@ export const CollectionDetails = ({ onChange, uploadMethod, coverImageUrl, minte
|
||||
updatable,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
if (importedCollectionDetails) {
|
||||
nameState.onChange(importedCollectionDetails.name)
|
||||
descriptionState.onChange(importedCollectionDetails.description)
|
||||
symbolState.onChange(importedCollectionDetails.symbol)
|
||||
externalLinkState.onChange(importedCollectionDetails.externalLink || '')
|
||||
setTimestamp(
|
||||
importedCollectionDetails.startTradingTime
|
||||
? new Date(parseInt(importedCollectionDetails.startTradingTime) / 1_000_000)
|
||||
: undefined,
|
||||
)
|
||||
setExplicit(importedCollectionDetails.explicit)
|
||||
setUpdatable(importedCollectionDetails.updatable)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [importedCollectionDetails])
|
||||
|
||||
const selectCoverImage = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.files === null) return toast.error('Error selecting cover image')
|
||||
if (event.target.files.length === 0) {
|
||||
|
@ -20,6 +20,7 @@ interface MintingDetailsProps {
|
||||
uploadMethod: UploadMethod
|
||||
minimumMintPrice: number
|
||||
mintingTokenFromFactory?: TokenInfo
|
||||
importedMintingDetails?: MintingDetailsDataProps
|
||||
}
|
||||
|
||||
export interface MintingDetailsDataProps {
|
||||
@ -37,6 +38,7 @@ export const MintingDetails = ({
|
||||
uploadMethod,
|
||||
minimumMintPrice,
|
||||
mintingTokenFromFactory,
|
||||
importedMintingDetails,
|
||||
}: MintingDetailsProps) => {
|
||||
const wallet = useWallet()
|
||||
|
||||
@ -113,6 +115,18 @@ export const MintingDetails = ({
|
||||
selectedMintToken,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
if (importedMintingDetails) {
|
||||
numberOfTokensState.onChange(importedMintingDetails.numTokens)
|
||||
unitPriceState.onChange(Number(importedMintingDetails.unitPrice) / 1_000_000)
|
||||
perAddressLimitState.onChange(importedMintingDetails.perAddressLimit)
|
||||
setTimestamp(new Date(Number(importedMintingDetails.startTime) / 1_000_000))
|
||||
paymentAddressState.onChange(importedMintingDetails.paymentAddress ? importedMintingDetails.paymentAddress : '')
|
||||
setSelectedMintToken(tokensList.find((token) => token.id === importedMintingDetails.selectedMintToken?.id))
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [importedMintingDetails])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<FormGroup subtitle="Information about your minting settings" title="Minting Details">
|
||||
@ -127,6 +141,7 @@ export const MintingDetails = ({
|
||||
<select
|
||||
className="py-[9px] px-4 mt-14 ml-2 placeholder:text-white/50 bg-white/10 rounded border-2 border-white/20 focus:ring focus:ring-plumbus-20"
|
||||
onChange={(e) => setSelectedMintToken(tokensList.find((t) => t.displayName === e.target.value))}
|
||||
value={selectedMintToken?.displayName}
|
||||
>
|
||||
{vendingMinterList
|
||||
.filter((minter) => minter.factoryAddress !== undefined && minter.updatable === false)
|
||||
|
@ -9,6 +9,7 @@ import { NumberInput, TextInput } from '../../forms/FormInput'
|
||||
|
||||
interface RoyaltyDetailsProps {
|
||||
onChange: (data: RoyaltyDetailsDataProps) => void
|
||||
importedRoyaltyDetails?: RoyaltyDetailsDataProps
|
||||
}
|
||||
|
||||
export interface RoyaltyDetailsDataProps {
|
||||
@ -19,7 +20,7 @@ export interface RoyaltyDetailsDataProps {
|
||||
|
||||
type RoyaltyState = 'none' | 'new'
|
||||
|
||||
export const RoyaltyDetails = ({ onChange }: RoyaltyDetailsProps) => {
|
||||
export const RoyaltyDetails = ({ onChange, importedRoyaltyDetails }: RoyaltyDetailsProps) => {
|
||||
const wallet = useWallet()
|
||||
const [royaltyState, setRoyaltyState] = useState<RoyaltyState>('none')
|
||||
|
||||
@ -60,6 +61,15 @@ export const RoyaltyDetails = ({ onChange }: RoyaltyDetailsProps) => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [royaltyState, royaltyPaymentAddressState.value, royaltyShareState.value])
|
||||
|
||||
useEffect(() => {
|
||||
if (importedRoyaltyDetails) {
|
||||
setRoyaltyState(importedRoyaltyDetails.royaltyType)
|
||||
royaltyPaymentAddressState.onChange(importedRoyaltyDetails.paymentAddress)
|
||||
royaltyShareState.onChange(importedRoyaltyDetails.share.toString())
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [importedRoyaltyDetails])
|
||||
|
||||
return (
|
||||
<div className="py-3 px-8 rounded border-2 border-white/20">
|
||||
<div className="flex justify-center">
|
||||
|
@ -31,6 +31,7 @@ interface UploadDetailsProps {
|
||||
onChange: (value: UploadDetailsDataProps) => void
|
||||
minterType: MinterType
|
||||
baseMinterAcquisitionMethod?: BaseMinterAcquisitionMethod
|
||||
importedUploadDetails?: UploadDetailsDataProps
|
||||
}
|
||||
|
||||
export interface UploadDetailsDataProps {
|
||||
@ -46,7 +47,12 @@ export interface UploadDetailsDataProps {
|
||||
baseMinterMetadataFile?: File
|
||||
}
|
||||
|
||||
export const UploadDetails = ({ onChange, minterType, baseMinterAcquisitionMethod }: UploadDetailsProps) => {
|
||||
export const UploadDetails = ({
|
||||
onChange,
|
||||
minterType,
|
||||
baseMinterAcquisitionMethod,
|
||||
importedUploadDetails,
|
||||
}: UploadDetailsProps) => {
|
||||
const [assetFilesArray, setAssetFilesArray] = useState<File[]>([])
|
||||
const [metadataFilesArray, setMetadataFilesArray] = useState<File[]>([])
|
||||
const [uploadMethod, setUploadMethod] = useState<UploadMethod>('new')
|
||||
@ -274,10 +280,31 @@ export const UploadDetails = ({ onChange, minterType, baseMinterAcquisitionMetho
|
||||
setMetadataFilesArray([])
|
||||
if (assetFilesRef.current) assetFilesRef.current.value = ''
|
||||
setAssetFilesArray([])
|
||||
if (!importedUploadDetails || minterType === 'base') {
|
||||
baseTokenUriState.onChange('')
|
||||
coverImageUrlState.onChange('')
|
||||
}
|
||||
}, [uploadMethod, minterType, baseMinterAcquisitionMethod])
|
||||
|
||||
useEffect(() => {
|
||||
if (importedUploadDetails) {
|
||||
if (importedUploadDetails.uploadMethod === 'new') {
|
||||
setUploadMethod('new')
|
||||
setUploadService(importedUploadDetails.uploadService)
|
||||
nftStorageApiKeyState.onChange(importedUploadDetails.nftStorageApiKey || '')
|
||||
pinataApiKeyState.onChange(importedUploadDetails.pinataApiKey || '')
|
||||
pinataSecretKeyState.onChange(importedUploadDetails.pinataSecretKey || '')
|
||||
baseTokenUriState.onChange(importedUploadDetails.baseTokenURI || '')
|
||||
coverImageUrlState.onChange(importedUploadDetails.imageUrl || '')
|
||||
} else if (importedUploadDetails.uploadMethod === 'existing') {
|
||||
setUploadMethod('existing')
|
||||
baseTokenUriState.onChange(importedUploadDetails.baseTokenURI || '')
|
||||
coverImageUrlState.onChange(importedUploadDetails.imageUrl || '')
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [importedUploadDetails])
|
||||
|
||||
return (
|
||||
<div className="justify-items-start mb-3 rounded border-2 border-white/20 flex-column">
|
||||
<div className="flex justify-center">
|
||||
|
@ -9,6 +9,7 @@ import { InputDateTime } from 'components/InputDateTime'
|
||||
import type { WhitelistFlexMember } from 'components/WhitelistFlexUpload'
|
||||
import { WhitelistFlexUpload } from 'components/WhitelistFlexUpload'
|
||||
import type { TokenInfo } from 'config/token'
|
||||
import { useWallet } from 'contexts/wallet'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { isValidAddress } from 'utils/isValidAddress'
|
||||
|
||||
@ -20,6 +21,7 @@ import { WhitelistUpload } from '../../WhitelistUpload'
|
||||
interface WhitelistDetailsProps {
|
||||
onChange: (data: WhitelistDetailsDataProps) => void
|
||||
mintingTokenFromFactory?: TokenInfo
|
||||
importedWhitelistDetails?: WhitelistDetailsDataProps
|
||||
}
|
||||
|
||||
export interface WhitelistDetailsDataProps {
|
||||
@ -40,7 +42,13 @@ type WhitelistState = 'none' | 'existing' | 'new'
|
||||
|
||||
type WhitelistType = 'standard' | 'flex'
|
||||
|
||||
export const WhitelistDetails = ({ onChange, mintingTokenFromFactory }: WhitelistDetailsProps) => {
|
||||
export const WhitelistDetails = ({
|
||||
onChange,
|
||||
mintingTokenFromFactory,
|
||||
importedWhitelistDetails,
|
||||
}: WhitelistDetailsProps) => {
|
||||
const wallet = useWallet()
|
||||
|
||||
const [whitelistState, setWhitelistState] = useState<WhitelistState>('none')
|
||||
const [whitelistType, setWhitelistType] = useState<WhitelistType>('standard')
|
||||
const [startDate, setStartDate] = useState<Date | undefined>(undefined)
|
||||
@ -93,8 +101,10 @@ export const WhitelistDetails = ({ onChange, mintingTokenFromFactory }: Whitelis
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!importedWhitelistDetails) {
|
||||
setWhitelistStandardArray([])
|
||||
setWhitelistFlexArray([])
|
||||
}
|
||||
}, [whitelistType])
|
||||
|
||||
useEffect(() => {
|
||||
@ -142,6 +152,63 @@ export const WhitelistDetails = ({ onChange, mintingTokenFromFactory }: Whitelis
|
||||
adminsMutable,
|
||||
])
|
||||
|
||||
// make the necessary changes with respect to imported whitelist details
|
||||
useEffect(() => {
|
||||
if (importedWhitelistDetails) {
|
||||
setWhitelistState(importedWhitelistDetails.whitelistState)
|
||||
setWhitelistType(importedWhitelistDetails.whitelistType)
|
||||
whitelistAddressState.onChange(
|
||||
importedWhitelistDetails.contractAddress ? importedWhitelistDetails.contractAddress : '',
|
||||
)
|
||||
unitPriceState.onChange(
|
||||
importedWhitelistDetails.unitPrice ? Number(importedWhitelistDetails.unitPrice) / 1000000 : 0,
|
||||
)
|
||||
memberLimitState.onChange(importedWhitelistDetails.memberLimit ? importedWhitelistDetails.memberLimit : 0)
|
||||
perAddressLimitState.onChange(
|
||||
importedWhitelistDetails.perAddressLimit ? importedWhitelistDetails.perAddressLimit : 0,
|
||||
)
|
||||
setStartDate(
|
||||
importedWhitelistDetails.startTime
|
||||
? new Date(Number(importedWhitelistDetails.startTime) / 1_000_000)
|
||||
: undefined,
|
||||
)
|
||||
setEndDate(
|
||||
importedWhitelistDetails.endTime ? new Date(Number(importedWhitelistDetails.endTime) / 1_000_000) : undefined,
|
||||
)
|
||||
setAdminsMutable(importedWhitelistDetails.adminsMutable ? importedWhitelistDetails.adminsMutable : true)
|
||||
importedWhitelistDetails.admins?.forEach((admin) => {
|
||||
addressListState.reset()
|
||||
addressListState.add({ address: admin })
|
||||
})
|
||||
if (importedWhitelistDetails.whitelistType === 'standard') {
|
||||
setWhitelistStandardArray([])
|
||||
importedWhitelistDetails.members?.forEach((member) => {
|
||||
setWhitelistStandardArray((standardArray) => [...standardArray, member as string])
|
||||
})
|
||||
} else {
|
||||
setWhitelistFlexArray([])
|
||||
importedWhitelistDetails.members?.forEach((member) => {
|
||||
setWhitelistFlexArray((flexArray) => [
|
||||
...flexArray,
|
||||
{
|
||||
address: (member as WhitelistFlexMember).address,
|
||||
mint_count: (member as WhitelistFlexMember).mint_count,
|
||||
},
|
||||
])
|
||||
})
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [importedWhitelistDetails])
|
||||
|
||||
useEffect(() => {
|
||||
if (whitelistState === 'new' && wallet.address) {
|
||||
addressListState.reset()
|
||||
addressListState.add({ address: wallet.address })
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [whitelistState, wallet.address])
|
||||
|
||||
return (
|
||||
<div className="py-3 px-8 rounded border-2 border-white/20">
|
||||
<div className="flex justify-center">
|
||||
@ -290,7 +357,6 @@ export const WhitelistDetails = ({ onChange, mintingTokenFromFactory }: Whitelis
|
||||
<div className="my-4 ml-4">
|
||||
<AddressList
|
||||
entries={addressListState.entries}
|
||||
isRequired
|
||||
onAdd={addressListState.add}
|
||||
onChange={addressListState.update}
|
||||
onRemove={addressListState.remove}
|
||||
|
@ -31,6 +31,7 @@ export function AddressList(props: AddressListProps) {
|
||||
{entries.map(([id], i) => (
|
||||
<Address
|
||||
key={`ib-${id}`}
|
||||
defaultValue={entries[i][1]}
|
||||
id={id}
|
||||
isLast={i === entries.length - 1}
|
||||
onAdd={onAdd}
|
||||
@ -48,9 +49,10 @@ export interface AddressProps {
|
||||
onAdd: AddressListProps['onAdd']
|
||||
onChange: AddressListProps['onChange']
|
||||
onRemove: AddressListProps['onRemove']
|
||||
defaultValue?: Address
|
||||
}
|
||||
|
||||
export function Address({ id, isLast, onAdd, onChange, onRemove }: AddressProps) {
|
||||
export function Address({ id, isLast, onAdd, onChange, onRemove, defaultValue }: AddressProps) {
|
||||
const wallet = useWallet()
|
||||
const Icon = useMemo(() => (isLast ? FaPlus : FaMinus), [isLast])
|
||||
|
||||
@ -60,6 +62,7 @@ export function Address({ id, isLast, onAdd, onChange, onRemove }: AddressProps)
|
||||
id: `ib-address-${htmlId}`,
|
||||
name: `ib-address-${htmlId}`,
|
||||
title: ``,
|
||||
defaultValue: defaultValue?.address,
|
||||
})
|
||||
|
||||
const resolveAddress = async (name: string) => {
|
||||
|
@ -28,6 +28,7 @@ interface CollectionDetailsProps {
|
||||
uploadMethod: UploadMethod
|
||||
coverImageUrl: string
|
||||
metadataStorageMethod: MetadataStorageMethod
|
||||
importedCollectionDetails?: CollectionDetailsDataProps
|
||||
}
|
||||
|
||||
export interface CollectionDetailsDataProps {
|
||||
@ -46,6 +47,7 @@ export const CollectionDetails = ({
|
||||
uploadMethod,
|
||||
metadataStorageMethod,
|
||||
coverImageUrl,
|
||||
importedCollectionDetails,
|
||||
}: CollectionDetailsProps) => {
|
||||
const [coverImage, setCoverImage] = useState<File | null>(null)
|
||||
const [timestamp, setTimestamp] = useState<Date | undefined>()
|
||||
@ -152,6 +154,23 @@ export const CollectionDetails = ({
|
||||
}
|
||||
}, [updatable])
|
||||
|
||||
useEffect(() => {
|
||||
if (importedCollectionDetails) {
|
||||
nameState.onChange(importedCollectionDetails.name)
|
||||
descriptionState.onChange(importedCollectionDetails.description)
|
||||
symbolState.onChange(importedCollectionDetails.symbol)
|
||||
//setCoverImage(importedCollectionDetails.imageFile[0] || null)
|
||||
externalLinkState.onChange(importedCollectionDetails.externalLink || '')
|
||||
setTimestamp(
|
||||
importedCollectionDetails.startTradingTime
|
||||
? new Date(parseInt(importedCollectionDetails.startTradingTime) / 1_000_000)
|
||||
: undefined,
|
||||
)
|
||||
setExplicit(importedCollectionDetails.explicit)
|
||||
setUpdatable(importedCollectionDetails.updatable)
|
||||
}
|
||||
}, [importedCollectionDetails])
|
||||
|
||||
const videoPreview = useMemo(() => {
|
||||
if (uploadMethod === 'new' && coverImage) {
|
||||
return (
|
||||
|
@ -20,6 +20,7 @@ export type UploadMethod = 'new' | 'existing'
|
||||
|
||||
interface ImageUploadDetailsProps {
|
||||
onChange: (value: ImageUploadDetailsDataProps) => void
|
||||
importedImageUploadDetails?: ImageUploadDetailsDataProps
|
||||
}
|
||||
|
||||
export interface ImageUploadDetailsDataProps {
|
||||
@ -33,7 +34,7 @@ export interface ImageUploadDetailsDataProps {
|
||||
coverImageUrl?: string
|
||||
}
|
||||
|
||||
export const ImageUploadDetails = ({ onChange }: ImageUploadDetailsProps) => {
|
||||
export const ImageUploadDetails = ({ onChange, importedImageUploadDetails }: ImageUploadDetailsProps) => {
|
||||
const [assetFile, setAssetFile] = useState<File>()
|
||||
const [uploadMethod, setUploadMethod] = useState<UploadMethod>('new')
|
||||
const [uploadService, setUploadService] = useState<UploadServiceType>('nft-storage')
|
||||
@ -140,6 +141,18 @@ export const ImageUploadDetails = ({ onChange }: ImageUploadDetailsProps) => {
|
||||
imageUrlState.onChange('')
|
||||
}, [uploadMethod])
|
||||
|
||||
useEffect(() => {
|
||||
if (importedImageUploadDetails) {
|
||||
setUploadMethod(importedImageUploadDetails.uploadMethod)
|
||||
setUploadService(importedImageUploadDetails.uploadService)
|
||||
nftStorageApiKeyState.onChange(importedImageUploadDetails.nftStorageApiKey || '')
|
||||
pinataApiKeyState.onChange(importedImageUploadDetails.pinataApiKey || '')
|
||||
pinataSecretKeyState.onChange(importedImageUploadDetails.pinataSecretKey || '')
|
||||
imageUrlState.onChange(importedImageUploadDetails.imageUrl || '')
|
||||
coverImageUrlState.onChange(importedImageUploadDetails.coverImageUrl || '')
|
||||
}
|
||||
}, [importedImageUploadDetails])
|
||||
|
||||
const previewUrl = imageUrlState.value.toLowerCase().trim().startsWith('ipfs://')
|
||||
? `https://ipfs-gw.stargaze-apis.com/ipfs/${imageUrlState.value.substring(7)}`
|
||||
: imageUrlState.value
|
||||
|
@ -19,6 +19,7 @@ interface MintingDetailsProps {
|
||||
uploadMethod: UploadMethod
|
||||
minimumMintPrice: number
|
||||
mintTokenFromFactory?: TokenInfo | undefined
|
||||
importedMintingDetails?: MintingDetailsDataProps
|
||||
}
|
||||
|
||||
export interface MintingDetailsDataProps {
|
||||
@ -35,12 +36,14 @@ export const MintingDetails = ({
|
||||
uploadMethod,
|
||||
minimumMintPrice,
|
||||
mintTokenFromFactory,
|
||||
importedMintingDetails,
|
||||
}: MintingDetailsProps) => {
|
||||
const wallet = useWallet()
|
||||
|
||||
const [timestamp, setTimestamp] = useState<Date | undefined>()
|
||||
const [endTimestamp, setEndTimestamp] = useState<Date | undefined>()
|
||||
const [selectedMintToken, setSelectedMintToken] = useState<TokenInfo | undefined>(stars)
|
||||
const [mintingDetailsImported, setMintingDetailsImported] = useState(false)
|
||||
|
||||
const unitPriceState = useNumberInputState({
|
||||
id: 'unitPrice',
|
||||
@ -75,7 +78,9 @@ export const MintingDetails = ({
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!importedMintingDetails || (importedMintingDetails && mintingDetailsImported)) {
|
||||
void resolvePaymentAddress()
|
||||
}
|
||||
}, [paymentAddressState.value])
|
||||
|
||||
useEffect(() => {
|
||||
@ -102,6 +107,20 @@ export const MintingDetails = ({
|
||||
selectedMintToken,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
if (importedMintingDetails) {
|
||||
console.log('Selected Token ID: ', importedMintingDetails.selectedMintToken?.id)
|
||||
unitPriceState.onChange(Number(importedMintingDetails.unitPrice) / 1000000)
|
||||
perAddressLimitState.onChange(importedMintingDetails.perAddressLimit)
|
||||
setTimestamp(new Date(Number(importedMintingDetails.startTime) / 1_000_000))
|
||||
setEndTimestamp(new Date(Number(importedMintingDetails.endTime) / 1_000_000))
|
||||
paymentAddressState.onChange(importedMintingDetails.paymentAddress ? importedMintingDetails.paymentAddress : '')
|
||||
setSelectedMintToken(tokensList.find((token) => token.id === importedMintingDetails.selectedMintToken?.id))
|
||||
setMintingDetailsImported(true)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [importedMintingDetails])
|
||||
|
||||
return (
|
||||
<div className="border-l-[1px] border-gray-500 border-opacity-20">
|
||||
<FormGroup subtitle="Information about your minting settings" title="Minting Details">
|
||||
@ -110,6 +129,7 @@ export const MintingDetails = ({
|
||||
<select
|
||||
className="py-[9px] px-4 mt-14 ml-4 placeholder:text-white/50 bg-white/10 rounded border-2 border-white/20 focus:ring focus:ring-plumbus-20"
|
||||
onChange={(e) => setSelectedMintToken(tokensList.find((t) => t.displayName === e.target.value))}
|
||||
value={selectedMintToken?.displayName}
|
||||
>
|
||||
{openEditionMinterList
|
||||
.filter((minter) => minter.factoryAddress !== undefined && minter.updatable === false)
|
||||
|
@ -28,6 +28,7 @@ export type UploadMethod = 'new' | 'existing'
|
||||
interface OffChainMetadataUploadDetailsProps {
|
||||
onChange: (value: OffChainMetadataUploadDetailsDataProps) => void
|
||||
metadataStorageMethod?: MetadataStorageMethod
|
||||
importedOffChainMetadataUploadDetails?: OffChainMetadataUploadDetailsDataProps
|
||||
}
|
||||
|
||||
export interface OffChainMetadataUploadDetailsDataProps {
|
||||
@ -41,11 +42,13 @@ export interface OffChainMetadataUploadDetailsDataProps {
|
||||
tokenURI?: string
|
||||
imageUrl?: string
|
||||
openEditionMinterMetadataFile?: File
|
||||
exportedMetadata?: any
|
||||
}
|
||||
|
||||
export const OffChainMetadataUploadDetails = ({
|
||||
onChange,
|
||||
metadataStorageMethod,
|
||||
importedOffChainMetadataUploadDetails,
|
||||
}: OffChainMetadataUploadDetailsProps) => {
|
||||
const [assetFilesArray, setAssetFilesArray] = useState<File[]>([])
|
||||
const [metadataFilesArray, setMetadataFilesArray] = useState<File[]>([])
|
||||
@ -53,6 +56,7 @@ export const OffChainMetadataUploadDetails = ({
|
||||
const [uploadService, setUploadService] = useState<UploadServiceType>('nft-storage')
|
||||
const [metadataFileArrayIndex, setMetadataFileArrayIndex] = useState(0)
|
||||
const [refreshMetadata, setRefreshMetadata] = useState(false)
|
||||
const [exportedMetadata, setExportedMetadata] = useState(undefined)
|
||||
|
||||
const [openEditionMinterMetadataFile, setOpenEditionMinterMetadataFile] = useState<File | undefined>()
|
||||
|
||||
@ -204,6 +208,7 @@ export const OffChainMetadataUploadDetails = ({
|
||||
.replace(regex, '')
|
||||
.trim(),
|
||||
openEditionMinterMetadataFile,
|
||||
exportedMetadata,
|
||||
}
|
||||
onChange(data)
|
||||
} catch (error: any) {
|
||||
@ -222,6 +227,7 @@ export const OffChainMetadataUploadDetails = ({
|
||||
coverImageUrlState.value,
|
||||
refreshMetadata,
|
||||
openEditionMinterMetadataFile,
|
||||
exportedMetadata,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
@ -229,10 +235,25 @@ export const OffChainMetadataUploadDetails = ({
|
||||
setMetadataFilesArray([])
|
||||
if (assetFilesRef.current) assetFilesRef.current.value = ''
|
||||
setAssetFilesArray([])
|
||||
if (!importedOffChainMetadataUploadDetails) {
|
||||
tokenUriState.onChange('')
|
||||
coverImageUrlState.onChange('')
|
||||
}
|
||||
}, [uploadMethod, metadataStorageMethod])
|
||||
|
||||
useEffect(() => {
|
||||
if (importedOffChainMetadataUploadDetails) {
|
||||
setUploadService(importedOffChainMetadataUploadDetails.uploadService)
|
||||
nftStorageApiKeyState.onChange(importedOffChainMetadataUploadDetails.nftStorageApiKey || '')
|
||||
pinataApiKeyState.onChange(importedOffChainMetadataUploadDetails.pinataApiKey || '')
|
||||
pinataSecretKeyState.onChange(importedOffChainMetadataUploadDetails.pinataSecretKey || '')
|
||||
setUploadMethod(importedOffChainMetadataUploadDetails.uploadMethod)
|
||||
tokenUriState.onChange(importedOffChainMetadataUploadDetails.tokenURI || '')
|
||||
coverImageUrlState.onChange(importedOffChainMetadataUploadDetails.imageUrl || '')
|
||||
// setOpenEditionMinterMetadataFile(importedOffChainMetadataUploadDetails.openEditionMinterMetadataFile)
|
||||
}
|
||||
}, [importedOffChainMetadataUploadDetails])
|
||||
|
||||
return (
|
||||
<div className="justify-items-start mb-3 rounded border-2 border-white/20 flex-column">
|
||||
<div className="flex justify-center">
|
||||
@ -447,6 +468,8 @@ export const OffChainMetadataUploadDetails = ({
|
||||
/>
|
||||
</div>
|
||||
<MetadataInput
|
||||
importedMetadata={importedOffChainMetadataUploadDetails?.exportedMetadata}
|
||||
onChange={setExportedMetadata}
|
||||
selectedAssetFile={assetFilesArray[0]}
|
||||
selectedMetadataFile={metadataFilesArray[0]}
|
||||
updateMetadataToUpload={updateOpenEditionMinterMetadataFile}
|
||||
|
@ -21,6 +21,7 @@ import type { UploadMethod } from './ImageUploadDetails'
|
||||
interface OnChainMetadataInputDetailsProps {
|
||||
onChange: (data: OnChainMetadataInputDetailsDataProps) => void
|
||||
uploadMethod: UploadMethod | undefined
|
||||
importedOnChainMetadataInputDetails?: OnChainMetadataInputDetailsDataProps
|
||||
}
|
||||
|
||||
export interface OnChainMetadataInputDetailsDataProps {
|
||||
@ -34,7 +35,11 @@ export interface OnChainMetadataInputDetailsDataProps {
|
||||
youtube_url?: string
|
||||
}
|
||||
|
||||
export const OnChainMetadataInputDetails = ({ onChange, uploadMethod }: OnChainMetadataInputDetailsProps) => {
|
||||
export const OnChainMetadataInputDetails = ({
|
||||
onChange,
|
||||
uploadMethod,
|
||||
importedOnChainMetadataInputDetails,
|
||||
}: OnChainMetadataInputDetailsProps) => {
|
||||
const wallet = useWallet()
|
||||
const [timestamp, setTimestamp] = useState<Date | undefined>(undefined)
|
||||
const [metadataFile, setMetadataFile] = useState<File>()
|
||||
@ -196,6 +201,26 @@ export const OnChainMetadataInputDetails = ({ onChange, uploadMethod }: OnChainM
|
||||
youtubeUrlState.value,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
if (importedOnChainMetadataInputDetails) {
|
||||
nameState.onChange(importedOnChainMetadataInputDetails.name || '')
|
||||
descriptionState.onChange(importedOnChainMetadataInputDetails.description || '')
|
||||
externalUrlState.onChange(importedOnChainMetadataInputDetails.external_url || '')
|
||||
youtubeUrlState.onChange(importedOnChainMetadataInputDetails.youtube_url || '')
|
||||
animationUrlState.onChange(importedOnChainMetadataInputDetails.animation_url || '')
|
||||
imageDataState.onChange(importedOnChainMetadataInputDetails.image_data || '')
|
||||
if (importedOnChainMetadataInputDetails.attributes) {
|
||||
attributesState.reset()
|
||||
importedOnChainMetadataInputDetails.attributes.forEach((attr) => {
|
||||
attributesState.add({
|
||||
trait_type: attr.trait_type,
|
||||
value: attr.value,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}, [importedOnChainMetadataInputDetails])
|
||||
|
||||
return (
|
||||
<div className="py-3 px-8 rounded border-2 border-white/20">
|
||||
<span className="ml-4 text-xl font-bold underline underline-offset-4">NFT Metadata</span>
|
||||
|
@ -57,6 +57,10 @@ export interface OpenEditionMinterDetailsDataProps {
|
||||
offChainMetadataUploadDetails?: OffChainMetadataUploadDetailsDataProps
|
||||
mintingDetails?: MintingDetailsDataProps
|
||||
metadataStorageMethod?: MetadataStorageMethod
|
||||
openEditionMinterContractAddress?: string | null
|
||||
coverImageUrl?: string | null
|
||||
tokenUri?: string | null
|
||||
tokenImageUri?: string | null
|
||||
}
|
||||
|
||||
interface OpenEditionMinterCreatorProps {
|
||||
@ -68,6 +72,7 @@ interface OpenEditionMinterCreatorProps {
|
||||
minimumUpdatableMintPrice?: string
|
||||
minterType?: MinterType
|
||||
mintTokenFromFactory?: TokenInfo | undefined
|
||||
importedOpenEditionMinterDetails?: OpenEditionMinterDetailsDataProps
|
||||
}
|
||||
|
||||
export interface OpenEditionMinterCreatorDataProps {
|
||||
@ -86,6 +91,7 @@ export const OpenEditionMinterCreator = ({
|
||||
minimumUpdatableMintPrice,
|
||||
minterType,
|
||||
mintTokenFromFactory,
|
||||
importedOpenEditionMinterDetails,
|
||||
}: OpenEditionMinterCreatorProps) => {
|
||||
const wallet = useWallet()
|
||||
const { openEditionMinter: openEditionMinterContract, openEditionFactory: openEditionFactoryContract } =
|
||||
@ -303,7 +309,13 @@ export const OpenEditionMinterCreator = ({
|
||||
if (!mintingDetails.perAddressLimit || mintingDetails.perAddressLimit < 1 || mintingDetails.perAddressLimit > 50)
|
||||
throw new Error('Invalid limit for tokens per address')
|
||||
if (mintingDetails.startTime === '') throw new Error('Start time is required')
|
||||
if (mintingDetails.endTime === '') throw new Error('End time is required')
|
||||
if (Number(mintingDetails.startTime) < new Date().getTime() * 1000000) throw new Error('Invalid start time')
|
||||
if (Number(mintingDetails.endTime) < Number(mintingDetails.startTime))
|
||||
throw new Error('End time cannot be earlier than start time')
|
||||
if (Number(mintingDetails.endTime) === Number(mintingDetails.startTime))
|
||||
throw new Error('End time cannot be equal to the start time')
|
||||
|
||||
if (
|
||||
mintingDetails.paymentAddress &&
|
||||
(!isValidAddress(mintingDetails.paymentAddress) || !mintingDetails.paymentAddress.startsWith('stars1'))
|
||||
@ -577,8 +589,6 @@ export const OpenEditionMinterCreator = ({
|
||||
},
|
||||
}
|
||||
|
||||
console.log('msg: ', msg)
|
||||
console.log('Using factory address: ', factoryAddressForSelectedDenom)
|
||||
const payload: OpenEditionFactoryDispatchExecuteArgs = {
|
||||
contract: collectionDetails?.updatable ? updatableFactoryAddressForSelectedDenom : factoryAddressForSelectedDenom,
|
||||
messages: openEditionFactoryMessages,
|
||||
@ -637,6 +647,10 @@ export const OpenEditionMinterCreator = ({
|
||||
offChainMetadataUploadDetails: offChainMetadataUploadDetails ? offChainMetadataUploadDetails : undefined,
|
||||
mintingDetails: mintingDetails ? mintingDetails : undefined,
|
||||
metadataStorageMethod,
|
||||
openEditionMinterContractAddress,
|
||||
coverImageUrl,
|
||||
tokenUri,
|
||||
tokenImageUri,
|
||||
}
|
||||
onDetailsChange(data)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@ -647,8 +661,19 @@ export const OpenEditionMinterCreator = ({
|
||||
onChainMetadataInputDetails,
|
||||
offChainMetadataUploadDetails,
|
||||
mintingDetails,
|
||||
metadataStorageMethod,
|
||||
openEditionMinterContractAddress,
|
||||
coverImageUrl,
|
||||
tokenUri,
|
||||
tokenImageUri,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
if (importedOpenEditionMinterDetails) {
|
||||
setMetadataStorageMethod(importedOpenEditionMinterDetails.metadataStorageMethod as MetadataStorageMethod)
|
||||
}
|
||||
}, [importedOpenEditionMinterDetails])
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* TODO: Cancel once we're able to index on-chain metadata */}
|
||||
@ -696,16 +721,23 @@ export const OpenEditionMinterCreator = ({
|
||||
</div>
|
||||
</div>
|
||||
</Conditional>
|
||||
<div className={clsx('my-4 mx-10')}>
|
||||
<div className={clsx('my-0 mx-10')}>
|
||||
<Conditional test={metadataStorageMethod === 'off-chain'}>
|
||||
<div>
|
||||
<OffChainMetadataUploadDetails onChange={setOffChainMetadataUploadDetails} />
|
||||
<OffChainMetadataUploadDetails
|
||||
importedOffChainMetadataUploadDetails={importedOpenEditionMinterDetails?.offChainMetadataUploadDetails}
|
||||
onChange={setOffChainMetadataUploadDetails}
|
||||
/>
|
||||
</div>
|
||||
</Conditional>
|
||||
<Conditional test={metadataStorageMethod === 'on-chain'}>
|
||||
<div>
|
||||
<ImageUploadDetails onChange={setImageUploadDetails} />
|
||||
<ImageUploadDetails
|
||||
importedImageUploadDetails={importedOpenEditionMinterDetails?.imageUploadDetails}
|
||||
onChange={setImageUploadDetails}
|
||||
/>
|
||||
<OnChainMetadataInputDetails
|
||||
importedOnChainMetadataInputDetails={importedOpenEditionMinterDetails?.onChainMetadataInputDetails}
|
||||
onChange={setOnChainMetadataInputDetails}
|
||||
uploadMethod={imageUploadDetails?.uploadMethod}
|
||||
/>
|
||||
@ -719,6 +751,7 @@ export const OpenEditionMinterCreator = ({
|
||||
? (offChainMetadataUploadDetails?.imageUrl as string)
|
||||
: (imageUploadDetails?.coverImageUrl as string)
|
||||
}
|
||||
importedCollectionDetails={importedOpenEditionMinterDetails?.collectionDetails}
|
||||
metadataStorageMethod={metadataStorageMethod}
|
||||
onChange={setCollectionDetails}
|
||||
uploadMethod={
|
||||
@ -728,6 +761,7 @@ export const OpenEditionMinterCreator = ({
|
||||
}
|
||||
/>
|
||||
<MintingDetails
|
||||
importedMintingDetails={importedOpenEditionMinterDetails?.mintingDetails}
|
||||
minimumMintPrice={
|
||||
collectionDetails?.updatable
|
||||
? Number(minimumUpdatableMintPrice) / 1000000
|
||||
@ -739,7 +773,10 @@ export const OpenEditionMinterCreator = ({
|
||||
/>
|
||||
</div>
|
||||
<div className="my-6">
|
||||
<RoyaltyDetails onChange={setRoyaltyDetails} />
|
||||
<RoyaltyDetails
|
||||
importedRoyaltyDetails={importedOpenEditionMinterDetails?.royaltyDetails}
|
||||
onChange={setRoyaltyDetails}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-end w-full">
|
||||
<Button
|
||||
|
@ -9,6 +9,7 @@ import { NumberInput, TextInput } from '../forms/FormInput'
|
||||
|
||||
interface RoyaltyDetailsProps {
|
||||
onChange: (data: RoyaltyDetailsDataProps) => void
|
||||
importedRoyaltyDetails?: RoyaltyDetailsDataProps
|
||||
}
|
||||
|
||||
export interface RoyaltyDetailsDataProps {
|
||||
@ -19,9 +20,10 @@ export interface RoyaltyDetailsDataProps {
|
||||
|
||||
type RoyaltyState = 'none' | 'new'
|
||||
|
||||
export const RoyaltyDetails = ({ onChange }: RoyaltyDetailsProps) => {
|
||||
export const RoyaltyDetails = ({ onChange, importedRoyaltyDetails }: RoyaltyDetailsProps) => {
|
||||
const wallet = useWallet()
|
||||
const [royaltyState, setRoyaltyState] = useState<RoyaltyState>('none')
|
||||
const [royaltyDetailsImported, setRoyaltyDetailsImported] = useState(false)
|
||||
|
||||
const royaltyPaymentAddressState = useInputState({
|
||||
id: 'royalty-payment-address',
|
||||
@ -40,6 +42,7 @@ export const RoyaltyDetails = ({ onChange }: RoyaltyDetailsProps) => {
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (!importedRoyaltyDetails || (importedRoyaltyDetails && royaltyDetailsImported)) {
|
||||
void resolveAddress(
|
||||
royaltyPaymentAddressState.value
|
||||
.toLowerCase()
|
||||
@ -57,9 +60,20 @@ export const RoyaltyDetails = ({ onChange }: RoyaltyDetailsProps) => {
|
||||
}
|
||||
onChange(data)
|
||||
})
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [royaltyState, royaltyPaymentAddressState.value, royaltyShareState.value])
|
||||
|
||||
useEffect(() => {
|
||||
if (importedRoyaltyDetails) {
|
||||
setRoyaltyState(importedRoyaltyDetails.royaltyType)
|
||||
royaltyPaymentAddressState.onChange(importedRoyaltyDetails.paymentAddress.toString())
|
||||
royaltyShareState.onChange(importedRoyaltyDetails.share.toString())
|
||||
setRoyaltyDetailsImported(true)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [importedRoyaltyDetails])
|
||||
|
||||
return (
|
||||
<div className="py-3 px-8 mx-10 rounded border-2 border-white/20">
|
||||
<div className="flex justify-center">
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "stargaze-studio",
|
||||
"version": "0.7.3",
|
||||
"version": "0.7.4",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
|
@ -30,6 +30,7 @@ import type { RoyaltyDetailsDataProps } from 'components/collections/creation/Ro
|
||||
import type { UploadDetailsDataProps } from 'components/collections/creation/UploadDetails'
|
||||
import type { WhitelistDetailsDataProps } from 'components/collections/creation/WhitelistDetails'
|
||||
import { Conditional } from 'components/Conditional'
|
||||
import { FormControl } from 'components/FormControl'
|
||||
import { LoadingModal } from 'components/LoadingModal'
|
||||
import type { OpenEditionMinterCreatorDataProps } from 'components/openEdition/OpenEditionMinterCreator'
|
||||
import { OpenEditionMinterCreator } from 'components/openEdition/OpenEditionMinterCreator'
|
||||
@ -44,6 +45,7 @@ import type { DispatchExecuteArgs as VendingFactoryDispatchExecuteArgs } from 'c
|
||||
import { dispatchExecute as vendingFactoryDispatchExecute } from 'contracts/vendingFactory/messages/execute'
|
||||
import type { NextPage } from 'next'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import type { ChangeEvent } from 'react'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { toast } from 'react-hot-toast'
|
||||
import { upload } from 'services/upload'
|
||||
@ -90,6 +92,17 @@ const CollectionCreationPage: NextPage = () => {
|
||||
const scrollRef = useRef<HTMLDivElement>(null)
|
||||
const sidetabRef = useRef<any>(null)
|
||||
|
||||
const [importedDetails, setImportedDetails] = useState<{
|
||||
minterType: MinterType
|
||||
collectionDetails: CollectionDetailsDataProps
|
||||
uploadDetails: UploadDetailsDataProps
|
||||
mintingDetails: MintingDetailsDataProps
|
||||
whitelistDetails: WhitelistDetailsDataProps
|
||||
royaltyDetails: RoyaltyDetailsDataProps
|
||||
baseMinterDetails: BaseMinterDetailsDataProps
|
||||
openEditionMinterDetails: OpenEditionMinterDetailsDataProps
|
||||
}>()
|
||||
|
||||
const [uploadDetails, setUploadDetails] = useState<UploadDetailsDataProps | null>(null)
|
||||
const [collectionDetails, setCollectionDetails] = useState<CollectionDetailsDataProps | null>(null)
|
||||
const [baseMinterDetails, setBaseMinterDetails] = useState<BaseMinterDetailsDataProps | null>(null)
|
||||
@ -1277,6 +1290,64 @@ const CollectionCreationPage: NextPage = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const exportDetails = () => {
|
||||
const details = {
|
||||
minterType,
|
||||
collectionDetails,
|
||||
uploadDetails,
|
||||
mintingDetails,
|
||||
whitelistDetails,
|
||||
royaltyDetails,
|
||||
baseMinterDetails,
|
||||
openEditionMinterDetails,
|
||||
vendingMinterContractAddress,
|
||||
baseTokenUri: `${baseTokenUri?.startsWith('ipfs://') ? baseTokenUri : `ipfs://${baseTokenUri}`}`,
|
||||
coverImageUrl:
|
||||
uploadDetails?.uploadMethod === 'new'
|
||||
? `ipfs://${coverImageUrl}/${collectionDetails?.imageFile[0]?.name as string}`
|
||||
: `${coverImageUrl}`,
|
||||
}
|
||||
const element = document.createElement('a')
|
||||
const file = new Blob([JSON.stringify(details)], { type: 'text/plain' })
|
||||
element.href = URL.createObjectURL(file)
|
||||
element.download = `${
|
||||
minterType === 'vending'
|
||||
? collectionDetails?.name
|
||||
? `${collectionDetails.name}-`
|
||||
: ''
|
||||
: openEditionMinterDetails?.collectionDetails
|
||||
? `${openEditionMinterDetails.collectionDetails.name}-`
|
||||
: ''
|
||||
}configuration-${new Date().toLocaleString().replaceAll(',', '_')}.json`
|
||||
document.body.appendChild(element) // Required for this to work in FireFox
|
||||
element.click()
|
||||
}
|
||||
const importDetails = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.files === null || event.target.files.length === 0) return toast.error('No files selected.')
|
||||
const file = event.target.files[0]
|
||||
const reader = new FileReader()
|
||||
reader.onload = (e) => {
|
||||
const contents = e.target?.result
|
||||
const details = JSON.parse(contents as string)
|
||||
setMinterType(details.minterType)
|
||||
if (details.vendingMinterContractAddress) {
|
||||
details.uploadDetails.uploadMethod = 'existing'
|
||||
details.uploadDetails.baseTokenURI = details.baseTokenUri
|
||||
details.uploadDetails.imageUrl = details.coverImageUrl
|
||||
}
|
||||
if (details.openEditionMinterDetails?.openEditionMinterContractAddress) {
|
||||
details.openEditionMinterDetails.offChainMetadataUploadDetails.uploadMethod = 'existing'
|
||||
details.openEditionMinterDetails.offChainMetadataUploadDetails.tokenURI =
|
||||
details.openEditionMinterDetails.tokenUri
|
||||
details.openEditionMinterDetails.offChainMetadataUploadDetails.imageUrl =
|
||||
details.openEditionMinterDetails.coverImageUrl
|
||||
}
|
||||
|
||||
setImportedDetails(details)
|
||||
}
|
||||
reader.readAsText(file)
|
||||
}
|
||||
|
||||
const syncCollections = useCallback(async () => {
|
||||
const collectionAddress =
|
||||
minterType === 'openEdition' ? openEditionMinterCreatorData?.sg721ContractAddress : sg721ContractAddress
|
||||
@ -1370,6 +1441,7 @@ const CollectionCreationPage: NextPage = () => {
|
||||
on how to create your collection
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mx-10" ref={scrollRef}>
|
||||
<Conditional
|
||||
test={minterType === 'openEdition' && openEditionMinterCreatorData?.openEditionMinterContractAddress !== null}
|
||||
@ -1609,6 +1681,7 @@ const CollectionCreationPage: NextPage = () => {
|
||||
'mx-10 mt-5',
|
||||
'grid before:absolute relative grid-cols-3 grid-flow-col items-stretch rounded',
|
||||
'before:inset-x-0 before:bottom-0 before:border-white/25',
|
||||
minterType !== 'base' ? 'rounded-none border-b-2 border-white/25' : 'border-0',
|
||||
)}
|
||||
>
|
||||
<div
|
||||
@ -1682,6 +1755,22 @@ const CollectionCreationPage: NextPage = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Conditional test={minterType !== 'base'}>
|
||||
<FormControl className={clsx('py-4 px-10 w-full')} title="Import Creation Configuration">
|
||||
<div className="flex flex-row justify-between mt-5 space-x-2">
|
||||
<input
|
||||
accept="application/json"
|
||||
className="py-4 px-4 w-1/3 rounded-sm border-[1px] border-zinc-500 border-dashed"
|
||||
onChange={(e) => importDetails(e)}
|
||||
type="file"
|
||||
/>
|
||||
<Button className="mt-3 h-1/2 w-1/8" onClick={() => exportDetails()}>
|
||||
Export Creation Configuration
|
||||
</Button>
|
||||
</div>
|
||||
</FormControl>
|
||||
</Conditional>
|
||||
|
||||
{minterType === 'base' && (
|
||||
<div>
|
||||
<BaseMinterDetails minterType={minterType} onChange={setBaseMinterDetails} />
|
||||
@ -1689,6 +1778,7 @@ const CollectionCreationPage: NextPage = () => {
|
||||
)}
|
||||
<Conditional test={minterType === 'openEdition'}>
|
||||
<OpenEditionMinterCreator
|
||||
importedOpenEditionMinterDetails={importedDetails?.openEditionMinterDetails}
|
||||
minimumMintPrice={minimumOpenEditionMintPrice as string}
|
||||
minimumUpdatableMintPrice={minimumOpenEditionUpdatableMintPrice as string}
|
||||
mintTokenFromFactory={mintTokenFromOpenEditionFactory}
|
||||
@ -1703,6 +1793,7 @@ const CollectionCreationPage: NextPage = () => {
|
||||
<Conditional test={minterType === 'vending' || minterType === 'base'}>
|
||||
<UploadDetails
|
||||
baseMinterAcquisitionMethod={baseMinterDetails?.baseMinterAcquisitionMethod}
|
||||
importedUploadDetails={importedDetails?.uploadDetails}
|
||||
minterType={minterType}
|
||||
onChange={setUploadDetails}
|
||||
/>
|
||||
@ -1723,6 +1814,7 @@ const CollectionCreationPage: NextPage = () => {
|
||||
>
|
||||
<CollectionDetails
|
||||
coverImageUrl={coverImageUrl as string}
|
||||
importedCollectionDetails={importedDetails?.collectionDetails}
|
||||
minterType={minterType}
|
||||
onChange={setCollectionDetails}
|
||||
uploadMethod={uploadDetails?.uploadMethod as UploadMethod}
|
||||
@ -1730,6 +1822,7 @@ const CollectionCreationPage: NextPage = () => {
|
||||
</Conditional>
|
||||
<Conditional test={minterType === 'vending'}>
|
||||
<MintingDetails
|
||||
importedMintingDetails={importedDetails?.mintingDetails}
|
||||
minimumMintPrice={
|
||||
whitelistDetails?.whitelistState !== 'none' && whitelistDetails?.whitelistType === 'flex'
|
||||
? Number(minimumFlexMintPrice) / 1000000
|
||||
@ -1766,10 +1859,14 @@ const CollectionCreationPage: NextPage = () => {
|
||||
>
|
||||
<div className="my-6">
|
||||
<Conditional test={minterType === 'vending'}>
|
||||
<WhitelistDetails mintingTokenFromFactory={mintTokenFromVendingFactory} onChange={setWhitelistDetails} />
|
||||
<WhitelistDetails
|
||||
importedWhitelistDetails={importedDetails?.whitelistDetails}
|
||||
mintingTokenFromFactory={mintTokenFromVendingFactory}
|
||||
onChange={setWhitelistDetails}
|
||||
/>
|
||||
<div className="my-6" />
|
||||
</Conditional>
|
||||
<RoyaltyDetails onChange={setRoyaltyDetails} />
|
||||
<RoyaltyDetails importedRoyaltyDetails={importedDetails?.royaltyDetails} onChange={setRoyaltyDetails} />
|
||||
</div>
|
||||
</Conditional>
|
||||
<Conditional test={readyToCreateVm && minterType === 'vending'}>
|
||||
|
Loading…
Reference in New Issue
Block a user