From c4f486f1f0d489d8a5f57e55335a954300e422cb Mon Sep 17 00:00:00 2001 From: Serkan Reis Date: Sun, 10 Sep 2023 13:42:37 +0300 Subject: [PATCH] Update collection creation logic for OE/off-chain metadata --- .../OffChainMetadataUploadDetails.tsx | 69 ++++++++++++++++++- .../openEdition/OpenEditionMinterCreator.tsx | 30 +++++--- 2 files changed, 89 insertions(+), 10 deletions(-) diff --git a/components/openEdition/OffChainMetadataUploadDetails.tsx b/components/openEdition/OffChainMetadataUploadDetails.tsx index e317a8e..df1f81c 100644 --- a/components/openEdition/OffChainMetadataUploadDetails.tsx +++ b/components/openEdition/OffChainMetadataUploadDetails.tsx @@ -18,6 +18,8 @@ import type { ChangeEvent } from 'react' import { useEffect, useRef, useState } from 'react' import { toast } from 'react-hot-toast' import type { UploadServiceType } from 'services/upload' +import type { AssetType } from 'utils/getAssetType' +import { getAssetType } from 'utils/getAssetType' import { uid } from 'utils/random' import { naturalCompare } from 'utils/sort' @@ -34,6 +36,8 @@ interface OffChainMetadataUploadDetailsProps { export interface OffChainMetadataUploadDetailsDataProps { assetFiles: File[] metadataFiles: File[] + thumbnailFile?: File + isThumbnailCompatible?: boolean uploadService: UploadServiceType nftStorageApiKey?: string pinataApiKey?: string @@ -52,16 +56,20 @@ export const OffChainMetadataUploadDetails = ({ }: OffChainMetadataUploadDetailsProps) => { const [assetFilesArray, setAssetFilesArray] = useState([]) const [metadataFilesArray, setMetadataFilesArray] = useState([]) + const [thumbnailFile, setThumbnailFile] = useState() + const [isThumbnailCompatible, setIsThumbnailCompatible] = useState(false) const [uploadMethod, setUploadMethod] = useState('new') const [uploadService, setUploadService] = useState('nft-storage') const [metadataFileArrayIndex, setMetadataFileArrayIndex] = useState(0) const [refreshMetadata, setRefreshMetadata] = useState(false) const [exportedMetadata, setExportedMetadata] = useState(undefined) - const [openEditionMinterMetadataFile, setOpenEditionMinterMetadataFile] = useState() + const thumbnailCompatibleAssetTypes: AssetType[] = ['video', 'audio', 'html'] + const assetFilesRef = useRef(null) const metadataFilesRef = useRef(null) + const thumbnailFilesRef = useRef(null) const nftStorageApiKeyState = useInputState({ id: 'nft-storage-api-key', @@ -104,7 +112,12 @@ export const OffChainMetadataUploadDetails = ({ const selectAssets = (event: ChangeEvent) => { setAssetFilesArray([]) setMetadataFilesArray([]) + setThumbnailFile(undefined) + setIsThumbnailCompatible(false) if (event.target.files === null) return + if (thumbnailCompatibleAssetTypes.includes(getAssetType(event.target.files[0].name))) { + setIsThumbnailCompatible(true) + } let loadedFileCount = 0 const files: File[] = [] let reader: FileReader @@ -166,6 +179,26 @@ export const OffChainMetadataUploadDetails = ({ } } + const selectThumbnail = (event: ChangeEvent) => { + setThumbnailFile(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: 'image/*' }) + } + // 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.') + setThumbnailFile(selectedFile) + } + } + const updateMetadataFileIndex = (index: number) => { setMetadataFileArrayIndex(index) setRefreshMetadata((prev) => !prev) @@ -188,6 +221,8 @@ export const OffChainMetadataUploadDetails = ({ const data: OffChainMetadataUploadDetailsDataProps = { assetFiles: assetFilesArray, metadataFiles: metadataFilesArray, + thumbnailFile, + isThumbnailCompatible, uploadService, nftStorageApiKey: nftStorageApiKeyState.value, pinataApiKey: pinataApiKeyState.value, @@ -218,6 +253,8 @@ export const OffChainMetadataUploadDetails = ({ }, [ assetFilesArray, metadataFilesArray, + thumbnailFile, + isThumbnailCompatible, uploadService, nftStorageApiKeyState.value, pinataApiKeyState.value, @@ -235,6 +272,8 @@ export const OffChainMetadataUploadDetails = ({ setMetadataFilesArray([]) if (assetFilesRef.current) assetFilesRef.current.value = '' setAssetFilesArray([]) + setThumbnailFile(undefined) + setIsThumbnailCompatible(false) if (!importedOffChainMetadataUploadDetails) { tokenUriState.onChange('') coverImageUrlState.onChange('') @@ -423,6 +462,34 @@ export const OffChainMetadataUploadDetails = ({ /> + +
+ +
+ +
+
+
{assetFilesArray.length > 0 && (
diff --git a/components/openEdition/OpenEditionMinterCreator.tsx b/components/openEdition/OpenEditionMinterCreator.tsx index 2375c07..a930af8 100644 --- a/components/openEdition/OpenEditionMinterCreator.tsx +++ b/components/openEdition/OpenEditionMinterCreator.tsx @@ -29,7 +29,6 @@ import { SG721_OPEN_EDITION_UPDATABLE_CODE_ID, } from 'utils/constants' import type { AssetType } from 'utils/getAssetType' -import { getAssetType } from 'utils/getAssetType' import { isValidAddress } from 'utils/isValidAddress' import { checkTokenUri } from 'utils/isValidTokenUri' import { uid } from 'utils/random' @@ -488,22 +487,35 @@ export const OpenEditionMinterCreator = ({ offChainMetadataUploadDetails.pinataApiKey as string, offChainMetadataUploadDetails.pinataSecretKey as string, ) - .then((assetUri: string) => { + .then(async (assetUri: string) => { + let thumbnailUri: string | undefined + if (offChainMetadataUploadDetails.isThumbnailCompatible && offChainMetadataUploadDetails.thumbnailFile) + thumbnailUri = await upload( + [offChainMetadataUploadDetails.thumbnailFile] as File[], + offChainMetadataUploadDetails.uploadService, + 'thumbnail', + offChainMetadataUploadDetails.nftStorageApiKey as string, + offChainMetadataUploadDetails.pinataApiKey as string, + offChainMetadataUploadDetails.pinataSecretKey as string, + ) + const thumbnailUriWithBase = thumbnailUri + ? `ipfs://${thumbnailUri}/${(offChainMetadataUploadDetails.thumbnailFile as File).name}` + : undefined + const fileArray: File[] = [] const reader: FileReader = new FileReader() reader.onload = (e) => { const data: any = JSON.parse(e.target?.result as string) - if ( - getAssetType(offChainMetadataUploadDetails.assetFiles[0].name) === 'audio' || - getAssetType(offChainMetadataUploadDetails.assetFiles[0].name) === 'video' || - getAssetType(offChainMetadataUploadDetails.assetFiles[0].name) === 'html' - ) { + if (offChainMetadataUploadDetails.isThumbnailCompatible) { data.animation_url = `ipfs://${assetUri}/${offChainMetadataUploadDetails.assetFiles[0].name}` } - if (getAssetType(offChainMetadataUploadDetails.assetFiles[0].name) !== 'html') - data.image = `ipfs://${assetUri}/${offChainMetadataUploadDetails.assetFiles[0].name}` + + data.image = + offChainMetadataUploadDetails.isThumbnailCompatible && offChainMetadataUploadDetails.thumbnailFile + ? thumbnailUriWithBase + : `ipfs://${assetUri}/${offChainMetadataUploadDetails.assetFiles[0].name}` if (data.description) { // eslint-disable-next-line @typescript-eslint/no-unsafe-call