From 5aa7676932bba4cbb0dc2698a1a2bf154d58f9a3 Mon Sep 17 00:00:00 2001 From: Oleksandr Khlopiachyi Date: Thu, 23 Jan 2025 19:17:32 +0200 Subject: [PATCH 1/3] feat: add ability to update nft data --- src/assets/GeneralIcon/index.tsx | 19 +++ .../ConfirmEditNFTDataModal/index.tsx | 143 ++++++++++++++++++ src/components/EditNFTModal/index.tsx | 138 +++++++++++++++++ src/components/FileUpload/index.tsx | 2 +- src/components/Layout/index.tsx | 4 + .../ViewNFTCollectionModal/index.tsx | 31 +++- src/features/general/generalSlice.ts | 12 ++ src/features/nft/nftSlice.ts | 6 + src/shared/types.ts | 1 + 9 files changed, 353 insertions(+), 3 deletions(-) create mode 100644 src/components/ConfirmEditNFTDataModal/index.tsx create mode 100644 src/components/EditNFTModal/index.tsx diff --git a/src/assets/GeneralIcon/index.tsx b/src/assets/GeneralIcon/index.tsx index 50dd230..8abce52 100644 --- a/src/assets/GeneralIcon/index.tsx +++ b/src/assets/GeneralIcon/index.tsx @@ -538,6 +538,25 @@ export const GeneralIcon: FC = ({ ); + case GeneralIconType.Edit: + return ( + + + + + ); default: return null; } diff --git a/src/components/ConfirmEditNFTDataModal/index.tsx b/src/components/ConfirmEditNFTDataModal/index.tsx new file mode 100644 index 0000000..4f0fb12 --- /dev/null +++ b/src/components/ConfirmEditNFTDataModal/index.tsx @@ -0,0 +1,143 @@ +import { AlertType, ButtonType, ConfirmationModalImageType } from "@/shared/types"; +import { ConfirmationModal } from "../ConfirmationModal"; +import { ConfirmationModalImage } from "@/assets/ConfirmationModalImage"; +import { Button } from "../Button"; +import { useAppDispatch, useAppSelector } from "@/store/hooks"; +import { useCallback, useMemo, useState } from "react"; +import { setIsConfirmEditNFTModalOpen, setIsConfirmNFTDeWhitelistModalOpen, setIsEditNFTModalOpen, setIsTxExecuting, setIsWhitelistNFTModalOpen } from "@/features/general/generalSlice"; +import { useEstimateTxGasFee } from "@/hooks/useEstimateTxGasFee"; +import { convertStringToAny, NFT } from "coreum-js"; +import { dispatchAlert } from "@/features/alerts/alertsSlice"; +import { NFTItem } from "../NFTItem"; +import { setSelectedNFTClass, setSelectedNFTSend, setShouldRefetchNFTItems } from "@/features/nft/nftSlice"; + +export const ConfirmEditNFTDataModal = () => { + const isConfirmEditNFTModalOpen = useAppSelector(state => state.general.isConfirmEditNFTModalOpen); + const selectedNFTSend = useAppSelector(state => state.nfts.selectedNFTSend); + const selectedNFTClass = useAppSelector(state => state.nfts.selectedNFTClass); + const account = useAppSelector(state => state.general.account); + const editNFTData = useAppSelector(state => state.nfts.editNFTData); + const isTxExecuting = useAppSelector(state => state.general.isTxExecuting); + + const [isTxSuccessful, setIsTxSuccessful] = useState(false); + + const dispatch = useAppDispatch(); + const { signingClient, getTxFee } = useEstimateTxGasFee(); + + const handleClose = useCallback(() => { + dispatch(setIsConfirmEditNFTModalOpen(false)); + dispatch(setSelectedNFTClass(null)); + dispatch(setSelectedNFTSend(null)); + setIsTxSuccessful(false); + }, []); + + const handleBackClick = useCallback(() => { + dispatch(setIsConfirmEditNFTModalOpen(false)); + dispatch(setIsEditNFTModalOpen(true)); + }, []); + + const handleConfirm = useCallback(async () => { + dispatch(setIsTxExecuting(true)); + + try { + // const nftDataValue = convertStringToAny(editNFTData); + // const editNFTDataMSg = NFT.UpdateData({ + // sender: account, + // classId: selectedNFTClass?.id || '', + // id: selectedNFTSend?.id || '', + // data:nftDataValue, + // }); + // const txFee = await getTxFee([editNFTDataMSg]); + // await signingClient?.signAndBroadcast(account, [editNFTDataMSg], txFee ? txFee.fee : 'auto'); + setIsTxSuccessful(true); + dispatch(setShouldRefetchNFTItems(true)); + } catch (error) { + dispatch(dispatchAlert({ + type: AlertType.Error, + title: 'NFT Whitelist Failed', + message: (error as { message: string}).message, + })); + } + + dispatch(setIsTxExecuting(false)); + }, [account, getTxFee, selectedNFTSend, signingClient, selectedNFTClass]); + + const renderContent = useMemo(() => { + if (isTxSuccessful) { + return ( +
+
+ Successfully Updated Data for NFT +
+
+
+ +
+
+
+
+
+ ); + } + + return ( +
+
+
+ Update Data for NFT +
+
+ This action will be applied and affect this targeted holder. Would you like to proceed? +
+
+
+
+
+ ); + }, [ + handleBackClick, + handleClose, + handleConfirm, + isTxExecuting, + isTxSuccessful, + selectedNFTClass?.name, + selectedNFTSend, + ]); + + return ( + + + {renderContent} + + ); +}; diff --git a/src/components/EditNFTModal/index.tsx b/src/components/EditNFTModal/index.tsx new file mode 100644 index 0000000..2bdde0a --- /dev/null +++ b/src/components/EditNFTModal/index.tsx @@ -0,0 +1,138 @@ +import { setIsConfirmEditNFTModalOpen, setIsConfirmNFTDeWhitelistModalOpen, setIsDeWhitelistNFTModalOpen, setIsEditNFTModalOpen, setIsNFTCollectionViewModalOpen } from "@/features/general/generalSlice"; +import { useAppDispatch, useAppSelector } from "@/store/hooks"; +import { useCallback, useMemo, useState } from "react"; +import { Modal } from "../Modal"; +import { Button } from "../Button"; +import { ButtonType, ChainInfo } from "@/shared/types"; +import { NFTItem } from "../NFTItem"; +import { Input } from "../Input"; +import { pasteValueFromClipboard } from "@/helpers/pasteValueFromClipboard"; +import { validateAddress } from "@/helpers/validateAddress"; +import { setDeWhitelistAccount, setEditNFTData } from "@/features/nft/nftSlice"; +import { FileUpload } from "../FileUpload"; +import { TextArea } from "../TextArea"; +import classNames from "classnames"; + +export const EditNFTModal = () => { + const isEditNFTModalOpen = useAppSelector(state => state.general.isEditNFTModalOpen); + const selectedNFTSend = useAppSelector(state => state.nfts.selectedNFTSend); + const selectedNFTClass = useAppSelector(state => state.nfts.selectedNFTClass); + const chains = useAppSelector(state => state.chains.list); + + const [data, setData] = useState(''); + const [fileContent, setFileContent] = useState(''); + + const coreumChain = useMemo(() => { + return chains.find((chain: ChainInfo) => chain.pretty_name.toLowerCase() === 'coreum'); + }, [chains]); + + const dispatch = useAppDispatch(); + + const handleCloseModal = useCallback(() => { + dispatch(setIsEditNFTModalOpen(false)); + setData(''); + }, []); + + const handleOnClickBackButton = useCallback(() => { + dispatch(setIsEditNFTModalOpen(false)); + dispatch(setIsNFTCollectionViewModalOpen(true)); + setData(''); + }, []); + + const handleConfirmEditNFTToken = useCallback(() => { + dispatch(setIsConfirmEditNFTModalOpen(true)); + dispatch(setIsEditNFTModalOpen(false)); + dispatch(setEditNFTData(fileContent.length ? fileContent : data)); + setData(''); + }, [data]); + + const isFormValid = useMemo(() => { + if (selectedNFTSend && (!!data.length || !!fileContent.length)) { + return true; + } + + return false; + }, [selectedNFTSend, data.length, fileContent.length]); + + return ( + +
+
+
+

+ Name +

+
+

+ {selectedNFTSend?.name || ''} +

+
+
+
+

+ URI +

+
+

+ {`${selectedNFTSend?.uri || ''}${selectedNFTSend?.uri_hash || ''}`} +

+
+
+
+
+ +
+ +
+
+ Or +
+
+