From 015ec869cd9250afbf167454962cf467cf0ef6f5 Mon Sep 17 00:00:00 2001 From: name-user1 Date: Thu, 22 Sep 2022 16:23:16 +0300 Subject: [PATCH] Single collection actions page --- components/collections/actions/Action.tsx | 195 ++++++++++++++++++ components/collections/actions/Combobox.tsx | 2 +- components/collections/queries/Combobox.tsx | 2 +- components/collections/queries/Queries.tsx | 99 +++++++++ pages/collections/actions.tsx | 215 +++++--------------- 5 files changed, 348 insertions(+), 165 deletions(-) create mode 100644 components/collections/actions/Action.tsx create mode 100644 components/collections/queries/Queries.tsx diff --git a/components/collections/actions/Action.tsx b/components/collections/actions/Action.tsx new file mode 100644 index 0000000..0328924 --- /dev/null +++ b/components/collections/actions/Action.tsx @@ -0,0 +1,195 @@ +import { Button } from 'components/Button' +import type { DispatchExecuteArgs } from 'components/collections/actions/actions' +import { dispatchExecute, isEitherType, previewExecutePayload } from 'components/collections/actions/actions' +import { ActionsCombobox } from 'components/collections/actions/Combobox' +import { useActionsComboboxState } from 'components/collections/actions/Combobox.hooks' +import { Conditional } from 'components/Conditional' +import { FormControl } from 'components/FormControl' +import { FormGroup } from 'components/FormGroup' +import { AddressInput, NumberInput } from 'components/forms/FormInput' +import { useInputState, useNumberInputState } from 'components/forms/FormInput.hooks' +import { InputDateTime } from 'components/InputDateTime' +import { JsonPreview } from 'components/JsonPreview' +import { TransactionHash } from 'components/TransactionHash' +import { WhitelistUpload } from 'components/WhitelistUpload' +import { useContracts } from 'contexts/contracts' +import { useWallet } from 'contexts/wallet' +import type { NextPage } from 'next' +import type { FormEvent } from 'react' +import { useMemo, useState } from 'react' +import { toast } from 'react-hot-toast' +import { FaArrowRight } from 'react-icons/fa' +import { useMutation } from 'react-query' + +import { TextInput } from '../../forms/FormInput' + +export const CollectionActions: NextPage = () => { + const { minter: minterContract, sg721: sg721Contract } = useContracts() + const wallet = useWallet() + const [lastTx, setLastTx] = useState('') + + const [timestamp, setTimestamp] = useState(undefined) + const [airdropArray, setAirdropArray] = useState([]) + + const actionComboboxState = useActionsComboboxState() + const type = actionComboboxState.value?.id + + const sg721ContractState = useInputState({ + id: 'sg721-contract-address', + name: 'sg721-contract-address', + title: 'Sg721 Address', + subtitle: 'Address of the Sg721 contract', + }) + + const minterContractState = useInputState({ + id: 'minter-contract-address', + name: 'minter-contract-address', + title: 'Minter Address', + subtitle: 'Address of the Minter contract', + }) + + const limitState = useNumberInputState({ + id: 'per-address-limi', + name: 'perAddressLimit', + title: 'Per Address Limit', + subtitle: 'Enter the per address limit', + }) + + const tokenIdState = useNumberInputState({ + id: 'token-id', + name: 'tokenId', + title: 'Token ID', + subtitle: 'Enter the token ID', + }) + + const batchNumberState = useNumberInputState({ + id: 'batch-number', + name: 'batchNumber', + title: 'Number of Tokens', + subtitle: 'Enter the number of tokens to mint', + }) + + const tokenIdListState = useInputState({ + id: 'token-id-list', + name: 'tokenIdList', + title: 'List of token IDs', + subtitle: + 'Specify individual token IDs separated by commas (e.g., 2, 4, 8) or a range of IDs separated by a colon (e.g., 8:13)', + }) + + const recipientState = useInputState({ + id: 'recipient-address', + name: 'recipient', + title: 'Recipient Address', + subtitle: 'Address of the recipient', + }) + + const whitelistState = useInputState({ + id: 'whitelist-address', + name: 'whitelistAddress', + title: 'Whitelist Address', + subtitle: 'Address of the whitelist contract', + }) + + const showWhitelistField = type === 'set_whitelist' + const showDateField = type === 'update_start_time' + const showLimitField = type === 'update_per_address_limit' + const showTokenIdField = isEitherType(type, ['transfer', 'mint_for', 'burn']) + const showNumberOfTokensField = type === 'batch_mint' + const showTokenIdListField = isEitherType(type, ['batch_burn', 'batch_transfer']) + const showRecipientField = isEitherType(type, ['transfer', 'mint_to', 'mint_for', 'batch_mint', 'batch_transfer']) + const showAirdropFileField = type === 'airdrop' + + const minterMessages = useMemo( + () => minterContract?.use(minterContractState.value), + [minterContract, minterContractState.value], + ) + const sg721Messages = useMemo( + () => sg721Contract?.use(sg721ContractState.value), + [sg721Contract, sg721ContractState.value], + ) + + const payload: DispatchExecuteArgs = { + whitelist: whitelistState.value, + startTime: timestamp ? (timestamp.getTime() * 1_000_000).toString() : '', + limit: limitState.value, + minterContract: minterContractState.value, + sg721Contract: sg721ContractState.value, + tokenId: tokenIdState.value, + tokenIds: tokenIdListState.value, + batchNumber: batchNumberState.value, + minterMessages, + sg721Messages, + recipient: recipientState.value, + recipients: airdropArray, + txSigner: wallet.address, + type, + } + const { isLoading, mutate } = useMutation( + async (event: FormEvent) => { + event.preventDefault() + if (!type) { + throw new Error('Please select an action!') + } + if (minterContractState.value === '' && sg721ContractState.value === '') { + throw new Error('Please enter minter and sg721 contract addresses!') + } + const txHash = await toast.promise(dispatchExecute(payload), { + error: `${type.charAt(0).toUpperCase() + type.slice(1)} execute failed!`, + loading: 'Executing message...', + success: (tx) => `Transaction ${tx} success!`, + }) + if (txHash) { + setLastTx(txHash) + } + }, + { + onError: (error) => { + toast.error(String(error)) + }, + }, + ) + + const airdropFileOnChange = (data: string[]) => { + setAirdropArray(data) + } + + return ( +
+
+
+ + {showRecipientField && } + {showWhitelistField && } + {showLimitField && } + {showTokenIdField && } + {showTokenIdListField && } + {showNumberOfTokensField && } + {showAirdropFileField && ( + + + + )} + + + setTimestamp(date)} value={timestamp} /> + + +
+
+
+ + + + +
+ + + +
+
+
+ ) +} diff --git a/components/collections/actions/Combobox.tsx b/components/collections/actions/Combobox.tsx index 2f59969..2e347c6 100644 --- a/components/collections/actions/Combobox.tsx +++ b/components/collections/actions/Combobox.tsx @@ -26,7 +26,7 @@ export const ActionsCombobox = ({ value, onChange }: ActionsComboboxProps) => { labelAs={Combobox.Label} onChange={onChange} subtitle="Collection actions" - title="Action" + title="" value={value} >
diff --git a/components/collections/queries/Combobox.tsx b/components/collections/queries/Combobox.tsx index ca65511..62485eb 100644 --- a/components/collections/queries/Combobox.tsx +++ b/components/collections/queries/Combobox.tsx @@ -25,7 +25,7 @@ export const QueryCombobox = ({ value, onChange }: QueryComboboxProps) => { labelAs={Combobox.Label} onChange={onChange} subtitle="Collection queries" - title="Query" + title="" value={value} >
diff --git a/components/collections/queries/Queries.tsx b/components/collections/queries/Queries.tsx new file mode 100644 index 0000000..3af7006 --- /dev/null +++ b/components/collections/queries/Queries.tsx @@ -0,0 +1,99 @@ +import { QueryCombobox } from 'components/collections/queries/Combobox' +import { useQueryComboboxState } from 'components/collections/queries/Combobox.hooks' +import { dispatchQuery } from 'components/collections/queries/query' +import { FormControl } from 'components/FormControl' +import { AddressInput, TextInput } from 'components/forms/FormInput' +import { useInputState } from 'components/forms/FormInput.hooks' +import { JsonPreview } from 'components/JsonPreview' +import { useContracts } from 'contexts/contracts' +import type { NextPage } from 'next' +import { useMemo } from 'react' +import { toast } from 'react-hot-toast' +import { useQuery } from 'react-query' + +export const CollectionQueries: NextPage = () => { + const { minter: minterContract, sg721: sg721Contract } = useContracts() + + const comboboxState = useQueryComboboxState() + const type = comboboxState.value?.id + + const sg721ContractState = useInputState({ + id: 'sg721-contract-address', + name: 'sg721-contract-address', + title: 'Sg721 Address', + subtitle: 'Address of the Sg721 contract', + }) + const sg721ContractAddress = sg721ContractState.value + + const minterContractState = useInputState({ + id: 'minter-contract-address', + name: 'minter-contract-address', + title: 'Minter Address', + subtitle: 'Address of the Minter contract', + }) + const minterContractAddress = minterContractState.value + + const tokenIdState = useInputState({ + id: 'token-id', + name: 'tokenId', + title: 'Token ID', + subtitle: 'Enter the token ID', + placeholder: '1', + }) + const tokenId = tokenIdState.value + + const addressState = useInputState({ + id: 'address', + name: 'address', + title: 'User Address', + subtitle: 'Address of the user', + }) + const address = addressState.value + + const showTokenIdField = type === 'token_info' + const showAddressField = type === 'tokens_minted_to_user' + + const minterMessages = useMemo( + () => minterContract?.use(minterContractAddress), + [minterContract, minterContractAddress], + ) + const sg721Messages = useMemo(() => sg721Contract?.use(sg721ContractAddress), [sg721Contract, sg721ContractAddress]) + + const { data: response } = useQuery( + [sg721Messages, minterMessages, type, tokenId, address] as const, + async ({ queryKey }) => { + const [_sg721Messages, _minterMessages, _type, _tokenId, _address] = queryKey + const result = await dispatchQuery({ + tokenId: _tokenId, + minterMessages: _minterMessages, + sg721Messages: _sg721Messages, + address: _address, + type: _type, + }) + return result + }, + { + placeholderData: null, + onError: (error: any) => { + toast.error(error.message) + }, + enabled: Boolean(sg721ContractAddress && minterContractAddress && type), + retry: false, + }, + ) + + return ( +
+
+ + {showAddressField && } + {showTokenIdField && } +
+
+ + + +
+
+ ) +} diff --git a/pages/collections/actions.tsx b/pages/collections/actions.tsx index e24bbdb..1b5f658 100644 --- a/pages/collections/actions.tsx +++ b/pages/collections/actions.tsx @@ -1,42 +1,21 @@ -import { Button } from 'components/Button' -import type { DispatchExecuteArgs } from 'components/collections/actions/actions' -import { dispatchExecute, isEitherType, previewExecutePayload } from 'components/collections/actions/actions' -import { ActionsCombobox } from 'components/collections/actions/Combobox' -import { useActionsComboboxState } from 'components/collections/actions/Combobox.hooks' -import { Conditional } from 'components/Conditional' +import { CollectionActions } from 'components/collections/actions/Action' +import { CollectionQueries } from 'components/collections/queries/Queries' import { ContractPageHeader } from 'components/ContractPageHeader' -import { FormControl } from 'components/FormControl' -import { FormGroup } from 'components/FormGroup' -import { AddressInput, NumberInput } from 'components/forms/FormInput' -import { useInputState, useNumberInputState } from 'components/forms/FormInput.hooks' -import { InputDateTime } from 'components/InputDateTime' -import { JsonPreview } from 'components/JsonPreview' -import { TransactionHash } from 'components/TransactionHash' -import { WhitelistUpload } from 'components/WhitelistUpload' +import { AddressInput } from 'components/forms/FormInput' +import { useInputState } from 'components/forms/FormInput.hooks' import { useContracts } from 'contexts/contracts' import { useWallet } from 'contexts/wallet' import type { NextPage } from 'next' import { NextSeo } from 'next-seo' -import type { FormEvent } from 'react' -import { useMemo, useState } from 'react' -import { toast } from 'react-hot-toast' -import { FaArrowRight } from 'react-icons/fa' -import { useMutation } from 'react-query' +import { useState } from 'react' import { withMetadata } from 'utils/layout' import { links } from 'utils/links' -import { TextInput } from '../../components/forms/FormInput' - const CollectionActionsPage: NextPage = () => { const { minter: minterContract, sg721: sg721Contract } = useContracts() const wallet = useWallet() - const [lastTx, setLastTx] = useState('') - const [timestamp, setTimestamp] = useState(undefined) - const [airdropArray, setAirdropArray] = useState([]) - - const comboboxState = useActionsComboboxState() - const type = comboboxState.value?.id + const [action, setAction] = useState(true) const sg721ContractState = useInputState({ id: 'sg721-contract-address', @@ -52,111 +31,6 @@ const CollectionActionsPage: NextPage = () => { subtitle: 'Address of the Minter contract', }) - const limitState = useNumberInputState({ - id: 'per-address-limi', - name: 'perAddressLimit', - title: 'Per Address Limit', - subtitle: 'Enter the per address limit', - }) - - const tokenIdState = useNumberInputState({ - id: 'token-id', - name: 'tokenId', - title: 'Token ID', - subtitle: 'Enter the token ID', - }) - - const batchNumberState = useNumberInputState({ - id: 'batch-number', - name: 'batchNumber', - title: 'Number of Tokens', - subtitle: 'Enter the number of tokens to mint', - }) - - const tokenIdListState = useInputState({ - id: 'token-id-list', - name: 'tokenIdList', - title: 'List of token IDs', - subtitle: - 'Specify individual token IDs separated by commas (e.g., 2, 4, 8) or a range of IDs separated by a colon (e.g., 8:13)', - }) - - const recipientState = useInputState({ - id: 'recipient-address', - name: 'recipient', - title: 'Recipient Address', - subtitle: 'Address of the recipient', - }) - - const whitelistState = useInputState({ - id: 'whitelist-address', - name: 'whitelistAddress', - title: 'Whitelist Address', - subtitle: 'Address of the whitelist contract', - }) - - const showWhitelistField = type === 'set_whitelist' - const showDateField = type === 'update_start_time' - const showLimitField = type === 'update_per_address_limit' - const showTokenIdField = isEitherType(type, ['transfer', 'mint_for', 'burn']) - const showNumberOfTokensField = type === 'batch_mint' - const showTokenIdListField = isEitherType(type, ['batch_burn', 'batch_transfer']) - const showRecipientField = isEitherType(type, ['transfer', 'mint_to', 'mint_for', 'batch_mint', 'batch_transfer']) - const showAirdropFileField = type === 'airdrop' - - const minterMessages = useMemo( - () => minterContract?.use(minterContractState.value), - [minterContract, minterContractState.value], - ) - const sg721Messages = useMemo( - () => sg721Contract?.use(sg721ContractState.value), - [sg721Contract, sg721ContractState.value], - ) - const payload: DispatchExecuteArgs = { - whitelist: whitelistState.value, - startTime: timestamp ? (timestamp.getTime() * 1_000_000).toString() : '', - limit: limitState.value, - minterContract: minterContractState.value, - sg721Contract: sg721ContractState.value, - tokenId: tokenIdState.value, - tokenIds: tokenIdListState.value, - batchNumber: batchNumberState.value, - minterMessages, - sg721Messages, - recipient: recipientState.value, - recipients: airdropArray, - txSigner: wallet.address, - type, - } - const { isLoading, mutate } = useMutation( - async (event: FormEvent) => { - event.preventDefault() - if (!type) { - throw new Error('Please select an action!') - } - if (minterContractState.value === '' && sg721ContractState.value === '') { - throw new Error('Please enter minter and sg721 contract addresses!') - } - const txHash = await toast.promise(dispatchExecute(payload), { - error: `${type.charAt(0).toUpperCase() + type.slice(1)} execute failed!`, - loading: 'Executing message...', - success: (tx) => `Transaction ${tx} success!`, - }) - if (txHash) { - setLastTx(txHash) - } - }, - { - onError: (error) => { - toast.error(String(error)) - }, - }, - ) - - const airdropFileOnChange = (data: string[]) => { - setAirdropArray(data) - } - return (
@@ -166,40 +40,55 @@ const CollectionActionsPage: NextPage = () => { title="Collection Actions" /> -
-
- + +
+ - - {showRecipientField && } - {showWhitelistField && } - {showLimitField && } - {showTokenIdField && } - {showTokenIdListField && } - {showNumberOfTokensField && } - {showAirdropFileField && ( - - - - )} - - - setTimestamp(date)} value={timestamp} /> - -
-
-
- - - - +
+
+
+
+ { + setAction(true) + }} + type="radio" + value="true" + /> + +
+
+ { + setAction(false) + }} + type="radio" + value="false" + /> + +
+
+
{(action && ) || }
- - -