Use existing base token URI for collection creation (#36)
* Use existing base token URI for collection creation * Cover image preview when using existing base token URI option * Fix cover image preview border for non-square images * check baseTokenURI and coverImageUrl validity * Fix typo * Cover image validity condition update * Remove unnecessary conditionals * Display correct collection creation results when using an existing base token URI * Fix: base token URI not being displayed correctly for new uploads * Post-review update * Remove extra props Co-authored-by: findolor <anakisci@gmail.com>
This commit is contained in:
parent
c7098cc40c
commit
1624f0c332
@ -1,4 +1,5 @@
|
|||||||
/* eslint-disable eslint-comments/disable-enable-pair */
|
/* eslint-disable eslint-comments/disable-enable-pair */
|
||||||
|
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||||
|
|
||||||
@ -11,9 +12,12 @@ import { useEffect, useState } from 'react'
|
|||||||
import { toast } from 'react-hot-toast'
|
import { toast } from 'react-hot-toast'
|
||||||
|
|
||||||
import { TextInput } from '../../forms/FormInput'
|
import { TextInput } from '../../forms/FormInput'
|
||||||
|
import type { UploadMethod } from './UploadDetails'
|
||||||
|
|
||||||
interface CollectionDetailsProps {
|
interface CollectionDetailsProps {
|
||||||
onChange: (data: CollectionDetailsDataProps) => void
|
onChange: (data: CollectionDetailsDataProps) => void
|
||||||
|
uploadMethod: UploadMethod
|
||||||
|
coverImageUrl: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CollectionDetailsDataProps {
|
export interface CollectionDetailsDataProps {
|
||||||
@ -23,7 +27,7 @@ export interface CollectionDetailsDataProps {
|
|||||||
externalLink?: string
|
externalLink?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CollectionDetails = ({ onChange }: CollectionDetailsProps) => {
|
export const CollectionDetails = ({ onChange, uploadMethod, coverImageUrl }: CollectionDetailsProps) => {
|
||||||
const [coverImage, setCoverImage] = useState<File | null>(null)
|
const [coverImage, setCoverImage] = useState<File | null>(null)
|
||||||
|
|
||||||
const nameState = useInputState({
|
const nameState = useInputState({
|
||||||
@ -84,23 +88,44 @@ export const CollectionDetails = ({ onChange }: CollectionDetailsProps) => {
|
|||||||
<FormGroup subtitle="Information about your collection" title="Collection Details">
|
<FormGroup subtitle="Information about your collection" title="Collection Details">
|
||||||
<TextInput {...nameState} isRequired />
|
<TextInput {...nameState} isRequired />
|
||||||
<TextInput {...descriptionState} isRequired />
|
<TextInput {...descriptionState} isRequired />
|
||||||
<FormControl isRequired title="Cover Image">
|
|
||||||
<input
|
<FormControl isRequired={uploadMethod === 'new'} title="Cover Image">
|
||||||
accept="image/*"
|
{uploadMethod === 'new' && (
|
||||||
className={clsx(
|
<input
|
||||||
'file:py-2 file:px-4 file:mr-4 file:bg-plumbus-light file:rounded file:border-0 cursor-pointer',
|
accept="image/*"
|
||||||
'before:hover:bg-white/5 before:transition',
|
className={clsx(
|
||||||
)}
|
'file:py-2 file:px-4 file:mr-4 file:bg-plumbus-light file:rounded file:border-0 cursor-pointer',
|
||||||
id="cover-image"
|
'before:hover:bg-white/5 before:transition',
|
||||||
onChange={selectCoverImage}
|
)}
|
||||||
type="file"
|
id="cover-image"
|
||||||
/>
|
onChange={selectCoverImage}
|
||||||
{coverImage !== null && (
|
type="file"
|
||||||
<div className="w-[200px] h-[200px] rounded border-2">
|
/>
|
||||||
<img alt="cover-preview" src={URL.createObjectURL(coverImage)} />
|
)}
|
||||||
|
|
||||||
|
{coverImage !== null && uploadMethod === 'new' && (
|
||||||
|
<div className="max-w-[200px] max-h-[200px] rounded border-2">
|
||||||
|
<img alt="no-preview-available" src={URL.createObjectURL(coverImage)} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{uploadMethod === 'existing' && coverImageUrl?.includes('ipfs://') && (
|
||||||
|
<div className="max-w-[200px] max-h-[200px] rounded border-2">
|
||||||
|
<img
|
||||||
|
alt="no-preview-available"
|
||||||
|
src={`https://ipfs.io/ipfs/${coverImageUrl.substring(coverImageUrl.lastIndexOf('ipfs://') + 7)}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{uploadMethod === 'existing' && coverImageUrl && !coverImageUrl?.includes('ipfs://') && (
|
||||||
|
<div className="max-w-[200px] max-h-[200px] rounded border-2">
|
||||||
|
<img alt="no-preview-available" src={coverImageUrl} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{uploadMethod === 'existing' && !coverImageUrl && (
|
||||||
|
<span className="italic font-light ">Waiting for cover image URL to be specified.</span>
|
||||||
|
)}
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<TextInput {...externalLinkState} />
|
<TextInput {...externalLinkState} />
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,10 +5,12 @@ import { InputDateTime } from 'components/InputDateTime'
|
|||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
|
|
||||||
import { NumberInput } from '../../forms/FormInput'
|
import { NumberInput } from '../../forms/FormInput'
|
||||||
|
import type { UploadMethod } from './UploadDetails'
|
||||||
|
|
||||||
interface MintingDetailsProps {
|
interface MintingDetailsProps {
|
||||||
onChange: (data: MintingDetailsDataProps) => void
|
onChange: (data: MintingDetailsDataProps) => void
|
||||||
numberOfTokens: number | undefined
|
numberOfTokens: number | undefined
|
||||||
|
uploadMethod: UploadMethod
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MintingDetailsDataProps {
|
export interface MintingDetailsDataProps {
|
||||||
@ -18,7 +20,7 @@ export interface MintingDetailsDataProps {
|
|||||||
startTime: string
|
startTime: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MintingDetails = ({ onChange, numberOfTokens }: MintingDetailsProps) => {
|
export const MintingDetails = ({ onChange, numberOfTokens, uploadMethod }: MintingDetailsProps) => {
|
||||||
const [timestamp, setTimestamp] = useState<Date | undefined>()
|
const [timestamp, setTimestamp] = useState<Date | undefined>()
|
||||||
|
|
||||||
const numberOfTokensState = useNumberInputState({
|
const numberOfTokensState = useNumberInputState({
|
||||||
@ -60,7 +62,12 @@ export const MintingDetails = ({ onChange, numberOfTokens }: MintingDetailsProps
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<FormGroup subtitle="Information about your minting settings" title="Minting Details">
|
<FormGroup subtitle="Information about your minting settings" title="Minting Details">
|
||||||
<NumberInput {...numberOfTokensState} disabled isRequired value={numberOfTokens} />
|
<NumberInput
|
||||||
|
{...numberOfTokensState}
|
||||||
|
disabled={uploadMethod === 'new'}
|
||||||
|
isRequired
|
||||||
|
value={uploadMethod === 'new' ? numberOfTokens : numberOfTokensState.value}
|
||||||
|
/>
|
||||||
<NumberInput {...unitPriceState} isRequired />
|
<NumberInput {...unitPriceState} isRequired />
|
||||||
<NumberInput {...perAddressLimitState} isRequired />
|
<NumberInput {...perAddressLimitState} isRequired />
|
||||||
<FormControl htmlId="timestamp" isRequired subtitle="Start time for the minting" title="Start Time">
|
<FormControl htmlId="timestamp" isRequired subtitle="Start time for the minting" title="Start Time">
|
||||||
|
@ -6,14 +6,13 @@ import { Conditional } from 'components/Conditional'
|
|||||||
import { TextInput } from 'components/forms/FormInput'
|
import { TextInput } from 'components/forms/FormInput'
|
||||||
import { useInputState } from 'components/forms/FormInput.hooks'
|
import { useInputState } from 'components/forms/FormInput.hooks'
|
||||||
import { MetadataModal } from 'components/MetadataModal'
|
import { MetadataModal } from 'components/MetadataModal'
|
||||||
import { setBaseTokenUri, setImage, useCollectionStore } from 'contexts/collection'
|
|
||||||
import type { ChangeEvent } from 'react'
|
import type { ChangeEvent } from 'react'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { toast } from 'react-hot-toast'
|
import { toast } from 'react-hot-toast'
|
||||||
import type { UploadServiceType } from 'services/upload'
|
import type { UploadServiceType } from 'services/upload'
|
||||||
import { naturalCompare } from 'utils/sort'
|
import { naturalCompare } from 'utils/sort'
|
||||||
|
|
||||||
type UploadMethod = 'new' | 'existing'
|
export type UploadMethod = 'new' | 'existing'
|
||||||
|
|
||||||
interface UploadDetailsProps {
|
interface UploadDetailsProps {
|
||||||
onChange: (value: UploadDetailsDataProps) => void
|
onChange: (value: UploadDetailsDataProps) => void
|
||||||
@ -26,10 +25,12 @@ export interface UploadDetailsDataProps {
|
|||||||
nftStorageApiKey?: string
|
nftStorageApiKey?: string
|
||||||
pinataApiKey?: string
|
pinataApiKey?: string
|
||||||
pinataSecretKey?: string
|
pinataSecretKey?: string
|
||||||
|
uploadMethod: UploadMethod
|
||||||
|
baseTokenURI?: string
|
||||||
|
imageUrl?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const UploadDetails = ({ onChange }: UploadDetailsProps) => {
|
export const UploadDetails = ({ onChange }: UploadDetailsProps) => {
|
||||||
const baseTokenURI = useCollectionStore().base_token_uri
|
|
||||||
const [assetFilesArray, setAssetFilesArray] = useState<File[]>([])
|
const [assetFilesArray, setAssetFilesArray] = useState<File[]>([])
|
||||||
const [metadataFilesArray, setMetadataFilesArray] = useState<File[]>([])
|
const [metadataFilesArray, setMetadataFilesArray] = useState<File[]>([])
|
||||||
const [uploadMethod, setUploadMethod] = useState<UploadMethod>('new')
|
const [uploadMethod, setUploadMethod] = useState<UploadMethod>('new')
|
||||||
@ -60,13 +61,21 @@ export const UploadDetails = ({ onChange }: UploadDetailsProps) => {
|
|||||||
defaultValue: '9d6f42dc01eaab15f52eac8f36cc4f0ee4184944cb3cdbcda229d06ecf877ee7',
|
defaultValue: '9d6f42dc01eaab15f52eac8f36cc4f0ee4184944cb3cdbcda229d06ecf877ee7',
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleChangeBaseTokenUri = (event: { target: { value: React.SetStateAction<string> } }) => {
|
const baseTokenUriState = useInputState({
|
||||||
setBaseTokenUri(event.target.value.toString())
|
id: 'baseTokenUri',
|
||||||
}
|
name: 'baseTokenUri',
|
||||||
|
title: 'Base Token URI',
|
||||||
|
placeholder: 'ipfs://',
|
||||||
|
defaultValue: '',
|
||||||
|
})
|
||||||
|
|
||||||
const handleChangeImage = (event: { target: { value: React.SetStateAction<string> } }) => {
|
const coverImageUrlState = useInputState({
|
||||||
setImage(event.target.value.toString())
|
id: 'coverImageUrl',
|
||||||
}
|
name: 'coverImageUrl',
|
||||||
|
title: 'Cover Image URL',
|
||||||
|
placeholder: 'ipfs://',
|
||||||
|
defaultValue: '',
|
||||||
|
})
|
||||||
|
|
||||||
const selectAssets = (event: ChangeEvent<HTMLInputElement>) => {
|
const selectAssets = (event: ChangeEvent<HTMLInputElement>) => {
|
||||||
const files: File[] = []
|
const files: File[] = []
|
||||||
@ -135,6 +144,9 @@ export const UploadDetails = ({ onChange }: UploadDetailsProps) => {
|
|||||||
nftStorageApiKey: nftStorageApiKeyState.value,
|
nftStorageApiKey: nftStorageApiKeyState.value,
|
||||||
pinataApiKey: pinataApiKeyState.value,
|
pinataApiKey: pinataApiKeyState.value,
|
||||||
pinataSecretKey: pinataSecretKeyState.value,
|
pinataSecretKey: pinataSecretKeyState.value,
|
||||||
|
uploadMethod,
|
||||||
|
baseTokenURI: baseTokenUriState.value,
|
||||||
|
imageUrl: coverImageUrlState.value,
|
||||||
}
|
}
|
||||||
onChange(data)
|
onChange(data)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
@ -147,11 +159,16 @@ export const UploadDetails = ({ onChange }: UploadDetailsProps) => {
|
|||||||
nftStorageApiKeyState.value,
|
nftStorageApiKeyState.value,
|
||||||
pinataApiKeyState.value,
|
pinataApiKeyState.value,
|
||||||
pinataSecretKeyState.value,
|
pinataSecretKeyState.value,
|
||||||
|
uploadMethod,
|
||||||
|
baseTokenUriState.value,
|
||||||
|
coverImageUrlState.value,
|
||||||
])
|
])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setAssetFilesArray([])
|
setAssetFilesArray([])
|
||||||
setMetadataFilesArray([])
|
setMetadataFilesArray([])
|
||||||
|
baseTokenUriState.onChange('')
|
||||||
|
coverImageUrlState.onChange('')
|
||||||
}, [uploadMethod])
|
}, [uploadMethod])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -192,14 +209,6 @@ export const UploadDetails = ({ onChange }: UploadDetailsProps) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{baseTokenURI && (
|
|
||||||
<Alert className="mt-5" type="info">
|
|
||||||
<a href={baseTokenURI} rel="noreferrer" target="_blank">
|
|
||||||
Base Token URI: {baseTokenURI}
|
|
||||||
</a>
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="p-3 py-5 pb-8">
|
<div className="p-3 py-5 pb-8">
|
||||||
<Conditional test={uploadMethod === 'existing'}>
|
<Conditional test={uploadMethod === 'existing'}>
|
||||||
<div className="ml-3 flex-column">
|
<div className="ml-3 flex-column">
|
||||||
@ -216,33 +225,10 @@ export const UploadDetails = ({ onChange }: UploadDetailsProps) => {
|
|||||||
and upload your assets & metadata manually to get a base URI for your collection.
|
and upload your assets & metadata manually to get a base URI for your collection.
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<label className="block mr-1 mb-1 ml-5 font-bold text-white dark:text-gray-300" htmlFor="coverImage">
|
<TextInput {...baseTokenUriState} className="w-1/2" />
|
||||||
Collection Cover Image
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
className="py-2 px-1 mx-5 mt-2 mb-2 w-1/2 bg-white/10 rounded border-2 border-white/20 focus:ring
|
|
||||||
focus:ring-plumbus-20
|
|
||||||
form-input, placeholder:text-white/50,"
|
|
||||||
id="coverImage"
|
|
||||||
onChange={handleChangeImage}
|
|
||||||
placeholder="ipfs://bafybeigi3bwpvyvsmnbj46ra4hyffcxdeaj6ntfk5jpic5mx27x6ih2qvq/images/1.png"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label
|
<TextInput {...coverImageUrlState} className="mt-2 w-1/2" />
|
||||||
className="block mt-3 mr-1 mb-1 ml-5 font-bold text-white dark:text-gray-300"
|
|
||||||
htmlFor="baseTokenURI"
|
|
||||||
>
|
|
||||||
Base Token URI
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
className="py-2 px-1 mx-5 mt-2 mb-2 w-1/2 bg-white/10 rounded border-2 border-white/20 focus:ring
|
|
||||||
focus:ring-plumbus-20
|
|
||||||
form-input, placeholder:text-white/50,"
|
|
||||||
id="baseTokenURI"
|
|
||||||
onChange={handleChangeBaseTokenUri}
|
|
||||||
placeholder="ipfs://..."
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Conditional>
|
</Conditional>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
/* eslint-disable eslint-comments/disable-enable-pair */
|
/* eslint-disable eslint-comments/disable-enable-pair */
|
||||||
|
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||||
|
|
||||||
@ -27,13 +28,13 @@ import { NextSeo } from 'next-seo'
|
|||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import useCollapse from 'react-collapsed'
|
import useCollapse from 'react-collapsed'
|
||||||
import { toast } from 'react-hot-toast'
|
import { toast } from 'react-hot-toast'
|
||||||
import type { UploadServiceType } from 'services/upload'
|
|
||||||
import { upload } from 'services/upload'
|
import { upload } from 'services/upload'
|
||||||
import { compareFileArrays } from 'utils/compareFileArrays'
|
import { compareFileArrays } from 'utils/compareFileArrays'
|
||||||
import { MINTER_CODE_ID, SG721_CODE_ID, WHITELIST_CODE_ID } from 'utils/constants'
|
import { MINTER_CODE_ID, SG721_CODE_ID, WHITELIST_CODE_ID } from 'utils/constants'
|
||||||
import { withMetadata } from 'utils/layout'
|
import { withMetadata } from 'utils/layout'
|
||||||
import { links } from 'utils/links'
|
import { links } from 'utils/links'
|
||||||
|
|
||||||
|
import type { UploadMethod } from '../../components/collections/creation/UploadDetails'
|
||||||
import { getAssetType } from '../../utils/getAssetType'
|
import { getAssetType } from '../../utils/getAssetType'
|
||||||
|
|
||||||
const CollectionCreationPage: NextPage = () => {
|
const CollectionCreationPage: NextPage = () => {
|
||||||
@ -55,11 +56,13 @@ const CollectionCreationPage: NextPage = () => {
|
|||||||
const [minterContractAddress, setMinterContractAddress] = useState<string | null>(null)
|
const [minterContractAddress, setMinterContractAddress] = useState<string | null>(null)
|
||||||
const [sg721ContractAddress, setSg721ContractAddress] = useState<string | null>(null)
|
const [sg721ContractAddress, setSg721ContractAddress] = useState<string | null>(null)
|
||||||
const [baseTokenUri, setBaseTokenUri] = useState<string | null>(null)
|
const [baseTokenUri, setBaseTokenUri] = useState<string | null>(null)
|
||||||
|
const [coverImageUrl, setCoverImageUrl] = useState<string | null>(null)
|
||||||
const [transactionHash, setTransactionHash] = useState<string | null>(null)
|
const [transactionHash, setTransactionHash] = useState<string | null>(null)
|
||||||
|
|
||||||
const createCollection = async () => {
|
const createCollection = async () => {
|
||||||
try {
|
try {
|
||||||
setBaseTokenUri(null)
|
setBaseTokenUri(null)
|
||||||
|
setCoverImageUrl(null)
|
||||||
setMinterContractAddress(null)
|
setMinterContractAddress(null)
|
||||||
setSg721ContractAddress(null)
|
setSg721ContractAddress(null)
|
||||||
setTransactionHash(null)
|
setTransactionHash(null)
|
||||||
@ -68,28 +71,34 @@ const CollectionCreationPage: NextPage = () => {
|
|||||||
checkMintingDetails()
|
checkMintingDetails()
|
||||||
checkWhitelistDetails()
|
checkWhitelistDetails()
|
||||||
checkRoyaltyDetails()
|
checkRoyaltyDetails()
|
||||||
|
if (uploadDetails?.uploadMethod === 'new') {
|
||||||
|
setUploading(true)
|
||||||
|
|
||||||
setUploading(true)
|
const baseUri = await uploadFiles()
|
||||||
|
//upload coverImageUri and append the file name
|
||||||
const baseUri = await uploadFiles()
|
const coverImageUri = await upload(
|
||||||
setBaseTokenUri(baseUri)
|
collectionDetails?.imageFile as File[],
|
||||||
//upload coverImageUri and append the file name
|
uploadDetails.uploadService,
|
||||||
const coverImageUri = await upload(
|
'cover',
|
||||||
collectionDetails?.imageFile as File[],
|
uploadDetails.nftStorageApiKey as string,
|
||||||
uploadDetails?.uploadService as UploadServiceType,
|
uploadDetails.pinataApiKey as string,
|
||||||
'cover',
|
uploadDetails.pinataSecretKey as string,
|
||||||
uploadDetails?.nftStorageApiKey as string,
|
)
|
||||||
uploadDetails?.pinataApiKey as string,
|
setUploading(false)
|
||||||
uploadDetails?.pinataSecretKey as string,
|
setBaseTokenUri(baseUri)
|
||||||
)
|
setCoverImageUrl(coverImageUri)
|
||||||
|
let whitelist: string | undefined
|
||||||
setUploading(false)
|
if (whitelistDetails?.whitelistType === 'existing') whitelist = whitelistDetails.contractAddress
|
||||||
|
else if (whitelistDetails?.whitelistType === 'new') whitelist = await instantiateWhitelist()
|
||||||
let whitelist: string | undefined
|
await instantiate(baseUri, coverImageUri, whitelist)
|
||||||
if (whitelistDetails?.whitelistType === 'existing') whitelist = whitelistDetails.contractAddress
|
} else {
|
||||||
else if (whitelistDetails?.whitelistType === 'new') whitelist = await instantiateWhitelist()
|
setBaseTokenUri(uploadDetails?.baseTokenURI as string)
|
||||||
|
setCoverImageUrl(uploadDetails?.imageUrl as string)
|
||||||
await instantiate(baseUri, coverImageUri, whitelist)
|
let whitelist: string | undefined
|
||||||
|
if (whitelistDetails?.whitelistType === 'existing') whitelist = whitelistDetails.contractAddress
|
||||||
|
else if (whitelistDetails?.whitelistType === 'new') whitelist = await instantiateWhitelist()
|
||||||
|
await instantiate(baseTokenUri as string, coverImageUrl as string, whitelist)
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
@ -132,9 +141,8 @@ const CollectionCreationPage: NextPage = () => {
|
|||||||
share: (Number(royaltyDetails.share) / 100).toString(),
|
share: (Number(royaltyDetails.share) / 100).toString(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const msg = {
|
const msg = {
|
||||||
base_token_uri: `ipfs://${baseUri}/`,
|
base_token_uri: `${uploadDetails?.uploadMethod === 'new' ? `ipfs://${baseUri}/` : `${baseUri}`}`,
|
||||||
num_tokens: mintingDetails?.numTokens,
|
num_tokens: mintingDetails?.numTokens,
|
||||||
sg721_code_id: SG721_CODE_ID,
|
sg721_code_id: SG721_CODE_ID,
|
||||||
sg721_instantiate_msg: {
|
sg721_instantiate_msg: {
|
||||||
@ -144,7 +152,11 @@ const CollectionCreationPage: NextPage = () => {
|
|||||||
collection_info: {
|
collection_info: {
|
||||||
creator: wallet.address,
|
creator: wallet.address,
|
||||||
description: collectionDetails?.description,
|
description: collectionDetails?.description,
|
||||||
image: `ipfs://${coverImageUri}/${collectionDetails?.imageFile[0].name as string}`,
|
image: `${
|
||||||
|
uploadDetails?.uploadMethod === 'new'
|
||||||
|
? `ipfs://${coverImageUri}/${collectionDetails?.imageFile[0].name as string}`
|
||||||
|
: `${coverImageUri}`
|
||||||
|
}`,
|
||||||
external_link: collectionDetails?.externalLink === '' ? null : collectionDetails?.externalLink,
|
external_link: collectionDetails?.externalLink === '' ? null : collectionDetails?.externalLink,
|
||||||
royalty_info: royaltyInfo,
|
royalty_info: royaltyInfo,
|
||||||
},
|
},
|
||||||
@ -223,19 +235,34 @@ const CollectionCreationPage: NextPage = () => {
|
|||||||
if (!uploadDetails) {
|
if (!uploadDetails) {
|
||||||
throw new Error('Please select assets and metadata')
|
throw new Error('Please select assets and metadata')
|
||||||
}
|
}
|
||||||
if (uploadDetails.assetFiles.length === 0) {
|
if (uploadDetails.uploadMethod === 'new' && uploadDetails.assetFiles.length === 0) {
|
||||||
throw new Error('Please select the assets')
|
throw new Error('Please select the assets')
|
||||||
}
|
}
|
||||||
if (uploadDetails.metadataFiles.length === 0) {
|
if (uploadDetails.uploadMethod === 'new' && uploadDetails.metadataFiles.length === 0) {
|
||||||
throw new Error('Please select the metadata files')
|
throw new Error('Please select the metadata files')
|
||||||
}
|
}
|
||||||
compareFileArrays(uploadDetails.assetFiles, uploadDetails.metadataFiles)
|
if (uploadDetails.uploadMethod === 'new') compareFileArrays(uploadDetails.assetFiles, uploadDetails.metadataFiles)
|
||||||
if (uploadDetails.uploadService === 'nft-storage') {
|
if (uploadDetails.uploadMethod === 'new') {
|
||||||
if (uploadDetails.nftStorageApiKey === '') {
|
if (uploadDetails.uploadService === 'nft-storage') {
|
||||||
throw new Error('Please enter a valid NFT Storage API key')
|
if (uploadDetails.nftStorageApiKey === '') {
|
||||||
|
throw new Error('Please enter a valid NFT Storage API key')
|
||||||
|
}
|
||||||
|
} else if (uploadDetails.pinataApiKey === '' || uploadDetails.pinataSecretKey === '') {
|
||||||
|
throw new Error('Please enter Pinata API and secret keys')
|
||||||
}
|
}
|
||||||
} else if (uploadDetails.pinataApiKey === '' || uploadDetails.pinataSecretKey === '') {
|
}
|
||||||
throw new Error('Please enter Pinata API and secret keys')
|
if (uploadDetails.uploadMethod === 'existing' && !uploadDetails.baseTokenURI?.includes('ipfs://')) {
|
||||||
|
throw new Error('Please specify a valid base token URI')
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
uploadDetails.uploadMethod === 'existing' &&
|
||||||
|
uploadDetails.imageUrl?.substring(uploadDetails.imageUrl.lastIndexOf('.') + 1) !== 'jpg' &&
|
||||||
|
uploadDetails.imageUrl?.substring(uploadDetails.imageUrl.lastIndexOf('.') + 1) !== 'png' &&
|
||||||
|
uploadDetails.imageUrl?.substring(uploadDetails.imageUrl.lastIndexOf('.') + 1) !== 'jpeg' &&
|
||||||
|
uploadDetails.imageUrl?.substring(uploadDetails.imageUrl.lastIndexOf('.') + 1) !== 'gif' &&
|
||||||
|
uploadDetails.imageUrl?.substring(uploadDetails.imageUrl.lastIndexOf('.') + 1) !== 'svg'
|
||||||
|
) {
|
||||||
|
throw new Error('Please specify a valid cover image URL')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,7 +270,8 @@ const CollectionCreationPage: NextPage = () => {
|
|||||||
if (!collectionDetails) throw new Error('Please fill out the collection details')
|
if (!collectionDetails) throw new Error('Please fill out the collection details')
|
||||||
if (collectionDetails.name === '') throw new Error('Collection name is required')
|
if (collectionDetails.name === '') throw new Error('Collection name is required')
|
||||||
if (collectionDetails.description === '') throw new Error('Collection description is required')
|
if (collectionDetails.description === '') throw new Error('Collection description is required')
|
||||||
if (collectionDetails.imageFile.length === 0) throw new Error('Collection cover image is required')
|
if (uploadDetails?.uploadMethod === 'new' && collectionDetails.imageFile.length === 0)
|
||||||
|
throw new Error('Collection cover image is required')
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkMintingDetails = () => {
|
const checkMintingDetails = () => {
|
||||||
@ -288,6 +316,11 @@ const CollectionCreationPage: NextPage = () => {
|
|||||||
if (minterContractAddress !== null) scrollRef.current?.scrollIntoView({ behavior: 'smooth' })
|
if (minterContractAddress !== null) scrollRef.current?.scrollIntoView({ behavior: 'smooth' })
|
||||||
}, [minterContractAddress])
|
}, [minterContractAddress])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setBaseTokenUri(uploadDetails?.baseTokenURI as string)
|
||||||
|
setCoverImageUrl(uploadDetails?.imageUrl as string)
|
||||||
|
}, [uploadDetails?.baseTokenURI, uploadDetails?.imageUrl])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<NextSeo title="Create Collection" />
|
<NextSeo title="Create Collection" />
|
||||||
@ -312,13 +345,26 @@ const CollectionCreationPage: NextPage = () => {
|
|||||||
<Alert className="mt-5" type="info">
|
<Alert className="mt-5" type="info">
|
||||||
<div>
|
<div>
|
||||||
Base Token URI:{' '}
|
Base Token URI:{' '}
|
||||||
<Anchor
|
{uploadDetails?.uploadMethod === 'new' && (
|
||||||
className="text-stargaze hover:underline"
|
<Anchor
|
||||||
external
|
className="text-stargaze hover:underline"
|
||||||
href={`https://ipfs.stargaze.zone/ipfs/${baseTokenUri as string}/`}
|
external
|
||||||
>
|
href={`https://ipfs.stargaze.zone/ipfs/${baseTokenUri as string}/`}
|
||||||
ipfs://{baseTokenUri as string}/
|
>
|
||||||
</Anchor>
|
ipfs://{baseTokenUri as string}/
|
||||||
|
</Anchor>
|
||||||
|
)}
|
||||||
|
{uploadDetails?.uploadMethod === 'existing' && (
|
||||||
|
<Anchor
|
||||||
|
className="text-stargaze hover:underline"
|
||||||
|
external
|
||||||
|
href={`https://ipfs.stargaze.zone/ipfs/${baseTokenUri?.substring(
|
||||||
|
baseTokenUri.lastIndexOf('ipfs://') + 7,
|
||||||
|
)}/`}
|
||||||
|
>
|
||||||
|
ipfs://{baseTokenUri?.substring(baseTokenUri.lastIndexOf('ipfs://') + 7)}/
|
||||||
|
</Anchor>
|
||||||
|
)}
|
||||||
<br />
|
<br />
|
||||||
Minter Contract Address:{' '}
|
Minter Contract Address:{' '}
|
||||||
<Anchor
|
<Anchor
|
||||||
@ -354,8 +400,16 @@ const CollectionCreationPage: NextPage = () => {
|
|||||||
<UploadDetails onChange={setUploadDetails} />
|
<UploadDetails onChange={setUploadDetails} />
|
||||||
|
|
||||||
<div className="flex justify-between py-3 px-8 rounded border-2 border-white/20 grid-col-2">
|
<div className="flex justify-between py-3 px-8 rounded border-2 border-white/20 grid-col-2">
|
||||||
<CollectionDetails onChange={setCollectionDetails} />
|
<CollectionDetails
|
||||||
<MintingDetails numberOfTokens={uploadDetails?.assetFiles.length} onChange={setMintingDetails} />
|
coverImageUrl={coverImageUrl as string}
|
||||||
|
onChange={setCollectionDetails}
|
||||||
|
uploadMethod={uploadDetails?.uploadMethod as UploadMethod}
|
||||||
|
/>
|
||||||
|
<MintingDetails
|
||||||
|
numberOfTokens={uploadDetails?.assetFiles.length}
|
||||||
|
onChange={setMintingDetails}
|
||||||
|
uploadMethod={uploadDetails?.uploadMethod as UploadMethod}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-between my-6">
|
<div className="flex justify-between my-6">
|
||||||
|
Loading…
Reference in New Issue
Block a user