Merge pull request #121 from public-awesome/develop
Sync development > main
This commit is contained in:
commit
1a076026e0
@ -1,9 +1,9 @@
|
||||
APP_VERSION=0.4.7
|
||||
APP_VERSION=0.4.8
|
||||
|
||||
NEXT_PUBLIC_PINATA_ENDPOINT_URL=https://api.pinata.cloud/pinning/pinFileToIPFS
|
||||
NEXT_PUBLIC_SG721_CODE_ID=793
|
||||
NEXT_PUBLIC_VENDING_MINTER_CODE_ID=275
|
||||
NEXT_PUBLIC_VENDING_FACTORY_ADDRESS="stars1s48apjumprma0d64ge88dmu7lr8exu02tlw90xxupgds0s25gfasx447dn"
|
||||
NEXT_PUBLIC_SG721_CODE_ID=1702
|
||||
NEXT_PUBLIC_VENDING_MINTER_CODE_ID=1701
|
||||
NEXT_PUBLIC_VENDING_FACTORY_ADDRESS="stars1xz4d6wzxqn3udgsm5qnr78y032xng4r2ycv7aw6mjtsuw59s2n9s93ec0v"
|
||||
NEXT_PUBLIC_BASE_FACTORY_ADDRESS="stars1c6juqgd7cm80afpmuszun66rl9zdc4kgfht8fk34tfq3zk87l78sdxngzv"
|
||||
NEXT_PUBLIC_SG721_NAME_ADDRESS="stars1fx74nkqkw2748av8j7ew7r3xt9cgjqduwn8m0ur5lhe49uhlsasszc5fhr"
|
||||
NEXT_PUBLIC_BASE_MINTER_CODE_ID=613
|
||||
|
@ -6,6 +6,8 @@ import { usePopper } from 'react-popper'
|
||||
export interface TooltipProps extends ComponentProps<'div'> {
|
||||
label: ReactNode
|
||||
children: ReactElement
|
||||
placement?: 'top' | 'bottom' | 'left' | 'right'
|
||||
backgroundColor?: string
|
||||
}
|
||||
|
||||
export const Tooltip = ({ label, children, ...props }: TooltipProps) => {
|
||||
@ -14,7 +16,7 @@ export const Tooltip = ({ label, children, ...props }: TooltipProps) => {
|
||||
const [show, setShow] = useState(false)
|
||||
|
||||
const { styles, attributes } = usePopper(referenceElement, popperElement, {
|
||||
placement: 'top',
|
||||
placement: props.placement ? props.placement : 'top',
|
||||
})
|
||||
|
||||
return (
|
||||
@ -32,7 +34,11 @@ export const Tooltip = ({ label, children, ...props }: TooltipProps) => {
|
||||
<div
|
||||
{...props}
|
||||
{...attributes.popper}
|
||||
className={clsx('py-1 px-2 m-1 text-sm bg-black/80 rounded shadow-md', props.className)}
|
||||
className={clsx(
|
||||
'py-1 px-2 m-1 text-sm rounded shadow-md',
|
||||
props.backgroundColor ? props.backgroundColor : 'bg-slate-900',
|
||||
props.className,
|
||||
)}
|
||||
ref={setPopperElement}
|
||||
style={{ ...styles.popper, ...props.style }}
|
||||
>
|
||||
|
@ -3,24 +3,30 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
|
||||
import { toUtf8 } from '@cosmjs/encoding'
|
||||
import clsx from 'clsx'
|
||||
import { Conditional } from 'components/Conditional'
|
||||
import { FormControl } from 'components/FormControl'
|
||||
import { useInputState, useNumberInputState } from 'components/forms/FormInput.hooks'
|
||||
import { useMetadataAttributesState } from 'components/forms/MetadataAttributes.hooks'
|
||||
import { InputDateTime } from 'components/InputDateTime'
|
||||
import { useWallet } from 'contexts/wallet'
|
||||
import type { Trait } from 'contracts/badgeHub'
|
||||
import { useEffect, useState } from 'react'
|
||||
import type { ChangeEvent } from 'react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { toast } from 'react-hot-toast'
|
||||
import { BADGE_HUB_ADDRESS } from 'utils/constants'
|
||||
|
||||
import { AddressInput, NumberInput, TextInput } from '../../forms/FormInput'
|
||||
import { MetadataAttributes } from '../../forms/MetadataAttributes'
|
||||
import { Tooltip } from '../../Tooltip'
|
||||
import type { MintRule, UploadMethod } from './ImageUploadDetails'
|
||||
|
||||
interface BadgeDetailsProps {
|
||||
onChange: (data: BadgeDetailsDataProps) => void
|
||||
uploadMethod: UploadMethod | undefined
|
||||
mintRule: MintRule
|
||||
metadataSize: number
|
||||
}
|
||||
|
||||
export interface BadgeDetailsDataProps {
|
||||
@ -38,10 +44,14 @@ export interface BadgeDetailsDataProps {
|
||||
youtube_url?: string
|
||||
}
|
||||
|
||||
export const BadgeDetails = ({ onChange }: BadgeDetailsProps) => {
|
||||
export const BadgeDetails = ({ metadataSize, onChange }: BadgeDetailsProps) => {
|
||||
const wallet = useWallet()
|
||||
const [timestamp, setTimestamp] = useState<Date | undefined>(undefined)
|
||||
const [transferrable, setTransferrable] = useState<boolean>(false)
|
||||
const [metadataFile, setMetadataFile] = useState<File>()
|
||||
const [metadataFeeRate, setMetadataFeeRate] = useState<number>(0)
|
||||
|
||||
const metadataFileRef = useRef<HTMLInputElement | null>(null)
|
||||
|
||||
const managerState = useInputState({
|
||||
id: 'manager-address',
|
||||
@ -109,6 +119,79 @@ export const BadgeDetails = ({ onChange }: BadgeDetailsProps) => {
|
||||
subtitle: 'YouTube URL for the badge',
|
||||
})
|
||||
|
||||
const parseMetadata = async () => {
|
||||
try {
|
||||
let parsedMetadata: any
|
||||
if (metadataFile) {
|
||||
attributesState.reset()
|
||||
parsedMetadata = JSON.parse(await metadataFile.text())
|
||||
|
||||
if (!parsedMetadata.attributes || parsedMetadata.attributes.length === 0) {
|
||||
attributesState.add({
|
||||
trait_type: '',
|
||||
value: '',
|
||||
})
|
||||
} else {
|
||||
for (let i = 0; i < parsedMetadata.attributes.length; i++) {
|
||||
attributesState.add({
|
||||
trait_type: parsedMetadata.attributes[i].trait_type,
|
||||
value: parsedMetadata.attributes[i].value,
|
||||
})
|
||||
}
|
||||
}
|
||||
nameState.onChange(parsedMetadata.name ? parsedMetadata.name : '')
|
||||
descriptionState.onChange(parsedMetadata.description ? parsedMetadata.description : '')
|
||||
externalUrlState.onChange(parsedMetadata.external_url ? parsedMetadata.external_url : '')
|
||||
youtubeUrlState.onChange(parsedMetadata.youtube_url ? parsedMetadata.youtube_url : '')
|
||||
animationUrlState.onChange(parsedMetadata.animation_url ? parsedMetadata.animation_url : '')
|
||||
backgroundColorState.onChange(parsedMetadata.background_color ? parsedMetadata.background_color : '')
|
||||
imageDataState.onChange(parsedMetadata.image_data ? parsedMetadata.image_data : '')
|
||||
} else {
|
||||
attributesState.reset()
|
||||
nameState.onChange('')
|
||||
descriptionState.onChange('')
|
||||
externalUrlState.onChange('')
|
||||
youtubeUrlState.onChange('')
|
||||
animationUrlState.onChange('')
|
||||
backgroundColorState.onChange('')
|
||||
imageDataState.onChange('')
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error('Error parsing metadata file: Invalid JSON format.')
|
||||
if (metadataFileRef.current) metadataFileRef.current.value = ''
|
||||
setMetadataFile(undefined)
|
||||
}
|
||||
}
|
||||
|
||||
const selectMetadata = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
setMetadataFile(undefined)
|
||||
if (event.target.files === null) return
|
||||
|
||||
let selectedFile: File
|
||||
const reader = new FileReader()
|
||||
reader.onload = (e) => {
|
||||
if (!event.target.files) return toast.error('No file selected.')
|
||||
if (!e.target?.result) return toast.error('Error parsing file.')
|
||||
selectedFile = new File([e.target.result], event.target.files[0].name, { type: 'application/json' })
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (event.target.files[0]) reader.readAsArrayBuffer(event.target.files[0])
|
||||
else return toast.error('No file selected.')
|
||||
reader.onloadend = () => {
|
||||
if (!event.target.files) return toast.error('No file selected.')
|
||||
setMetadataFile(selectedFile)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
void parseMetadata()
|
||||
if (!metadataFile)
|
||||
attributesState.add({
|
||||
trait_type: '',
|
||||
value: '',
|
||||
})
|
||||
}, [metadataFile])
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
const data: BadgeDetailsDataProps = {
|
||||
@ -155,13 +238,25 @@ export const BadgeDetails = ({ onChange }: BadgeDetailsProps) => {
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
if (attributesState.values.length === 0)
|
||||
attributesState.add({
|
||||
trait_type: '',
|
||||
value: '',
|
||||
})
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
const retrieveFeeRate = async () => {
|
||||
try {
|
||||
if (wallet.client) {
|
||||
const feeRateRaw = await wallet.client.queryContractRaw(
|
||||
BADGE_HUB_ADDRESS,
|
||||
toUtf8(Buffer.from(Buffer.from('fee_rate').toString('hex'), 'hex').toString()),
|
||||
)
|
||||
console.log('Fee Rate Raw: ', feeRateRaw)
|
||||
const feeRate = JSON.parse(new TextDecoder().decode(feeRateRaw as Uint8Array))
|
||||
setMetadataFeeRate(Number(feeRate.metadata))
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error('Error retrieving metadata fee rate.')
|
||||
setMetadataFeeRate(0)
|
||||
console.log('Error retrieving fee rate: ', error)
|
||||
}
|
||||
}
|
||||
void retrieveFeeRate()
|
||||
}, [wallet.client])
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -172,19 +267,35 @@ export const BadgeDetails = ({ onChange }: BadgeDetailsProps) => {
|
||||
<TextInput className="mt-2" {...descriptionState} />
|
||||
<NumberInput className="mt-2" {...maxSupplyState} />
|
||||
<TextInput className="mt-2" {...externalUrlState} />
|
||||
|
||||
<FormControl className="mt-2" htmlId="expiry-date" subtitle="Badge minting expiry date" title="Expiry Date">
|
||||
<InputDateTime minDate={new Date()} onChange={(date) => setTimestamp(date)} value={timestamp} />
|
||||
</FormControl>
|
||||
<div className="mt-2 form-control">
|
||||
<label className="justify-start cursor-pointer label">
|
||||
<span className="mr-4 font-bold">Transferrable</span>
|
||||
<input
|
||||
checked={transferrable}
|
||||
className={`toggle ${transferrable ? `bg-stargaze` : `bg-gray-600`}`}
|
||||
onClick={() => setTransferrable(!transferrable)}
|
||||
type="checkbox"
|
||||
/>
|
||||
</label>
|
||||
<div className="grid grid-cols-2">
|
||||
<div className="mt-2 w-1/3 form-control">
|
||||
<label className="justify-start cursor-pointer label">
|
||||
<span className="mr-4 font-bold">Transferrable</span>
|
||||
<input
|
||||
checked={transferrable}
|
||||
className={`toggle ${transferrable ? `bg-stargaze` : `bg-gray-600`}`}
|
||||
onClick={() => setTransferrable(!transferrable)}
|
||||
type="checkbox"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<Conditional test={managerState.value !== ''}>
|
||||
<Tooltip
|
||||
backgroundColor="bg-stargaze"
|
||||
className="bg-yellow-600"
|
||||
label="This is only an estimate. Be sure to check the final amount before signing the transaction."
|
||||
placement="bottom"
|
||||
>
|
||||
<div className="grid grid-cols-2 ml-12 w-full">
|
||||
<div className="mt-4 font-bold">Fee Estimate:</div>
|
||||
<span className="mt-4">{(metadataSize * Number(metadataFeeRate)) / 1000000} stars</span>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</Conditional>
|
||||
</div>
|
||||
</div>
|
||||
<div className={clsx('ml-10')}>
|
||||
@ -197,6 +308,40 @@ export const BadgeDetails = ({ onChange }: BadgeDetailsProps) => {
|
||||
title="Traits"
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<Tooltip
|
||||
backgroundColor="bg-blue-500"
|
||||
label="A metadata file can be selected to automatically fill in the related fields."
|
||||
placement="bottom"
|
||||
>
|
||||
<div>
|
||||
<label
|
||||
className="block mt-2 mr-1 mb-1 w-full font-bold text-white dark:text-gray-300"
|
||||
htmlFor="assetFile"
|
||||
>
|
||||
Metadata File Selection (optional)
|
||||
</label>
|
||||
<div
|
||||
className={clsx(
|
||||
'flex relative justify-center items-center mt-2 space-y-4 w-full h-32',
|
||||
'rounded border-2 border-white/20 border-dashed',
|
||||
)}
|
||||
>
|
||||
<input
|
||||
accept="application/json"
|
||||
className={clsx(
|
||||
'file:py-2 file:px-4 file:mr-4 file:bg-plumbus-light file:rounded file:border-0 cursor-pointer',
|
||||
'before:absolute before:inset-0 before:hover:bg-white/5 before:transition',
|
||||
)}
|
||||
id="metadataFile"
|
||||
onChange={selectMetadata}
|
||||
ref={metadataFileRef}
|
||||
type="file"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -172,7 +172,7 @@ export const ImageUploadDetails = ({ onChange, mintRule }: ImageUploadDetailsPro
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-3 py-5 pb-8">
|
||||
<div className="p-3 py-5 pb-4">
|
||||
<Conditional test={uploadMethod === 'existing'}>
|
||||
<div className="ml-3 flex-column">
|
||||
<p className="mb-5 ml-5">
|
||||
@ -187,8 +187,17 @@ export const ImageUploadDetails = ({ onChange, mintRule }: ImageUploadDetailsPro
|
||||
</Anchor>{' '}
|
||||
and upload your image manually to get an image URL for your badge.
|
||||
</p>
|
||||
<div>
|
||||
<TextInput {...imageUrlState} className="mt-2 ml-4 w-1/2" />
|
||||
<div className="flex flex-row w-full">
|
||||
<TextInput {...imageUrlState} className="mt-2 ml-6 w-full max-w-2xl" />
|
||||
<Conditional test={imageUrlState.value !== ''}>
|
||||
<div className="mt-2 ml-4 w-1/4 border-2 border-dashed">
|
||||
<img
|
||||
alt="badge-preview"
|
||||
className="w-full"
|
||||
src={imageUrlState.value.replace('IPFS://', 'ipfs://').replace(/,/g, '').replace(/"/g, '').trim()}
|
||||
/>
|
||||
</div>
|
||||
</Conditional>
|
||||
</div>
|
||||
</div>
|
||||
</Conditional>
|
||||
@ -252,31 +261,33 @@ export const ImageUploadDetails = ({ onChange, mintRule }: ImageUploadDetailsPro
|
||||
|
||||
<div className="mt-6">
|
||||
<div className="grid grid-cols-2">
|
||||
<div className="w-full">
|
||||
<div>
|
||||
<label
|
||||
className="block mt-5 mr-1 mb-1 ml-8 w-full font-bold text-white dark:text-gray-300"
|
||||
htmlFor="assetFile"
|
||||
>
|
||||
Image Selection
|
||||
</label>
|
||||
<div
|
||||
className={clsx(
|
||||
'flex relative justify-center items-center mx-8 mt-2 space-y-4 w-full h-32',
|
||||
'rounded border-2 border-white/20 border-dashed',
|
||||
)}
|
||||
>
|
||||
<input
|
||||
accept="image/*"
|
||||
<div>
|
||||
<div className="w-full">
|
||||
<div>
|
||||
<label
|
||||
className="block mt-5 mr-1 mb-1 ml-8 w-full font-bold text-white dark:text-gray-300"
|
||||
htmlFor="assetFile"
|
||||
>
|
||||
Image Selection
|
||||
</label>
|
||||
<div
|
||||
className={clsx(
|
||||
'file:py-2 file:px-4 file:mr-4 file:bg-plumbus-light file:rounded file:border-0 cursor-pointer',
|
||||
'before:absolute before:inset-0 before:hover:bg-white/5 before:transition',
|
||||
'flex relative justify-center items-center mx-8 mt-2 space-y-4 w-full h-32',
|
||||
'rounded border-2 border-white/20 border-dashed',
|
||||
)}
|
||||
id="assetFile"
|
||||
onChange={selectAsset}
|
||||
ref={assetFileRef}
|
||||
type="file"
|
||||
/>
|
||||
>
|
||||
<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:absolute before:inset-0 before:hover:bg-white/5 before:transition',
|
||||
)}
|
||||
id="assetFile"
|
||||
onChange={selectAsset}
|
||||
ref={assetFileRef}
|
||||
type="file"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -151,7 +151,7 @@ export const CollectionActions = ({
|
||||
name: 'royaltyShare',
|
||||
title: 'Share Percentage',
|
||||
subtitle: 'Percentage of royalties to be paid',
|
||||
placeholder: '8%',
|
||||
placeholder: '5%',
|
||||
})
|
||||
|
||||
const showTokenUriField = type === 'mint_token_uri'
|
||||
|
@ -36,7 +36,7 @@ export const RoyaltyDetails = ({ onChange }: RoyaltyDetailsProps) => {
|
||||
name: 'royaltyShare',
|
||||
title: 'Share Percentage',
|
||||
subtitle: 'Percentage of royalties to be paid',
|
||||
placeholder: '8%',
|
||||
placeholder: '5%',
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -73,9 +73,10 @@ export function MetadataAttribute({ id, isLast, onAdd, onChange, onRemove, defau
|
||||
}, [traitTypeState.value, traitValueState.value, id])
|
||||
|
||||
return (
|
||||
<div className="grid relative grid-cols-[1fr_1fr_auto] space-x-2">
|
||||
<div className="grid relative 2xl:grid-cols-[1fr_1fr_auto] 2xl:space-x-2">
|
||||
<TraitTypeInput {...traitTypeState} />
|
||||
<TraitValueInput {...traitValueState} />
|
||||
|
||||
<div className="flex justify-end items-end pb-2 w-8">
|
||||
<button
|
||||
className="flex justify-center items-center p-2 bg-stargaze-80 hover:bg-plumbus-60 rounded-full"
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "stargaze-studio",
|
||||
"version": "0.4.7",
|
||||
"version": "0.4.8",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* eslint-disable eslint-comments/disable-enable-pair */
|
||||
|
||||
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
||||
/* eslint-disable no-nested-ternary */
|
||||
|
||||
@ -22,12 +23,14 @@ import { useInputState } from 'components/forms/FormInput.hooks'
|
||||
import { Tooltip } from 'components/Tooltip'
|
||||
import { useContracts } from 'contexts/contracts'
|
||||
import { useWallet } from 'contexts/wallet'
|
||||
import type { Badge } from 'contracts/badgeHub'
|
||||
import type { DispatchExecuteArgs as BadgeHubDispatchExecuteArgs } from 'contracts/badgeHub/messages/execute'
|
||||
import { dispatchExecute as badgeHubDispatchExecute } from 'contracts/badgeHub/messages/execute'
|
||||
import * as crypto from 'crypto'
|
||||
import { toPng } from 'html-to-image'
|
||||
import type { NextPage } from 'next'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import sizeof from 'object-sizeof'
|
||||
import { QRCodeSVG } from 'qrcode.react'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { toast } from 'react-hot-toast'
|
||||
@ -59,6 +62,7 @@ const BadgeCreationPage: NextPage = () => {
|
||||
const [readyToCreateBadge, setReadyToCreateBadge] = useState(false)
|
||||
const [mintRule, setMintRule] = useState<MintRule>('by_key')
|
||||
const [resolvedMinterAddress, setResolvedMinterAddress] = useState<string>('')
|
||||
const [tempBadge, setTempBadge] = useState<Badge>()
|
||||
|
||||
const [badgeId, setBadgeId] = useState<string | null>(null)
|
||||
const [imageUrl, setImageUrl] = useState<string | null>(null)
|
||||
@ -138,6 +142,37 @@ const BadgeCreationPage: NextPage = () => {
|
||||
void resolveMinterAddress()
|
||||
}, [designatedMinterState.value])
|
||||
|
||||
useEffect(() => {
|
||||
const badge = {
|
||||
manager: badgeDetails?.manager as string,
|
||||
metadata: {
|
||||
name: badgeDetails?.name || undefined,
|
||||
description: badgeDetails?.description || undefined,
|
||||
image: imageUrl || undefined,
|
||||
image_data: badgeDetails?.image_data || undefined,
|
||||
external_url: badgeDetails?.external_url || undefined,
|
||||
attributes: badgeDetails?.attributes || undefined,
|
||||
background_color: badgeDetails?.background_color || undefined,
|
||||
animation_url: badgeDetails?.animation_url || undefined,
|
||||
youtube_url: badgeDetails?.youtube_url || undefined,
|
||||
},
|
||||
transferrable: badgeDetails?.transferrable as boolean,
|
||||
rule:
|
||||
mintRule === 'by_key'
|
||||
? {
|
||||
by_key: keyState.value,
|
||||
}
|
||||
: mintRule === 'by_minter'
|
||||
? {
|
||||
by_minter: resolvedMinterAddress,
|
||||
}
|
||||
: 'by_keys',
|
||||
expiry: badgeDetails?.expiry || undefined,
|
||||
max_supply: badgeDetails?.max_supply || undefined,
|
||||
}
|
||||
setTempBadge(badge)
|
||||
}, [badgeDetails, keyState.value, mintRule, resolvedMinterAddress, imageUrl])
|
||||
|
||||
const createNewBadge = async () => {
|
||||
try {
|
||||
if (!wallet.initialized) throw new Error('Wallet not connected')
|
||||
@ -568,20 +603,27 @@ const BadgeCreationPage: NextPage = () => {
|
||||
mintRule !== 'by_key' ? 'bg-stargaze/5 hover:bg-stargaze/80' : 'hover:bg-white/5',
|
||||
)}
|
||||
>
|
||||
<button
|
||||
className="p-4 w-full h-full text-left bg-transparent"
|
||||
onClick={() => {
|
||||
setMintRule('by_key')
|
||||
setReadyToCreateBadge(false)
|
||||
setBadgeId(null)
|
||||
}}
|
||||
type="button"
|
||||
<Tooltip
|
||||
backgroundColor="bg-blue-500"
|
||||
className="m-0 w-1/3"
|
||||
label="The same single private key can be utilized by multiple users to share badge minting authority. Ideal for projects with multiple administrators."
|
||||
placement="bottom"
|
||||
>
|
||||
<h4 className="font-bold">Mint Rule: By Key</h4>
|
||||
<span className="text-sm text-white/80 line-clamp-2">
|
||||
Badges can be minted more than once with a badge specific message signed by a designated private key.
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
className="p-4 w-full h-full text-left bg-transparent"
|
||||
onClick={() => {
|
||||
setMintRule('by_key')
|
||||
setReadyToCreateBadge(false)
|
||||
setBadgeId(null)
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<h4 className="font-bold">Mint Rule: By Key</h4>
|
||||
<span className="text-sm text-white/80 line-clamp-4">
|
||||
Multiple badges can be minted to different addresses by the owner of a single designated key.
|
||||
</span>
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div
|
||||
className={clsx(
|
||||
@ -591,20 +633,27 @@ const BadgeCreationPage: NextPage = () => {
|
||||
mintRule !== 'by_keys' ? 'bg-stargaze/5 hover:bg-stargaze/80' : 'hover:bg-white/5',
|
||||
)}
|
||||
>
|
||||
<button
|
||||
className="p-4 w-full h-full text-left bg-transparent"
|
||||
onClick={() => {
|
||||
setMintRule('by_keys')
|
||||
setReadyToCreateBadge(false)
|
||||
setBadgeId(null)
|
||||
}}
|
||||
type="button"
|
||||
<Tooltip
|
||||
backgroundColor="bg-blue-500"
|
||||
className="m-0 w-1/3"
|
||||
label="The key pairs are intended to be saved and shared with others. Each user can claim a badge separately using the key pair that they received."
|
||||
placement="bottom"
|
||||
>
|
||||
<h4 className="font-bold">Mint Rule: By Keys</h4>
|
||||
<span className="text-sm text-white/80 line-clamp-2">
|
||||
Similar to the By Key rule, however each designated private key can only be used once to mint a badge.
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
className="p-4 w-full h-full text-left bg-transparent"
|
||||
onClick={() => {
|
||||
setMintRule('by_keys')
|
||||
setReadyToCreateBadge(false)
|
||||
setBadgeId(null)
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<h4 className="font-bold">Mint Rule: By Keys</h4>
|
||||
<span className="text-sm text-white/80 line-clamp-4">
|
||||
Multiple key pairs are generated and designated to be only used once to mint a single badge.
|
||||
</span>
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div
|
||||
className={clsx(
|
||||
@ -614,20 +663,28 @@ const BadgeCreationPage: NextPage = () => {
|
||||
mintRule !== 'by_minter' ? 'bg-stargaze/5 hover:bg-stargaze/80' : 'hover:bg-white/5',
|
||||
)}
|
||||
>
|
||||
<button
|
||||
className="p-4 w-full h-full text-left bg-transparent"
|
||||
onClick={() => {
|
||||
setMintRule('by_minter')
|
||||
setReadyToCreateBadge(false)
|
||||
setBadgeId(null)
|
||||
}}
|
||||
type="button"
|
||||
<Tooltip
|
||||
backgroundColor="bg-blue-500"
|
||||
className="m-0 w-1/3"
|
||||
label="The most basic approach. However, having just one authorized address for minting badges might limit your ability to delegate that responsibility."
|
||||
placement="bottom"
|
||||
>
|
||||
<h4 className="font-bold">Mint Rule: By Minter</h4>
|
||||
<span className="text-sm text-white/80 line-clamp-2">
|
||||
Badges can be minted by a designated minter account.
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
className="p-4 w-full h-full text-left bg-transparent"
|
||||
onClick={() => {
|
||||
setMintRule('by_minter')
|
||||
setReadyToCreateBadge(false)
|
||||
setBadgeId(null)
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<h4 className="font-bold">Mint Rule: By Minter</h4>
|
||||
<span className="text-sm text-white/80 line-clamp-4">
|
||||
No key designation. Multiple badges can be minted to different addresses by a pre-determined minter
|
||||
address.
|
||||
</span>
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -671,6 +728,11 @@ const BadgeCreationPage: NextPage = () => {
|
||||
|
||||
<div className="flex justify-between py-3 px-8 rounded border-2 border-white/20 grid-col-2">
|
||||
<BadgeDetails
|
||||
metadataSize={
|
||||
tempBadge?.metadata.image === undefined
|
||||
? Number(sizeof(tempBadge)) + Number(sizeof(tempBadge?.metadata.attributes)) + 150
|
||||
: Number(sizeof(tempBadge)) + Number(sizeof(tempBadge.metadata.attributes))
|
||||
}
|
||||
mintRule={mintRule}
|
||||
onChange={setBadgeDetails}
|
||||
uploadMethod={imageUploadDetails?.uploadMethod ? imageUploadDetails.uploadMethod : 'new'}
|
||||
|
@ -49,12 +49,12 @@ const BadgeList: NextPage = () => {
|
||||
{myBadges.map((badge: any, index: any) => {
|
||||
return (
|
||||
<tr key={index}>
|
||||
<td className="w-[55%] bg-black">
|
||||
<td className="w-[35%] bg-black">
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="avatar">
|
||||
<div className="w-28 h-28 mask mask-squircle">
|
||||
<img
|
||||
alt="Cover"
|
||||
alt="badge-preview"
|
||||
src={
|
||||
(badge?.image as string).startsWith('ipfs')
|
||||
? `https://ipfs-gw.stargaze-apis.com/ipfs/${(badge?.image as string).substring(7)}`
|
||||
@ -64,13 +64,15 @@ const BadgeList: NextPage = () => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="pl-2">
|
||||
<p className="overflow-auto max-w-xs font-bold no-scrollbar ">{badge.name}</p>
|
||||
<p className="overflow-auto max-w-xs font-bold no-scrollbar ">
|
||||
{badge.name ? badge.name : 'No name provided.'}
|
||||
</p>
|
||||
<p className="max-w-xs text-sm truncate opacity-50">Badge ID: {badge.tokenId}</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="overflow-auto w-[35%] max-w-xl bg-black no-scrollbar">
|
||||
{badge.description}
|
||||
<td className="overflow-auto w-[55%] max-w-xl bg-black no-scrollbar">
|
||||
{badge.description ? badge.description : 'No description provided.'}
|
||||
{/* <br /> */}
|
||||
{/* <span className="badge badge-ghost badge-sm"></span> */}
|
||||
</td>
|
||||
|
@ -105,7 +105,7 @@ const BaseMinterInstantiatePage: NextPage = () => {
|
||||
name: 'royaltyShare',
|
||||
title: 'Share Percentage',
|
||||
subtitle: 'Percentage of royalties to be paid',
|
||||
placeholder: '8%',
|
||||
placeholder: '5%',
|
||||
})
|
||||
|
||||
const { data, isLoading, mutate } = useMutation(
|
||||
|
@ -103,7 +103,7 @@ const VendingMinterInstantiatePage: NextPage = () => {
|
||||
name: 'royaltyShare',
|
||||
title: 'Share Percentage',
|
||||
subtitle: 'Percentage of royalties to be paid',
|
||||
placeholder: '8%',
|
||||
placeholder: '5%',
|
||||
})
|
||||
|
||||
const unitPriceState = useNumberInputState({
|
||||
|
Loading…
Reference in New Issue
Block a user