diff --git a/CHANGELOG.md b/CHANGELOG.md index c8872fbaa..917350389 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,6 +76,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Improvements +- [#553](https://github.com/alleslabs/celatone-frontend/pull/553) Use Cmd on Mac OS while Ctrl on others - [#548](https://github.com/alleslabs/celatone-frontend/pull/548) Handle interaction page query param and refactor page - [#546](https://github.com/alleslabs/celatone-frontend/pull/546) Handle 404 on the current selected chain - [#540](https://github.com/alleslabs/celatone-frontend/pull/540) Add open proposal configuration diff --git a/src/lib/app-provider/hooks/index.ts b/src/lib/app-provider/hooks/index.ts index 1c16c0e72..adec0f195 100644 --- a/src/lib/app-provider/hooks/index.ts +++ b/src/lib/app-provider/hooks/index.ts @@ -14,3 +14,4 @@ export * from "./useConfig"; export * from "./useCurrentChain"; export * from "./useConvertHexAddress"; export * from "./usePreviousPathname"; +export * from "./usePlatform"; diff --git a/src/lib/app-provider/hooks/usePlatform.ts b/src/lib/app-provider/hooks/usePlatform.ts new file mode 100644 index 000000000..c03f690f2 --- /dev/null +++ b/src/lib/app-provider/hooks/usePlatform.ts @@ -0,0 +1,16 @@ +import { useMemo } from "react"; + +export const usePlatform = () => { + return useMemo(() => { + if (typeof navigator === "undefined") { + return "Unknown"; + } + const { userAgent } = navigator; + const isMac = /Mac OS X/.test(userAgent); + return isMac ? "Mac" : "Windows"; + }, []); +}; + +export const useIsMac = () => { + return usePlatform() === "Mac"; +}; diff --git a/src/lib/pages/execute/components/JsonExecute.tsx b/src/lib/pages/execute/components/JsonExecute.tsx index febcb971a..cdae57f55 100644 --- a/src/lib/pages/execute/components/JsonExecute.tsx +++ b/src/lib/pages/execute/components/JsonExecute.tsx @@ -10,6 +10,7 @@ import { useExecuteContractTx, useCurrentChain, useMobile, + useIsMac, } from "lib/app-provider"; import { useAttachFunds } from "lib/app-provider/hooks/useAttachFunds"; import { useSimulateFeeQuery } from "lib/app-provider/queries"; @@ -63,6 +64,7 @@ export const JsonExecute = ({ // --------------DEPENDENCIES----------------// // ------------------------------------------// const isMobile = useMobile(); + const isMac = useIsMac(); const { address } = useCurrentChain(); const fabricateFee = useFabricateFee(); const executeTx = useExecuteContractTx(); @@ -237,10 +239,12 @@ export const JsonExecute = ({ assetsSelect, ]); + const isButtonDisabled = !enableExecute || !fee || isFetching; useEffect(() => { const keydownHandler = (e: KeyboardEvent) => { // TODO: problem with safari if focusing in the textarea - if (e.ctrlKey && e.key === "Enter") { + const specialKey = isMac ? e.metaKey : e.ctrlKey; + if (!isButtonDisabled && specialKey && e.key === "Enter") { proceed(); } }; @@ -294,12 +298,12 @@ export const JsonExecute = ({ fontSize="14px" p="6px 16px" onClick={proceed} - isDisabled={!enableExecute || !fee || isFetching} + isDisabled={isButtonDisabled} leftIcon={} isLoading={processing} sx={{ pointerEvents: processing && "none" }} > - Execute {!isMobile && "(Ctrl + Enter)"} + Execute {!isMobile && ` (${isMac ? "⌘" : "Ctrl"} + Enter)`} diff --git a/src/lib/pages/interact/component/form/ExecuteArea.tsx b/src/lib/pages/interact/component/form/ExecuteArea.tsx index f2e8dea8b..56578142f 100644 --- a/src/lib/pages/interact/component/form/ExecuteArea.tsx +++ b/src/lib/pages/interact/component/form/ExecuteArea.tsx @@ -9,6 +9,7 @@ import { useSimulateFeeQuery, useExecuteModuleTx, useCurrentChain, + useIsMac, } from "lib/app-provider"; import { AbiForm } from "lib/components/abi"; import { ConnectWalletAlert } from "lib/components/ConnectWalletAlert"; @@ -35,6 +36,7 @@ export const ExecuteArea = ({ ? fn.params.slice(1) : fn.params; + const isMac = useIsMac(); const { address } = useCurrentChain(); const fabricateFee = useFabricateFee(); const executeModuleTx = useExecuteModuleTx(); @@ -119,6 +121,21 @@ export const ExecuteArea = ({ return () => {}; }, [address, data, enableExecute, executeFn, moduleAddress, moduleName]); + const isButtonDisabled = !enableExecute || !fee || isFetching; + useEffect(() => { + const keydownHandler = (e: KeyboardEvent) => { + // TODO: problem with safari if focusing in the textarea + const specialKey = isMac ? e.metaKey : e.ctrlKey; + if (!isButtonDisabled && specialKey && e.key === "Enter") { + proceed(); + } + }; + document.addEventListener("keydown", keydownHandler); + return () => { + document.removeEventListener("keydown", keydownHandler); + }; + }); + return ( {fn.is_entry ? ( @@ -166,12 +183,12 @@ export const ExecuteArea = ({ fontSize="14px" p="6px 16px" onClick={proceed} - isDisabled={!enableExecute || !fee || isFetching} + isDisabled={isButtonDisabled} leftIcon={} isLoading={processing} sx={{ pointerEvents: processing && "none" }} > - Execute + Execute{` (${isMac ? "⌘" : "Ctrl"} + Enter)`} diff --git a/src/lib/pages/interact/component/form/ViewArea.tsx b/src/lib/pages/interact/component/form/ViewArea.tsx index 579100ba8..7f3cbf693 100644 --- a/src/lib/pages/interact/component/form/ViewArea.tsx +++ b/src/lib/pages/interact/component/form/ViewArea.tsx @@ -9,8 +9,9 @@ import { Spinner, Text, } from "@chakra-ui/react"; -import { useState } from "react"; +import { useEffect, useState } from "react"; +import { useIsMac } from "lib/app-provider"; import { AbiForm } from "lib/components/abi"; import { CustomIcon } from "lib/components/icon"; import JsonReadOnly from "lib/components/json/JsonReadOnly"; @@ -34,6 +35,7 @@ export const ViewArea = ({ moduleName: string; fn: ExposedFunction; }) => { + const isMac = useIsMac(); const [abiData, setAbiData] = useState({ typeArgs: getAbiInitialData(fn.generic_type_params.length), args: getAbiInitialData(fn.params.length), @@ -61,7 +63,19 @@ export const ViewArea = ({ }; const isLoading = queryFetching || queryRefetching; - const isDisabled = Boolean(abiErrors.length); + const isButtonDisabled = Boolean(abiErrors.length); + useEffect(() => { + const keydownHandler = (e: KeyboardEvent) => { + // TODO: problem with safari if focusing in the textarea + const specialKey = isMac ? e.metaKey : e.ctrlKey; + if (!isButtonDisabled && specialKey && e.key === "Enter") handleQuery(); + }; + document.addEventListener("keydown", keydownHandler); + return () => { + document.removeEventListener("keydown", keydownHandler); + }; + }); + return ( @@ -79,11 +93,11 @@ export const ViewArea = ({ p="6px 16px" size={{ base: "sm", md: "md" }} onClick={handleQuery} - isDisabled={isDisabled} + isDisabled={isButtonDisabled} isLoading={isLoading} leftIcon={} > - View + View{` (${isMac ? "⌘" : "Ctrl"} + Enter)`} diff --git a/src/lib/pages/query/components/JsonQuery.tsx b/src/lib/pages/query/components/JsonQuery.tsx index b6857989a..399a0430d 100644 --- a/src/lib/pages/query/components/JsonQuery.tsx +++ b/src/lib/pages/query/components/JsonQuery.tsx @@ -9,6 +9,7 @@ import { CELATONE_QUERY_KEYS, useBaseApiRoute, useCurrentChain, + useIsMac, useMobile, } from "lib/app-provider"; import { ContractCmdButton } from "lib/components/ContractCmdButton"; @@ -42,6 +43,7 @@ interface JsonQueryProps { export const JsonQuery = ({ contractAddress, initialMsg }: JsonQueryProps) => { const { track, trackAction } = useTrack(); const isMobile = useMobile(); + const isMac = useIsMac(); const { isFetching: cmdsFetching, queryCmds } = useQueryCmds(contractAddress); const lcdEndpoint = useBaseApiRoute("rest"); const { addActivity } = useContractStore(); @@ -88,10 +90,12 @@ export const JsonQuery = ({ contractAddress, initialMsg }: JsonQueryProps) => { refetch(); }; + const isButtonDisabled = jsonValidate(msg) !== null; useEffect(() => { const keydownHandler = (e: KeyboardEvent) => { // TODO: problem with safari if focusing in the textarea - if (e.ctrlKey && e.key === "Enter") handleQuery(); + const specialKey = isMac ? e.metaKey : e.ctrlKey; + if (!isButtonDisabled && specialKey && e.key === "Enter") handleQuery(); }; document.addEventListener("keydown", keydownHandler); return () => { @@ -161,11 +165,11 @@ export const JsonQuery = ({ contractAddress, initialMsg }: JsonQueryProps) => { p="6px 16px" size={{ base: "sm", md: "md" }} onClick={handleQuery} - isDisabled={jsonValidate(msg) !== null} + isDisabled={isButtonDisabled} isLoading={queryFetching || queryRefetching} leftIcon={} > - Query {!isMobile && "(Ctrl + Enter)"} + Query{!isMobile && ` (${isMac ? "⌘" : "Ctrl"} + Enter)`}