From 8f546fccb41cb5e02235a3525869ff22e7ff6882 Mon Sep 17 00:00:00 2001 From: poomthiti Date: Fri, 20 Jan 2023 16:50:50 +0700 Subject: [PATCH 01/16] feat: update admin flow --- src/env.ts | 1 + src/lib/app-fns/tx/updateAdmin.tsx | 73 ++++++++ src/lib/app-provider/tx/index.ts | 3 + src/lib/app-provider/tx/updateAdmin.ts | 44 +++++ src/lib/components/ErrorMessageRender.tsx | 21 +++ src/lib/components/forms/TextInput.tsx | 6 +- src/lib/data/constant.ts | 1 + .../pages/execute/components/ExecuteArea.tsx | 17 +- src/lib/pages/update-admin/index.tsx | 175 ++++++++++++++++-- src/lib/types/tx/msg.ts | 10 +- src/pages/[network]/update-admin.tsx | 3 + src/pages/update-admin.tsx | 3 + 12 files changed, 328 insertions(+), 29 deletions(-) create mode 100644 src/lib/app-fns/tx/updateAdmin.tsx create mode 100644 src/lib/app-provider/tx/updateAdmin.ts create mode 100644 src/lib/components/ErrorMessageRender.tsx create mode 100644 src/pages/[network]/update-admin.tsx create mode 100644 src/pages/update-admin.tsx diff --git a/src/env.ts b/src/env.ts index 52da8aa09..25f1f12ad 100644 --- a/src/env.ts +++ b/src/env.ts @@ -46,6 +46,7 @@ export const MSG_TYPE_URL = { [MsgType.STORE_CODE]: "/cosmwasm.wasm.v1.MsgStoreCode", [MsgType.INSTANTIATE]: "/cosmwasm.wasm.v1.MsgInstantiateContract", [MsgType.EXECUTE]: "/cosmwasm.wasm.v1.MsgExecuteContract", + [MsgType.UPDATE_ADMIN]: "/cosmwasm.wasm.v1.MsgUpdateAdmin", }; export const CELATONE_CONSTANTS: CelatoneConstants = { diff --git a/src/lib/app-fns/tx/updateAdmin.tsx b/src/lib/app-fns/tx/updateAdmin.tsx new file mode 100644 index 000000000..0f8cb148d --- /dev/null +++ b/src/lib/app-fns/tx/updateAdmin.tsx @@ -0,0 +1,73 @@ +import { Icon } from "@chakra-ui/react"; +import type { + ExecuteResult, + SigningCosmWasmClient, +} from "@cosmjs/cosmwasm-stargate"; +import type { StdFee } from "@cosmjs/stargate"; +import { pipe } from "@rx-stream/pipe"; +import { MdCheckCircle } from "react-icons/md"; +import type { Observable } from "rxjs"; + +import { ExplorerLink } from "lib/components/ExplorerLink"; +import type { ContractAddr, HumanAddr, TxResultRendering } from "lib/types"; +import { TxStreamPhase } from "lib/types"; +import { formatUFee } from "lib/utils"; + +import { catchTxError, postTx, sendingTx } from "./common"; + +interface UpdateAdminTxParams { + address: HumanAddr; + contractAddress: ContractAddr; + newAdmin: HumanAddr | ContractAddr; + fee: StdFee; + client: SigningCosmWasmClient; + onTxSucceed?: () => void; + onTxFailed?: () => void; +} + +export const updateAdminTx = ({ + address, + contractAddress, + newAdmin, + fee, + client, + onTxSucceed, + onTxFailed, +}: UpdateAdminTxParams): Observable => { + return pipe( + sendingTx(fee), + postTx({ + postFn: () => + client.updateAdmin(address, contractAddress, newAdmin, fee, undefined), + }), + ({ value: txInfo }) => { + onTxSucceed?.(); + return { + value: null, + phase: TxStreamPhase.SUCCEED, + receipts: [ + { + title: "Tx Hash", + value: txInfo.transactionHash, + html: ( + + ), + }, + { + title: "Tx Fee", + value: `${formatUFee( + txInfo.events.find((e) => e.type === "tx")?.attributes[0].value ?? + "0u" + )}`, + }, + ], + receiptInfo: { + header: "Transaction Complete", + headerIcon: ( + + ), + }, + } as TxResultRendering; + } + )().pipe(catchTxError(onTxFailed)); +}; diff --git a/src/lib/app-provider/tx/index.ts b/src/lib/app-provider/tx/index.ts index 8f7fec410..eddb8cf3b 100644 --- a/src/lib/app-provider/tx/index.ts +++ b/src/lib/app-provider/tx/index.ts @@ -1,2 +1,5 @@ export * from "./upload"; +export * from "./execute"; +export * from "./instantiate"; export * from "./resend"; +export * from "./updateAdmin"; diff --git a/src/lib/app-provider/tx/updateAdmin.ts b/src/lib/app-provider/tx/updateAdmin.ts new file mode 100644 index 000000000..eee083423 --- /dev/null +++ b/src/lib/app-provider/tx/updateAdmin.ts @@ -0,0 +1,44 @@ +import type { StdFee } from "@cosmjs/stargate"; +import { useWallet } from "@cosmos-kit/react"; +import { useCallback } from "react"; + +import { updateAdminTx } from "lib/app-fns/tx/updateAdmin"; +import type { ContractAddr, HumanAddr } from "lib/types"; + +export interface UpdateAdminStreamParams { + contractAddress: ContractAddr; + newAdmin: HumanAddr | ContractAddr; + estimatedFee: StdFee | undefined; + onTxSucceed?: () => void; + onTxFailed?: () => void; +} + +export const useUpdateAdminTx = () => { + const { address, getCosmWasmClient } = useWallet(); + + return useCallback( + async ({ + contractAddress, + newAdmin, + estimatedFee, + onTxSucceed, + onTxFailed, + }: UpdateAdminStreamParams) => { + const client = await getCosmWasmClient(); + if (!address || !client) + throw new Error("Please check your wallet connection."); + if (!estimatedFee) return null; + + return updateAdminTx({ + address: address as HumanAddr, + contractAddress, + newAdmin, + fee: estimatedFee, + client, + onTxSucceed, + onTxFailed, + }); + }, + [address, getCosmWasmClient] + ); +}; diff --git a/src/lib/components/ErrorMessageRender.tsx b/src/lib/components/ErrorMessageRender.tsx new file mode 100644 index 000000000..c6e985f86 --- /dev/null +++ b/src/lib/components/ErrorMessageRender.tsx @@ -0,0 +1,21 @@ +import type { FlexProps } from "@chakra-ui/react"; +import { Flex, Icon, Text } from "@chakra-ui/react"; +import { IoIosWarning } from "react-icons/io"; + +interface ErrorMessageRenderProps extends FlexProps { + error: Error["message"]; +} + +export const ErrorMessageRender = ({ + error, + ...restProps +}: ErrorMessageRenderProps) => { + return ( + + + + {error} + + + ); +}; diff --git a/src/lib/components/forms/TextInput.tsx b/src/lib/components/forms/TextInput.tsx index 6c06d6b13..0bc1659bf 100644 --- a/src/lib/components/forms/TextInput.tsx +++ b/src/lib/components/forms/TextInput.tsx @@ -11,12 +11,14 @@ import { import type { FormControlProps } from "@chakra-ui/react"; import type { HTMLInputTypeAttribute, Dispatch, SetStateAction } from "react"; +import type { ContractAddr, HumanAddr } from "lib/types"; + import type { FormStatus } from "./FormStatus"; import { getResponseMsg, getStatusIcon } from "./FormStatus"; export interface TextInputProps extends FormControlProps { value: string; - setInputState: Dispatch>; + setInputState: Dispatch>; label?: string; labelBgColor?: string; helperText?: string; @@ -64,7 +66,7 @@ export const TextInput = ({ maxLength={maxLength} /> - {status && getStatusIcon(status.state)} + {status && getStatusIcon(status.state, "20px")} diff --git a/src/lib/data/constant.ts b/src/lib/data/constant.ts index 530cc25e1..6da6de359 100644 --- a/src/lib/data/constant.ts +++ b/src/lib/data/constant.ts @@ -62,6 +62,7 @@ export const typeUrlDict = { [MsgType.STORE_CODE]: "/cosmwasm.wasm.v1.MsgStoreCode", [MsgType.INSTANTIATE]: "/cosmwasm.wasm.v1.MsgInstantiateContract", [MsgType.EXECUTE]: "/cosmwasm.wasm.v1.MsgExecuteContract", + [MsgType.UPDATE_ADMIN]: "/cosmwasm.wasm.v1.MsgUpdateAdmin", }; export const DEFAULT_RPC_ERROR = "Invalid format, or Something went wrong"; diff --git a/src/lib/pages/execute/components/ExecuteArea.tsx b/src/lib/pages/execute/components/ExecuteArea.tsx index 87f2bf55f..51e4268cf 100644 --- a/src/lib/pages/execute/components/ExecuteArea.tsx +++ b/src/lib/pages/execute/components/ExecuteArea.tsx @@ -1,10 +1,9 @@ -import { Box, Flex, Button, ButtonGroup, Icon, Text } from "@chakra-ui/react"; +import { Box, Flex, Button, ButtonGroup, Text } from "@chakra-ui/react"; import type { StdFee } from "@cosmjs/stargate"; import { useWallet } from "@cosmos-kit/react"; import { useCallback, useEffect, useMemo, useState } from "react"; import { useFieldArray, useFormState, useWatch } from "react-hook-form"; import type { Control, UseFormSetValue } from "react-hook-form"; -import { IoIosWarning } from "react-icons/io"; import { MdInput } from "react-icons/md"; import type { ExecutePageState } from "../types"; @@ -13,6 +12,7 @@ import { useSimulateFeeQuery } from "lib/app-provider/queries"; import { useExecuteContractTx } from "lib/app-provider/tx/execute"; import { ContractCmdButton } from "lib/components/ContractCmdButton"; import { CopyButton } from "lib/components/CopyButton"; +import { ErrorMessageRender } from "lib/components/ErrorMessageRender"; import { EstimatedFeeRender } from "lib/components/EstimatedFeeRender"; import { AssetInput, ControllerInput } from "lib/components/forms"; import JsonInput from "lib/components/json/JsonInput"; @@ -62,7 +62,7 @@ export const ExecuteArea = ({ control, setValue, cmds }: ExecuteAreaProps) => { const [fee, setFee] = useState(); const [msg, setMsg] = useState(initialMsg); - const [error, setError] = useState(""); + const [error, setError] = useState(); const [composedTxMsg, setComposedTxMsg] = useState([]); const [processing, setProcessing] = useState(false); @@ -116,7 +116,7 @@ export const ExecuteArea = ({ control, setValue, cmds }: ExecuteAreaProps) => { useEffect(() => { if (enableExecute) { - setError(""); + setError(undefined); const funds = assets .filter((asset) => asset.amount && asset.denom) @@ -193,14 +193,7 @@ export const ExecuteArea = ({ control, setValue, cmds }: ExecuteAreaProps) => { setText={setMsg} height="240px" /> - {error && ( - - - - {error} - - - )} + {error && } diff --git a/src/lib/pages/update-admin/index.tsx b/src/lib/pages/update-admin/index.tsx index 7da1b9b7f..ed2b45646 100644 --- a/src/lib/pages/update-admin/index.tsx +++ b/src/lib/pages/update-admin/index.tsx @@ -1,31 +1,178 @@ -import { Heading, Text } from "@chakra-ui/react"; +import { Button, Flex, Heading } from "@chakra-ui/react"; +import type { StdFee } from "@cosmjs/stargate"; +import { useWallet } from "@cosmos-kit/react"; import { useRouter } from "next/router"; -import { useState } from "react"; +import { useCallback, useEffect, useState } from "react"; +import { + useFabricateFee, + useInternalNavigate, + useSimulateFeeQuery, + useUpdateAdminTx, +} from "lib/app-provider"; +import { ContractSelectSection } from "lib/components/ContractSelectSection"; +import { ErrorMessageRender } from "lib/components/ErrorMessageRender"; +import { EstimatedFeeRender } from "lib/components/EstimatedFeeRender"; +import type { FormStatus } from "lib/components/forms"; +import { TextInput } from "lib/components/forms"; import WasmPageContainer from "lib/components/WasmPageContainer"; -import { useValidateAddress } from "lib/hooks"; -import { getFirstQueryParam } from "lib/utils"; +import { useGetAddressType, useValidateAddress } from "lib/hooks"; +import { useTxBroadcast } from "lib/providers/tx-broadcast"; +import type { ContractAddr, HumanAddr } from "lib/types"; +import { MsgType } from "lib/types"; +import { composeMsg, getFirstQueryParam } from "lib/utils"; const UpdateAdmin = () => { const router = useRouter(); - const { validateContractAddress } = useValidateAddress(); + const { address } = useWallet(); + const { validateContractAddress, validateUserAddress } = useValidateAddress(); + const getAddressType = useGetAddressType(); + const navigate = useInternalNavigate(); + const fabricateFee = useFabricateFee(); + const updateAdminTx = useUpdateAdminTx(); + const { broadcast } = useTxBroadcast(); - const contractAddressParam = getFirstQueryParam(router.query.contract); + const [adminAddress, setAdminAddress] = useState(""); + const [adminFormStatus, setAdminFormStatus] = useState({ + state: "init", + message: "", + }); + const [estimatedFee, setEstimatedFee] = useState(); + const [simulateError, setSimulateError] = useState(); - const [contractAddress] = useState( - !validateContractAddress(contractAddressParam) - ? contractAddressParam - : undefined + const contractAddressParam = getFirstQueryParam( + router.query.contract + ) as ContractAddr; + + const onContractPathChange = useCallback( + (contract?: ContractAddr) => { + navigate({ + pathname: "/update-admin", + query: { ...(contract && { contract }) }, + options: { shallow: true }, + }); + }, + [navigate] ); + const { isFetching } = useSimulateFeeQuery({ + enabled: + !!address && + !!contractAddressParam && + adminFormStatus.state === "success", + messages: [ + composeMsg(MsgType.UPDATE_ADMIN, { + sender: address as HumanAddr, + newAdmin: adminAddress as HumanAddr | ContractAddr, + contract: contractAddressParam, + }), + ], + onSuccess: (fee) => { + if (fee) { + setSimulateError(undefined); + setEstimatedFee(fabricateFee(fee)); + } else setEstimatedFee(undefined); + }, + onError: (e) => { + setSimulateError(e.message); + setEstimatedFee(undefined); + }, + }); + + const proceed = useCallback(async () => { + const stream = await updateAdminTx({ + contractAddress: contractAddressParam, + newAdmin: adminAddress as HumanAddr | ContractAddr, + estimatedFee, + }); + + if (stream) broadcast(stream); + }, [ + adminAddress, + contractAddressParam, + updateAdminTx, + broadcast, + estimatedFee, + ]); + + useEffect(() => { + if (contractAddressParam && validateContractAddress(contractAddressParam)) { + onContractPathChange(); + } + }, [contractAddressParam, onContractPathChange, validateContractAddress]); + + useEffect(() => { + if (!adminAddress) setAdminFormStatus({ state: "init" }); + else { + const addressType = getAddressType(adminAddress); + if (addressType === "invalid_address") { + setAdminFormStatus({ + state: "error", + message: "Invalid address length", + }); + } else { + const validateResult = + addressType === "user_address" + ? validateUserAddress(adminAddress) + : validateContractAddress(adminAddress); + if (validateResult) { + setAdminFormStatus({ state: "error", message: validateResult }); + } else { + setAdminFormStatus({ state: "success" }); + } + } + } + }, [ + adminAddress, + getAddressType, + validateContractAddress, + validateUserAddress, + ]); + return ( - + Update Admin - - {contractAddress} - + onContractPathChange(contract)} + /> + + +

Transaction Fee:

+ +
+ {simulateError && ( + + )} +
); }; diff --git a/src/lib/types/tx/msg.ts b/src/lib/types/tx/msg.ts index 8b2f5e491..4e260d03a 100644 --- a/src/lib/types/tx/msg.ts +++ b/src/lib/types/tx/msg.ts @@ -6,6 +6,7 @@ export enum MsgType { STORE_CODE = "STORE_CODE", INSTANTIATE = "INSTANTIATE", EXECUTE = "EXECUTE", + UPDATE_ADMIN = "UPDATE_ADMIN", } export enum AccessType { @@ -43,10 +44,17 @@ export interface MsgExecuteContract { funds: Coin[]; } +export interface MsgUpdateAdmin { + sender: HumanAddr; + newAdmin: HumanAddr | ContractAddr; + contract: ContractAddr; +} + export type TxMessage = | MsgStoreCode | MsgInstantiateContract - | MsgExecuteContract; + | MsgExecuteContract + | MsgUpdateAdmin; export interface ComposedMsg { typeUrl: string; diff --git a/src/pages/[network]/update-admin.tsx b/src/pages/[network]/update-admin.tsx new file mode 100644 index 000000000..d17f2a5c8 --- /dev/null +++ b/src/pages/[network]/update-admin.tsx @@ -0,0 +1,3 @@ +import UpdateAdmin from "lib/pages/update-admin"; + +export default UpdateAdmin; diff --git a/src/pages/update-admin.tsx b/src/pages/update-admin.tsx new file mode 100644 index 000000000..d17f2a5c8 --- /dev/null +++ b/src/pages/update-admin.tsx @@ -0,0 +1,3 @@ +import UpdateAdmin from "lib/pages/update-admin"; + +export default UpdateAdmin; From e701f9b8e4f596d79c9b01bfb08adeb75d956595 Mon Sep 17 00:00:00 2001 From: poomthiti Date: Fri, 20 Jan 2023 16:55:02 +0700 Subject: [PATCH 02/16] refactor: remove MsgTypeUrl from celatone constants --- src/env.ts | 9 --------- src/types.ts | 3 +-- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/env.ts b/src/env.ts index 25f1f12ad..3a805cdee 100644 --- a/src/env.ts +++ b/src/env.ts @@ -1,4 +1,3 @@ -import { MsgType } from "lib/types"; import type { ContractAddr, ChainGasPrice, Token, U } from "lib/types"; import type { CelatoneConstants, CelatoneContractAddress } from "types"; @@ -42,18 +41,10 @@ export const LCD_ENDPOINT: Record = { export const MAX_FILE_SIZE = 800_000; -export const MSG_TYPE_URL = { - [MsgType.STORE_CODE]: "/cosmwasm.wasm.v1.MsgStoreCode", - [MsgType.INSTANTIATE]: "/cosmwasm.wasm.v1.MsgInstantiateContract", - [MsgType.EXECUTE]: "/cosmwasm.wasm.v1.MsgExecuteContract", - [MsgType.UPDATE_ADMIN]: "/cosmwasm.wasm.v1.MsgUpdateAdmin", -}; - export const CELATONE_CONSTANTS: CelatoneConstants = { gasAdjustment: 1.6, lcdEndpoint: LCD_ENDPOINT, maxFileSize: MAX_FILE_SIZE, - msgTypeUrl: MSG_TYPE_URL, }; export const DUMMY_MNEMONIC = process.env.NEXT_PUBLIC_DUMMY_MNEMONIC; diff --git a/src/types.ts b/src/types.ts index 570fcc768..06b345f68 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,5 +1,5 @@ import type { AppConstants } from "lib/app-provider/types"; -import type { ContractAddr, MsgType } from "lib/types"; +import type { ContractAddr } from "lib/types"; export interface CelatoneContractAddress { example: ContractAddr; @@ -7,5 +7,4 @@ export interface CelatoneContractAddress { export interface CelatoneConstants extends AppConstants { lcdEndpoint: Record; maxFileSize: number; - msgTypeUrl: { [key in MsgType]: string }; } From be730e5a284e64764c0dca5237881f1ac3af8d60 Mon Sep 17 00:00:00 2001 From: poomthiti Date: Fri, 20 Jan 2023 17:05:40 +0700 Subject: [PATCH 03/16] feat: add connect wallet alert and remove update-admin path --- CHANGELOG.md | 1 + src/lib/pages/{update-admin => admin}/index.tsx | 7 ++++++- src/pages/[network]/admin.tsx | 2 +- src/pages/[network]/update-admin.tsx | 3 --- src/pages/admin.tsx | 2 +- src/pages/update-admin.tsx | 3 --- 6 files changed, 9 insertions(+), 9 deletions(-) rename src/lib/pages/{update-admin => admin}/index.tsx (95%) delete mode 100644 src/pages/[network]/update-admin.tsx delete mode 100644 src/pages/update-admin.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index a4ce895a6..0442bb15f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +- [#113](https://github.com/alleslabs/celatone-frontend/pull/113) Update admin page ui and wireup - [#92](https://github.com/alleslabs/celatone-frontend/pull/92) Create select contract component for admin and migrate pages - [#101](https://github.com/alleslabs/celatone-frontend/pull/101) Fix incorrect truncating of proposal id in contract detail's migration table - [#100](https://github.com/alleslabs/celatone-frontend/pull/100) Fix contract instantiated time parsing diff --git a/src/lib/pages/update-admin/index.tsx b/src/lib/pages/admin/index.tsx similarity index 95% rename from src/lib/pages/update-admin/index.tsx rename to src/lib/pages/admin/index.tsx index ed2b45646..eec228b7c 100644 --- a/src/lib/pages/update-admin/index.tsx +++ b/src/lib/pages/admin/index.tsx @@ -10,6 +10,7 @@ import { useSimulateFeeQuery, useUpdateAdminTx, } from "lib/app-provider"; +import { ConnectWalletAlert } from "lib/components/ConnectWalletAlert"; import { ContractSelectSection } from "lib/components/ContractSelectSection"; import { ErrorMessageRender } from "lib/components/ErrorMessageRender"; import { EstimatedFeeRender } from "lib/components/EstimatedFeeRender"; @@ -47,7 +48,7 @@ const UpdateAdmin = () => { const onContractPathChange = useCallback( (contract?: ContractAddr) => { navigate({ - pathname: "/update-admin", + pathname: "/admin", query: { ...(contract && { contract }) }, options: { shallow: true }, }); @@ -134,6 +135,10 @@ const UpdateAdmin = () => { Update Admin + Date: Mon, 23 Jan 2023 14:27:45 +0700 Subject: [PATCH 04/16] refactor: apply option to StdFee --- src/lib/app-provider/tx/updateAdmin.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/app-provider/tx/updateAdmin.ts b/src/lib/app-provider/tx/updateAdmin.ts index eee083423..bb9ee397e 100644 --- a/src/lib/app-provider/tx/updateAdmin.ts +++ b/src/lib/app-provider/tx/updateAdmin.ts @@ -3,12 +3,12 @@ import { useWallet } from "@cosmos-kit/react"; import { useCallback } from "react"; import { updateAdminTx } from "lib/app-fns/tx/updateAdmin"; -import type { ContractAddr, HumanAddr } from "lib/types"; +import type { ContractAddr, HumanAddr, Option } from "lib/types"; export interface UpdateAdminStreamParams { contractAddress: ContractAddr; newAdmin: HumanAddr | ContractAddr; - estimatedFee: StdFee | undefined; + estimatedFee: Option; onTxSucceed?: () => void; onTxFailed?: () => void; } From af972d4c05b8bdf689ad5fc94ba651276c577240 Mon Sep 17 00:00:00 2001 From: poomthiti Date: Tue, 24 Jan 2023 13:43:34 +0700 Subject: [PATCH 05/16] fix: add contract admin validation to handle wallet change --- src/lib/pages/admin/index.tsx | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/lib/pages/admin/index.tsx b/src/lib/pages/admin/index.tsx index eec228b7c..e2e097dc4 100644 --- a/src/lib/pages/admin/index.tsx +++ b/src/lib/pages/admin/index.tsx @@ -1,6 +1,7 @@ import { Button, Flex, Heading } from "@chakra-ui/react"; import type { StdFee } from "@cosmjs/stargate"; import { useWallet } from "@cosmos-kit/react"; +import { useQuery } from "@tanstack/react-query"; import { useRouter } from "next/router"; import { useCallback, useEffect, useState } from "react"; @@ -17,8 +18,9 @@ import { EstimatedFeeRender } from "lib/components/EstimatedFeeRender"; import type { FormStatus } from "lib/components/forms"; import { TextInput } from "lib/components/forms"; import WasmPageContainer from "lib/components/WasmPageContainer"; -import { useGetAddressType, useValidateAddress } from "lib/hooks"; +import { useEndpoint, useGetAddressType, useValidateAddress } from "lib/hooks"; import { useTxBroadcast } from "lib/providers/tx-broadcast"; +import { queryInstantiateInfo } from "lib/services/contract"; import type { ContractAddr, HumanAddr } from "lib/types"; import { MsgType } from "lib/types"; import { composeMsg, getFirstQueryParam } from "lib/utils"; @@ -32,6 +34,7 @@ const UpdateAdmin = () => { const fabricateFee = useFabricateFee(); const updateAdminTx = useUpdateAdminTx(); const { broadcast } = useTxBroadcast(); + const endpoint = useEndpoint(); const [adminAddress, setAdminAddress] = useState(""); const [adminFormStatus, setAdminFormStatus] = useState({ @@ -96,12 +99,32 @@ const UpdateAdmin = () => { estimatedFee, ]); + /** + * @remarks Contract admin validation + */ + useQuery( + ["query", "instantiateInfo", endpoint, contractAddressParam], + async () => queryInstantiateInfo(endpoint, contractAddressParam), + { + enabled: !!contractAddressParam && !!address, + refetchOnWindowFocus: false, + retry: 0, + onSuccess: (contractInfo) => { + if (contractInfo.admin !== address) onContractPathChange(); + }, + onError: () => onContractPathChange(), + } + ); + useEffect(() => { if (contractAddressParam && validateContractAddress(contractAddressParam)) { onContractPathChange(); } }, [contractAddressParam, onContractPathChange, validateContractAddress]); + /** + * @remarks Admin address input validation + */ useEffect(() => { if (!adminAddress) setAdminFormStatus({ state: "init" }); else { From 024be885c87d9132c7b013e53b8c86a8462573fe Mon Sep 17 00:00:00 2001 From: poomthiti Date: Tue, 24 Jan 2023 13:49:33 +0700 Subject: [PATCH 06/16] refactor: rewrite onError --- src/lib/pages/admin/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/pages/admin/index.tsx b/src/lib/pages/admin/index.tsx index e2e097dc4..9327ca1fc 100644 --- a/src/lib/pages/admin/index.tsx +++ b/src/lib/pages/admin/index.tsx @@ -112,7 +112,7 @@ const UpdateAdmin = () => { onSuccess: (contractInfo) => { if (contractInfo.admin !== address) onContractPathChange(); }, - onError: () => onContractPathChange(), + onError: onContractPathChange, } ); From adee91a17a9b7b793d16f17283f7402477507c7a Mon Sep 17 00:00:00 2001 From: poomthiti Date: Tue, 24 Jan 2023 14:48:45 +0700 Subject: [PATCH 07/16] fix: also handle unconnected state --- src/lib/pages/admin/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/pages/admin/index.tsx b/src/lib/pages/admin/index.tsx index 9327ca1fc..441cf8e62 100644 --- a/src/lib/pages/admin/index.tsx +++ b/src/lib/pages/admin/index.tsx @@ -106,7 +106,7 @@ const UpdateAdmin = () => { ["query", "instantiateInfo", endpoint, contractAddressParam], async () => queryInstantiateInfo(endpoint, contractAddressParam), { - enabled: !!contractAddressParam && !!address, + enabled: !!contractAddressParam, refetchOnWindowFocus: false, retry: 0, onSuccess: (contractInfo) => { From 28f134f29770b52f788e85e9957014374863dd59 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Tue, 24 Jan 2023 15:34:12 +0700 Subject: [PATCH 08/16] fix: change public project query, display project image --- src/env.ts | 2 +- src/lib/model/contract.ts | 32 +++++++++-------- .../components/ContractTop.tsx | 35 +++++++++++++------ .../PublicContractDesc.tsx | 4 +-- .../contract-description/UserContractDesc.tsx | 5 ++- src/lib/services/contractService.ts | 27 ++++++++++++++ 6 files changed, 76 insertions(+), 29 deletions(-) diff --git a/src/env.ts b/src/env.ts index f41c44393..a6f5f1a1a 100644 --- a/src/env.ts +++ b/src/env.ts @@ -83,7 +83,7 @@ export const getChainApiPath = (chainName: string) => { export const getMainnetApiPath = (chainId: string) => { switch (chainId) { case "osmo-test-4": - case "osmosis": + case "osmosis-1": return "osmosis-1"; default: return undefined; diff --git a/src/lib/model/contract.ts b/src/lib/model/contract.ts index bc272f3b4..7c2342e0c 100644 --- a/src/lib/model/contract.ts +++ b/src/lib/model/contract.ts @@ -7,7 +7,6 @@ import { useCodeStore, useContractStore, useLCDEndpoint } from "lib/hooks"; import { useAssetInfos } from "lib/services/assetService"; import type { InstantiateInfo, PublicInfo } from "lib/services/contract"; import { - queryPublicInfo, queryContractBalances, queryInstantiateInfo, } from "lib/services/contract"; @@ -19,12 +18,15 @@ import { useMigrationHistoriesCountByContractAddress, useTxsCountByContractAddress, useRelatedProposalsCountByContractAddress, + usePublicProjectByContractAddress, } from "lib/services/contractService"; +import { usePublicProjectBySlugQuery } from "lib/services/publicProject"; import type { CodeLocalInfo } from "lib/stores/code"; import type { ContractLocalInfo, ContractListInfo } from "lib/stores/contract"; import type { BalanceWithAssetInfo, ContractAddr, + Detail, HumanAddr, Option, } from "lib/types"; @@ -35,7 +37,10 @@ export interface ContractData { codeInfo: Option; contractLocalInfo: Option; instantiateInfo: Option; - publicInfo: Option; + publicProject: { + publicInfo: Option; + publicDetail: Option; + }; balances: Option; initMsg: string; initTxHash: Option; @@ -87,13 +92,18 @@ export const useInstantiatedMockInfoByMe = (): ContractListInfo => { export const useContractData = ( contractAddress: ContractAddr -): ContractData | undefined => { +): Option => { const { indexerGraphClient } = useCelatoneApp(); const { currentChainRecord } = useWallet(); const { getCodeLocalInfo } = useCodeStore(); const { getContractLocalInfo } = useContractStore(); const endpoint = useLCDEndpoint(); const assetInfos = useAssetInfos(); + const { data: publicInfo } = + usePublicProjectByContractAddress(contractAddress); + const { data: publicInfoBySlug } = usePublicProjectBySlugQuery( + publicInfo?.slug + ); const { data: instantiateInfo } = useQuery( ["query", "instantiateInfo", contractAddress], @@ -126,17 +136,6 @@ export const useContractData = ( return -1; }); - const { data: publicInfo } = useQuery( - ["query", "publicInfo", contractAddress], - async () => - queryPublicInfo( - currentChainRecord?.name, - currentChainRecord?.chain.chain_id, - contractAddress - ), - { enabled: !!currentChainRecord } - ); - const codeInfo = instantiateInfo ? getCodeLocalInfo(Number(instantiateInfo.codeId)) : undefined; @@ -155,7 +154,10 @@ export const useContractData = ( codeInfo, contractLocalInfo, instantiateInfo, - publicInfo, + publicProject: { + publicInfo, + publicDetail: publicInfoBySlug?.details, + }, balances: contractBalancesWithAssetInfos, initMsg: instantiateDetail.initMsg, initTxHash: instantiateDetail.initTxHash, diff --git a/src/lib/pages/contract-details/components/ContractTop.tsx b/src/lib/pages/contract-details/components/ContractTop.tsx index d844738e2..79d9179bb 100644 --- a/src/lib/pages/contract-details/components/ContractTop.tsx +++ b/src/lib/pages/contract-details/components/ContractTop.tsx @@ -6,7 +6,9 @@ import { Button, Icon, IconButton, + Image, } from "@chakra-ui/react"; +import router from "next/router"; import { MdBookmark, MdBookmarkBorder, @@ -24,18 +26,20 @@ import { } from "lib/components/modal"; import type { ContractData } from "lib/model/contract"; import type { ContractAddr } from "lib/types"; +import { getFirstQueryParam } from "lib/utils"; interface ContractTopProps { contractData: ContractData; } export const ContractTop = ({ contractData }: ContractTopProps) => { const navigate = useInternalNavigate(); - const { contractLocalInfo, instantiateInfo, publicInfo } = contractData; - - const contractAddress = instantiateInfo?.contractAddress as ContractAddr; + const { contractLocalInfo, instantiateInfo, publicProject } = contractData; + const contractAddress = getFirstQueryParam(router.query.contractAddress); const displayName = - contractLocalInfo?.name || publicInfo?.name || instantiateInfo?.label; + contractLocalInfo?.name || + publicProject.publicInfo?.name || + instantiateInfo?.label; const goToQuery = () => { navigate({ @@ -74,7 +78,7 @@ export const ContractTop = ({ contractData }: ContractTopProps) => { return ( { return ( - - {displayName} - + + {publicProject.publicDetail && ( + {publicProject.publicDetail.name} + )} + + {displayName} + + { {contractData.instantiateInfo?.label} - {publicInfo?.name && ( + {publicProject.publicInfo?.name && ( Public Contract Name: - {publicInfo?.name} + {publicProject.publicInfo?.name} )} diff --git a/src/lib/pages/contract-details/components/contract-description/PublicContractDesc.tsx b/src/lib/pages/contract-details/components/contract-description/PublicContractDesc.tsx index ab2083b87..d043c6113 100644 --- a/src/lib/pages/contract-details/components/contract-description/PublicContractDesc.tsx +++ b/src/lib/pages/contract-details/components/contract-description/PublicContractDesc.tsx @@ -17,8 +17,8 @@ export const PublicContractDesc = ({ const [showMore, setShowMore] = useState(false); const description = useMemo( - () => contractData.publicInfo?.description, - [contractData.publicInfo?.description] + () => contractData.publicProject.publicInfo?.description, + [contractData.publicProject.publicInfo?.description] ); const [ref, { noClamp, clampedText, key }] = useClampText({ diff --git a/src/lib/pages/contract-details/components/contract-description/UserContractDesc.tsx b/src/lib/pages/contract-details/components/contract-description/UserContractDesc.tsx index ffcd2b201..0c0e9e270 100644 --- a/src/lib/pages/contract-details/components/contract-description/UserContractDesc.tsx +++ b/src/lib/pages/contract-details/components/contract-description/UserContractDesc.tsx @@ -23,7 +23,10 @@ export const UserContractDesc = ({ contractData }: UserContractDescProps) => { const [ref, { noClamp, clampedText, key }] = useClampText({ text: description || "No contract description", ellipsis: "...", - lines: textLine(!contractData.publicInfo?.description, showMore), + lines: textLine( + !contractData.publicProject.publicInfo?.description, + showMore + ), }); const renderEditContractButton = () => { diff --git a/src/lib/services/contractService.ts b/src/lib/services/contractService.ts index 239885df6..33a4e7ff2 100644 --- a/src/lib/services/contractService.ts +++ b/src/lib/services/contractService.ts @@ -1,7 +1,10 @@ +import { useWallet } from "@cosmos-kit/react"; import type { UseQueryResult } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query"; +import axios from "axios"; import { useCallback } from "react"; +import { CELATONE_API_ENDPOINT, getChainApiPath } from "env"; import { useCelatoneApp } from "lib/app-provider"; import { getInstantiatedListByUserQueryDocument, @@ -40,6 +43,8 @@ import { unwrap, } from "lib/utils"; +import type { PublicInfo } from "./contract"; + interface InstantiateDetail { initMsg: string; initTxHash?: string; @@ -411,3 +416,25 @@ export const useRelatedProposalsCountByContractAddress = ( enabled: !!contractAddress, }); }; + +export const usePublicProjectByContractAddress = ( + contractAddress: Option +) => { + const { currentChainRecord } = useWallet(); + const queryFn = useCallback(async (): Promise> => { + if (!contractAddress) throw new Error("Contract address not found"); + if (!currentChainRecord) throw new Error("No chain selected"); + return axios + .get( + `${CELATONE_API_ENDPOINT}/contracts/${getChainApiPath( + currentChainRecord.chain.chain_name + )}/${currentChainRecord.chain.chain_id}/${contractAddress}` + ) + .then(({ data: projectInfo }) => projectInfo); + }, [contractAddress, currentChainRecord]); + + return useQuery(["public_project_by_contract_address"], queryFn, { + keepPreviousData: true, + enabled: !!contractAddress, + }); +}; From b482e80071e868940e7870ee8d20dbe1e71c9c54 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Tue, 24 Jan 2023 16:01:56 +0700 Subject: [PATCH 09/16] chore: add changelog, fix image display condition --- CHANGELOG.md | 1 + src/lib/pages/contract-details/components/ContractTop.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b28d3a3e7..93296d726 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -109,6 +109,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Bug fixes +- [#124](https://github.com/alleslabs/celatone-frontend/pull/124) Fix public project query, display project image in contract details page - [#111](https://github.com/alleslabs/celatone-frontend/pull/111) Fix recent activities navigation and instantiate encode/decode - [#105](https://github.com/alleslabs/celatone-frontend/pull/105) Propoerly show instantiator of code contracts and contract in the instantiated list - [#42](https://github.com/alleslabs/celatone-frontend/pull/42) Properly show CTAs on contract-list page and edit zero/disconnected state diff --git a/src/lib/pages/contract-details/components/ContractTop.tsx b/src/lib/pages/contract-details/components/ContractTop.tsx index 79d9179bb..3aeade345 100644 --- a/src/lib/pages/contract-details/components/ContractTop.tsx +++ b/src/lib/pages/contract-details/components/ContractTop.tsx @@ -101,7 +101,7 @@ export const ContractTop = ({ contractData }: ContractTopProps) => { - {publicProject.publicDetail && ( + {publicProject.publicDetail?.logo && ( Date: Tue, 24 Jan 2023 18:21:52 +0700 Subject: [PATCH 10/16] fix: display public contract description --- .../contract-description/ContractDesc.tsx | 4 +++- .../PublicContractDesc.tsx | 22 ++++++++----------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/lib/pages/contract-details/components/contract-description/ContractDesc.tsx b/src/lib/pages/contract-details/components/contract-description/ContractDesc.tsx index d7f064145..7c4d952a1 100644 --- a/src/lib/pages/contract-details/components/contract-description/ContractDesc.tsx +++ b/src/lib/pages/contract-details/components/contract-description/ContractDesc.tsx @@ -11,7 +11,9 @@ interface ContractDescProps { export const ContractDesc = ({ contractData }: ContractDescProps) => { return ( - + {contractData.publicProject.publicInfo?.description && ( + + )} ); diff --git a/src/lib/pages/contract-details/components/contract-description/PublicContractDesc.tsx b/src/lib/pages/contract-details/components/contract-description/PublicContractDesc.tsx index d043c6113..e4e1aaef0 100644 --- a/src/lib/pages/contract-details/components/contract-description/PublicContractDesc.tsx +++ b/src/lib/pages/contract-details/components/contract-description/PublicContractDesc.tsx @@ -22,13 +22,11 @@ export const PublicContractDesc = ({ ); const [ref, { noClamp, clampedText, key }] = useClampText({ - text: description || "", + text: description || "No public contract description", ellipsis: "...", lines: textLine(!contractData.contractLocalInfo?.description, showMore), }); - if (!description) return null; - return ( - - } - key={key} - > - {showMore ? description : clampedText} - - + } + key={key} + > + {showMore ? description : clampedText} + {!noClamp && ( Date: Wed, 25 Jan 2023 10:36:12 +0700 Subject: [PATCH 11/16] fix: add return type --- src/lib/services/contractService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/services/contractService.ts b/src/lib/services/contractService.ts index 33a4e7ff2..18c92fbb7 100644 --- a/src/lib/services/contractService.ts +++ b/src/lib/services/contractService.ts @@ -419,7 +419,7 @@ export const useRelatedProposalsCountByContractAddress = ( export const usePublicProjectByContractAddress = ( contractAddress: Option -) => { +): UseQueryResult => { const { currentChainRecord } = useWallet(); const queryFn = useCallback(async (): Promise> => { if (!contractAddress) throw new Error("Contract address not found"); From 2d9a3b21d2860206e70527303cff869a38233cfe Mon Sep 17 00:00:00 2001 From: bkioshn Date: Thu, 26 Jan 2023 11:57:44 +0700 Subject: [PATCH 12/16] chore: move file, change name --- src/lib/model/contract.ts | 5 ++-- .../{ContractDesc.tsx => index.tsx} | 0 src/lib/pages/contract-details/index.tsx | 2 +- src/lib/services/contract.ts | 15 +++++----- src/lib/services/contractService.ts | 27 ----------------- src/lib/services/publicProjectService.ts | 30 +++++++++++++++++++ src/lib/types/projects.ts | 7 +++++ 7 files changed, 48 insertions(+), 38 deletions(-) rename src/lib/pages/contract-details/components/contract-description/{ContractDesc.tsx => index.tsx} (100%) create mode 100644 src/lib/services/publicProjectService.ts diff --git a/src/lib/model/contract.ts b/src/lib/model/contract.ts index 7c2342e0c..e6e8694d6 100644 --- a/src/lib/model/contract.ts +++ b/src/lib/model/contract.ts @@ -5,7 +5,7 @@ import { useCelatoneApp } from "lib/app-provider"; import { INSTANTIATED_LIST_NAME } from "lib/data"; import { useCodeStore, useContractStore, useLCDEndpoint } from "lib/hooks"; import { useAssetInfos } from "lib/services/assetService"; -import type { InstantiateInfo, PublicInfo } from "lib/services/contract"; +import type { InstantiateInfo } from "lib/services/contract"; import { queryContractBalances, queryInstantiateInfo, @@ -18,9 +18,9 @@ import { useMigrationHistoriesCountByContractAddress, useTxsCountByContractAddress, useRelatedProposalsCountByContractAddress, - usePublicProjectByContractAddress, } from "lib/services/contractService"; import { usePublicProjectBySlugQuery } from "lib/services/publicProject"; +import { usePublicProjectByContractAddress } from "lib/services/publicProjectService"; import type { CodeLocalInfo } from "lib/stores/code"; import type { ContractLocalInfo, ContractListInfo } from "lib/stores/contract"; import type { @@ -29,6 +29,7 @@ import type { Detail, HumanAddr, Option, + PublicInfo, } from "lib/types"; import { formatSlugName } from "lib/utils"; diff --git a/src/lib/pages/contract-details/components/contract-description/ContractDesc.tsx b/src/lib/pages/contract-details/components/contract-description/index.tsx similarity index 100% rename from src/lib/pages/contract-details/components/contract-description/ContractDesc.tsx rename to src/lib/pages/contract-details/components/contract-description/index.tsx diff --git a/src/lib/pages/contract-details/index.tsx b/src/lib/pages/contract-details/index.tsx index 393fe5a6a..44d2984f7 100644 --- a/src/lib/pages/contract-details/index.tsx +++ b/src/lib/pages/contract-details/index.tsx @@ -22,7 +22,7 @@ import type { ContractAddr } from "lib/types"; import { getFirstQueryParam, jsonPrettify } from "lib/utils"; import { CommandSection } from "./components/CommandSection"; -import { ContractDesc } from "./components/contract-description/ContractDesc"; +import { ContractDesc } from "./components/contract-description"; import { ContractTop } from "./components/ContractTop"; import { InstantiateInfo } from "./components/InstantiateInfo"; import { JsonInfo } from "./components/JsonInfo"; diff --git a/src/lib/services/contract.ts b/src/lib/services/contract.ts index 6deefb820..bddb77143 100644 --- a/src/lib/services/contract.ts +++ b/src/lib/services/contract.ts @@ -3,7 +3,13 @@ import type { GraphQLClient } from "graphql-request"; import { CELATONE_API_ENDPOINT, getChainApiPath } from "env"; import { getBlockTimestampByHeightQueryDocument } from "lib/data/queries"; -import type { Balance, ContractAddr, HumanAddr, Option } from "lib/types"; +import type { + Balance, + ContractAddr, + HumanAddr, + Option, + PublicInfo, +} from "lib/types"; import { encode, parseDateDefault } from "lib/utils"; interface ContractResponse { @@ -41,13 +47,6 @@ export interface InstantiateInfo { raw: ContractResponse; } -export interface PublicInfo { - slug: string; - name: string; - contractAddress: ContractAddr; - description: string; -} - export const queryData = async ( endpoint: string, contractAddress: ContractAddr, diff --git a/src/lib/services/contractService.ts b/src/lib/services/contractService.ts index 18c92fbb7..239885df6 100644 --- a/src/lib/services/contractService.ts +++ b/src/lib/services/contractService.ts @@ -1,10 +1,7 @@ -import { useWallet } from "@cosmos-kit/react"; import type { UseQueryResult } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query"; -import axios from "axios"; import { useCallback } from "react"; -import { CELATONE_API_ENDPOINT, getChainApiPath } from "env"; import { useCelatoneApp } from "lib/app-provider"; import { getInstantiatedListByUserQueryDocument, @@ -43,8 +40,6 @@ import { unwrap, } from "lib/utils"; -import type { PublicInfo } from "./contract"; - interface InstantiateDetail { initMsg: string; initTxHash?: string; @@ -416,25 +411,3 @@ export const useRelatedProposalsCountByContractAddress = ( enabled: !!contractAddress, }); }; - -export const usePublicProjectByContractAddress = ( - contractAddress: Option -): UseQueryResult => { - const { currentChainRecord } = useWallet(); - const queryFn = useCallback(async (): Promise> => { - if (!contractAddress) throw new Error("Contract address not found"); - if (!currentChainRecord) throw new Error("No chain selected"); - return axios - .get( - `${CELATONE_API_ENDPOINT}/contracts/${getChainApiPath( - currentChainRecord.chain.chain_name - )}/${currentChainRecord.chain.chain_id}/${contractAddress}` - ) - .then(({ data: projectInfo }) => projectInfo); - }, [contractAddress, currentChainRecord]); - - return useQuery(["public_project_by_contract_address"], queryFn, { - keepPreviousData: true, - enabled: !!contractAddress, - }); -}; diff --git a/src/lib/services/publicProjectService.ts b/src/lib/services/publicProjectService.ts new file mode 100644 index 000000000..c92d58f18 --- /dev/null +++ b/src/lib/services/publicProjectService.ts @@ -0,0 +1,30 @@ +import { useWallet } from "@cosmos-kit/react"; +import type { UseQueryResult } from "@tanstack/react-query"; +import { useQuery } from "@tanstack/react-query"; +import axios from "axios"; +import { useCallback } from "react"; + +import { CELATONE_API_ENDPOINT, getChainApiPath } from "env"; +import type { Option, PublicInfo } from "lib/types"; + +export const usePublicProjectByContractAddress = ( + contractAddress: Option +): UseQueryResult => { + const { currentChainRecord } = useWallet(); + const queryFn = useCallback(async () => { + if (!contractAddress) throw new Error("Contract address not found"); + if (!currentChainRecord) throw new Error("No chain selected"); + return axios + .get( + `${CELATONE_API_ENDPOINT}/contracts/${getChainApiPath( + currentChainRecord.chain.chain_name + )}/${currentChainRecord.chain.chain_id}/${contractAddress}` + ) + .then(({ data: projectInfo }) => projectInfo); + }, [contractAddress, currentChainRecord]); + + return useQuery(["public_project_by_contract_address"], queryFn, { + keepPreviousData: true, + enabled: !!contractAddress, + }); +}; diff --git a/src/lib/types/projects.ts b/src/lib/types/projects.ts index 834b1134c..08b4f9eb4 100644 --- a/src/lib/types/projects.ts +++ b/src/lib/types/projects.ts @@ -56,3 +56,10 @@ export interface PublicProjectInfo { details: Detail; slug: string; } + +export interface PublicInfo { + slug: string; + name: string; + contractAddress: ContractAddr; + description: string; +} From 8cb5d4fbfe0d667b599efe674b07d1c93a7019f1 Mon Sep 17 00:00:00 2001 From: bkioshn Date: Thu, 26 Jan 2023 15:33:03 +0700 Subject: [PATCH 13/16] chore: move function, fix description logic --- src/lib/model/contract.ts | 10 +-- .../PublicContractDesc.tsx | 6 +- .../contract-description/UserContractDesc.tsx | 5 +- .../public-project/components/AllProject.tsx | 4 +- src/lib/pages/public-project/data.ts | 4 +- src/lib/services/publicProject.ts | 68 ------------------- src/lib/services/publicProjectService.ts | 67 +++++++++++++++++- 7 files changed, 81 insertions(+), 83 deletions(-) delete mode 100644 src/lib/services/publicProject.ts diff --git a/src/lib/model/contract.ts b/src/lib/model/contract.ts index e6e8694d6..bf52ebe6d 100644 --- a/src/lib/model/contract.ts +++ b/src/lib/model/contract.ts @@ -19,8 +19,10 @@ import { useTxsCountByContractAddress, useRelatedProposalsCountByContractAddress, } from "lib/services/contractService"; -import { usePublicProjectBySlugQuery } from "lib/services/publicProject"; -import { usePublicProjectByContractAddress } from "lib/services/publicProjectService"; +import { + usePublicProjectByContractAddress, + usePublicProjectBySlug, +} from "lib/services/publicProjectService"; import type { CodeLocalInfo } from "lib/stores/code"; import type { ContractLocalInfo, ContractListInfo } from "lib/stores/contract"; import type { @@ -102,9 +104,7 @@ export const useContractData = ( const assetInfos = useAssetInfos(); const { data: publicInfo } = usePublicProjectByContractAddress(contractAddress); - const { data: publicInfoBySlug } = usePublicProjectBySlugQuery( - publicInfo?.slug - ); + const { data: publicInfoBySlug } = usePublicProjectBySlug(publicInfo?.slug); const { data: instantiateInfo } = useQuery( ["query", "instantiateInfo", contractAddress], diff --git a/src/lib/pages/contract-details/components/contract-description/PublicContractDesc.tsx b/src/lib/pages/contract-details/components/contract-description/PublicContractDesc.tsx index e4e1aaef0..cc1964d99 100644 --- a/src/lib/pages/contract-details/components/contract-description/PublicContractDesc.tsx +++ b/src/lib/pages/contract-details/components/contract-description/PublicContractDesc.tsx @@ -17,12 +17,14 @@ export const PublicContractDesc = ({ const [showMore, setShowMore] = useState(false); const description = useMemo( - () => contractData.publicProject.publicInfo?.description, + () => + contractData.publicProject.publicInfo?.description || + "No public contract description", [contractData.publicProject.publicInfo?.description] ); const [ref, { noClamp, clampedText, key }] = useClampText({ - text: description || "No public contract description", + text: description, ellipsis: "...", lines: textLine(!contractData.contractLocalInfo?.description, showMore), }); diff --git a/src/lib/pages/contract-details/components/contract-description/UserContractDesc.tsx b/src/lib/pages/contract-details/components/contract-description/UserContractDesc.tsx index 0c0e9e270..ce7124fa2 100644 --- a/src/lib/pages/contract-details/components/contract-description/UserContractDesc.tsx +++ b/src/lib/pages/contract-details/components/contract-description/UserContractDesc.tsx @@ -16,12 +16,13 @@ export const UserContractDesc = ({ contractData }: UserContractDescProps) => { const [showMore, setShowMore] = useState(false); const description = useMemo( - () => contractData.contractLocalInfo?.description, + () => + contractData.contractLocalInfo?.description || "No contract description", [contractData.contractLocalInfo?.description] ); const [ref, { noClamp, clampedText, key }] = useClampText({ - text: description || "No contract description", + text: description, ellipsis: "...", lines: textLine( !contractData.publicProject.publicInfo?.description, diff --git a/src/lib/pages/public-project/components/AllProject.tsx b/src/lib/pages/public-project/components/AllProject.tsx index ddef13ba5..f8fb26219 100644 --- a/src/lib/pages/public-project/components/AllProject.tsx +++ b/src/lib/pages/public-project/components/AllProject.tsx @@ -8,7 +8,7 @@ import { MdOutlineManageSearch, MdSearchOff } from "react-icons/md"; import { TextInput } from "lib/components/forms"; import { EmptyState } from "lib/components/state/EmptyState"; import { usePublicProjectStore } from "lib/hooks"; -import { usePublicProjectsQuery } from "lib/services/publicProject"; +import { usePublicProjects } from "lib/services/publicProjectService"; import type { PublicProjectInfo } from "lib/types"; import { PublicProjectCard } from "./PublicProjectCard"; @@ -17,7 +17,7 @@ const sortByAtoZ = (projects: PublicProjectInfo[]) => projects.sort((a, b) => a.details.name.localeCompare(b.details.name)); export const AllProject = observer(() => { - const { data: publicProjectInfo } = usePublicProjectsQuery(); + const { data: publicProjectInfo } = usePublicProjects(); const [searchKeyword, setSearchKeyword] = useState(""); const { getSavedPublicProjects } = usePublicProjectStore(); const savedProjects = getSavedPublicProjects(); diff --git a/src/lib/pages/public-project/data.ts b/src/lib/pages/public-project/data.ts index 8730c777e..a9820c480 100644 --- a/src/lib/pages/public-project/data.ts +++ b/src/lib/pages/public-project/data.ts @@ -1,6 +1,6 @@ import { useRouter } from "next/router"; -import { usePublicProjectBySlugQuery } from "lib/services/publicProject"; +import { usePublicProjectBySlug } from "lib/services/publicProjectService"; import { getFirstQueryParam } from "lib/utils"; // TODO: @@ -12,7 +12,7 @@ import { getFirstQueryParam } from "lib/utils"; export const usePublicData = () => { const router = useRouter(); const projectSlug = getFirstQueryParam(router.query.slug); - const { data: projectInfo } = usePublicProjectBySlugQuery(projectSlug); + const { data: projectInfo } = usePublicProjectBySlug(projectSlug); return { publicCodes: projectInfo?.codes || [], diff --git a/src/lib/services/publicProject.ts b/src/lib/services/publicProject.ts deleted file mode 100644 index ce456b641..000000000 --- a/src/lib/services/publicProject.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { useWallet } from "@cosmos-kit/react"; -import { useQuery } from "@tanstack/react-query"; -import axios from "axios"; -import { useCallback } from "react"; - -import { CELATONE_API_ENDPOINT, getChainApiPath, getMainnetApiPath } from "env"; -import type { - Option, - RawContract, - RawPublicProjectInfo, - PublicProjectInfo, - Contract, -} from "lib/types"; - -const parseContract = (raw: RawContract): Contract => ({ - contractAddress: raw.address, - description: raw.description, - name: raw.name, - slug: raw.slug, -}); - -export const usePublicProjectsQuery = () => { - const { currentChainRecord } = useWallet(); - - const queryFn = useCallback(async () => { - if (!currentChainRecord) throw new Error("No chain selected"); - - return axios - .get( - `${CELATONE_API_ENDPOINT}/projects/${getChainApiPath( - currentChainRecord.chain.chain_name - )}/${getMainnetApiPath(currentChainRecord.chain.chain_id)}` - ) - .then(({ data: projects }) => - projects.map((project) => ({ - ...project, - contracts: project.contracts.map(parseContract), - })) - ); - }, [currentChainRecord]); - - return useQuery(["public_project"], queryFn, { - keepPreviousData: true, - }); -}; - -export const usePublicProjectBySlugQuery = (slug: Option) => { - const { currentChainRecord } = useWallet(); - const queryFn = useCallback(async (): Promise> => { - if (!slug) throw new Error("No project selected"); - if (!currentChainRecord) throw new Error("No chain selected"); - return axios - .get( - `${CELATONE_API_ENDPOINT}/projects/${getChainApiPath( - currentChainRecord.chain.chain_name - )}/${getMainnetApiPath(currentChainRecord.chain.chain_id)}/${slug}` - ) - .then(({ data: project }) => ({ - ...project, - contracts: project.contracts.map(parseContract), - })); - }, [currentChainRecord, slug]); - - return useQuery(["public_project_by_slug"], queryFn, { - keepPreviousData: true, - enabled: !!slug, - }); -}; diff --git a/src/lib/services/publicProjectService.ts b/src/lib/services/publicProjectService.ts index c92d58f18..a56d9f437 100644 --- a/src/lib/services/publicProjectService.ts +++ b/src/lib/services/publicProjectService.ts @@ -4,8 +4,71 @@ import { useQuery } from "@tanstack/react-query"; import axios from "axios"; import { useCallback } from "react"; -import { CELATONE_API_ENDPOINT, getChainApiPath } from "env"; -import type { Option, PublicInfo } from "lib/types"; +import { CELATONE_API_ENDPOINT, getChainApiPath, getMainnetApiPath } from "env"; +import type { + Contract, + Option, + PublicInfo, + PublicProjectInfo, + RawContract, + RawPublicProjectInfo, +} from "lib/types"; + +const parseContract = (raw: RawContract): Contract => ({ + contractAddress: raw.address, + description: raw.description, + name: raw.name, + slug: raw.slug, +}); + +export const usePublicProjects = () => { + const { currentChainRecord } = useWallet(); + + const queryFn = useCallback(async () => { + // eslint-disable-next-line sonarjs/no-duplicate-string + if (!currentChainRecord) throw new Error("No chain selected"); + + return axios + .get( + `${CELATONE_API_ENDPOINT}/projects/${getChainApiPath( + currentChainRecord.chain.chain_name + )}/${getMainnetApiPath(currentChainRecord.chain.chain_id)}` + ) + .then(({ data: projects }) => + projects.map((project) => ({ + ...project, + contracts: project.contracts.map(parseContract), + })) + ); + }, [currentChainRecord]); + + return useQuery(["public_project"], queryFn, { + keepPreviousData: true, + }); +}; + +export const usePublicProjectBySlug = (slug: Option) => { + const { currentChainRecord } = useWallet(); + const queryFn = useCallback(async (): Promise> => { + if (!slug) throw new Error("No project selected"); + if (!currentChainRecord) throw new Error("No chain selected"); + return axios + .get( + `${CELATONE_API_ENDPOINT}/projects/${getChainApiPath( + currentChainRecord.chain.chain_name + )}/${getMainnetApiPath(currentChainRecord.chain.chain_id)}/${slug}` + ) + .then(({ data: project }) => ({ + ...project, + contracts: project.contracts.map(parseContract), + })); + }, [currentChainRecord, slug]); + + return useQuery(["public_project_by_slug"], queryFn, { + keepPreviousData: true, + enabled: !!slug, + }); +}; export const usePublicProjectByContractAddress = ( contractAddress: Option From 3a9e801ef94c2ed4309c682bbe12f55de832c83a Mon Sep 17 00:00:00 2001 From: poomthiti Date: Thu, 26 Jan 2023 17:39:59 +0700 Subject: [PATCH 14/16] refactor: change error message type to string --- src/lib/components/ErrorMessageRender.tsx | 2 +- src/lib/components/forms/TextInput.tsx | 4 +--- src/lib/pages/admin/index.tsx | 2 +- src/lib/pages/execute/components/ExecuteArea.tsx | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/lib/components/ErrorMessageRender.tsx b/src/lib/components/ErrorMessageRender.tsx index c6e985f86..595a28f22 100644 --- a/src/lib/components/ErrorMessageRender.tsx +++ b/src/lib/components/ErrorMessageRender.tsx @@ -3,7 +3,7 @@ import { Flex, Icon, Text } from "@chakra-ui/react"; import { IoIosWarning } from "react-icons/io"; interface ErrorMessageRenderProps extends FlexProps { - error: Error["message"]; + error: string; } export const ErrorMessageRender = ({ diff --git a/src/lib/components/forms/TextInput.tsx b/src/lib/components/forms/TextInput.tsx index 0bc1659bf..1afd9c261 100644 --- a/src/lib/components/forms/TextInput.tsx +++ b/src/lib/components/forms/TextInput.tsx @@ -11,14 +11,12 @@ import { import type { FormControlProps } from "@chakra-ui/react"; import type { HTMLInputTypeAttribute, Dispatch, SetStateAction } from "react"; -import type { ContractAddr, HumanAddr } from "lib/types"; - import type { FormStatus } from "./FormStatus"; import { getResponseMsg, getStatusIcon } from "./FormStatus"; export interface TextInputProps extends FormControlProps { value: string; - setInputState: Dispatch>; + setInputState: Dispatch>; label?: string; labelBgColor?: string; helperText?: string; diff --git a/src/lib/pages/admin/index.tsx b/src/lib/pages/admin/index.tsx index 441cf8e62..1f3d9f43d 100644 --- a/src/lib/pages/admin/index.tsx +++ b/src/lib/pages/admin/index.tsx @@ -42,7 +42,7 @@ const UpdateAdmin = () => { message: "", }); const [estimatedFee, setEstimatedFee] = useState(); - const [simulateError, setSimulateError] = useState(); + const [simulateError, setSimulateError] = useState(); const contractAddressParam = getFirstQueryParam( router.query.contract diff --git a/src/lib/pages/execute/components/ExecuteArea.tsx b/src/lib/pages/execute/components/ExecuteArea.tsx index 51e4268cf..8b87fd923 100644 --- a/src/lib/pages/execute/components/ExecuteArea.tsx +++ b/src/lib/pages/execute/components/ExecuteArea.tsx @@ -62,7 +62,7 @@ export const ExecuteArea = ({ control, setValue, cmds }: ExecuteAreaProps) => { const [fee, setFee] = useState(); const [msg, setMsg] = useState(initialMsg); - const [error, setError] = useState(); + const [error, setError] = useState(); const [composedTxMsg, setComposedTxMsg] = useState([]); const [processing, setProcessing] = useState(false); From fb35d9d520fdf408412639598652a5b198a33b59 Mon Sep 17 00:00:00 2001 From: poomthiti Date: Thu, 26 Jan 2023 18:31:13 +0700 Subject: [PATCH 15/16] feat: change transaction completed modal wording and buttons --- src/lib/app-fns/tx/updateAdmin.tsx | 3 ++- src/lib/components/modal/tx/ButtonSection.tsx | 21 +++++++++++++++++++ src/lib/types/tx/model.ts | 7 ++++++- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/lib/app-fns/tx/updateAdmin.tsx b/src/lib/app-fns/tx/updateAdmin.tsx index 0f8cb148d..c0d39f441 100644 --- a/src/lib/app-fns/tx/updateAdmin.tsx +++ b/src/lib/app-fns/tx/updateAdmin.tsx @@ -62,11 +62,12 @@ export const updateAdminTx = ({ }, ], receiptInfo: { - header: "Transaction Complete", + header: "Update Admin Complete", headerIcon: ( ), }, + actionVariant: "admin", } as TxResultRendering; } )().pipe(catchTxError(onTxFailed)); diff --git a/src/lib/components/modal/tx/ButtonSection.tsx b/src/lib/components/modal/tx/ButtonSection.tsx index ac7b81d1e..ca45a2c59 100644 --- a/src/lib/components/modal/tx/ButtonSection.tsx +++ b/src/lib/components/modal/tx/ButtonSection.tsx @@ -1,5 +1,6 @@ import { Button, Icon } from "@chakra-ui/react"; import { useWallet } from "@cosmos-kit/react"; +import { useRouter } from "next/router"; import { useCallback } from "react"; import { FiChevronRight } from "react-icons/fi"; @@ -20,6 +21,7 @@ export const ButtonSection = ({ }: ButtonSectionProps) => { const navigate = useInternalNavigate(); const { currentChainName } = useWallet(); + const router = useRouter(); const openExplorer = useCallback(() => { const txHash = receipts.find((r) => r.title === "Tx Hash")?.value; @@ -64,6 +66,25 @@ export const ButtonSection = ({ ); + case "admin": + return ( + <> + + + + ); case "rejected": case "resend": return ( diff --git a/src/lib/types/tx/model.ts b/src/lib/types/tx/model.ts index a2f3fbf2c..1216a76c9 100644 --- a/src/lib/types/tx/model.ts +++ b/src/lib/types/tx/model.ts @@ -23,7 +23,12 @@ export interface ReceiptInfo { description?: ReactNode; } -export type ActionVariant = "sending" | "upload" | "rejected" | "resend"; +export type ActionVariant = + | "sending" + | "upload" + | "rejected" + | "resend" + | "admin"; export interface TxResultRendering { /** From f1d32e9168b26b608e5a827b0756404e979eb5c0 Mon Sep 17 00:00:00 2001 From: poomthiti Date: Thu, 26 Jan 2023 18:44:31 +0700 Subject: [PATCH 16/16] chore: change variant wording --- src/lib/app-fns/tx/updateAdmin.tsx | 2 +- src/lib/components/modal/tx/ButtonSection.tsx | 2 +- src/lib/types/tx/model.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/app-fns/tx/updateAdmin.tsx b/src/lib/app-fns/tx/updateAdmin.tsx index c0d39f441..103c8342e 100644 --- a/src/lib/app-fns/tx/updateAdmin.tsx +++ b/src/lib/app-fns/tx/updateAdmin.tsx @@ -67,7 +67,7 @@ export const updateAdminTx = ({ ), }, - actionVariant: "admin", + actionVariant: "update-admin", } as TxResultRendering; } )().pipe(catchTxError(onTxFailed)); diff --git a/src/lib/components/modal/tx/ButtonSection.tsx b/src/lib/components/modal/tx/ButtonSection.tsx index ca45a2c59..bb0b831fc 100644 --- a/src/lib/components/modal/tx/ButtonSection.tsx +++ b/src/lib/components/modal/tx/ButtonSection.tsx @@ -66,7 +66,7 @@ export const ButtonSection = ({ ); - case "admin": + case "update-admin": return ( <>