Merge branch 'develop' into hardcoded-wl-code-id
This commit is contained in:
@@ -26,7 +26,7 @@ import { resolveAddress } from 'utils/resolveAddress'
|
||||
|
||||
import type { CollectionInfo } from '../../../contracts/sg721/contract'
|
||||
import { TextInput } from '../../forms/FormInput'
|
||||
import type { MinterType } from './Combobox'
|
||||
import type { MinterType, Sg721Type } from './Combobox'
|
||||
|
||||
interface CollectionActionsProps {
|
||||
minterContractAddress: string
|
||||
@@ -35,6 +35,7 @@ interface CollectionActionsProps {
|
||||
vendingMinterMessages: VendingMinterInstance | undefined
|
||||
baseMinterMessages: BaseMinterInstance | undefined
|
||||
minterType: MinterType
|
||||
sg721Type: Sg721Type
|
||||
}
|
||||
|
||||
type ExplicitContentType = true | false | undefined
|
||||
@@ -46,6 +47,7 @@ export const CollectionActions = ({
|
||||
vendingMinterMessages,
|
||||
baseMinterMessages,
|
||||
minterType,
|
||||
sg721Type,
|
||||
}: CollectionActionsProps) => {
|
||||
const wallet = useWallet()
|
||||
const [lastTx, setLastTx] = useState('')
|
||||
@@ -100,7 +102,15 @@ export const CollectionActions = ({
|
||||
id: 'token-uri',
|
||||
name: 'tokenURI',
|
||||
title: 'Token URI',
|
||||
subtitle: 'URI for the token to be minted',
|
||||
subtitle: 'URI for the token',
|
||||
placeholder: 'ipfs://',
|
||||
})
|
||||
|
||||
const baseURIState = useInputState({
|
||||
id: 'base-uri',
|
||||
name: 'baseURI',
|
||||
title: 'Base URI',
|
||||
subtitle: 'Base URI to batch update token metadata with',
|
||||
placeholder: 'ipfs://',
|
||||
})
|
||||
|
||||
@@ -154,13 +164,18 @@ export const CollectionActions = ({
|
||||
placeholder: '5%',
|
||||
})
|
||||
|
||||
const showTokenUriField = type === 'mint_token_uri'
|
||||
const showTokenUriField = isEitherType(type, ['mint_token_uri', 'update_token_metadata'])
|
||||
const showWhitelistField = type === 'set_whitelist'
|
||||
const showDateField = isEitherType(type, ['update_start_time', 'update_start_trading_time'])
|
||||
const showLimitField = type === 'update_per_address_limit'
|
||||
const showTokenIdField = isEitherType(type, ['transfer', 'mint_for', 'burn'])
|
||||
const showTokenIdField = isEitherType(type, ['transfer', 'mint_for', 'burn', 'update_token_metadata'])
|
||||
const showNumberOfTokensField = type === 'batch_mint'
|
||||
const showTokenIdListField = isEitherType(type, ['batch_burn', 'batch_transfer', 'batch_mint_for'])
|
||||
const showTokenIdListField = isEitherType(type, [
|
||||
'batch_burn',
|
||||
'batch_transfer',
|
||||
'batch_mint_for',
|
||||
'batch_update_token_metadata',
|
||||
])
|
||||
const showRecipientField = isEitherType(type, [
|
||||
'transfer',
|
||||
'mint_to',
|
||||
@@ -176,6 +191,7 @@ export const CollectionActions = ({
|
||||
const showExternalLinkField = type === 'update_collection_info'
|
||||
const showRoyaltyRelatedFields = type === 'update_collection_info'
|
||||
const showExplicitContentField = type === 'update_collection_info'
|
||||
const showBaseUriField = type === 'batch_update_token_metadata'
|
||||
|
||||
const payload: DispatchExecuteArgs = {
|
||||
whitelist: whitelistState.value,
|
||||
@@ -185,7 +201,9 @@ export const CollectionActions = ({
|
||||
sg721Contract: sg721ContractAddress,
|
||||
tokenId: tokenIdState.value,
|
||||
tokenIds: tokenIdListState.value,
|
||||
tokenUri: tokenURIState.value,
|
||||
tokenUri: tokenURIState.value.trim().endsWith('/')
|
||||
? tokenURIState.value.trim().slice(0, -1)
|
||||
: tokenURIState.value.trim(),
|
||||
batchNumber: batchNumberState.value,
|
||||
vendingMinterMessages,
|
||||
baseMinterMessages,
|
||||
@@ -195,6 +213,9 @@ export const CollectionActions = ({
|
||||
txSigner: wallet.address,
|
||||
type,
|
||||
price: priceState.value.toString(),
|
||||
baseUri: baseURIState.value.trim().endsWith('/')
|
||||
? baseURIState.value.trim().slice(0, -1)
|
||||
: baseURIState.value.trim(),
|
||||
collectionInfo,
|
||||
}
|
||||
const resolveRecipientAddress = async () => {
|
||||
@@ -350,13 +371,14 @@ export const CollectionActions = ({
|
||||
<form>
|
||||
<div className="grid grid-cols-2 mt-4">
|
||||
<div className="mr-2">
|
||||
<ActionsCombobox minterType={minterType} {...actionComboboxState} />
|
||||
<ActionsCombobox minterType={minterType} sg721Type={sg721Type} {...actionComboboxState} />
|
||||
{showRecipientField && <AddressInput {...recipientState} />}
|
||||
{showTokenUriField && <TextInput className="mt-2" {...tokenURIState} />}
|
||||
{showWhitelistField && <AddressInput {...whitelistState} />}
|
||||
{showLimitField && <NumberInput {...limitState} />}
|
||||
{showTokenIdField && <NumberInput {...tokenIdState} />}
|
||||
{showTokenIdField && <NumberInput className="mt-2" {...tokenIdState} />}
|
||||
{showTokenIdListField && <TextInput className="mt-2" {...tokenIdListState} />}
|
||||
{showBaseUriField && <TextInput className="mt-2" {...baseURIState} />}
|
||||
{showNumberOfTokensField && <NumberInput className="mt-2" {...batchNumberState} />}
|
||||
{showPriceField && <NumberInput className="mt-2" {...priceState} />}
|
||||
{showDescriptionField && <TextInput className="my-2" {...descriptionState} />}
|
||||
|
||||
@@ -6,27 +6,31 @@ import { Fragment, useEffect, useState } from 'react'
|
||||
import { FaChevronDown, FaInfoCircle } from 'react-icons/fa'
|
||||
|
||||
import type { ActionListItem } from './actions'
|
||||
import { BASE_ACTION_LIST, VENDING_ACTION_LIST } from './actions'
|
||||
import { BASE_ACTION_LIST, SG721_UPDATABLE_ACTION_LIST, VENDING_ACTION_LIST } from './actions'
|
||||
|
||||
export type MinterType = 'base' | 'vending'
|
||||
export type Sg721Type = 'updatable' | 'base'
|
||||
|
||||
export interface ActionsComboboxProps {
|
||||
value: ActionListItem | null
|
||||
onChange: (item: ActionListItem) => void
|
||||
minterType?: MinterType
|
||||
sg721Type?: Sg721Type
|
||||
}
|
||||
|
||||
export const ActionsCombobox = ({ value, onChange, minterType }: ActionsComboboxProps) => {
|
||||
export const ActionsCombobox = ({ value, onChange, minterType, sg721Type }: ActionsComboboxProps) => {
|
||||
const [search, setSearch] = useState('')
|
||||
const [ACTION_LIST, SET_ACTION_LIST] = useState<ActionListItem[]>(VENDING_ACTION_LIST)
|
||||
|
||||
useEffect(() => {
|
||||
if (minterType === 'base') {
|
||||
SET_ACTION_LIST(BASE_ACTION_LIST)
|
||||
} else {
|
||||
SET_ACTION_LIST(VENDING_ACTION_LIST)
|
||||
}
|
||||
}, [minterType])
|
||||
if (sg721Type === 'updatable') SET_ACTION_LIST(BASE_ACTION_LIST.concat(SG721_UPDATABLE_ACTION_LIST))
|
||||
else SET_ACTION_LIST(BASE_ACTION_LIST)
|
||||
} else if (minterType === 'vending') {
|
||||
if (sg721Type === 'updatable') SET_ACTION_LIST(VENDING_ACTION_LIST.concat(SG721_UPDATABLE_ACTION_LIST))
|
||||
else SET_ACTION_LIST(VENDING_ACTION_LIST)
|
||||
} else SET_ACTION_LIST(VENDING_ACTION_LIST.concat(SG721_UPDATABLE_ACTION_LIST))
|
||||
}, [minterType, sg721Type])
|
||||
|
||||
const filtered =
|
||||
search === '' ? ACTION_LIST : matchSorter(ACTION_LIST, search, { keys: ['id', 'name', 'description'] })
|
||||
|
||||
@@ -9,9 +9,7 @@ import type { BaseMinterInstance } from '../../../contracts/baseMinter/contract'
|
||||
export type ActionType = typeof ACTION_TYPES[number]
|
||||
|
||||
export const ACTION_TYPES = [
|
||||
'mint',
|
||||
'mint_token_uri',
|
||||
'purge',
|
||||
'update_mint_price',
|
||||
'update_discount_price',
|
||||
'remove_discount_price',
|
||||
@@ -32,6 +30,9 @@ export const ACTION_TYPES = [
|
||||
'shuffle',
|
||||
'airdrop',
|
||||
'burn_remaining',
|
||||
'update_token_metadata',
|
||||
'batch_update_token_metadata',
|
||||
'freeze_token_metadata',
|
||||
] as const
|
||||
|
||||
export interface ActionListItem {
|
||||
@@ -84,11 +85,6 @@ export const BASE_ACTION_LIST: ActionListItem[] = [
|
||||
]
|
||||
|
||||
export const VENDING_ACTION_LIST: ActionListItem[] = [
|
||||
{
|
||||
id: 'mint',
|
||||
name: 'Mint',
|
||||
description: `Mint a token`,
|
||||
},
|
||||
{
|
||||
id: 'update_mint_price',
|
||||
name: 'Update Mint Price',
|
||||
@@ -111,7 +107,7 @@ export const VENDING_ACTION_LIST: ActionListItem[] = [
|
||||
},
|
||||
{
|
||||
id: 'batch_mint',
|
||||
name: 'Batch Mint',
|
||||
name: 'Batch Mint To',
|
||||
description: `Mint multiple tokens to a user`,
|
||||
},
|
||||
{
|
||||
@@ -189,10 +185,23 @@ export const VENDING_ACTION_LIST: ActionListItem[] = [
|
||||
name: 'Burn Remaining Tokens',
|
||||
description: 'Burn remaining tokens',
|
||||
},
|
||||
]
|
||||
|
||||
export const SG721_UPDATABLE_ACTION_LIST: ActionListItem[] = [
|
||||
{
|
||||
id: 'purge',
|
||||
name: 'Purge',
|
||||
description: `Purge`,
|
||||
id: 'update_token_metadata',
|
||||
name: 'Update Token Metadata',
|
||||
description: `Update the metadata URI for a token`,
|
||||
},
|
||||
{
|
||||
id: 'batch_update_token_metadata',
|
||||
name: 'Batch Update Token Metadata',
|
||||
description: `Update the metadata URI for a range of tokens`,
|
||||
},
|
||||
{
|
||||
id: 'freeze_token_metadata',
|
||||
name: 'Freeze Token Metadata',
|
||||
description: `Render the metadata for tokens no longer updatable`,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -213,9 +222,7 @@ export type DispatchExecuteArgs = {
|
||||
txSigner: string
|
||||
} & (
|
||||
| { type: undefined }
|
||||
| { type: Select<'mint'> }
|
||||
| { type: Select<'mint_token_uri'>; tokenUri: string }
|
||||
| { type: Select<'purge'> }
|
||||
| { type: Select<'update_mint_price'>; price: string }
|
||||
| { type: Select<'update_discount_price'>; price: string }
|
||||
| { type: Select<'remove_discount_price'> }
|
||||
@@ -236,6 +243,9 @@ export type DispatchExecuteArgs = {
|
||||
| { type: Select<'burn_remaining'> }
|
||||
| { type: Select<'update_collection_info'>; collectionInfo: CollectionInfo | undefined }
|
||||
| { type: Select<'freeze_collection_info'> }
|
||||
| { type: Select<'update_token_metadata'>; tokenId: number; tokenUri: string }
|
||||
| { type: Select<'batch_update_token_metadata'>; tokenIds: string; baseUri: string }
|
||||
| { type: Select<'freeze_token_metadata'> }
|
||||
)
|
||||
|
||||
export const dispatchExecute = async (args: DispatchExecuteArgs) => {
|
||||
@@ -244,15 +254,9 @@ export const dispatchExecute = async (args: DispatchExecuteArgs) => {
|
||||
throw new Error('Cannot execute actions')
|
||||
}
|
||||
switch (args.type) {
|
||||
case 'mint': {
|
||||
return vendingMinterMessages.mint(txSigner)
|
||||
}
|
||||
case 'mint_token_uri': {
|
||||
return baseMinterMessages.mint(txSigner, args.tokenUri)
|
||||
}
|
||||
case 'purge': {
|
||||
return vendingMinterMessages.purge(txSigner)
|
||||
}
|
||||
case 'update_mint_price': {
|
||||
return vendingMinterMessages.updateMintPrice(txSigner, args.price)
|
||||
}
|
||||
@@ -289,6 +293,15 @@ export const dispatchExecute = async (args: DispatchExecuteArgs) => {
|
||||
case 'freeze_collection_info': {
|
||||
return sg721Messages.freezeCollectionInfo()
|
||||
}
|
||||
case 'update_token_metadata': {
|
||||
return sg721Messages.updateTokenMetadata(args.tokenId.toString(), args.tokenUri)
|
||||
}
|
||||
case 'batch_update_token_metadata': {
|
||||
return sg721Messages.batchUpdateTokenMetadata(args.tokenIds, args.baseUri)
|
||||
}
|
||||
case 'freeze_token_metadata': {
|
||||
return sg721Messages.freezeTokenMetadata()
|
||||
}
|
||||
case 'shuffle': {
|
||||
return vendingMinterMessages.shuffle(txSigner)
|
||||
}
|
||||
@@ -328,15 +341,9 @@ export const previewExecutePayload = (args: DispatchExecuteArgs) => {
|
||||
const { messages: baseMinterMessages } = useBaseMinterContract()
|
||||
const { minterContract, sg721Contract } = args
|
||||
switch (args.type) {
|
||||
case 'mint': {
|
||||
return vendingMinterMessages(minterContract)?.mint()
|
||||
}
|
||||
case 'mint_token_uri': {
|
||||
return baseMinterMessages(minterContract)?.mint(args.tokenUri)
|
||||
}
|
||||
case 'purge': {
|
||||
return vendingMinterMessages(minterContract)?.purge()
|
||||
}
|
||||
case 'update_mint_price': {
|
||||
return vendingMinterMessages(minterContract)?.updateMintPrice(args.price)
|
||||
}
|
||||
@@ -373,6 +380,15 @@ export const previewExecutePayload = (args: DispatchExecuteArgs) => {
|
||||
case 'freeze_collection_info': {
|
||||
return sg721Messages(sg721Contract)?.freezeCollectionInfo()
|
||||
}
|
||||
case 'update_token_metadata': {
|
||||
return sg721Messages(sg721Contract)?.updateTokenMetadata(args.tokenId.toString(), args.tokenUri)
|
||||
}
|
||||
case 'batch_update_token_metadata': {
|
||||
return sg721Messages(sg721Contract)?.batchUpdateTokenMetadata(args.tokenIds, args.baseUri)
|
||||
}
|
||||
case 'freeze_token_metadata': {
|
||||
return sg721Messages(sg721Contract)?.freezeTokenMetadata()
|
||||
}
|
||||
case 'shuffle': {
|
||||
return vendingMinterMessages(minterContract)?.shuffle()
|
||||
}
|
||||
|
||||
@@ -8,8 +8,9 @@ import { FormControl } from 'components/FormControl'
|
||||
import { FormGroup } from 'components/FormGroup'
|
||||
import { useInputState } from 'components/forms/FormInput.hooks'
|
||||
import { InputDateTime } from 'components/InputDateTime'
|
||||
import { Tooltip } from 'components/Tooltip'
|
||||
import type { ChangeEvent } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { toast } from 'react-hot-toast'
|
||||
|
||||
import { TextInput } from '../../forms/FormInput'
|
||||
@@ -31,12 +32,16 @@ export interface CollectionDetailsDataProps {
|
||||
externalLink?: string
|
||||
startTradingTime?: string
|
||||
explicit: boolean
|
||||
updatable: boolean
|
||||
}
|
||||
|
||||
export const CollectionDetails = ({ onChange, uploadMethod, coverImageUrl, minterType }: CollectionDetailsProps) => {
|
||||
const [coverImage, setCoverImage] = useState<File | null>(null)
|
||||
const [timestamp, setTimestamp] = useState<Date | undefined>()
|
||||
const [explicit, setExplicit] = useState<boolean>(false)
|
||||
const [updatable, setUpdatable] = useState<boolean>(false)
|
||||
|
||||
const initialRender = useRef(true)
|
||||
|
||||
const nameState = useInputState({
|
||||
id: 'name',
|
||||
@@ -76,6 +81,7 @@ export const CollectionDetails = ({ onChange, uploadMethod, coverImageUrl, minte
|
||||
externalLink: externalLinkState.value || undefined,
|
||||
startTradingTime: timestamp ? (timestamp.getTime() * 1_000_000).toString() : '',
|
||||
explicit,
|
||||
updatable,
|
||||
}
|
||||
onChange(data)
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -91,6 +97,7 @@ export const CollectionDetails = ({ onChange, uploadMethod, coverImageUrl, minte
|
||||
coverImage,
|
||||
timestamp,
|
||||
explicit,
|
||||
updatable,
|
||||
])
|
||||
|
||||
const selectCoverImage = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
@@ -109,6 +116,22 @@ export const CollectionDetails = ({ onChange, uploadMethod, coverImageUrl, minte
|
||||
reader.readAsArrayBuffer(event.target.files[0])
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (initialRender.current) {
|
||||
initialRender.current = false
|
||||
} else if (updatable) {
|
||||
toast.success('Token metadata will be updatable upon collection creation.', {
|
||||
style: { maxWidth: 'none' },
|
||||
icon: '✅📝',
|
||||
})
|
||||
} else {
|
||||
toast.error('Token metadata will not be updatable upon collection creation.', {
|
||||
style: { maxWidth: 'none' },
|
||||
icon: '⛔🔏',
|
||||
})
|
||||
}
|
||||
}, [updatable])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<FormGroup subtitle="Information about your collection" title="Collection Details">
|
||||
@@ -174,7 +197,7 @@ export const CollectionDetails = ({ onChange, uploadMethod, coverImageUrl, minte
|
||||
<div className={clsx(minterType === 'base' ? 'flex flex-col -ml-16 space-y-2' : 'flex flex-col space-y-2')}>
|
||||
<div>
|
||||
<div className="flex">
|
||||
<span className="mt-1 text-sm first-letter:capitalize">
|
||||
<span className="mt-1 ml-[2px] text-sm first-letter:capitalize">
|
||||
Does the collection contain explicit content?
|
||||
</span>
|
||||
<div className="ml-2 font-bold form-check form-check-inline">
|
||||
@@ -216,6 +239,35 @@ export const CollectionDetails = ({ onChange, uploadMethod, coverImageUrl, minte
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Tooltip
|
||||
label={
|
||||
<div className="grid grid-flow-row">
|
||||
<span>
|
||||
ℹ️ When enabled, the metadata for tokens can be updated after the collection is created until the
|
||||
metadata is frozen by the creator.
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
placement="bottom"
|
||||
>
|
||||
<div
|
||||
className={clsx(
|
||||
minterType === 'base'
|
||||
? 'flex flex-col -ml-16 space-y-2 w-1/2 form-control'
|
||||
: 'flex flex-col space-y-2 w-3/4 form-control',
|
||||
)}
|
||||
>
|
||||
<label className="justify-start cursor-pointer label">
|
||||
<span className="mr-4 font-bold">Updatable Token Metadata</span>
|
||||
<input
|
||||
checked={updatable}
|
||||
className={`toggle ${updatable ? `bg-stargaze` : `bg-gray-600`}`}
|
||||
onClick={() => setUpdatable(!updatable)}
|
||||
type="checkbox"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</FormGroup>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user