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 @typescript-eslint/no-unnecessary-condition */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
|
||||
@ -11,9 +12,12 @@ import { useEffect, useState } from 'react'
|
||||
import { toast } from 'react-hot-toast'
|
||||
|
||||
import { TextInput } from '../../forms/FormInput'
|
||||
import type { UploadMethod } from './UploadDetails'
|
||||
|
||||
interface CollectionDetailsProps {
|
||||
onChange: (data: CollectionDetailsDataProps) => void
|
||||
uploadMethod: UploadMethod
|
||||
coverImageUrl: string
|
||||
}
|
||||
|
||||
export interface CollectionDetailsDataProps {
|
||||
@ -23,7 +27,7 @@ export interface CollectionDetailsDataProps {
|
||||
externalLink?: string
|
||||
}
|
||||
|
||||
export const CollectionDetails = ({ onChange }: CollectionDetailsProps) => {
|
||||
export const CollectionDetails = ({ onChange, uploadMethod, coverImageUrl }: CollectionDetailsProps) => {
|
||||
const [coverImage, setCoverImage] = useState<File | null>(null)
|
||||
|
||||
const nameState = useInputState({
|
||||
@ -84,23 +88,44 @@ export const CollectionDetails = ({ onChange }: CollectionDetailsProps) => {
|
||||
<FormGroup subtitle="Information about your collection" title="Collection Details">
|
||||
<TextInput {...nameState} isRequired />
|
||||
<TextInput {...descriptionState} isRequired />
|
||||
<FormControl isRequired title="Cover Image">
|
||||
<input
|
||||
accept="image/*"
|
||||
className={clsx(
|
||||
'file:py-2 file:px-4 file:mr-4 file:bg-plumbus-light file:rounded file:border-0 cursor-pointer',
|
||||
'before:hover:bg-white/5 before:transition',
|
||||
)}
|
||||
id="cover-image"
|
||||
onChange={selectCoverImage}
|
||||
type="file"
|
||||
/>
|
||||
{coverImage !== null && (
|
||||
<div className="w-[200px] h-[200px] rounded border-2">
|
||||
<img alt="cover-preview" src={URL.createObjectURL(coverImage)} />
|
||||
|
||||
<FormControl isRequired={uploadMethod === 'new'} title="Cover Image">
|
||||
{uploadMethod === 'new' && (
|
||||
<input
|
||||
accept="image/*"
|
||||
className={clsx(
|
||||
'file:py-2 file:px-4 file:mr-4 file:bg-plumbus-light file:rounded file:border-0 cursor-pointer',
|
||||
'before:hover:bg-white/5 before:transition',
|
||||
)}
|
||||
id="cover-image"
|
||||
onChange={selectCoverImage}
|
||||
type="file"
|
||||
/>
|
||||
)}
|
||||
|
||||
{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>
|
||||
)}
|
||||
{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>
|
||||
|
||||
<TextInput {...externalLinkState} />
|
||||
</FormGroup>
|
||||
</div>
|
||||
|
@ -5,10 +5,12 @@ import { InputDateTime } from 'components/InputDateTime'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
|
||||
import { NumberInput } from '../../forms/FormInput'
|
||||
import type { UploadMethod } from './UploadDetails'
|
||||
|
||||
interface MintingDetailsProps {
|
||||
onChange: (data: MintingDetailsDataProps) => void
|
||||
numberOfTokens: number | undefined
|
||||
uploadMethod: UploadMethod
|
||||
}
|
||||
|
||||
export interface MintingDetailsDataProps {
|
||||
@ -18,7 +20,7 @@ export interface MintingDetailsDataProps {
|
||||
startTime: string
|
||||
}
|
||||
|
||||
export const MintingDetails = ({ onChange, numberOfTokens }: MintingDetailsProps) => {
|
||||
export const MintingDetails = ({ onChange, numberOfTokens, uploadMethod }: MintingDetailsProps) => {
|
||||
const [timestamp, setTimestamp] = useState<Date | undefined>()
|
||||
|
||||
const numberOfTokensState = useNumberInputState({
|
||||
@ -60,7 +62,12 @@ export const MintingDetails = ({ onChange, numberOfTokens }: MintingDetailsProps
|
||||
return (
|
||||
<div>
|
||||
<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 {...perAddressLimitState} isRequired />
|
||||
<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 { useInputState } from 'components/forms/FormInput.hooks'
|
||||
import { MetadataModal } from 'components/MetadataModal'
|
||||
import { setBaseTokenUri, setImage, useCollectionStore } from 'contexts/collection'
|
||||
import type { ChangeEvent } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { toast } from 'react-hot-toast'
|
||||
import type { UploadServiceType } from 'services/upload'
|
||||
import { naturalCompare } from 'utils/sort'
|
||||
|
||||
type UploadMethod = 'new' | 'existing'
|
||||
export type UploadMethod = 'new' | 'existing'
|
||||
|
||||
interface UploadDetailsProps {
|
||||
onChange: (value: UploadDetailsDataProps) => void
|
||||
@ -26,10 +25,12 @@ export interface UploadDetailsDataProps {
|
||||
nftStorageApiKey?: string
|
||||
pinataApiKey?: string
|
||||
pinataSecretKey?: string
|
||||
uploadMethod: UploadMethod
|
||||
baseTokenURI?: string
|
||||
imageUrl?: string
|
||||
}
|
||||
|
||||
export const UploadDetails = ({ onChange }: UploadDetailsProps) => {
|
||||
const baseTokenURI = useCollectionStore().base_token_uri
|
||||
const [assetFilesArray, setAssetFilesArray] = useState<File[]>([])
|
||||
const [metadataFilesArray, setMetadataFilesArray] = useState<File[]>([])
|
||||
const [uploadMethod, setUploadMethod] = useState<UploadMethod>('new')
|
||||
@ -60,13 +61,21 @@ export const UploadDetails = ({ onChange }: UploadDetailsProps) => {
|
||||
defaultValue: '9d6f42dc01eaab15f52eac8f36cc4f0ee4184944cb3cdbcda229d06ecf877ee7',
|
||||
})
|
||||
|
||||
const handleChangeBaseTokenUri = (event: { target: { value: React.SetStateAction<string> } }) => {
|
||||
setBaseTokenUri(event.target.value.toString())
|
||||
}
|
||||
const baseTokenUriState = useInputState({
|
||||
id: 'baseTokenUri',
|
||||
name: 'baseTokenUri',
|
||||
title: 'Base Token URI',
|
||||
placeholder: 'ipfs://',
|
||||
defaultValue: '',
|
||||
})
|
||||
|
||||
const handleChangeImage = (event: { target: { value: React.SetStateAction<string> } }) => {
|
||||
setImage(event.target.value.toString())
|
||||
}
|
||||
const coverImageUrlState = useInputState({
|
||||
id: 'coverImageUrl',
|
||||
name: 'coverImageUrl',
|
||||
title: 'Cover Image URL',
|
||||
placeholder: 'ipfs://',
|
||||
defaultValue: '',
|
||||
})
|
||||
|
||||
const selectAssets = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const files: File[] = []
|
||||
@ -135,6 +144,9 @@ export const UploadDetails = ({ onChange }: UploadDetailsProps) => {
|
||||
nftStorageApiKey: nftStorageApiKeyState.value,
|
||||
pinataApiKey: pinataApiKeyState.value,
|
||||
pinataSecretKey: pinataSecretKeyState.value,
|
||||
uploadMethod,
|
||||
baseTokenURI: baseTokenUriState.value,
|
||||
imageUrl: coverImageUrlState.value,
|
||||
}
|
||||
onChange(data)
|
||||
} catch (error: any) {
|
||||
@ -147,11 +159,16 @@ export const UploadDetails = ({ onChange }: UploadDetailsProps) => {
|
||||
nftStorageApiKeyState.value,
|
||||
pinataApiKeyState.value,
|
||||
pinataSecretKeyState.value,
|
||||
uploadMethod,
|
||||
baseTokenUriState.value,
|
||||
coverImageUrlState.value,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
setAssetFilesArray([])
|
||||
setMetadataFilesArray([])
|
||||
baseTokenUriState.onChange('')
|
||||
coverImageUrlState.onChange('')
|
||||
}, [uploadMethod])
|
||||
|
||||
return (
|
||||
@ -192,14 +209,6 @@ export const UploadDetails = ({ onChange }: UploadDetailsProps) => {
|
||||
</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">
|
||||
<Conditional test={uploadMethod === 'existing'}>
|
||||
<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.
|
||||
</p>
|
||||
<div>
|
||||
<label className="block mr-1 mb-1 ml-5 font-bold text-white dark:text-gray-300" htmlFor="coverImage">
|
||||
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"
|
||||
/>
|
||||
<TextInput {...baseTokenUriState} className="w-1/2" />
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
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://..."
|
||||
/>
|
||||
<TextInput {...coverImageUrlState} className="mt-2 w-1/2" />
|
||||
</div>
|
||||
</div>
|
||||
</Conditional>
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* 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-member-access */
|
||||
|
||||
@ -27,13 +28,13 @@ import { NextSeo } from 'next-seo'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import useCollapse from 'react-collapsed'
|
||||
import { toast } from 'react-hot-toast'
|
||||
import type { UploadServiceType } from 'services/upload'
|
||||
import { upload } from 'services/upload'
|
||||
import { compareFileArrays } from 'utils/compareFileArrays'
|
||||
import { MINTER_CODE_ID, SG721_CODE_ID, WHITELIST_CODE_ID } from 'utils/constants'
|
||||
import { withMetadata } from 'utils/layout'
|
||||
import { links } from 'utils/links'
|
||||
|
||||
import type { UploadMethod } from '../../components/collections/creation/UploadDetails'
|
||||
import { getAssetType } from '../../utils/getAssetType'
|
||||
|
||||
const CollectionCreationPage: NextPage = () => {
|
||||
@ -55,11 +56,13 @@ const CollectionCreationPage: NextPage = () => {
|
||||
const [minterContractAddress, setMinterContractAddress] = useState<string | null>(null)
|
||||
const [sg721ContractAddress, setSg721ContractAddress] = 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 createCollection = async () => {
|
||||
try {
|
||||
setBaseTokenUri(null)
|
||||
setCoverImageUrl(null)
|
||||
setMinterContractAddress(null)
|
||||
setSg721ContractAddress(null)
|
||||
setTransactionHash(null)
|
||||
@ -68,28 +71,34 @@ const CollectionCreationPage: NextPage = () => {
|
||||
checkMintingDetails()
|
||||
checkWhitelistDetails()
|
||||
checkRoyaltyDetails()
|
||||
if (uploadDetails?.uploadMethod === 'new') {
|
||||
setUploading(true)
|
||||
|
||||
setUploading(true)
|
||||
|
||||
const baseUri = await uploadFiles()
|
||||
setBaseTokenUri(baseUri)
|
||||
//upload coverImageUri and append the file name
|
||||
const coverImageUri = await upload(
|
||||
collectionDetails?.imageFile as File[],
|
||||
uploadDetails?.uploadService as UploadServiceType,
|
||||
'cover',
|
||||
uploadDetails?.nftStorageApiKey as string,
|
||||
uploadDetails?.pinataApiKey as string,
|
||||
uploadDetails?.pinataSecretKey as string,
|
||||
)
|
||||
|
||||
setUploading(false)
|
||||
|
||||
let whitelist: string | undefined
|
||||
if (whitelistDetails?.whitelistType === 'existing') whitelist = whitelistDetails.contractAddress
|
||||
else if (whitelistDetails?.whitelistType === 'new') whitelist = await instantiateWhitelist()
|
||||
|
||||
await instantiate(baseUri, coverImageUri, whitelist)
|
||||
const baseUri = await uploadFiles()
|
||||
//upload coverImageUri and append the file name
|
||||
const coverImageUri = await upload(
|
||||
collectionDetails?.imageFile as File[],
|
||||
uploadDetails.uploadService,
|
||||
'cover',
|
||||
uploadDetails.nftStorageApiKey as string,
|
||||
uploadDetails.pinataApiKey as string,
|
||||
uploadDetails.pinataSecretKey as string,
|
||||
)
|
||||
setUploading(false)
|
||||
setBaseTokenUri(baseUri)
|
||||
setCoverImageUrl(coverImageUri)
|
||||
let whitelist: string | undefined
|
||||
if (whitelistDetails?.whitelistType === 'existing') whitelist = whitelistDetails.contractAddress
|
||||
else if (whitelistDetails?.whitelistType === 'new') whitelist = await instantiateWhitelist()
|
||||
await instantiate(baseUri, coverImageUri, whitelist)
|
||||
} else {
|
||||
setBaseTokenUri(uploadDetails?.baseTokenURI as string)
|
||||
setCoverImageUrl(uploadDetails?.imageUrl as string)
|
||||
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
|
||||
} catch (error: any) {
|
||||
@ -132,9 +141,8 @@ const CollectionCreationPage: NextPage = () => {
|
||||
share: (Number(royaltyDetails.share) / 100).toString(),
|
||||
}
|
||||
}
|
||||
|
||||
const msg = {
|
||||
base_token_uri: `ipfs://${baseUri}/`,
|
||||
base_token_uri: `${uploadDetails?.uploadMethod === 'new' ? `ipfs://${baseUri}/` : `${baseUri}`}`,
|
||||
num_tokens: mintingDetails?.numTokens,
|
||||
sg721_code_id: SG721_CODE_ID,
|
||||
sg721_instantiate_msg: {
|
||||
@ -144,7 +152,11 @@ const CollectionCreationPage: NextPage = () => {
|
||||
collection_info: {
|
||||
creator: wallet.address,
|
||||
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,
|
||||
royalty_info: royaltyInfo,
|
||||
},
|
||||
@ -223,19 +235,34 @@ const CollectionCreationPage: NextPage = () => {
|
||||
if (!uploadDetails) {
|
||||
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')
|
||||
}
|
||||
if (uploadDetails.metadataFiles.length === 0) {
|
||||
if (uploadDetails.uploadMethod === 'new' && uploadDetails.metadataFiles.length === 0) {
|
||||
throw new Error('Please select the metadata files')
|
||||
}
|
||||
compareFileArrays(uploadDetails.assetFiles, uploadDetails.metadataFiles)
|
||||
if (uploadDetails.uploadService === 'nft-storage') {
|
||||
if (uploadDetails.nftStorageApiKey === '') {
|
||||
throw new Error('Please enter a valid NFT Storage API key')
|
||||
if (uploadDetails.uploadMethod === 'new') compareFileArrays(uploadDetails.assetFiles, uploadDetails.metadataFiles)
|
||||
if (uploadDetails.uploadMethod === 'new') {
|
||||
if (uploadDetails.uploadService === 'nft-storage') {
|
||||
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.name === '') throw new Error('Collection name 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 = () => {
|
||||
@ -288,6 +316,11 @@ const CollectionCreationPage: NextPage = () => {
|
||||
if (minterContractAddress !== null) scrollRef.current?.scrollIntoView({ behavior: 'smooth' })
|
||||
}, [minterContractAddress])
|
||||
|
||||
useEffect(() => {
|
||||
setBaseTokenUri(uploadDetails?.baseTokenURI as string)
|
||||
setCoverImageUrl(uploadDetails?.imageUrl as string)
|
||||
}, [uploadDetails?.baseTokenURI, uploadDetails?.imageUrl])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<NextSeo title="Create Collection" />
|
||||
@ -312,13 +345,26 @@ const CollectionCreationPage: NextPage = () => {
|
||||
<Alert className="mt-5" type="info">
|
||||
<div>
|
||||
Base Token URI:{' '}
|
||||
<Anchor
|
||||
className="text-stargaze hover:underline"
|
||||
external
|
||||
href={`https://ipfs.stargaze.zone/ipfs/${baseTokenUri as string}/`}
|
||||
>
|
||||
ipfs://{baseTokenUri as string}/
|
||||
</Anchor>
|
||||
{uploadDetails?.uploadMethod === 'new' && (
|
||||
<Anchor
|
||||
className="text-stargaze hover:underline"
|
||||
external
|
||||
href={`https://ipfs.stargaze.zone/ipfs/${baseTokenUri as string}/`}
|
||||
>
|
||||
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 />
|
||||
Minter Contract Address:{' '}
|
||||
<Anchor
|
||||
@ -354,8 +400,16 @@ const CollectionCreationPage: NextPage = () => {
|
||||
<UploadDetails onChange={setUploadDetails} />
|
||||
|
||||
<div className="flex justify-between py-3 px-8 rounded border-2 border-white/20 grid-col-2">
|
||||
<CollectionDetails onChange={setCollectionDetails} />
|
||||
<MintingDetails numberOfTokens={uploadDetails?.assetFiles.length} onChange={setMintingDetails} />
|
||||
<CollectionDetails
|
||||
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 className="flex justify-between my-6">
|
||||
|
Loading…
Reference in New Issue
Block a user