/* eslint-disable eslint-comments/disable-enable-pair */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import clsx from 'clsx' import { Conditional } from 'components/Conditional' import { useInputState } from 'components/forms/FormInput.hooks' import { useMetadataAttributesState } from 'components/forms/MetadataAttributes.hooks' import type { Trait } from 'contracts/badgeHub' import type { ChangeEvent } from 'react' import { useEffect, useRef, useState } from 'react' import { toast } from 'react-hot-toast' import { TextInput } from '../forms/FormInput' import { MetadataAttributes } from '../forms/MetadataAttributes' import { Tooltip } from '../Tooltip' import type { UploadMethod } from './ImageUploadDetails' interface OnChainMetadataInputDetailsProps { onChange: (data: OnChainMetadataInputDetailsDataProps) => void uploadMethod: UploadMethod | undefined importedOnChainMetadataInputDetails?: OnChainMetadataInputDetailsDataProps } export interface OnChainMetadataInputDetailsDataProps { name?: string description?: string attributes?: Trait[] image_data?: string external_url?: string background_color?: string animation_url?: string youtube_url?: string } export const OnChainMetadataInputDetails = ({ onChange, uploadMethod, importedOnChainMetadataInputDetails, }: OnChainMetadataInputDetailsProps) => { const [timestamp, setTimestamp] = useState(undefined) const [metadataFile, setMetadataFile] = useState() const [metadataFeeRate, setMetadataFeeRate] = useState(0) const metadataFileRef = useRef(null) const nameState = useInputState({ id: 'name', name: 'name', title: 'Name', placeholder: 'My Awesome Collection', }) const descriptionState = useInputState({ id: 'description', name: 'description', title: 'Description', placeholder: 'My Awesome Collection Description', }) const imageDataState = useInputState({ id: 'metadata-image-data', name: 'metadata-image-data', title: 'Image Data', subtitle: 'Raw SVG image data', }) const externalUrlState = useInputState({ id: 'metadata-external-url', name: 'metadata-external-url', title: 'External URL', subtitle: 'External URL for the token', placeholder: 'https://', }) const attributesState = useMetadataAttributesState() const animationUrlState = useInputState({ id: 'metadata-animation-url', name: 'metadata-animation-url', title: 'Animation URL', subtitle: 'Animation URL for the token', placeholder: 'https://', }) const youtubeUrlState = useInputState({ id: 'metadata-youtube-url', name: 'metadata-youtube-url', title: 'YouTube URL', subtitle: 'YouTube URL for the token', placeholder: 'https://', }) 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 : '') imageDataState.onChange(parsedMetadata.image_data ? parsedMetadata.image_data : '') } else { attributesState.reset() nameState.onChange('') descriptionState.onChange('') externalUrlState.onChange('') youtubeUrlState.onChange('') animationUrlState.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) => { 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.replaceAll('#', ''), { 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: OnChainMetadataInputDetailsDataProps = { name: nameState.value || undefined, description: descriptionState.value || undefined, attributes: attributesState.values[0]?.trait_type && attributesState.values[0]?.value ? attributesState.values .map((attr) => ({ trait_type: attr.trait_type, value: attr.value, })) .filter((attr) => attr.trait_type && attr.value) : undefined, image_data: imageDataState.value || undefined, external_url: externalUrlState.value || undefined, animation_url: animationUrlState.value.trim() || undefined, youtube_url: youtubeUrlState.value || undefined, } onChange(data) // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { toast.error(error.message, { style: { maxWidth: 'none' } }) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [ nameState.value, descriptionState.value, timestamp, imageDataState.value, externalUrlState.value, attributesState.values, animationUrlState.value, youtubeUrlState.value, ]) useEffect(() => { if (importedOnChainMetadataInputDetails) { nameState.onChange(importedOnChainMetadataInputDetails.name || '') descriptionState.onChange(importedOnChainMetadataInputDetails.description || '') externalUrlState.onChange(importedOnChainMetadataInputDetails.external_url || '') youtubeUrlState.onChange(importedOnChainMetadataInputDetails.youtube_url || '') animationUrlState.onChange(importedOnChainMetadataInputDetails.animation_url || '') imageDataState.onChange(importedOnChainMetadataInputDetails.image_data || '') if (importedOnChainMetadataInputDetails.attributes) { attributesState.reset() importedOnChainMetadataInputDetails.attributes.forEach((attr) => { attributesState.add({ trait_type: attr.trait_type, value: attr.value, }) }) } } }, [importedOnChainMetadataInputDetails]) return (
NFT Metadata
) }