From eec4919ed2bcf204e08732fbda45f3c5cf511a61 Mon Sep 17 00:00:00 2001 From: poomthiti Date: Mon, 21 Aug 2023 15:31:45 +0700 Subject: [PATCH 1/3] feat: switch to schema tab when available and prefill,expand,and scroll to the prefill msg --- CHANGELOG.md | 1 + src/lib/app-provider/contexts/app.tsx | 25 +--- src/lib/app-provider/contexts/index.ts | 1 + src/lib/app-provider/contexts/nav.tsx | 42 ++++++ src/lib/components/ContractSelectSection.tsx | 140 +++++++++--------- src/lib/layout/index.tsx | 4 +- src/lib/pages/home/index.tsx | 8 +- src/lib/pages/query/components/QueryArea.tsx | 9 +- .../pages/query/components/SchemaQuery.tsx | 62 ++++++-- src/lib/utils/json.ts | 10 ++ src/pages/_app.tsx | 14 +- 11 files changed, 203 insertions(+), 113 deletions(-) create mode 100644 src/lib/app-provider/contexts/nav.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f3a5b434..a21dcd3a3 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 +- [#486](https://github.com/alleslabs/celatone-frontend/pull/486) Switch to schema tab when available and prefill,expand,and scroll to the prefill msg - [#482](https://github.com/alleslabs/celatone-frontend/pull/482) Add json schema functionality to query page - [#477](https://github.com/alleslabs/celatone-frontend/pull/477) Add json schema section on code detail page - [#475](https://github.com/alleslabs/celatone-frontend/pull/475) Add json schema functionality to instantiate contract diff --git a/src/lib/app-provider/contexts/app.tsx b/src/lib/app-provider/contexts/app.tsx index 967f78395..d0870a9fb 100644 --- a/src/lib/app-provider/contexts/app.tsx +++ b/src/lib/app-provider/contexts/app.tsx @@ -1,7 +1,7 @@ import { useModalTheme } from "@cosmos-kit/react"; import { GraphQLClient } from "graphql-request"; import { observer } from "mobx-react-lite"; -import type { Dispatch, ReactNode, SetStateAction } from "react"; +import type { ReactNode } from "react"; import { useCallback, useState, @@ -21,13 +21,11 @@ import { SUPPORTED_CHAIN_IDS } from "env"; import { LoadingOverlay } from "lib/components/LoadingOverlay"; import { NetworkErrorState } from "lib/components/state/NetworkErrorState"; import { DEFAULT_ADDRESS } from "lib/data"; -import { useLocalStorage } from "lib/hooks/useLocalStorage"; import { useCodeStore, useContractStore, usePublicProjectStore, } from "lib/providers/store"; -import type { Option } from "lib/types"; import { formatUserKey } from "lib/utils"; interface AppProviderProps { @@ -40,10 +38,6 @@ interface AppContextInterface { chainConfig: ChainConfig; indexerGraphClient: GraphQLClient; constants: ProjectConstants; - isExpand: boolean; - isDevMode: Option; - setIsExpand: Dispatch>; - setIsDevMode: Dispatch>>; } const AppContext = createContext({ @@ -52,10 +46,6 @@ const AppContext = createContext({ chainConfig: DEFAULT_CHAIN_CONFIG, indexerGraphClient: new GraphQLClient(DEFAULT_CHAIN_CONFIG.indexer), constants: PROJECT_CONSTANTS, - isExpand: false, - isDevMode: undefined, - setIsExpand: () => {}, - setIsDevMode: () => {}, }); export const AppProvider = observer(({ children }: AppProviderProps) => { @@ -67,13 +57,6 @@ export const AppProvider = observer(({ children }: AppProviderProps) => { const [currentChainName, setCurrentChainName] = useState(); const [currentChainId, setCurrentChainId] = useState(""); - // TODO - Revisit localstorage - const [isDevMode, setIsDevMode] = useLocalStorage>( - "devMode", - undefined - ); - const [isExpand, setIsExpand] = useLocalStorage("navbar", false); - // Remark: this function is only used in useSelectChain. Do not use in other places. const handleOnChainIdChange = useCallback((newChainId: string) => { const config = CHAIN_CONFIGS[newChainId]; @@ -90,12 +73,8 @@ export const AppProvider = observer(({ children }: AppProviderProps) => { chainConfig, indexerGraphClient: new GraphQLClient(chainConfig.indexer), constants: PROJECT_CONSTANTS, - isDevMode, - isExpand, - setIsDevMode, - setIsExpand, }; - }, [currentChainId, isDevMode, isExpand, setIsDevMode, setIsExpand]); + }, [currentChainId]); useEffect(() => { if (currentChainName) { diff --git a/src/lib/app-provider/contexts/index.ts b/src/lib/app-provider/contexts/index.ts index ac5307def..641ade193 100644 --- a/src/lib/app-provider/contexts/index.ts +++ b/src/lib/app-provider/contexts/index.ts @@ -1 +1,2 @@ export * from "./app"; +export * from "./nav"; diff --git a/src/lib/app-provider/contexts/nav.tsx b/src/lib/app-provider/contexts/nav.tsx new file mode 100644 index 000000000..8eea19795 --- /dev/null +++ b/src/lib/app-provider/contexts/nav.tsx @@ -0,0 +1,42 @@ +import type { Dispatch, ReactNode, SetStateAction } from "react"; +import { useContext, createContext, useMemo } from "react"; + +import { useLocalStorage } from "lib/hooks/useLocalStorage"; +import type { Option } from "lib/types"; + +interface NavContextInterface { + isExpand: boolean; + isDevMode: Option; + setIsExpand: Dispatch>; + setIsDevMode: Dispatch>>; +} +const NavContext = createContext({ + isExpand: false, + isDevMode: undefined, + setIsExpand: () => {}, + setIsDevMode: () => {}, +}); + +export const NavProvider = ({ children }: { children: ReactNode }) => { + const [isDevMode, setIsDevMode] = useLocalStorage>( + "devMode", + undefined + ); + const [isExpand, setIsExpand] = useLocalStorage("navbar", false); + + const states = useMemo( + () => ({ + isDevMode, + isExpand, + setIsDevMode, + setIsExpand, + }), + [isDevMode, isExpand, setIsDevMode, setIsExpand] + ); + + return {children}; +}; + +export const useNavContext = (): NavContextInterface => { + return useContext(NavContext); +}; diff --git a/src/lib/components/ContractSelectSection.tsx b/src/lib/components/ContractSelectSection.tsx index 2c1ed7621..bcde0c295 100644 --- a/src/lib/components/ContractSelectSection.tsx +++ b/src/lib/components/ContractSelectSection.tsx @@ -12,6 +12,7 @@ import type { Addr, ContractAddr, Option } from "lib/types"; import { ExplorerLink } from "./ExplorerLink"; import { CustomIcon } from "./icon"; +import { LoadingOverlay } from "./LoadingOverlay"; import { EditContractDetailsModal, SaveContractDetailsModal } from "./modal"; import { SelectContractAdmin, @@ -156,7 +157,7 @@ export const ContractSelectSection = observer( mode: "all", }); - const { refetch } = useContractDetailByContractAddress( + const { refetch, isFetching } = useContractDetailByContractAddress( contractAddress, (data) => { successCallback?.(data); @@ -188,77 +189,80 @@ export const ContractSelectSection = observer( const style = modeStyle(mode); return ( - - - - Contract Address - {!notSelected ? ( - - ) : ( - - Not Selected - - )} - - - Contract Name - - - - {mode === "all-lists" && contractState.isValid && ( - - )} - {mode === "all-lists" ? ( - - ) : ( - + {isFetching && } + + + + Contract Address + {!notSelected ? ( + + ) : ( + + Not Selected + + )} + + + Contract Name + - )} + + + {mode === "all-lists" && contractState.isValid && ( + + )} + {mode === "all-lists" ? ( + + ) : ( + + )} + - + ); } ); diff --git a/src/lib/layout/index.tsx b/src/lib/layout/index.tsx index 180746438..171a1e431 100644 --- a/src/lib/layout/index.tsx +++ b/src/lib/layout/index.tsx @@ -3,7 +3,7 @@ import { useRouter } from "next/router"; import type { ReactNode } from "react"; import { useEffect, useMemo } from "react"; -import { useCelatoneApp, useMobile } from "lib/app-provider"; +import { useMobile, useNavContext } from "lib/app-provider"; import { scrollToTop } from "lib/utils"; import Footer from "./Footer"; @@ -19,7 +19,7 @@ type LayoutProps = { const Layout = ({ children }: LayoutProps) => { const router = useRouter(); const isMobile = useMobile(); - const { isExpand, isDevMode, setIsExpand, setIsDevMode } = useCelatoneApp(); + const { isExpand, isDevMode, setIsExpand, setIsDevMode } = useNavContext(); const defaultRow = "70px 48px 1fr"; const mode = useMemo(() => { diff --git a/src/lib/pages/home/index.tsx b/src/lib/pages/home/index.tsx index f83c50f01..2b4409f39 100644 --- a/src/lib/pages/home/index.tsx +++ b/src/lib/pages/home/index.tsx @@ -4,7 +4,11 @@ import { useRouter } from "next/router"; import { useEffect } from "react"; import { CURR_THEME } from "env"; -import { useCelatoneApp, useInternalNavigate } from "lib/app-provider"; +import { + useCelatoneApp, + useInternalNavigate, + useNavContext, +} from "lib/app-provider"; import { ConnectWalletAlert } from "lib/components/ConnectWalletAlert"; import { CustomIcon } from "lib/components/icon"; import PageContainer from "lib/components/PageContainer"; @@ -108,7 +112,7 @@ const calculateAverageBlockTime = ( const Home = () => { const router = useRouter(); const navigate = useInternalNavigate(); - const { isDevMode } = useCelatoneApp(); + const { isDevMode } = useNavContext(); const { chainConfig: { prettyName }, diff --git a/src/lib/pages/query/components/QueryArea.tsx b/src/lib/pages/query/components/QueryArea.tsx index 59c576c49..1be05e451 100644 --- a/src/lib/pages/query/components/QueryArea.tsx +++ b/src/lib/pages/query/components/QueryArea.tsx @@ -32,6 +32,9 @@ export const QueryArea = ({ useEffect(() => { if (!schema) setTab(MessageTabs.JSON_INPUT); + else { + setTab(MessageTabs.YOUR_SCHEMA); + } }, [schema]); return ( @@ -61,7 +64,11 @@ export const QueryArea = ({ } schemaContent={ codeHash ? ( - + ) : ( import("lib/components/modal/CodeSnippet"), { ssr: false, @@ -49,6 +53,7 @@ interface QueryComponentInterface { contractAddress: ContractAddr; lcdEndpoint: string; walletAddress: Option; + initialMsg: Record; addActivity: (activity: Activity) => void; } @@ -58,6 +63,7 @@ const QueryComponent = ({ contractAddress, lcdEndpoint, walletAddress, + initialMsg, addActivity, }: QueryComponentInterface) => { const [msg, setMsg] = useState("{}"); @@ -117,7 +123,7 @@ const QueryComponent = ({ }, [msgSchema.inputRequired, refetch]); return ( - +
@@ -140,6 +146,7 @@ const QueryComponent = ({ formId={`query-${msgSchema.title}`} schema={msgSchema.schema} onChange={(data) => setMsg(JSON.stringify(data))} + initialFormData={initialMsg} /> { - try { - return JSON.parse(res); - } catch (_) { - return {}; - } - })()} + initialFormData={parseSchemaInitialData(res)} /> {!msgSchema.inputRequired && ( @@ -247,13 +248,29 @@ const QueryComponent = ({ interface SchemaQueryProps { schema: Option; contractAddress: ContractAddr; + initialMsg: string; } -export const SchemaQuery = ({ schema, contractAddress }: SchemaQueryProps) => { +const resolveInitialMsg = ( + initialMsg: string, + msgSchema: QueryExecuteSchema +) => { + const parsed = parseSchemaInitialData(initialMsg); + return Object.keys(parsed)[0] === msgSchema.schema.required?.[0] + ? parsed + : {}; +}; + +export const SchemaQuery = ({ + schema, + contractAddress, + initialMsg, +}: SchemaQueryProps) => { const { addActivity } = useContractStore(); const { address } = useCurrentChain(); const lcdEndpoint = useBaseApiRoute("rest"); + const accordionRef = useRef(null); const [keyword, setKeyword] = useState(""); const [expandedIndexes, setExpandedIndexes] = useState([]); @@ -263,6 +280,27 @@ export const SchemaQuery = ({ schema, contractAddress }: SchemaQueryProps) => { [schema, keyword] ); + useEffect(() => { + if (schema && initialMsg && accordionRef.current) { + try { + const parsedMsg = JSON.parse(initialMsg); + const msgIndex = schema.findIndex( + ([msg]) => msg.schema.required?.[0] === Object.keys(parsedMsg)[0] + ); + setExpandedIndexes((prev) => + prev.includes(msgIndex) ? prev : prev.concat(msgIndex) + ); + const el = document.querySelector( + `.msg-${schema[msgIndex][0].schema.required?.[0]}` + ); + el?.scrollIntoView(); + } catch (_) { + // + } + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [schema, initialMsg, accordionRef.current]); + if (!schema) return null; return ( @@ -289,6 +327,7 @@ export const SchemaQuery = ({ schema, contractAddress }: SchemaQueryProps) => { {filteredMsgs?.length ? ( { contractAddress={contractAddress} lcdEndpoint={lcdEndpoint} walletAddress={address} + initialMsg={resolveInitialMsg(initialMsg, msg)} addActivity={addActivity} /> ))} diff --git a/src/lib/utils/json.ts b/src/lib/utils/json.ts index 48f0670d4..f743ab333 100644 --- a/src/lib/utils/json.ts +++ b/src/lib/utils/json.ts @@ -18,3 +18,13 @@ export const jsonPrettify = (text: string) => { }; export const jsonLineCount = (text: string) => text.split(/\n/).length; + +export const parseSchemaInitialData = ( + json: string +): Record => { + try { + return JSON.parse(json); + } catch (_) { + return {}; + } +}; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 3a32134f8..41b94ade0 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -11,7 +11,7 @@ import Head from "next/head"; import Script from "next/script"; import { CHAIN_CONFIGS } from "config/chain"; -import { AppProvider } from "lib/app-provider/contexts/app"; +import { AppProvider, NavProvider } from "lib/app-provider"; import { localosmosis, localosmosisAsset, @@ -99,11 +99,13 @@ const MyApp = ({ Component, pageProps }: AppProps) => { /> - - - - - + + + + + + + From bfb058e016b351d6a29c446979841648629f8751 Mon Sep 17 00:00:00 2001 From: poomthiti Date: Mon, 21 Aug 2023 16:00:50 +0700 Subject: [PATCH 2/3] fix: set msg state on prefill --- src/lib/pages/query/components/SchemaQuery.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/pages/query/components/SchemaQuery.tsx b/src/lib/pages/query/components/SchemaQuery.tsx index 2ee3c0cf0..ceb67ded5 100644 --- a/src/lib/pages/query/components/SchemaQuery.tsx +++ b/src/lib/pages/query/components/SchemaQuery.tsx @@ -70,6 +70,10 @@ const QueryComponent = ({ const [res, setRes] = useState("{}"); const [queryError, setQueryError] = useState(""); + useEffect(() => { + if (Object.keys(initialMsg).length) setMsg(JSON.stringify(initialMsg)); + }, [initialMsg]); + // TODO: Abstract query const { refetch, From dec5f34a0dd48091615443421563cb1b600f865b Mon Sep 17 00:00:00 2001 From: evilpeach Date: Tue, 22 Aug 2023 10:34:36 +0700 Subject: [PATCH 3/3] fix: add key in list StyledCustomTab --- .../code-details/components/json-schema/CodeSchemaSection.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib/pages/code-details/components/json-schema/CodeSchemaSection.tsx b/src/lib/pages/code-details/components/json-schema/CodeSchemaSection.tsx index 6784dc810..040d9b049 100644 --- a/src/lib/pages/code-details/components/json-schema/CodeSchemaSection.tsx +++ b/src/lib/pages/code-details/components/json-schema/CodeSchemaSection.tsx @@ -104,7 +104,9 @@ export const CodeSchemaSection = ({ Full Schema {SchemaMsgTabList.map((schemaProperty) => ( - {capitalize(schemaProperty)}Msg + + {capitalize(schemaProperty)}Msg + ))}