From 196dacb0d8cbf6407b9b6b734dc9e96ed0b2802c Mon Sep 17 00:00:00 2001 From: Jennie Alles Date: Mon, 24 Jun 2024 16:32:28 +0700 Subject: [PATCH 01/24] feat(components): new network selector --- src/config/chain/default.ts | 1 + src/config/chain/initia.ts | 18 ++ src/config/chain/neutron.ts | 4 + src/config/chain/osmosis.ts | 8 + src/config/chain/sei.ts | 4 + src/config/chain/stargaze.ts | 3 + src/config/chain/terra.ts | 8 + src/config/chain/types.ts | 2 + src/lib/layout/NetworkCard.tsx | 62 +++++++ src/lib/layout/NetworkMenu.tsx | 221 +++++++++++++++++++---- src/lib/styles/theme/components/index.ts | 2 + src/lib/styles/theme/components/kbd.ts | 10 + 12 files changed, 311 insertions(+), 32 deletions(-) create mode 100644 src/lib/layout/NetworkCard.tsx create mode 100644 src/lib/styles/theme/components/kbd.ts diff --git a/src/config/chain/default.ts b/src/config/chain/default.ts index 7aca8eb5a..2f783eb98 100644 --- a/src/config/chain/default.ts +++ b/src/config/chain/default.ts @@ -5,6 +5,7 @@ export const DEFAULT_CHAIN_CONFIG: ChainConfig = { chain: "", registryChainName: "", prettyName: "", + networkType: "testnet", lcd: "", rpc: "", indexer: "", diff --git a/src/config/chain/initia.ts b/src/config/chain/initia.ts index d40275d24..ff725e427 100644 --- a/src/config/chain/initia.ts +++ b/src/config/chain/initia.ts @@ -12,6 +12,8 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { chain: "initia", registryChainName: "blackwingtestnet", prettyName: "Blackwing Testnet Lite", + networkType: "testnet", + logoUrl: "https://assets.alleslabs.dev/integrations/chains/blackwing.png", lcd: "https://maze-rest-18bdff44-3aa4-425e-9bc0-06a2afa40af8.ase1-prod.newmetric.xyz", rpc: "https://maze-rpc-18bdff44-3aa4-425e-9bc0-06a2afa40af8.ase1-prod.newmetric.xyz", indexer: "https://tomcat-1-graphql.alleslabs.dev/v1/graphql", @@ -60,6 +62,8 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { chain: "initia", registryChainName: "initiatestnet", prettyName: "Initia", + networkType: "testnet", + logoUrl: "https://assets.alleslabs.dev/integrations/chains/initia.png", lcd: "https://lcd.initiation-1.initia.xyz", rpc: "https://rpc.initiation-1.initia.xyz", indexer: "https://initiation-1-graphql.alleslabs.dev/v1/graphql", @@ -108,6 +112,8 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { chain: "initia", registryChainName: "minimovetestnet", prettyName: "Minimove", + networkType: "testnet", + logoUrl: "https://assets.alleslabs.dev/integrations/chains/minimove.png", lcd: "https://lcd.minimove-1.initia.xyz", rpc: "https://rpc.minimove-1.initia.xyz", indexer: "https://minimove-1-graphql.alleslabs.dev/v1/graphql", @@ -156,6 +162,8 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { chain: "initia", registryChainName: "miniwasmtestnet", prettyName: "Miniwasm", + networkType: "testnet", + logoUrl: "https://assets.alleslabs.dev/integrations/chains/miniwasm.png", lcd: "https://lcd.miniwasm-1.initia.xyz", rpc: "https://rpc.miniwasm-1.initia.xyz:443", indexer: "https://miniwasm-1-graphql.alleslabs.dev/v1/graphql", @@ -203,6 +211,8 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { chain: "initia", registryChainName: "blackwingtestnet", prettyName: "Blackwing", + networkType: "testnet", + logoUrl: "https://assets.alleslabs.dev/integrations/chains/blackwing.png", lcd: "https://maze-rest-18bdff44-3aa4-425e-9bc0-06a2afa40af8.ase1-prod.newmetric.xyz", rpc: "https://maze-rpc-18bdff44-3aa4-425e-9bc0-06a2afa40af8.ase1-prod.newmetric.xyz", indexer: "https://tomcat-1-graphql.alleslabs.dev/v1/graphql", @@ -251,6 +261,8 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { chain: "initia", registryChainName: "initaitestnet", prettyName: "INIT AI", + networkType: "testnet", + logoUrl: "https://assets.alleslabs.dev/integrations/chains/initai.png", lcd: "https://maze-rest-617bacff-7d34-4eb8-87f4-ee16fb4e0ac7.ue1-prod.newmetric.xyz", rpc: "https://maze-rpc-617bacff-7d34-4eb8-87f4-ee16fb4e0ac7.ue1-prod.newmetric.xyz", indexer: "https://init-ai-1-graphql.alleslabs.dev/v1/graphql", @@ -299,6 +311,8 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { chain: "initia", registryChainName: "noontestnet", prettyName: "Noon", + networkType: "testnet", + logoUrl: "https://assets.alleslabs.dev/integrations/chains/noon.png", lcd: "https://burrito-1-lcd.lunchlunch.xyz", rpc: "https://burrito-1-rpc.lunchlunch.xyz", indexer: "https://burrito-1-graphql.alleslabs.dev/v1/graphql", @@ -347,6 +361,8 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { chain: "initia", registryChainName: "tucanatestnet", prettyName: "Tucana", + networkType: "testnet", + logoUrl: "https://assets.alleslabs.dev/integrations/chains/tucana.png", lcd: "https://maze-rest-c9796789-107d-49ab-b6de-059724d2a91d.ue1-prod.newmetric.xyz", rpc: "https://maze-rpc-c9796789-107d-49ab-b6de-059724d2a91d.ue1-prod.newmetric.xyz", indexer: "https://birdee-1-graphql.alleslabs.dev/v1/graphql", @@ -394,6 +410,8 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { chain: "initia", registryChainName: "civitiatestnet", prettyName: "Civitia", + networkType: "testnet", + logoUrl: "https://assets.alleslabs.dev/integrations/chains/civitia.png", lcd: "https://maze-rest-sequencer-beab9b6f-d96d-435e-9caf-5679296d8172.ue1-prod.newmetric.xyz", rpc: "https://maze-rpc-sequencer-beab9b6f-d96d-435e-9caf-5679296d8172.ue1-prod.newmetric.xyz", indexer: "https://landlord-1-graphql.alleslabs.dev/v1/graphql", diff --git a/src/config/chain/neutron.ts b/src/config/chain/neutron.ts index 50075767c..a21d76d80 100644 --- a/src/config/chain/neutron.ts +++ b/src/config/chain/neutron.ts @@ -8,6 +8,8 @@ export const NEUTRON_CHAIN_CONFIGS: ChainConfigs = { chain: "neutron", registryChainName: "neutron", prettyName: "Neutron", + networkType: "mainnet", + logoUrl: "https://assets.alleslabs.dev/integrations/chains/neutron.png", lcd: "https://rest-kralum.neutron-1.neutron.org", rpc: "https://rpc-kralum.neutron-1.neutron.org:443", indexer: "https://neutron-1-graphql.alleslabs.dev/v1/graphql", @@ -54,6 +56,8 @@ export const NEUTRON_CHAIN_CONFIGS: ChainConfigs = { chain: "neutron", registryChainName: "neutrontestnet", prettyName: "Neutron Testnet", + networkType: "testnet", + logoUrl: "https://assets.alleslabs.dev/integrations/chains/neutron.png", lcd: "https://rest-palvus.pion-1.ntrn.tech:443", rpc: "https://rpc-palvus.pion-1.ntrn.tech:443", indexer: "https://pion-1-graphql.alleslabs.dev/v1/graphql", diff --git a/src/config/chain/osmosis.ts b/src/config/chain/osmosis.ts index 71b2e301f..6d6442d69 100644 --- a/src/config/chain/osmosis.ts +++ b/src/config/chain/osmosis.ts @@ -2,12 +2,16 @@ import { wallets as keplrWallets } from "@cosmos-kit/keplr"; import type { ChainConfigs } from "./types"; +const OSMOSIS_LOGO = + "https://assets.alleslabs.dev/integrations/chains/osmo.png"; export const OSMOSIS_CHAIN_CONFIGS: ChainConfigs = { "osmosis-1": { tier: "full", chain: "osmosis", registryChainName: "osmosis", prettyName: "Osmosis", + networkType: "mainnet", + logoUrl: OSMOSIS_LOGO, lcd: "https://lcd.osmosis.zone", rpc: "https://rpc.osmosis.zone:443", indexer: "https://osmosis-1-graphql.alleslabs.dev/v1/graphql", @@ -56,6 +60,8 @@ export const OSMOSIS_CHAIN_CONFIGS: ChainConfigs = { chain: "osmosis", registryChainName: "osmosistestnet", prettyName: "Osmosis Testnet", + networkType: "testnet", + logoUrl: OSMOSIS_LOGO, lcd: "https://lcd.osmotest5.osmosis.zone", rpc: "https://osmosis-testnet-rpc.polkachu.com:443", indexer: "", @@ -105,6 +111,8 @@ export const OSMOSIS_CHAIN_CONFIGS: ChainConfigs = { chain: "osmosis", registryChainName: "localosmosis", prettyName: "Local Osmosis", + networkType: "testnet", + logoUrl: OSMOSIS_LOGO, lcd: "http://localhost/rest", rpc: "http://localhost:80/rpc/", indexer: "http://localhost/hasura/v1/graphql", diff --git a/src/config/chain/sei.ts b/src/config/chain/sei.ts index a9c3d56d2..cc5b95876 100644 --- a/src/config/chain/sei.ts +++ b/src/config/chain/sei.ts @@ -14,6 +14,8 @@ export const SEI_CHAIN_CONFIGS: ChainConfigs = { chain: "sei", registryChainName: "sei", prettyName: "Sei", + networkType: "mainnet", + logoUrl: "https://assets.alleslabs.dev/integrations/chains/sei.png", lcd: "https://sei-api.polkachu.com", rpc: "https://sei-rpc.polkachu.com:443", indexer: "https://pacific-1-graphql.alleslabs.dev/v1/graphql", @@ -62,6 +64,8 @@ export const SEI_CHAIN_CONFIGS: ChainConfigs = { chain: "sei", registryChainName: "seitestnet2", prettyName: "Sei Testnet2", + networkType: "testnet", + logoUrl: "https://assets.alleslabs.dev/integrations/chains/sei.png", lcd: "https://rest.atlantic-2.seinetwork.io", rpc: "https://rpc.atlantic-2.seinetwork.io:443", indexer: "https://atlantic-2-graphql.alleslabs.dev/v1/graphql", diff --git a/src/config/chain/stargaze.ts b/src/config/chain/stargaze.ts index 474998751..bf6dd29c8 100644 --- a/src/config/chain/stargaze.ts +++ b/src/config/chain/stargaze.ts @@ -8,6 +8,8 @@ export const STARGAZE_CHAIN_CONFIGS: ChainConfigs = { chain: "stargaze", registryChainName: "stargaze", prettyName: "Stargaze", + networkType: "mainnet", + logoUrl: "https://assets.alleslabs.dev/integrations/chains/neutron.png", lcd: "https://rest.stargaze-apis.com", rpc: "https://rpc.stargaze-apis.com:443", indexer: "https://stargaze-mainnet-graphql.alleslabs.dev/v1/graphql", @@ -53,6 +55,7 @@ export const STARGAZE_CHAIN_CONFIGS: ChainConfigs = { chain: "stargaze", registryChainName: "stargazetestnet", prettyName: "Stargaze Testnet", + networkType: "testnet", lcd: "https://rest.elgafar-1.stargaze-apis.com", rpc: "https://rpc.elgafar-1.stargaze-apis.com", indexer: "https://elgafar-1-graphql.alleslabs.dev/v1/graphql", diff --git a/src/config/chain/terra.ts b/src/config/chain/terra.ts index 93b76efbe..980eec837 100644 --- a/src/config/chain/terra.ts +++ b/src/config/chain/terra.ts @@ -3,12 +3,16 @@ import { wallets as staionWallets } from "@cosmos-kit/station"; import type { ChainConfigs } from "./types"; +const TERRA_LOGO = "https://assets.alleslabs.dev/integrations/chains/terra.png"; + export const TERRA_CHAIN_CONFIGS: ChainConfigs = { "pisco-1-lite": { tier: "lite", chain: "terra", registryChainName: "terra2testnet", prettyName: "Terra Testnet Lite", + networkType: "testnet", + logoUrl: TERRA_LOGO, lcd: "https://terra-testnet-api.polkachu.com", rpc: "https://terra-testnet-rpc.polkachu.com:443", indexer: "", @@ -55,6 +59,8 @@ export const TERRA_CHAIN_CONFIGS: ChainConfigs = { chain: "terra", registryChainName: "terra2", prettyName: "Terra", + networkType: "mainnet", + logoUrl: TERRA_LOGO, lcd: "https://phoenix-lcd.terra.dev:443", rpc: "https://terra2-rpc.lavenderfive.com:443", indexer: "https://terra-mainnet-graphql.alleslabs.dev/v1/graphql", @@ -101,6 +107,8 @@ export const TERRA_CHAIN_CONFIGS: ChainConfigs = { chain: "terra", registryChainName: "terra2testnet", prettyName: "Terra Testnet", + networkType: "testnet", + logoUrl: TERRA_LOGO, lcd: "https://terra-testnet-api.polkachu.com", rpc: "https://terra-testnet-rpc.polkachu.com:443", indexer: "https://pisco-1-graphql.alleslabs.dev/v1/graphql", diff --git a/src/config/chain/types.ts b/src/config/chain/types.ts index 178474cf1..241ee1ea7 100644 --- a/src/config/chain/types.ts +++ b/src/config/chain/types.ts @@ -56,6 +56,8 @@ export interface ChainConfig { chain: string; registryChainName: string; prettyName: string; + logoUrl?: string; + networkType: "mainnet" | "testnet"; lcd: string; rpc: string; indexer: string; diff --git a/src/lib/layout/NetworkCard.tsx b/src/lib/layout/NetworkCard.tsx new file mode 100644 index 000000000..4a47d4478 --- /dev/null +++ b/src/lib/layout/NetworkCard.tsx @@ -0,0 +1,62 @@ +import { Box, Flex, Image, Text, useToken } from "@chakra-ui/react"; + +import { CHAIN_CONFIGS } from "config/chain"; +import { useSelectChain } from "lib/app-provider"; + +interface NetworkCardProps { + image?: string; + chainId: string; + isSelected: boolean; +} + +export const NetworkCard = ({ + image, + chainId, + isSelected, +}: NetworkCardProps) => { + const selectChain = useSelectChain(); + const [secondaryDarker] = useToken("colors", ["secondary.darker"]); + const fallbackImage = `https://ui-avatars.com/api/?name=${CHAIN_CONFIGS[chainId]?.prettyName || chainId}&background=${secondaryDarker.replace("#", "")}&color=fff`; + + return ( + selectChain(chainId)} + > + + + + + {CHAIN_CONFIGS[chainId]?.prettyName || chainId} + + + {chainId} + + + + ); +}; diff --git a/src/lib/layout/NetworkMenu.tsx b/src/lib/layout/NetworkMenu.tsx index bae3e210e..e332f4ea2 100644 --- a/src/lib/layout/NetworkMenu.tsx +++ b/src/lib/layout/NetworkMenu.tsx @@ -1,28 +1,100 @@ import { track } from "@amplitude/analytics-browser"; import { + Accordion, + AccordionButton, + AccordionIcon, + AccordionItem, + AccordionPanel, + Divider, + Drawer, + DrawerBody, + DrawerCloseButton, + DrawerContent, + DrawerHeader, + DrawerOverlay, Flex, + Heading, + Kbd, Menu, MenuButton, - MenuItem, - MenuList, Text, + useDisclosure, } from "@chakra-ui/react"; +import { useEffect, useMemo, useState } from "react"; import { CHAIN_CONFIGS } from "config/chain"; import { AmpEvent } from "lib/amplitude"; -import { useCelatoneApp, useMobile, useSelectChain } from "lib/app-provider"; +import { useCelatoneApp, useIsMac, useMobile } from "lib/app-provider"; import { CustomIcon } from "lib/components/icon"; +import InputWithIcon from "lib/components/InputWithIcon"; +import { EmptyState } from "lib/components/state"; + +import { NetworkCard } from "./NetworkCard"; export const NetworkMenu = () => { const isMobile = useMobile(); + const isMac = useIsMac(); const { currentChainId, availableChainIds } = useCelatoneApp(); - const selectChain = useSelectChain(); + const { isOpen, onClose, onOpen } = useDisclosure(); + + const [keyword, setKeyword] = useState(""); const width = isMobile ? "220px" : "170px"; + useEffect(() => { + const openSearchHandler = (event: KeyboardEvent) => { + const specialKey = isMac ? event.metaKey : event.ctrlKey; + if (event.key === "l" && specialKey) { + event.preventDefault(); + if (isOpen) { + onClose(); + } else { + onOpen(); + } + } + }; + document.addEventListener("keydown", openSearchHandler); + return () => { + document.removeEventListener("keydown", openSearchHandler); + }; + }, [isMac, isOpen, onClose, onOpen]); + + const testnetChains = availableChainIds.filter( + (chain) => CHAIN_CONFIGS[chain]?.networkType === "testnet" + ); + + const filteredTestnetChains = useMemo(() => { + if (!keyword) return testnetChains; + return testnetChains.filter( + (chain) => + CHAIN_CONFIGS[chain]?.prettyName + .toLowerCase() + .includes(keyword.toLowerCase()) || + chain.toLowerCase().includes(keyword.toLowerCase()) + ); + }, [keyword, testnetChains]); + + const mainnetChain = availableChainIds.filter( + (chain) => CHAIN_CONFIGS[chain]?.networkType === "mainnet" + ); + + const filteredMainnetChains = useMemo(() => { + if (!keyword) return mainnetChain; + return mainnetChain.filter( + (chain) => + CHAIN_CONFIGS[chain]?.prettyName + .toLowerCase() + .includes(keyword.toLowerCase()) || + chain.toLowerCase().includes(keyword.toLowerCase()) + ); + }, [keyword, mainnetChain]); + return ( track(AmpEvent.USE_SELECT_NETWORK)} + onOpen={() => { + track(AmpEvent.USE_SELECT_NETWORK); + onOpen(); + }} autoSelect={!isMobile} > { - - {availableChainIds.map((chainId) => ( - selectChain(chainId)} - transition="all 0.25s ease-in-out" - isDisabled={!(chainId in CHAIN_CONFIGS)} - > - - - - {CHAIN_CONFIGS[chainId]?.prettyName || chainId} - - - {chainId} - + + + + + + + Select Network + + + + + {isMac ? "⌘" : "Ctrl"} + + + + + l + + - {chainId === currentChainId && ( - - )} - - ))} - + + + + + setKeyword(e.target.value)} + amptrackSection="network-search" + /> + + + + + + Mainnet + + + + + + {filteredMainnetChains.length ? ( + + {filteredMainnetChains.map((chainId) => ( + + ))} + + ) : ( + + )} + + + + + + + + Testnet + + + + + + {filteredTestnetChains.length ? ( + + {filteredTestnetChains.map((chainId) => ( + + ))} + + ) : ( + + )} + + + + + + + ); }; diff --git a/src/lib/styles/theme/components/index.ts b/src/lib/styles/theme/components/index.ts index bdc79a0a7..cbc2a2853 100644 --- a/src/lib/styles/theme/components/index.ts +++ b/src/lib/styles/theme/components/index.ts @@ -7,6 +7,7 @@ import { Drawer } from "./drawer"; import { Form } from "./form"; import { Heading } from "./heading"; import { Input } from "./input"; +import { Kbd } from "./kbd"; import { Link } from "./link"; import { Menu } from "./menu"; import { Modal } from "./modal"; @@ -29,6 +30,7 @@ export const components = { Link, Form, Input, + Kbd, Modal, Radio, Select, diff --git a/src/lib/styles/theme/components/kbd.ts b/src/lib/styles/theme/components/kbd.ts new file mode 100644 index 000000000..b91fe5875 --- /dev/null +++ b/src/lib/styles/theme/components/kbd.ts @@ -0,0 +1,10 @@ +import { defineStyleConfig } from "@chakra-ui/react"; + +export const Kbd = defineStyleConfig({ + baseStyle: { + borderRadius: "4px", + borderColor: "gray.700", + backgroundColor: "gray.800", + padding: "2px 6px", + }, +}); From 128681d683169c70cc3238f52a04b15008b44e8c Mon Sep 17 00:00:00 2001 From: Jennie Alles Date: Wed, 26 Jun 2024 14:34:30 +0700 Subject: [PATCH 02/24] fix(components): adjust pinned network --- src/lib/components/icon/CustomIcon.tsx | 20 ++ src/lib/layout/Header.tsx | 2 +- src/lib/layout/NetworkCard.tsx | 62 ----- src/lib/layout/NetworkMenu.tsx | 240 ------------------ src/lib/layout/mobile/NavDrawer.tsx | 2 +- .../layout/network-selector/NetworkCard.tsx | 126 +++++++++ .../layout/network-selector/NetworkMenu.tsx | 140 ++++++++++ .../network-selector/NetworkMenuBody.tsx | 177 +++++++++++++ src/lib/providers/store.tsx | 5 + src/lib/stores/networks.ts | 62 +++++ src/lib/stores/root.ts | 4 + 11 files changed, 536 insertions(+), 304 deletions(-) delete mode 100644 src/lib/layout/NetworkCard.tsx delete mode 100644 src/lib/layout/NetworkMenu.tsx create mode 100644 src/lib/layout/network-selector/NetworkCard.tsx create mode 100644 src/lib/layout/network-selector/NetworkMenu.tsx create mode 100644 src/lib/layout/network-selector/NetworkMenuBody.tsx create mode 100644 src/lib/stores/networks.ts diff --git a/src/lib/components/icon/CustomIcon.tsx b/src/lib/components/icon/CustomIcon.tsx index 9530df92f..7c205cb11 100644 --- a/src/lib/components/icon/CustomIcon.tsx +++ b/src/lib/components/icon/CustomIcon.tsx @@ -1104,6 +1104,26 @@ export const ICONS = { ), viewBox: "0 -6 16 16", }, + pin: { + svg: ( + + ), + viewBox: "0 0 18 18", + }, + "pin-solid": { + svg: ( + + ), + viewBox: "0 0 18 18", + }, plus: { svg: ( { diff --git a/src/lib/layout/NetworkCard.tsx b/src/lib/layout/NetworkCard.tsx deleted file mode 100644 index 4a47d4478..000000000 --- a/src/lib/layout/NetworkCard.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { Box, Flex, Image, Text, useToken } from "@chakra-ui/react"; - -import { CHAIN_CONFIGS } from "config/chain"; -import { useSelectChain } from "lib/app-provider"; - -interface NetworkCardProps { - image?: string; - chainId: string; - isSelected: boolean; -} - -export const NetworkCard = ({ - image, - chainId, - isSelected, -}: NetworkCardProps) => { - const selectChain = useSelectChain(); - const [secondaryDarker] = useToken("colors", ["secondary.darker"]); - const fallbackImage = `https://ui-avatars.com/api/?name=${CHAIN_CONFIGS[chainId]?.prettyName || chainId}&background=${secondaryDarker.replace("#", "")}&color=fff`; - - return ( - selectChain(chainId)} - > - - - - - {CHAIN_CONFIGS[chainId]?.prettyName || chainId} - - - {chainId} - - - - ); -}; diff --git a/src/lib/layout/NetworkMenu.tsx b/src/lib/layout/NetworkMenu.tsx deleted file mode 100644 index e332f4ea2..000000000 --- a/src/lib/layout/NetworkMenu.tsx +++ /dev/null @@ -1,240 +0,0 @@ -import { track } from "@amplitude/analytics-browser"; -import { - Accordion, - AccordionButton, - AccordionIcon, - AccordionItem, - AccordionPanel, - Divider, - Drawer, - DrawerBody, - DrawerCloseButton, - DrawerContent, - DrawerHeader, - DrawerOverlay, - Flex, - Heading, - Kbd, - Menu, - MenuButton, - Text, - useDisclosure, -} from "@chakra-ui/react"; -import { useEffect, useMemo, useState } from "react"; - -import { CHAIN_CONFIGS } from "config/chain"; -import { AmpEvent } from "lib/amplitude"; -import { useCelatoneApp, useIsMac, useMobile } from "lib/app-provider"; -import { CustomIcon } from "lib/components/icon"; -import InputWithIcon from "lib/components/InputWithIcon"; -import { EmptyState } from "lib/components/state"; - -import { NetworkCard } from "./NetworkCard"; - -export const NetworkMenu = () => { - const isMobile = useMobile(); - const isMac = useIsMac(); - const { currentChainId, availableChainIds } = useCelatoneApp(); - const { isOpen, onClose, onOpen } = useDisclosure(); - - const [keyword, setKeyword] = useState(""); - - const width = isMobile ? "220px" : "170px"; - - useEffect(() => { - const openSearchHandler = (event: KeyboardEvent) => { - const specialKey = isMac ? event.metaKey : event.ctrlKey; - if (event.key === "l" && specialKey) { - event.preventDefault(); - if (isOpen) { - onClose(); - } else { - onOpen(); - } - } - }; - document.addEventListener("keydown", openSearchHandler); - return () => { - document.removeEventListener("keydown", openSearchHandler); - }; - }, [isMac, isOpen, onClose, onOpen]); - - const testnetChains = availableChainIds.filter( - (chain) => CHAIN_CONFIGS[chain]?.networkType === "testnet" - ); - - const filteredTestnetChains = useMemo(() => { - if (!keyword) return testnetChains; - return testnetChains.filter( - (chain) => - CHAIN_CONFIGS[chain]?.prettyName - .toLowerCase() - .includes(keyword.toLowerCase()) || - chain.toLowerCase().includes(keyword.toLowerCase()) - ); - }, [keyword, testnetChains]); - - const mainnetChain = availableChainIds.filter( - (chain) => CHAIN_CONFIGS[chain]?.networkType === "mainnet" - ); - - const filteredMainnetChains = useMemo(() => { - if (!keyword) return mainnetChain; - return mainnetChain.filter( - (chain) => - CHAIN_CONFIGS[chain]?.prettyName - .toLowerCase() - .includes(keyword.toLowerCase()) || - chain.toLowerCase().includes(keyword.toLowerCase()) - ); - }, [keyword, mainnetChain]); - - return ( - { - track(AmpEvent.USE_SELECT_NETWORK); - onOpen(); - }} - autoSelect={!isMobile} - > - - - - {currentChainId} - - - - - - - - - - - Select Network - - - - - {isMac ? "⌘" : "Ctrl"} - - - - - l - - - - - - - - - setKeyword(e.target.value)} - amptrackSection="network-search" - /> - - - - - - Mainnet - - - - - - {filteredMainnetChains.length ? ( - - {filteredMainnetChains.map((chainId) => ( - - ))} - - ) : ( - - )} - - - - - - - - Testnet - - - - - - {filteredTestnetChains.length ? ( - - {filteredTestnetChains.map((chainId) => ( - - ))} - - ) : ( - - )} - - - - - - - - - ); -}; diff --git a/src/lib/layout/mobile/NavDrawer.tsx b/src/lib/layout/mobile/NavDrawer.tsx index ac7563199..8f6efae4a 100644 --- a/src/lib/layout/mobile/NavDrawer.tsx +++ b/src/lib/layout/mobile/NavDrawer.tsx @@ -13,7 +13,7 @@ import { useDisclosure, } from "@chakra-ui/react"; -import { NetworkMenu } from "../NetworkMenu"; +import { NetworkMenu } from "../network-selector/NetworkMenu"; import { AmpEvent, track } from "lib/amplitude"; import { useGovConfig, diff --git a/src/lib/layout/network-selector/NetworkCard.tsx b/src/lib/layout/network-selector/NetworkCard.tsx new file mode 100644 index 000000000..470d56430 --- /dev/null +++ b/src/lib/layout/network-selector/NetworkCard.tsx @@ -0,0 +1,126 @@ +import { Box, Flex, Image, Text, useToast, useToken } from "@chakra-ui/react"; +import { observer } from "mobx-react-lite"; +import { useCallback } from "react"; + +import { CHAIN_CONFIGS } from "config/chain"; +import { useSelectChain } from "lib/app-provider"; +import { CustomIcon } from "lib/components/icon"; +import { useNetworkStore } from "lib/providers/store"; + +interface NetworkCardProps { + image?: string; + chainId: string; + isSelected: boolean; +} + +export const NetworkCard = observer( + ({ image, chainId, isSelected }: NetworkCardProps) => { + const selectChain = useSelectChain(); + const [secondaryDarker] = useToken("colors", ["secondary.darker"]); + const fallbackImage = `https://ui-avatars.com/api/?name=${CHAIN_CONFIGS[chainId]?.prettyName || chainId}&background=${secondaryDarker.replace("#", "")}&color=fff`; + const { isNetworkPinned, pinNetwork, removeNetwork } = useNetworkStore(); + + const toast = useToast({ + status: "success", + duration: 5000, + isClosable: false, + position: "bottom-right", + icon: , + }); + + const handleSave = useCallback(() => { + pinNetwork({ + name: CHAIN_CONFIGS[chainId]?.prettyName, + chainId, + logo: image || fallbackImage, + }); + toast({ + title: `Pinned \u2018${CHAIN_CONFIGS[chainId]?.prettyName}\u2019 successfully`, + }); + }, [pinNetwork, image, chainId, toast, fallbackImage]); + + const handleRemove = useCallback(() => { + removeNetwork(chainId); + toast({ + title: `\u2018${CHAIN_CONFIGS[chainId]?.prettyName}\u2019 is removed from Pinned Networks`, + }); + }, [removeNetwork, chainId, toast]); + + return ( + .icon-container": { + opacity: 1, + }, + }} + onClick={() => selectChain(chainId)} + > + + + + + + {CHAIN_CONFIGS[chainId]?.prettyName || chainId} + + + {chainId} + + + + {isNetworkPinned(chainId) ? ( + { + e.stopPropagation(); + handleRemove(); + }} + > + + + ) : ( + { + if (chainId) { + e.stopPropagation(); + handleSave(); + } + }} + sx={{ opacity: 0, transition: "opacity 0.25s ease-in-out" }} + > + + + )} + {/* */} + + ); + } +); diff --git a/src/lib/layout/network-selector/NetworkMenu.tsx b/src/lib/layout/network-selector/NetworkMenu.tsx new file mode 100644 index 000000000..9d34147e5 --- /dev/null +++ b/src/lib/layout/network-selector/NetworkMenu.tsx @@ -0,0 +1,140 @@ +import { track } from "@amplitude/analytics-browser"; +import { + Drawer, + DrawerBody, + DrawerCloseButton, + DrawerContent, + DrawerHeader, + DrawerOverlay, + Flex, + Heading, + Kbd, + Menu, + MenuButton, + Text, + useDisclosure, +} from "@chakra-ui/react"; +import { observer } from "mobx-react-lite"; +import { useEffect, useState } from "react"; + +import { AmpEvent } from "lib/amplitude"; +import { useCelatoneApp, useIsMac, useMobile } from "lib/app-provider"; +import { CustomIcon } from "lib/components/icon"; +import InputWithIcon from "lib/components/InputWithIcon"; + +import { NetworkMenuBody } from "./NetworkMenuBody"; + +const NetworkButton = ({ + isMobile, + currentChainId, +}: { + isMobile: boolean; + currentChainId: string; +}) => { + const width = isMobile ? "220px" : "170px"; + return ( + + + + {currentChainId} + + + + + ); +}; + +export const NetworkMenu = observer(() => { + const isMobile = useMobile(); + const isMac = useIsMac(); + const { currentChainId } = useCelatoneApp(); + const { isOpen, onClose, onOpen } = useDisclosure(); + const [keyword, setKeyword] = useState(""); + + useEffect(() => { + const openSearchHandler = (event: KeyboardEvent) => { + const specialKey = isMac ? event.metaKey : event.ctrlKey; + if (event.key === `\\` && specialKey) { + event.preventDefault(); + if (isOpen) { + onClose(); + } else { + onOpen(); + } + } + }; + document.addEventListener("keydown", openSearchHandler); + return () => { + document.removeEventListener("keydown", openSearchHandler); + }; + }, [isMac, isOpen, onClose, onOpen]); + + return ( + { + track(AmpEvent.USE_SELECT_NETWORK); + onOpen(); + }} + autoSelect={!isMobile} + > + + + + + + + + Select Network + + + + + {isMac ? "⌘" : "Ctrl"} + + + + + \ + + + + + + + + + setKeyword(e.target.value)} + amptrackSection="network-search" + /> + + + + + + + ); +}); diff --git a/src/lib/layout/network-selector/NetworkMenuBody.tsx b/src/lib/layout/network-selector/NetworkMenuBody.tsx new file mode 100644 index 000000000..9c4bc73c2 --- /dev/null +++ b/src/lib/layout/network-selector/NetworkMenuBody.tsx @@ -0,0 +1,177 @@ +import { + Accordion, + AccordionButton, + AccordionIcon, + AccordionItem, + AccordionPanel, + Divider, + Flex, + Heading, +} from "@chakra-ui/react"; +import { observer } from "mobx-react-lite"; +import { useCallback, useMemo } from "react"; + +import { CHAIN_CONFIGS } from "config/chain"; +import { useCelatoneApp } from "lib/app-provider"; +import { EmptyState } from "lib/components/state"; +import { useNetworkStore } from "lib/providers/store"; + +import { NetworkCard } from "./NetworkCard"; + +interface AccordionNetworkListProps { + title: string; + networks: string[]; + currentChainId: string; + isHidden?: boolean; +} + +interface NetworkMenuBodyProps { + currentChainId: string; + keyword: string; +} + +const AccordionNetworkList = ({ + title, + networks, + currentChainId, + isHidden = false, +}: AccordionNetworkListProps) => ( + +); + +export const NetworkMenuBody = observer( + ({ currentChainId, keyword }: NetworkMenuBodyProps) => { + const { availableChainIds } = useCelatoneApp(); + const filteredChains = useCallback( + (type: string) => + useMemo(() => { + const filtered = availableChainIds.filter( + (chain) => CHAIN_CONFIGS[chain]?.networkType === type + ); + if (!keyword) return filtered; + return filtered.filter( + (chain) => + CHAIN_CONFIGS[chain]?.prettyName + .toLowerCase() + .includes(keyword.toLowerCase()) || + chain.toLowerCase().includes(keyword.toLowerCase()) + ); + }, [type]), + [keyword, availableChainIds] + ); + + const filteredTestnetChains = filteredChains("testnet"); + const filteredMainnetChains = filteredChains("mainnet"); + + const { getPinnedNetworks } = useNetworkStore(); + const pinnedNetworks = getPinnedNetworks(); + + const filteredPinnedNetworks = useMemo(() => { + if (!keyword) return [...pinnedNetworks]; + return pinnedNetworks.filter( + (network) => + CHAIN_CONFIGS[network.chainId]?.prettyName + .toLowerCase() + .includes(keyword.toLowerCase()) || + network.chainId.toLowerCase().includes(keyword.toLowerCase()) + ); + }, [pinnedNetworks, keyword]); + + const areAllNetworksEmpty = + !filteredPinnedNetworks.length && + !filteredMainnetChains.length && + !filteredTestnetChains.length; + + return ( + <> + + + {filteredPinnedNetworks.length > 0 && ( + + )} + + {filteredMainnetChains.length > 0 && ( + + )} + + + {areAllNetworksEmpty && ( + + )} + + ); + } +); diff --git a/src/lib/providers/store.tsx b/src/lib/providers/store.tsx index 15ce8e6a6..18d2f63fa 100644 --- a/src/lib/providers/store.tsx +++ b/src/lib/providers/store.tsx @@ -59,3 +59,8 @@ export function useSchemaStore() { const { schemaStore } = useStore(); return schemaStore; } + +export function useNetworkStore() { + const { networkStore } = useStore(); + return networkStore; +} diff --git a/src/lib/stores/networks.ts b/src/lib/stores/networks.ts new file mode 100644 index 000000000..cecf8a4d5 --- /dev/null +++ b/src/lib/stores/networks.ts @@ -0,0 +1,62 @@ +import { makeAutoObservable } from "mobx"; +import { isHydrated, makePersistable } from "mobx-persist-store"; + +import type { Dict } from "lib/types"; + +export interface Network { + name: string; + chainId: string; + logo: string; +} + +export class NetworkStore { + private userKey: string; + + networks: Dict; + + constructor() { + this.userKey = ""; + this.networks = {}; + + makeAutoObservable(this, {}, { autoBind: true }); + + makePersistable(this, { + name: "NetworkStore", + properties: ["networks"], + }); + } + + get isHydrated(): boolean { + return isHydrated(this); + } + + isNetworkUserKeyExist(): boolean { + return !!this.userKey; + } + + setNetworkUserKey(userKey: string) { + this.userKey = userKey; + } + + getPinnedNetworks(): Network[] { + return this.networks[this.userKey] ?? []; + } + + isNetworkPinned(chainId: string): boolean { + const networkByUserKey = this.getPinnedNetworks(); + + return networkByUserKey.findIndex((x) => x.chainId === chainId) > -1; + } + + pinNetwork(newNetwork: Network): void { + if (!this.isNetworkPinned(newNetwork.chainId)) { + this.networks[this.userKey] = [...this.getPinnedNetworks(), newNetwork]; + } + } + + removeNetwork(chainId: string): void { + this.networks[this.userKey] = this.networks[this.userKey]?.filter( + (each) => each.chainId !== chainId + ); + } +} diff --git a/src/lib/stores/root.ts b/src/lib/stores/root.ts index 03f5c71e5..fe997a5b8 100644 --- a/src/lib/stores/root.ts +++ b/src/lib/stores/root.ts @@ -1,6 +1,7 @@ import { AccountStore } from "./account"; import { CodeStore } from "./code"; import { ContractStore } from "./contract"; +import { NetworkStore } from "./networks"; import { PublicProjectStore } from "./project"; import { SchemaStore } from "./schema"; @@ -15,11 +16,14 @@ export class RootStore { schemaStore: SchemaStore; + networkStore: NetworkStore; + constructor() { this.accountStore = new AccountStore(); this.codeStore = new CodeStore(); this.contractStore = new ContractStore(); this.publicProjectStore = new PublicProjectStore(); this.schemaStore = new SchemaStore(); + this.networkStore = new NetworkStore(); } } From 233c66a3eea77ed1772c070cf92112a71acd8ca6 Mon Sep 17 00:00:00 2001 From: Jennie Alles Date: Mon, 1 Jul 2024 18:06:48 +0700 Subject: [PATCH 03/24] fix(components): sort network --- package.json | 2 + pnpm-lock.yaml | 49 +++++++++++ src/lib/components/icon/CustomIcon.tsx | 9 ++ .../layout/network-selector/NetworkCard.tsx | 57 ++++++------ .../network-selector/NetworkMenuBody.tsx | 55 +++++++++--- .../layout/network-selector/SortableItem.tsx | 66 ++++++++++++++ .../layout/network-selector/SortableList.tsx | 86 +++++++++++++++++++ src/lib/stores/networks.ts | 1 + 8 files changed, 288 insertions(+), 37 deletions(-) create mode 100644 src/lib/layout/network-selector/SortableItem.tsx create mode 100644 src/lib/layout/network-selector/SortableList.tsx diff --git a/package.json b/package.json index 961295852..92fa96a51 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,8 @@ "@cosmos-kit/react": "2.15.0", "@cosmos-kit/station": "2.9.0", "@cosmos-kit/station-extension": "2.10.0", + "@dnd-kit/core": "^6.1.0", + "@dnd-kit/sortable": "^8.0.0", "@emotion/react": "^11.11.3", "@emotion/styled": "^11.11.0", "@graphql-codegen/cli": "^5.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 73af87e3b..a23c74616 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -74,6 +74,12 @@ dependencies: '@cosmos-kit/station-extension': specifier: 2.10.0 version: 2.10.0(@chain-registry/types@0.17.0)(@cosmjs/amino@0.32.3)(@cosmjs/proto-signing@0.32.3)(@terra-money/terra.js@3.1.10)(axios@1.6.5) + '@dnd-kit/core': + specifier: ^6.1.0 + version: 6.1.0(react-dom@18.2.0)(react@18.2.0) + '@dnd-kit/sortable': + specifier: ^8.0.0 + version: 8.0.0(@dnd-kit/core@6.1.0)(react@18.2.0) '@emotion/react': specifier: ^11.11.3 version: 11.11.3(@types/react@18.2.48)(react@18.2.0) @@ -3759,6 +3765,49 @@ packages: engines: {node: '>=10.0.0'} dev: true + /@dnd-kit/accessibility@3.1.0(react@18.2.0): + resolution: {integrity: sha512-ea7IkhKvlJUv9iSHJOnxinBcoOI3ppGnnL+VDJ75O45Nss6HtZd8IdN8touXPDtASfeI2T2LImb8VOZcL47wjQ==} + peerDependencies: + react: '>=16.8.0' + dependencies: + react: 18.2.0 + tslib: 2.6.2 + dev: false + + /@dnd-kit/core@6.1.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-J3cQBClB4TVxwGo3KEjssGEXNJqGVWx17aRTZ1ob0FliR5IjYgTxl5YJbKTzA6IzrtelotH19v6y7uoIRUZPSg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + dependencies: + '@dnd-kit/accessibility': 3.1.0(react@18.2.0) + '@dnd-kit/utilities': 3.2.2(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + tslib: 2.6.2 + dev: false + + /@dnd-kit/sortable@8.0.0(@dnd-kit/core@6.1.0)(react@18.2.0): + resolution: {integrity: sha512-U3jk5ebVXe1Lr7c2wU7SBZjcWdQP+j7peHJfCspnA81enlu88Mgd7CC8Q+pub9ubP7eKVETzJW+IBAhsqbSu/g==} + peerDependencies: + '@dnd-kit/core': ^6.1.0 + react: '>=16.8.0' + dependencies: + '@dnd-kit/core': 6.1.0(react-dom@18.2.0)(react@18.2.0) + '@dnd-kit/utilities': 3.2.2(react@18.2.0) + react: 18.2.0 + tslib: 2.6.2 + dev: false + + /@dnd-kit/utilities@3.2.2(react@18.2.0): + resolution: {integrity: sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==} + peerDependencies: + react: '>=16.8.0' + dependencies: + react: 18.2.0 + tslib: 2.6.2 + dev: false + /@emotion/babel-plugin@11.11.0: resolution: {integrity: sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==} dependencies: diff --git a/src/lib/components/icon/CustomIcon.tsx b/src/lib/components/icon/CustomIcon.tsx index 7c205cb11..8fbc62c25 100644 --- a/src/lib/components/icon/CustomIcon.tsx +++ b/src/lib/components/icon/CustomIcon.tsx @@ -651,6 +651,15 @@ export const ICONS = { ), viewBox: viewBox121616, }, + drag: { + svg: ( + + ), + viewBox: "0 0 20 20", + }, edit: { svg: ( .icon-container": { + "> .icon-wrapper > .icon-container": { opacity: 1, }, }} @@ -93,33 +93,42 @@ export const NetworkCard = observer( - {isNetworkPinned(chainId) ? ( + { - e.stopPropagation(); - handleRemove(); - }} - > - - - ) : ( - { - if (chainId) { - e.stopPropagation(); - handleSave(); - } - }} - sx={{ opacity: 0, transition: "opacity 0.25s ease-in-out" }} + opacity={0} + _hover={{ opacity: 1, transition: "opacity 0.25s ease-in-out" }} > - + - )} - {/* */} + {isNetworkPinned(chainId) ? ( + { + e.stopPropagation(); + handleRemove(); + }} + > + + + ) : ( + { + if (chainId) { + e.stopPropagation(); + handleSave(); + } + }} + sx={{ opacity: 0, transition: "opacity 0.25s ease-in-out" }} + > + + + )} + ); } diff --git a/src/lib/layout/network-selector/NetworkMenuBody.tsx b/src/lib/layout/network-selector/NetworkMenuBody.tsx index 9c4bc73c2..3a0df7d38 100644 --- a/src/lib/layout/network-selector/NetworkMenuBody.tsx +++ b/src/lib/layout/network-selector/NetworkMenuBody.tsx @@ -8,8 +8,9 @@ import { Flex, Heading, } from "@chakra-ui/react"; +import { DndContext } from "@dnd-kit/core"; import { observer } from "mobx-react-lite"; -import { useCallback, useMemo } from "react"; +import { useCallback, useMemo, useState } from "react"; import { CHAIN_CONFIGS } from "config/chain"; import { useCelatoneApp } from "lib/app-provider"; @@ -17,6 +18,7 @@ import { EmptyState } from "lib/components/state"; import { useNetworkStore } from "lib/providers/store"; import { NetworkCard } from "./NetworkCard"; +import { SortableList } from "./SortableList"; interface AccordionNetworkListProps { title: string; @@ -60,9 +62,14 @@ const AccordionNetworkList = ({ ); +export const ItemTypes = { + CARD: "card", +}; + export const NetworkMenuBody = observer( ({ currentChainId, keyword }: NetworkMenuBodyProps) => { const { availableChainIds } = useCelatoneApp(); + const filteredChains = useCallback( (type: string) => useMemo(() => { @@ -103,8 +110,10 @@ export const NetworkMenuBody = observer( !filteredMainnetChains.length && !filteredTestnetChains.length; + const [items, setItems] = useState(filteredPinnedNetworks); + return ( - <> + - - {filteredPinnedNetworks.map((network) => ( - - ))} - + {/* TODO: sortable network pin */} + null + // + // {network.id} + // + // + // + } + /> + {/* + + {filteredPinnedNetworks.map((network) => ( + + ))} + + */} {filteredPinnedNetworks.length > 0 && ( @@ -171,7 +200,7 @@ export const NetworkMenuBody = observer( Please check your keyword." /> )} - + ); } ); diff --git a/src/lib/layout/network-selector/SortableItem.tsx b/src/lib/layout/network-selector/SortableItem.tsx new file mode 100644 index 000000000..b2f78803d --- /dev/null +++ b/src/lib/layout/network-selector/SortableItem.tsx @@ -0,0 +1,66 @@ +import { Flex } from "@chakra-ui/react"; +import type { DraggableSyntheticListeners } from "@dnd-kit/core"; +import { createContext, useContext } from "react"; + +// interface Props { +// id: UniqueIdentifier; +// } + +interface Context { + attributes: Record; + listeners: DraggableSyntheticListeners; + ref(node: HTMLElement | null): void; +} + +const SortableItemContext = createContext({ + attributes: {}, + listeners: undefined, + ref() {}, +}); + +export const SortableItem = () => { + // const { + // attributes, + // listeners, + // setNodeRef, + // setActivatorNodeRef, + // } = useSortable({ id }); + + // const context = useMemo( + // () => ({ + // attributes, + // listeners, + // ref: setActivatorNodeRef, + // }), + // [attributes, listeners, setActivatorNodeRef] + // ); + + return null; + // return ( + // + //
  • + // {children} + //
  • + //
    + // ); +}; + +export function DragHandle() { + const { attributes, listeners, ref } = useContext(SortableItemContext); + + return ( + + ); +} diff --git a/src/lib/layout/network-selector/SortableList.tsx b/src/lib/layout/network-selector/SortableList.tsx new file mode 100644 index 000000000..5f90102b1 --- /dev/null +++ b/src/lib/layout/network-selector/SortableList.tsx @@ -0,0 +1,86 @@ +import type { Active } from "@dnd-kit/core"; +import { + DndContext, + DragOverlay, + KeyboardSensor, + PointerSensor, + useSensor, + useSensors, +} from "@dnd-kit/core"; +import { + arrayMove, + SortableContext, + sortableKeyboardCoordinates, +} from "@dnd-kit/sortable"; +import type { ReactNode } from "react"; +import React, { useMemo, useState } from "react"; + +import type { Network } from "lib/stores/networks"; + +// const dropAnimationConfig: DropAnimation = { +// sideEffects: defaultDropAnimationSideEffects({ +// styles: { +// active: { +// opacity: "0.4", +// }, +// }, +// }), +// }; + +interface SortableListProps { + items: Network[]; + onChange(items: Network[]): void; + renderItem(item: Network): ReactNode; +} + +export const SortableList = ({ + items, + onChange, + renderItem, +}: SortableListProps) => { + const [isActive, setIsActive] = useState(null); + + const activeItem = useMemo( + () => items.find((item) => item.id === isActive?.id), + [isActive, items] + ); + const sensors = useSensors( + useSensor(PointerSensor), + useSensor(KeyboardSensor, { + coordinateGetter: sortableKeyboardCoordinates, + }) + ); + + return ( + { + setIsActive(active); + }} + onDragEnd={({ active, over }) => { + if (over && active.id !== over?.id) { + const activeIndex = items.findIndex(({ id }) => id === active.id); + const overIndex = items.findIndex(({ id }) => id === over.id); + + onChange(arrayMove(items, activeIndex, overIndex)); + } + setIsActive(null); + }} + onDragCancel={() => { + setIsActive(null); + }} + > + +
      + {items.map((item) => ( + + {renderItem(item)} + {item.chainId} + + ))} +
    +
    + {activeItem ? renderItem(activeItem) : null} +
    + ); +}; diff --git a/src/lib/stores/networks.ts b/src/lib/stores/networks.ts index cecf8a4d5..25d1751d5 100644 --- a/src/lib/stores/networks.ts +++ b/src/lib/stores/networks.ts @@ -7,6 +7,7 @@ export interface Network { name: string; chainId: string; logo: string; + id: string; } export class NetworkStore { From 8da9dc1bfc6bf1bd04106cf2475678978cd23f87 Mon Sep 17 00:00:00 2001 From: Poafs1 Date: Tue, 2 Jul 2024 10:45:57 +0700 Subject: [PATCH 04/24] feat(components): add dnd kit --- package.json | 1 + pnpm-lock.yaml | 3 + .../network-selector/NetworkMenuBody.tsx | 123 +++++++++++++----- .../layout/network-selector/SortableItem.tsx | 66 ---------- .../layout/network-selector/SortableList.tsx | 86 ------------ 5 files changed, 95 insertions(+), 184 deletions(-) delete mode 100644 src/lib/layout/network-selector/SortableItem.tsx delete mode 100644 src/lib/layout/network-selector/SortableList.tsx diff --git a/package.json b/package.json index 92fa96a51..354f46bf4 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "@cosmos-kit/station-extension": "2.10.0", "@dnd-kit/core": "^6.1.0", "@dnd-kit/sortable": "^8.0.0", + "@dnd-kit/utilities": "^3.2.2", "@emotion/react": "^11.11.3", "@emotion/styled": "^11.11.0", "@graphql-codegen/cli": "^5.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a23c74616..ee6226010 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -80,6 +80,9 @@ dependencies: '@dnd-kit/sortable': specifier: ^8.0.0 version: 8.0.0(@dnd-kit/core@6.1.0)(react@18.2.0) + '@dnd-kit/utilities': + specifier: ^3.2.2 + version: 3.2.2(react@18.2.0) '@emotion/react': specifier: ^11.11.3 version: 11.11.3(@types/react@18.2.48)(react@18.2.0) diff --git a/src/lib/layout/network-selector/NetworkMenuBody.tsx b/src/lib/layout/network-selector/NetworkMenuBody.tsx index 3a0df7d38..eaddbf503 100644 --- a/src/lib/layout/network-selector/NetworkMenuBody.tsx +++ b/src/lib/layout/network-selector/NetworkMenuBody.tsx @@ -4,11 +4,29 @@ import { AccordionIcon, AccordionItem, AccordionPanel, + Box, Divider, Flex, Heading, } from "@chakra-ui/react"; -import { DndContext } from "@dnd-kit/core"; +import type { DragEndEvent } from "@dnd-kit/core"; +import { + closestCenter, + DndContext, + KeyboardSensor, + PointerSensor, + useSensor, + useSensors, +} from "@dnd-kit/core"; +import { + arrayMove, + SortableContext, + sortableKeyboardCoordinates, + useSortable, + verticalListSortingStrategy, +} from "@dnd-kit/sortable"; +import { CSS } from "@dnd-kit/utilities"; +import { findIndex } from "lodash"; import { observer } from "mobx-react-lite"; import { useCallback, useMemo, useState } from "react"; @@ -18,7 +36,6 @@ import { EmptyState } from "lib/components/state"; import { useNetworkStore } from "lib/providers/store"; import { NetworkCard } from "./NetworkCard"; -import { SortableList } from "./SortableList"; interface AccordionNetworkListProps { title: string; @@ -66,6 +83,34 @@ export const ItemTypes = { CARD: "card", }; +const SortableItem = ({ + chainId, + currentChainId, +}: { + chainId: string; + currentChainId: string; +}) => { + const { attributes, listeners, setNodeRef, transform, transition } = + useSortable({ + id: chainId, + }); + + const style = { + transform: transform ? CSS.Transform.toString(transform) : "none", + transition: transition || "transform 250ms ease", + }; + + return ( + + + + ); +}; + export const NetworkMenuBody = observer( ({ currentChainId, keyword }: NetworkMenuBodyProps) => { const { availableChainIds } = useCelatoneApp(); @@ -112,8 +157,33 @@ export const NetworkMenuBody = observer( const [items, setItems] = useState(filteredPinnedNetworks); + const sensors = useSensors( + useSensor(PointerSensor), + useSensor(KeyboardSensor, { + coordinateGetter: sortableKeyboardCoordinates, + }) + ); + + const handleDragEnd = (event: DragEndEvent) => { + const { active, over } = event; + + if (active.id !== over?.id) { + setItems((newItems) => { + const oldIndex = findIndex( + items, + (item) => item.chainId === active.id + ); + const newIndex = findIndex( + items, + (item) => item.chainId === over?.id + ); + return arrayMove(newItems, oldIndex, newIndex); + }); + } + }; + return ( - + <> - {/* TODO: sortable network pin */} - null - // - // {network.id} - // - // - // - } - /> - {/* - - {filteredPinnedNetworks.map((network) => ( - + + {items.map((item) => ( + ))} - - */} + + {filteredPinnedNetworks.length > 0 && ( @@ -200,7 +259,7 @@ export const NetworkMenuBody = observer( Please check your keyword." /> )} - + ); } ); diff --git a/src/lib/layout/network-selector/SortableItem.tsx b/src/lib/layout/network-selector/SortableItem.tsx deleted file mode 100644 index b2f78803d..000000000 --- a/src/lib/layout/network-selector/SortableItem.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { Flex } from "@chakra-ui/react"; -import type { DraggableSyntheticListeners } from "@dnd-kit/core"; -import { createContext, useContext } from "react"; - -// interface Props { -// id: UniqueIdentifier; -// } - -interface Context { - attributes: Record; - listeners: DraggableSyntheticListeners; - ref(node: HTMLElement | null): void; -} - -const SortableItemContext = createContext({ - attributes: {}, - listeners: undefined, - ref() {}, -}); - -export const SortableItem = () => { - // const { - // attributes, - // listeners, - // setNodeRef, - // setActivatorNodeRef, - // } = useSortable({ id }); - - // const context = useMemo( - // () => ({ - // attributes, - // listeners, - // ref: setActivatorNodeRef, - // }), - // [attributes, listeners, setActivatorNodeRef] - // ); - - return null; - // return ( - // - //
  • - // {children} - //
  • - //
    - // ); -}; - -export function DragHandle() { - const { attributes, listeners, ref } = useContext(SortableItemContext); - - return ( - - ); -} diff --git a/src/lib/layout/network-selector/SortableList.tsx b/src/lib/layout/network-selector/SortableList.tsx deleted file mode 100644 index 5f90102b1..000000000 --- a/src/lib/layout/network-selector/SortableList.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import type { Active } from "@dnd-kit/core"; -import { - DndContext, - DragOverlay, - KeyboardSensor, - PointerSensor, - useSensor, - useSensors, -} from "@dnd-kit/core"; -import { - arrayMove, - SortableContext, - sortableKeyboardCoordinates, -} from "@dnd-kit/sortable"; -import type { ReactNode } from "react"; -import React, { useMemo, useState } from "react"; - -import type { Network } from "lib/stores/networks"; - -// const dropAnimationConfig: DropAnimation = { -// sideEffects: defaultDropAnimationSideEffects({ -// styles: { -// active: { -// opacity: "0.4", -// }, -// }, -// }), -// }; - -interface SortableListProps { - items: Network[]; - onChange(items: Network[]): void; - renderItem(item: Network): ReactNode; -} - -export const SortableList = ({ - items, - onChange, - renderItem, -}: SortableListProps) => { - const [isActive, setIsActive] = useState(null); - - const activeItem = useMemo( - () => items.find((item) => item.id === isActive?.id), - [isActive, items] - ); - const sensors = useSensors( - useSensor(PointerSensor), - useSensor(KeyboardSensor, { - coordinateGetter: sortableKeyboardCoordinates, - }) - ); - - return ( - { - setIsActive(active); - }} - onDragEnd={({ active, over }) => { - if (over && active.id !== over?.id) { - const activeIndex = items.findIndex(({ id }) => id === active.id); - const overIndex = items.findIndex(({ id }) => id === over.id); - - onChange(arrayMove(items, activeIndex, overIndex)); - } - setIsActive(null); - }} - onDragCancel={() => { - setIsActive(null); - }} - > - -
      - {items.map((item) => ( - - {renderItem(item)} - {item.chainId} - - ))} -
    -
    - {activeItem ? renderItem(activeItem) : null} -
    - ); -}; From 17bd4ea807103df2ca8d328b583de126dfa8f40f Mon Sep 17 00:00:00 2001 From: Jennie Alles Date: Thu, 4 Jul 2024 02:24:28 +0700 Subject: [PATCH 05/24] fix(components): add layer --- src/config/chain/default.ts | 1 + src/config/chain/initia.ts | 9 + src/config/chain/neutron.ts | 2 + src/config/chain/osmosis.ts | 3 + src/config/chain/sei.ts | 2 + src/config/chain/stargaze.ts | 2 + src/config/chain/terra.ts | 3 + src/config/chain/types.ts | 1 + .../layout/network-selector/NetworkCard.tsx | 112 ++++++---- .../network-selector/NetworkMenuBody.tsx | 193 ++++++++++++------ src/lib/stores/networks.ts | 10 + 11 files changed, 235 insertions(+), 103 deletions(-) diff --git a/src/config/chain/default.ts b/src/config/chain/default.ts index 2f783eb98..170d330c5 100644 --- a/src/config/chain/default.ts +++ b/src/config/chain/default.ts @@ -6,6 +6,7 @@ export const DEFAULT_CHAIN_CONFIG: ChainConfig = { registryChainName: "", prettyName: "", networkType: "testnet", + layer: null, lcd: "", rpc: "", indexer: "", diff --git a/src/config/chain/initia.ts b/src/config/chain/initia.ts index ff725e427..556a3dc34 100644 --- a/src/config/chain/initia.ts +++ b/src/config/chain/initia.ts @@ -13,6 +13,7 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "blackwingtestnet", prettyName: "Blackwing Testnet Lite", networkType: "testnet", + layer: "2", logoUrl: "https://assets.alleslabs.dev/integrations/chains/blackwing.png", lcd: "https://maze-rest-18bdff44-3aa4-425e-9bc0-06a2afa40af8.ase1-prod.newmetric.xyz", rpc: "https://maze-rpc-18bdff44-3aa4-425e-9bc0-06a2afa40af8.ase1-prod.newmetric.xyz", @@ -63,6 +64,7 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "initiatestnet", prettyName: "Initia", networkType: "testnet", + layer: "1", logoUrl: "https://assets.alleslabs.dev/integrations/chains/initia.png", lcd: "https://lcd.initiation-1.initia.xyz", rpc: "https://rpc.initiation-1.initia.xyz", @@ -113,6 +115,7 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "minimovetestnet", prettyName: "Minimove", networkType: "testnet", + layer: "2", logoUrl: "https://assets.alleslabs.dev/integrations/chains/minimove.png", lcd: "https://lcd.minimove-1.initia.xyz", rpc: "https://rpc.minimove-1.initia.xyz", @@ -163,6 +166,7 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "miniwasmtestnet", prettyName: "Miniwasm", networkType: "testnet", + layer: "2", logoUrl: "https://assets.alleslabs.dev/integrations/chains/miniwasm.png", lcd: "https://lcd.miniwasm-1.initia.xyz", rpc: "https://rpc.miniwasm-1.initia.xyz:443", @@ -212,6 +216,7 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "blackwingtestnet", prettyName: "Blackwing", networkType: "testnet", + layer: "2", logoUrl: "https://assets.alleslabs.dev/integrations/chains/blackwing.png", lcd: "https://maze-rest-18bdff44-3aa4-425e-9bc0-06a2afa40af8.ase1-prod.newmetric.xyz", rpc: "https://maze-rpc-18bdff44-3aa4-425e-9bc0-06a2afa40af8.ase1-prod.newmetric.xyz", @@ -262,6 +267,7 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "initaitestnet", prettyName: "INIT AI", networkType: "testnet", + layer: "2", logoUrl: "https://assets.alleslabs.dev/integrations/chains/initai.png", lcd: "https://maze-rest-617bacff-7d34-4eb8-87f4-ee16fb4e0ac7.ue1-prod.newmetric.xyz", rpc: "https://maze-rpc-617bacff-7d34-4eb8-87f4-ee16fb4e0ac7.ue1-prod.newmetric.xyz", @@ -312,6 +318,7 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "noontestnet", prettyName: "Noon", networkType: "testnet", + layer: "2", logoUrl: "https://assets.alleslabs.dev/integrations/chains/noon.png", lcd: "https://burrito-1-lcd.lunchlunch.xyz", rpc: "https://burrito-1-rpc.lunchlunch.xyz", @@ -362,6 +369,7 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "tucanatestnet", prettyName: "Tucana", networkType: "testnet", + layer: "2", logoUrl: "https://assets.alleslabs.dev/integrations/chains/tucana.png", lcd: "https://maze-rest-c9796789-107d-49ab-b6de-059724d2a91d.ue1-prod.newmetric.xyz", rpc: "https://maze-rpc-c9796789-107d-49ab-b6de-059724d2a91d.ue1-prod.newmetric.xyz", @@ -411,6 +419,7 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "civitiatestnet", prettyName: "Civitia", networkType: "testnet", + layer: "2", logoUrl: "https://assets.alleslabs.dev/integrations/chains/civitia.png", lcd: "https://maze-rest-sequencer-beab9b6f-d96d-435e-9caf-5679296d8172.ue1-prod.newmetric.xyz", rpc: "https://maze-rpc-sequencer-beab9b6f-d96d-435e-9caf-5679296d8172.ue1-prod.newmetric.xyz", diff --git a/src/config/chain/neutron.ts b/src/config/chain/neutron.ts index a21d76d80..64ef1b038 100644 --- a/src/config/chain/neutron.ts +++ b/src/config/chain/neutron.ts @@ -9,6 +9,7 @@ export const NEUTRON_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "neutron", prettyName: "Neutron", networkType: "mainnet", + layer: null, logoUrl: "https://assets.alleslabs.dev/integrations/chains/neutron.png", lcd: "https://rest-kralum.neutron-1.neutron.org", rpc: "https://rpc-kralum.neutron-1.neutron.org:443", @@ -57,6 +58,7 @@ export const NEUTRON_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "neutrontestnet", prettyName: "Neutron Testnet", networkType: "testnet", + layer: null, logoUrl: "https://assets.alleslabs.dev/integrations/chains/neutron.png", lcd: "https://rest-palvus.pion-1.ntrn.tech:443", rpc: "https://rpc-palvus.pion-1.ntrn.tech:443", diff --git a/src/config/chain/osmosis.ts b/src/config/chain/osmosis.ts index 55ca2aab6..d4476108c 100644 --- a/src/config/chain/osmosis.ts +++ b/src/config/chain/osmosis.ts @@ -11,6 +11,7 @@ export const OSMOSIS_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "osmosis", prettyName: "Osmosis", networkType: "mainnet", + layer: null, logoUrl: OSMOSIS_LOGO, lcd: "https://lcd.osmosis.zone", rpc: "https://rpc.osmosis.zone:443", @@ -61,6 +62,7 @@ export const OSMOSIS_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "osmosistestnet", prettyName: "Osmosis Testnet", networkType: "testnet", + layer: null, logoUrl: OSMOSIS_LOGO, lcd: "https://lcd.osmotest5.osmosis.zone", rpc: "https://osmosis-testnet-rpc.polkachu.com:443", @@ -112,6 +114,7 @@ export const OSMOSIS_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "localosmosis", prettyName: "Local Osmosis", networkType: "testnet", + layer: null, logoUrl: OSMOSIS_LOGO, lcd: "http://localhost/rest", rpc: "http://localhost:80/rpc/", diff --git a/src/config/chain/sei.ts b/src/config/chain/sei.ts index cc5b95876..f72734504 100644 --- a/src/config/chain/sei.ts +++ b/src/config/chain/sei.ts @@ -15,6 +15,7 @@ export const SEI_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "sei", prettyName: "Sei", networkType: "mainnet", + layer: null, logoUrl: "https://assets.alleslabs.dev/integrations/chains/sei.png", lcd: "https://sei-api.polkachu.com", rpc: "https://sei-rpc.polkachu.com:443", @@ -65,6 +66,7 @@ export const SEI_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "seitestnet2", prettyName: "Sei Testnet2", networkType: "testnet", + layer: null, logoUrl: "https://assets.alleslabs.dev/integrations/chains/sei.png", lcd: "https://rest.atlantic-2.seinetwork.io", rpc: "https://rpc.atlantic-2.seinetwork.io:443", diff --git a/src/config/chain/stargaze.ts b/src/config/chain/stargaze.ts index bf6dd29c8..681d93be3 100644 --- a/src/config/chain/stargaze.ts +++ b/src/config/chain/stargaze.ts @@ -9,6 +9,7 @@ export const STARGAZE_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "stargaze", prettyName: "Stargaze", networkType: "mainnet", + layer: null, logoUrl: "https://assets.alleslabs.dev/integrations/chains/neutron.png", lcd: "https://rest.stargaze-apis.com", rpc: "https://rpc.stargaze-apis.com:443", @@ -56,6 +57,7 @@ export const STARGAZE_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "stargazetestnet", prettyName: "Stargaze Testnet", networkType: "testnet", + layer: null, lcd: "https://rest.elgafar-1.stargaze-apis.com", rpc: "https://rpc.elgafar-1.stargaze-apis.com", indexer: "https://elgafar-1-graphql.alleslabs.dev/v1/graphql", diff --git a/src/config/chain/terra.ts b/src/config/chain/terra.ts index 980eec837..c4f847c70 100644 --- a/src/config/chain/terra.ts +++ b/src/config/chain/terra.ts @@ -12,6 +12,7 @@ export const TERRA_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "terra2testnet", prettyName: "Terra Testnet Lite", networkType: "testnet", + layer: null, logoUrl: TERRA_LOGO, lcd: "https://terra-testnet-api.polkachu.com", rpc: "https://terra-testnet-rpc.polkachu.com:443", @@ -60,6 +61,7 @@ export const TERRA_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "terra2", prettyName: "Terra", networkType: "mainnet", + layer: null, logoUrl: TERRA_LOGO, lcd: "https://phoenix-lcd.terra.dev:443", rpc: "https://terra2-rpc.lavenderfive.com:443", @@ -108,6 +110,7 @@ export const TERRA_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "terra2testnet", prettyName: "Terra Testnet", networkType: "testnet", + layer: null, logoUrl: TERRA_LOGO, lcd: "https://terra-testnet-api.polkachu.com", rpc: "https://terra-testnet-rpc.polkachu.com:443", diff --git a/src/config/chain/types.ts b/src/config/chain/types.ts index 241ee1ea7..e959fb3d1 100644 --- a/src/config/chain/types.ts +++ b/src/config/chain/types.ts @@ -58,6 +58,7 @@ export interface ChainConfig { prettyName: string; logoUrl?: string; networkType: "mainnet" | "testnet"; + layer: "1" | "2" | null; lcd: string; rpc: string; indexer: string; diff --git a/src/lib/layout/network-selector/NetworkCard.tsx b/src/lib/layout/network-selector/NetworkCard.tsx index 85188b457..6155c0134 100644 --- a/src/lib/layout/network-selector/NetworkCard.tsx +++ b/src/lib/layout/network-selector/NetworkCard.tsx @@ -11,10 +11,20 @@ interface NetworkCardProps { image?: string; chainId: string; isSelected: boolean; + isDraggable?: boolean; } +const iconProps = { + cursor: "pointer", + className: "icon-container", + align: "center", + padding: 1, +}; + +const customTransition = "opacity 0.1s ease-in-out"; + export const NetworkCard = observer( - ({ image, chainId, isSelected }: NetworkCardProps) => { + ({ image, chainId, isSelected, isDraggable = false }: NetworkCardProps) => { const selectChain = useSelectChain(); const [secondaryDarker] = useToken("colors", ["secondary.darker"]); const fallbackImage = `https://ui-avatars.com/api/?name=${CHAIN_CONFIGS[chainId]?.prettyName || chainId}&background=${secondaryDarker.replace("#", "")}&color=fff`; @@ -28,24 +38,40 @@ export const NetworkCard = observer( icon: , }); - const handleSave = useCallback(() => { - pinNetwork({ - name: CHAIN_CONFIGS[chainId]?.prettyName, - chainId, - logo: image || fallbackImage, - id: "", - }); - toast({ - title: `Pinned \u2018${CHAIN_CONFIGS[chainId]?.prettyName}\u2019 successfully`, - }); - }, [pinNetwork, image, chainId, toast, fallbackImage]); + const handleSave = useCallback( + (e: React.MouseEvent) => { + e.stopPropagation(); + pinNetwork({ + name: CHAIN_CONFIGS[chainId]?.prettyName, + chainId, + logo: image || fallbackImage, + id: "", + }); + toast({ + title: `Pinned \u2018${CHAIN_CONFIGS[chainId]?.prettyName}\u2019 successfully`, + }); + }, + [pinNetwork, image, chainId, toast, fallbackImage] + ); + + const handleRemove = useCallback( + (e: React.MouseEvent) => { + e.stopPropagation(); + removeNetwork(chainId); + toast({ + title: `\u2018${CHAIN_CONFIGS[chainId]?.prettyName}\u2019 is removed from Pinned Networks`, + }); + }, + [removeNetwork, chainId, toast] + ); - const handleRemove = useCallback(() => { - removeNetwork(chainId); - toast({ - title: `\u2018${CHAIN_CONFIGS[chainId]?.prettyName}\u2019 is removed from Pinned Networks`, - }); - }, [removeNetwork, chainId, toast]); + const handleClick = useCallback( + (e: React.MouseEvent) => { + e.stopPropagation(); + selectChain(chainId); + }, + [selectChain, chainId] + ); return ( selectChain(chainId)} + onClick={handleClick} + cursor={!isDraggable ? "pointer" : "inherit"} > - - - - - {isNetworkPinned(chainId) ? ( + + {isDraggable && ( { - e.stopPropagation(); - handleRemove(); + className="icon-container" + opacity={0} + _hover={{ opacity: 1, transition: customTransition }} + > + + + )} + {isNetworkPinned(chainId) ? ( + ) : ( { - if (chainId) { - e.stopPropagation(); - handleSave(); - } - }} + {...iconProps} + onClick={handleSave} sx={{ opacity: 0, transition: "opacity 0.25s ease-in-out" }} + _hover={{ + background: isSelected ? "gray.800" : "gray.900", + borderRadius: 4, + transition: customTransition, + }} > diff --git a/src/lib/layout/network-selector/NetworkMenuBody.tsx b/src/lib/layout/network-selector/NetworkMenuBody.tsx index eaddbf503..8ca5c51cc 100644 --- a/src/lib/layout/network-selector/NetworkMenuBody.tsx +++ b/src/lib/layout/network-selector/NetworkMenuBody.tsx @@ -9,24 +9,23 @@ import { Flex, Heading, } from "@chakra-ui/react"; -import type { DragEndEvent } from "@dnd-kit/core"; +import type { Active, DragEndEvent, DropAnimation } from "@dnd-kit/core"; import { closestCenter, + defaultDropAnimationSideEffects, DndContext, - KeyboardSensor, - PointerSensor, + DragOverlay, + MouseSensor, + TouchSensor, useSensor, useSensors, } from "@dnd-kit/core"; import { - arrayMove, SortableContext, - sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy, } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; -import { findIndex } from "lodash"; import { observer } from "mobx-react-lite"; import { useCallback, useMemo, useState } from "react"; @@ -39,9 +38,12 @@ import { NetworkCard } from "./NetworkCard"; interface AccordionNetworkListProps { title: string; - networks: string[]; + normalNetworks: string[]; + l1Networks?: string[]; + l2Networks?: string[]; currentChainId: string; isHidden?: boolean; + isMove?: boolean; } interface NetworkMenuBodyProps { @@ -51,9 +53,12 @@ interface NetworkMenuBodyProps { const AccordionNetworkList = ({ title, - networks, + normalNetworks, + l1Networks, + l2Networks, currentChainId, isHidden = false, + isMove = false, }: AccordionNetworkListProps) => ( - - {networks.map((chainId) => ( - - ))} - + {isMove ? ( + <> + {l1Networks && ( + + {l1Networks.map((chainId) => ( + + ))} + + )} + {l2Networks && ( + + {l2Networks.map((chainId) => ( + + ))} + + )} + + ) : ( + + {normalNetworks.map((chainId) => ( + + ))} + + )} ); @@ -83,6 +117,16 @@ export const ItemTypes = { CARD: "card", }; +const dropAnimationConfig: DropAnimation = { + sideEffects: defaultDropAnimationSideEffects({ + styles: { + active: { + opacity: "0.4", + }, + }, + }), +}; + const SortableItem = ({ chainId, currentChainId, @@ -90,19 +134,34 @@ const SortableItem = ({ chainId: string; currentChainId: string; }) => { - const { attributes, listeners, setNodeRef, transform, transition } = - useSortable({ - id: chainId, - }); + const { + attributes, + listeners, + setNodeRef, + transform, + transition, + isDragging, + } = useSortable({ + id: chainId, + }); const style = { + opacity: isDragging ? 0.7 : undefined, + cursor: isDragging ? "grabbing" : "grab", transform: transform ? CSS.Transform.toString(transform) : "none", transition: transition || "transform 250ms ease", }; return ( - + e.stopPropagation()} + > { @@ -155,35 +214,43 @@ export const NetworkMenuBody = observer( !filteredMainnetChains.length && !filteredTestnetChains.length; - const [items, setItems] = useState(filteredPinnedNetworks); + const [dndActive, setDndActive] = useState(null); + + const activeItem = useMemo( + () => filteredPinnedNetworks.find((item) => item.id === dndActive?.id), + [dndActive, filteredPinnedNetworks] + ); const sensors = useSensors( - useSensor(PointerSensor), - useSensor(KeyboardSensor, { - coordinateGetter: sortableKeyboardCoordinates, - }) + useSensor(MouseSensor, { + activationConstraint: { + delay: 100, + tolerance: 5, + }, + }), + useSensor(TouchSensor) ); const handleDragEnd = (event: DragEndEvent) => { const { active, over } = event; if (active.id !== over?.id) { - setItems((newItems) => { - const oldIndex = findIndex( - items, - (item) => item.chainId === active.id - ); - const newIndex = findIndex( - items, - (item) => item.chainId === over?.id - ); - return arrayMove(newItems, oldIndex, newIndex); - }); + setPinnedNetworks(active.id.toString(), over?.id.toString()); } }; return ( - <> + { + setDndActive(active); + }} + onDragEnd={handleDragEnd} + onDragCancel={() => { + setDndActive(null); + }} + > - {/* // TODO: Implement DND */} - item.chainId)} + strategy={verticalListSortingStrategy} > - - {items.map((item) => ( - - ))} - - + {filteredPinnedNetworks.map((item) => ( + + ))} + + + {activeItem ? ( + + ) : null} + {filteredPinnedNetworks.length > 0 && ( @@ -232,7 +301,7 @@ export const NetworkMenuBody = observer( {filteredMainnetChains.length > 0 && ( @@ -245,7 +314,7 @@ export const NetworkMenuBody = observer( @@ -259,7 +328,7 @@ export const NetworkMenuBody = observer( Please check your keyword." /> )} - + ); } ); diff --git a/src/lib/stores/networks.ts b/src/lib/stores/networks.ts index 25d1751d5..2a737798b 100644 --- a/src/lib/stores/networks.ts +++ b/src/lib/stores/networks.ts @@ -1,3 +1,5 @@ +import { arrayMove } from "@dnd-kit/sortable"; +import { findIndex } from "lodash"; import { makeAutoObservable } from "mobx"; import { isHydrated, makePersistable } from "mobx-persist-store"; @@ -60,4 +62,12 @@ export class NetworkStore { (each) => each.chainId !== chainId ); } + + setPinnedNetworks(activeId: string, overId?: string): void { + const newItems = this.networks[this.userKey] ?? []; + const items = [...newItems]; + const oldIndex = findIndex(items, (item) => item.chainId === activeId); + const newIndex = findIndex(items, (item) => item.chainId === overId); + this.networks[this.userKey] = arrayMove(items, oldIndex, newIndex); + } } From 5994cc3cbe10c8b52912a15a7d4f5ab8c465d850 Mon Sep 17 00:00:00 2001 From: Jennie Alles Date: Mon, 8 Jul 2024 02:02:07 +0700 Subject: [PATCH 06/24] fix(components): add layer section --- .github/workflows/jest.yml | 4 +- .../network-selector/NetworkAccordion.tsx | 120 ++++++++++++++++++ .../network-selector/NetworkMenuBody.tsx | 114 ++++------------- 3 files changed, 144 insertions(+), 94 deletions(-) create mode 100644 src/lib/layout/network-selector/NetworkAccordion.tsx diff --git a/.github/workflows/jest.yml b/.github/workflows/jest.yml index 2a9a6a33a..feb1c8b8a 100644 --- a/.github/workflows/jest.yml +++ b/.github/workflows/jest.yml @@ -11,10 +11,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: pnpm/action-setup@v2 + - uses: pnpm/action-setup@v4 name: Install pnpm with: - version: 8 + version: 8.14.1 run_install: false - name: Run Tests run: | diff --git a/src/lib/layout/network-selector/NetworkAccordion.tsx b/src/lib/layout/network-selector/NetworkAccordion.tsx new file mode 100644 index 000000000..9ea2c1ba6 --- /dev/null +++ b/src/lib/layout/network-selector/NetworkAccordion.tsx @@ -0,0 +1,120 @@ +import { + AccordionButton, + AccordionIcon, + AccordionItem, + AccordionPanel, + Badge, + Flex, + Heading, + Text, +} from "@chakra-ui/react"; + +import { CHAIN_CONFIGS } from "config/chain"; +import { useInitia } from "lib/app-provider"; + +import { NetworkCard } from "./NetworkCard"; + +interface NetworkAccodionProps { + title: string; + normalNetworks: string[]; + currentChainId: string; + isHidden?: boolean; +} + +interface SectionTitleProps { + networks: string[]; + title: string; + mt?: number; + mb?: number; +} + +const SectionTitle = ({ + networks, + title, + mt = 4, + mb = 4, +}: SectionTitleProps) => ( + + + {title} + + + {networks.length !== undefined ? "N/A" : networks.length} + + +); + +export const NetworkAccodion = ({ + title, + normalNetworks, + currentChainId, + isHidden = false, +}: NetworkAccodionProps) => { + const isInitia = useInitia(); + const l1Networks = normalNetworks.filter( + (chainId) => CHAIN_CONFIGS[chainId]?.layer === "1" + ); + const l2Networks = normalNetworks.filter( + (chainId) => CHAIN_CONFIGS[chainId]?.layer === "2" + ); + + return ( + + ); +}; diff --git a/src/lib/layout/network-selector/NetworkMenuBody.tsx b/src/lib/layout/network-selector/NetworkMenuBody.tsx index 8ca5c51cc..9941717ae 100644 --- a/src/lib/layout/network-selector/NetworkMenuBody.tsx +++ b/src/lib/layout/network-selector/NetworkMenuBody.tsx @@ -27,92 +27,21 @@ import { } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; import { observer } from "mobx-react-lite"; -import { useCallback, useMemo, useState } from "react"; +import { useMemo, useState } from "react"; import { CHAIN_CONFIGS } from "config/chain"; import { useCelatoneApp } from "lib/app-provider"; import { EmptyState } from "lib/components/state"; import { useNetworkStore } from "lib/providers/store"; +import { NetworkAccodion } from "./NetworkAccordion"; import { NetworkCard } from "./NetworkCard"; -interface AccordionNetworkListProps { - title: string; - normalNetworks: string[]; - l1Networks?: string[]; - l2Networks?: string[]; - currentChainId: string; - isHidden?: boolean; - isMove?: boolean; -} - interface NetworkMenuBodyProps { currentChainId: string; keyword: string; } -const AccordionNetworkList = ({ - title, - normalNetworks, - l1Networks, - l2Networks, - currentChainId, - isHidden = false, - isMove = false, -}: AccordionNetworkListProps) => ( - -); - export const ItemTypes = { CARD: "card", }; @@ -174,26 +103,27 @@ export const NetworkMenuBody = observer( ({ currentChainId, keyword }: NetworkMenuBodyProps) => { const { availableChainIds } = useCelatoneApp(); - const filteredChains = useCallback( - (type: string) => - useMemo(() => { - const filtered = availableChainIds.filter( - (chain) => CHAIN_CONFIGS[chain]?.networkType === type - ); - if (!keyword) return filtered; - return filtered.filter( - (chain) => - CHAIN_CONFIGS[chain]?.prettyName + const filteredChains = useMemo(() => { + const filterChains = (type: "mainnet" | "testnet") => { + return availableChainIds + .filter((chain) => CHAIN_CONFIGS[chain]?.networkType === type) + .filter( + (network) => + !keyword || + CHAIN_CONFIGS[network]?.prettyName .toLowerCase() .includes(keyword.toLowerCase()) || - chain.toLowerCase().includes(keyword.toLowerCase()) + network.toLowerCase().includes(keyword.toLowerCase()) ); - }, [type]), - [keyword, availableChainIds] - ); + }; + return { + testnet: filterChains("testnet"), + mainnet: filterChains("mainnet"), + }; + }, [availableChainIds, keyword]); - const filteredTestnetChains = filteredChains("testnet"); - const filteredMainnetChains = filteredChains("mainnet"); + const filteredTestnetChains = filteredChains.testnet; + const filteredMainnetChains = filteredChains.mainnet; const { getPinnedNetworks, setPinnedNetworks } = useNetworkStore(); const pinnedNetworks = getPinnedNetworks(); @@ -267,7 +197,7 @@ export const NetworkMenuBody = observer( - + item.chainId)} strategy={verticalListSortingStrategy} @@ -298,7 +228,7 @@ export const NetworkMenuBody = observer( mb={4} /> )} - )} - Date: Wed, 10 Jul 2024 14:59:44 +0700 Subject: [PATCH 07/24] fix(components): add arrow key navigation --- CHANGELOG.md | 1 + .../network-selector/NetworkAccordion.tsx | 46 ++++---- .../layout/network-selector/NetworkCard.tsx | 28 ++++- .../layout/network-selector/NetworkMenu.tsx | 14 +-- .../network-selector/NetworkMenuBody.tsx | 111 ++++++++++++++++-- 5 files changed, 159 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b983a050..cea0483c0 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 +- [#1005](https://github.com/alleslabs/celatone-frontend/pull/1005) New network selector - [#934](https://github.com/alleslabs/celatone-frontend/pull/934) Add view module in mobile - [#950](https://github.com/alleslabs/celatone-frontend/pull/950) Add initia username - [#971](https://github.com/alleslabs/celatone-frontend/pull/971) Support search functionality with LCD endpoint diff --git a/src/lib/layout/network-selector/NetworkAccordion.tsx b/src/lib/layout/network-selector/NetworkAccordion.tsx index 9ea2c1ba6..8396d4e50 100644 --- a/src/lib/layout/network-selector/NetworkAccordion.tsx +++ b/src/lib/layout/network-selector/NetworkAccordion.tsx @@ -11,6 +11,7 @@ import { import { CHAIN_CONFIGS } from "config/chain"; import { useInitia } from "lib/app-provider"; +import type { Option } from "lib/types"; import { NetworkCard } from "./NetworkCard"; @@ -19,27 +20,24 @@ interface NetworkAccodionProps { normalNetworks: string[]; currentChainId: string; isHidden?: boolean; + cursor: Option; + setCursor: (index: Option) => void; + startIndex: number; } interface SectionTitleProps { networks: string[]; title: string; - mt?: number; mb?: number; } -const SectionTitle = ({ - networks, - title, - mt = 4, - mb = 4, -}: SectionTitleProps) => ( - +const SectionTitle = ({ networks, title, mb = 4 }: SectionTitleProps) => ( + {title} - {networks.length !== undefined ? "N/A" : networks.length} + {networks.length !== undefined ? networks.length : "N/A"} ); @@ -49,6 +47,9 @@ export const NetworkAccodion = ({ normalNetworks, currentChainId, isHidden = false, + cursor, + setCursor, + startIndex, }: NetworkAccodionProps) => { const isInitia = useInitia(); const l1Networks = normalNetworks.filter( @@ -71,32 +72,34 @@ export const NetworkAccodion = ({ {isInitia ? ( <> - {l1Networks && ( - - - {l1Networks.map((chainId) => ( + {l1Networks.length > 0 && ( + + + {l1Networks.map((chainId, index) => ( ))} )} - {l2Networks && ( + {l2Networks.length > 0 && ( - {l2Networks.map((chainId) => ( + {l2Networks.map((chainId, index) => ( ))} @@ -104,12 +107,15 @@ export const NetworkAccodion = ({ ) : ( - {normalNetworks.map((chainId) => ( + {normalNetworks.map((chainId, index) => ( ))} diff --git a/src/lib/layout/network-selector/NetworkCard.tsx b/src/lib/layout/network-selector/NetworkCard.tsx index 6155c0134..ab8d893c5 100644 --- a/src/lib/layout/network-selector/NetworkCard.tsx +++ b/src/lib/layout/network-selector/NetworkCard.tsx @@ -6,12 +6,16 @@ import { CHAIN_CONFIGS } from "config/chain"; import { useSelectChain } from "lib/app-provider"; import { CustomIcon } from "lib/components/icon"; import { useNetworkStore } from "lib/providers/store"; +import type { Option } from "lib/types"; interface NetworkCardProps { image?: string; chainId: string; isSelected: boolean; isDraggable?: boolean; + index?: number; + cursor: Option; + setCursor: (index: Option) => void; } const iconProps = { @@ -24,7 +28,15 @@ const iconProps = { const customTransition = "opacity 0.1s ease-in-out"; export const NetworkCard = observer( - ({ image, chainId, isSelected, isDraggable = false }: NetworkCardProps) => { + ({ + image, + chainId, + isSelected, + isDraggable = false, + index, + cursor, + setCursor, + }: NetworkCardProps) => { const selectChain = useSelectChain(); const [secondaryDarker] = useToken("colors", ["secondary.darker"]); const fallbackImage = `https://ui-avatars.com/api/?name=${CHAIN_CONFIGS[chainId]?.prettyName || chainId}&background=${secondaryDarker.replace("#", "")}&color=fff`; @@ -73,8 +85,19 @@ export const NetworkCard = observer( [selectChain, chainId] ); + let cardBackground; + + if (isSelected) { + cardBackground = "gray.700"; + } else if (index === cursor) { + cardBackground = "gray.800"; + } else { + cardBackground = "transparent"; + } + return ( .icon-wrapper > .icon-container": { opacity: 1, }, }} + onMouseMove={() => index !== cursor && setCursor(index)} onClick={handleClick} cursor={!isDraggable ? "pointer" : "inherit"} > diff --git a/src/lib/layout/network-selector/NetworkMenu.tsx b/src/lib/layout/network-selector/NetworkMenu.tsx index 9d34147e5..bb50489d7 100644 --- a/src/lib/layout/network-selector/NetworkMenu.tsx +++ b/src/lib/layout/network-selector/NetworkMenu.tsx @@ -15,12 +15,11 @@ import { useDisclosure, } from "@chakra-ui/react"; import { observer } from "mobx-react-lite"; -import { useEffect, useState } from "react"; +import { useEffect } from "react"; import { AmpEvent } from "lib/amplitude"; import { useCelatoneApp, useIsMac, useMobile } from "lib/app-provider"; import { CustomIcon } from "lib/components/icon"; -import InputWithIcon from "lib/components/InputWithIcon"; import { NetworkMenuBody } from "./NetworkMenuBody"; @@ -65,7 +64,6 @@ export const NetworkMenu = observer(() => { const isMac = useIsMac(); const { currentChainId } = useCelatoneApp(); const { isOpen, onClose, onOpen } = useDisclosure(); - const [keyword, setKeyword] = useState(""); useEffect(() => { const openSearchHandler = (event: KeyboardEvent) => { @@ -119,17 +117,9 @@ export const NetworkMenu = observer(() => { - setKeyword(e.target.value)} - amptrackSection="network-search" - /> diff --git a/src/lib/layout/network-selector/NetworkMenuBody.tsx b/src/lib/layout/network-selector/NetworkMenuBody.tsx index 9941717ae..3c1445f70 100644 --- a/src/lib/layout/network-selector/NetworkMenuBody.tsx +++ b/src/lib/layout/network-selector/NetworkMenuBody.tsx @@ -1,3 +1,4 @@ +/* eslint-disable sonarjs/cognitive-complexity */ import { Accordion, AccordionButton, @@ -7,6 +8,7 @@ import { Box, Divider, Flex, + FormControl, Heading, } from "@chakra-ui/react"; import type { Active, DragEndEvent, DropAnimation } from "@dnd-kit/core"; @@ -27,23 +29,39 @@ import { } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; import { observer } from "mobx-react-lite"; -import { useMemo, useState } from "react"; +import { useCallback, useMemo, useState } from "react"; +import type { KeyboardEvent as ReactKeyboardEvent } from "react"; import { CHAIN_CONFIGS } from "config/chain"; -import { useCelatoneApp } from "lib/app-provider"; +import { useCelatoneApp, useSelectChain } from "lib/app-provider"; +import InputWithIcon from "lib/components/InputWithIcon"; import { EmptyState } from "lib/components/state"; import { useNetworkStore } from "lib/providers/store"; +import type { Option } from "lib/types"; import { NetworkAccodion } from "./NetworkAccordion"; import { NetworkCard } from "./NetworkCard"; interface NetworkMenuBodyProps { currentChainId: string; - keyword: string; + onClose: () => void; } -export const ItemTypes = { - CARD: "card", +const getNextCursor = ( + key: string, + current: Option, + lastIndex: number +) => { + switch (key) { + case "ArrowUp": + if (current === undefined) return lastIndex; + return current <= 0 ? lastIndex : current - 1; + case "ArrowDown": + if (current === undefined) return 0; + return current >= lastIndex ? 0 : current + 1; + default: + return undefined; + } }; const dropAnimationConfig: DropAnimation = { @@ -59,9 +77,15 @@ const dropAnimationConfig: DropAnimation = { const SortableItem = ({ chainId, currentChainId, + index, + cursor, + setCursor, }: { chainId: string; currentChainId: string; + index?: number; + cursor: Option; + setCursor: (index: Option) => void; }) => { const { attributes, @@ -94,15 +118,22 @@ const SortableItem = ({ image={CHAIN_CONFIGS[chainId]?.logoUrl} chainId={chainId} isSelected={chainId === currentChainId} + index={index} + cursor={cursor} + setCursor={setCursor} /> ); }; export const NetworkMenuBody = observer( - ({ currentChainId, keyword }: NetworkMenuBodyProps) => { + ({ currentChainId, onClose }: NetworkMenuBodyProps) => { + const selectChain = useSelectChain(); const { availableChainIds } = useCelatoneApp(); + const [cursor, setCursor] = useState(); + const [keyword, setKeyword] = useState(""); + // Get chains info const filteredChains = useMemo(() => { const filterChains = (type: "mainnet" | "testnet") => { return availableChainIds @@ -139,6 +170,8 @@ export const NetworkMenuBody = observer( ); }, [pinnedNetworks, keyword]); + // Drag and drop feature + const areAllNetworksEmpty = !filteredPinnedNetworks.length && !filteredMainnetChains.length && @@ -169,6 +202,46 @@ export const NetworkMenuBody = observer( } }; + // Navigate with arrow keys + + const allNetworks = useMemo( + () => [ + ...filteredPinnedNetworks, + ...filteredMainnetChains, + ...filteredTestnetChains, + ], + [filteredPinnedNetworks, filteredMainnetChains, filteredTestnetChains] + ); + + const handleOnKeyEnter = useCallback( + (e: ReactKeyboardEvent) => { + if (!allNetworks.length) return; + switch (e.key) { + case "ArrowUp": + case "ArrowDown": { + const lastIndex = allNetworks.length - 1; + const nextCursor = getNextCursor(e.key, cursor, lastIndex); + const listItem = document.getElementById(`item-${nextCursor}`); + e.preventDefault(); + setCursor(nextCursor); + listItem?.scrollIntoView({ block: "nearest", inline: "center" }); + break; + } + case "Enter": + e.currentTarget.blur(); + if (cursor) { + const selectedNetwork = allNetworks[cursor].toString(); + selectChain(selectedNetwork); + onClose(); + } + break; + default: + break; + } + }, + [allNetworks, cursor, selectChain, onClose, setCursor] + ); + return ( + + setKeyword(e.target.value)} + onKeyDown={handleOnKeyEnter} + amptrackSection="network-search" + /> + item.chainId)} strategy={verticalListSortingStrategy} > - {filteredPinnedNetworks.map((item) => ( + {filteredPinnedNetworks.map((item, index) => ( ))} @@ -216,6 +303,8 @@ export const NetworkMenuBody = observer( key={activeItem.chainId} chainId={activeItem.chainId} currentChainId={currentChainId} + cursor={cursor} + setCursor={setCursor} /> ) : null} @@ -233,6 +322,9 @@ export const NetworkMenuBody = observer( title="Mainnet" normalNetworks={filteredMainnetChains} currentChainId={currentChainId} + cursor={cursor} + setCursor={setCursor} + startIndex={filteredPinnedNetworks.length} /> {filteredMainnetChains.length > 0 && ( {areAllNetworksEmpty && ( From 93ffca96015901bd3da080c49442e3383b47217c Mon Sep 17 00:00:00 2001 From: Jennie Alles Date: Wed, 10 Jul 2024 17:57:50 +0700 Subject: [PATCH 08/24] fix(components): refactor --- .../network-selector/NetworkAccordion.tsx | 5 + .../layout/network-selector/NetworkCard.tsx | 178 +++++++++++------- .../NetworkDragItemWrapper.tsx | 64 +++++++ .../layout/network-selector/NetworkMenu.tsx | 4 +- .../network-selector/NetworkMenuBody.tsx | 72 ++----- 5 files changed, 196 insertions(+), 127 deletions(-) create mode 100644 src/lib/layout/network-selector/NetworkDragItemWrapper.tsx diff --git a/src/lib/layout/network-selector/NetworkAccordion.tsx b/src/lib/layout/network-selector/NetworkAccordion.tsx index 8396d4e50..77ac15910 100644 --- a/src/lib/layout/network-selector/NetworkAccordion.tsx +++ b/src/lib/layout/network-selector/NetworkAccordion.tsx @@ -23,6 +23,7 @@ interface NetworkAccodionProps { cursor: Option; setCursor: (index: Option) => void; startIndex: number; + onClose: () => void; } interface SectionTitleProps { @@ -50,6 +51,7 @@ export const NetworkAccodion = ({ cursor, setCursor, startIndex, + onClose, }: NetworkAccodionProps) => { const isInitia = useInitia(); const l1Networks = normalNetworks.filter( @@ -84,6 +86,7 @@ export const NetworkAccodion = ({ index={startIndex + index} cursor={cursor} setCursor={setCursor} + onClose={onClose} /> ))} @@ -100,6 +103,7 @@ export const NetworkAccodion = ({ index={startIndex + l1Networks.length + index} cursor={cursor} setCursor={setCursor} + onClose={onClose} /> ))} @@ -116,6 +120,7 @@ export const NetworkAccodion = ({ index={startIndex + index} cursor={cursor} setCursor={setCursor} + onClose={onClose} /> ))} diff --git a/src/lib/layout/network-selector/NetworkCard.tsx b/src/lib/layout/network-selector/NetworkCard.tsx index ab8d893c5..b36f38142 100644 --- a/src/lib/layout/network-selector/NetworkCard.tsx +++ b/src/lib/layout/network-selector/NetworkCard.tsx @@ -8,16 +8,28 @@ import { CustomIcon } from "lib/components/icon"; import { useNetworkStore } from "lib/providers/store"; import type { Option } from "lib/types"; -interface NetworkCardProps { - image?: string; +interface NetworkCommonProps { chainId: string; isSelected: boolean; isDraggable?: boolean; +} + +interface NetworkCardProps extends NetworkCommonProps { + image?: string; index?: number; cursor: Option; setCursor: (index: Option) => void; + onClose: () => void; +} + +interface NetworkIconsProps extends NetworkCommonProps { + handleSave: (e: React.MouseEvent) => void; + handleRemove: (e: React.MouseEvent) => void; + isNetworkPinned: (chainId: string) => boolean; } +const customTransition = "opacity 0.1s ease-in-out"; + const iconProps = { cursor: "pointer", className: "icon-container", @@ -25,7 +37,88 @@ const iconProps = { padding: 1, }; -const customTransition = "opacity 0.1s ease-in-out"; +const getCardBackground = ( + index?: number, + cursor?: Option, + isSelected?: boolean +) => { + if (index === cursor) { + return "gray.800"; + } + if (isSelected) { + return "gray.700"; + } + return "transparent"; +}; + +const getDisplayCursor = (isDraggable: boolean, isSelected: boolean) => { + if (isDraggable) { + return "inherit"; + } + if (isSelected) { + return "default"; + } + return "pointer"; +}; + +const useHandleToast = () => { + return useToast({ + status: "success", + duration: 5000, + isClosable: false, + position: "bottom-right", + icon: , + }); +}; + +const NetworkIcons = ({ + chainId, + isSelected, + isDraggable, + handleSave, + handleRemove, + isNetworkPinned, +}: NetworkIconsProps) => ( + + {isDraggable && ( + + + + )} + {isNetworkPinned(chainId) ? ( + + + + ) : ( + + + + )} + +); export const NetworkCard = observer( ({ @@ -36,19 +129,14 @@ export const NetworkCard = observer( index, cursor, setCursor, + onClose, }: NetworkCardProps) => { const selectChain = useSelectChain(); const [secondaryDarker] = useToken("colors", ["secondary.darker"]); const fallbackImage = `https://ui-avatars.com/api/?name=${CHAIN_CONFIGS[chainId]?.prettyName || chainId}&background=${secondaryDarker.replace("#", "")}&color=fff`; const { isNetworkPinned, pinNetwork, removeNetwork } = useNetworkStore(); - const toast = useToast({ - status: "success", - duration: 5000, - isClosable: false, - position: "bottom-right", - icon: , - }); + const toast = useHandleToast(); const handleSave = useCallback( (e: React.MouseEvent) => { @@ -81,20 +169,11 @@ export const NetworkCard = observer( (e: React.MouseEvent) => { e.stopPropagation(); selectChain(chainId); + onClose(); }, - [selectChain, chainId] + [selectChain, chainId, onClose] ); - let cardBackground; - - if (isSelected) { - cardBackground = "gray.700"; - } else if (index === cursor) { - cardBackground = "gray.800"; - } else { - cardBackground = "transparent"; - } - return ( index !== cursor && setCursor(index)} _hover={{ - background: !isSelected && "gray.800", + background: isSelected ? "gray.700" : "gray.800", "> .icon-wrapper > .icon-container": { opacity: 1, }, }} - onMouseMove={() => index !== cursor && setCursor(index)} - onClick={handleClick} - cursor={!isDraggable ? "pointer" : "inherit"} > - - {isDraggable && ( - - - - )} - {isNetworkPinned(chainId) ? ( - - - - ) : ( - - - - )} - + ); } diff --git a/src/lib/layout/network-selector/NetworkDragItemWrapper.tsx b/src/lib/layout/network-selector/NetworkDragItemWrapper.tsx new file mode 100644 index 000000000..c122b9920 --- /dev/null +++ b/src/lib/layout/network-selector/NetworkDragItemWrapper.tsx @@ -0,0 +1,64 @@ +import { Box } from "@chakra-ui/react"; +import { useSortable } from "@dnd-kit/sortable"; +import { CSS } from "@dnd-kit/utilities"; + +import { CHAIN_CONFIGS } from "config/chain"; +import type { Option } from "lib/types"; + +import { NetworkCard } from "./NetworkCard"; + +interface NetworkDragItemWrapperProps { + chainId: string; + currentChainId: string; + index?: number; + cursor: Option; + setCursor: (index: Option) => void; + onClose: () => void; +} +export const NetworkDragItemWrapper = ({ + chainId, + currentChainId, + index, + cursor, + setCursor, + onClose, +}: NetworkDragItemWrapperProps) => { + const { + attributes, + listeners, + setNodeRef, + transform, + transition, + isDragging, + } = useSortable({ + id: chainId, + }); + + const style = { + opacity: isDragging ? 0.7 : undefined, + cursor: isDragging ? "grabbing" : "grab", + transform: transform ? CSS.Transform.toString(transform) : "none", + transition: transition || "transform 250ms ease", + }; + + return ( + e.stopPropagation()} + > + + + ); +}; diff --git a/src/lib/layout/network-selector/NetworkMenu.tsx b/src/lib/layout/network-selector/NetworkMenu.tsx index bb50489d7..f66f98a69 100644 --- a/src/lib/layout/network-selector/NetworkMenu.tsx +++ b/src/lib/layout/network-selector/NetworkMenu.tsx @@ -68,7 +68,7 @@ export const NetworkMenu = observer(() => { useEffect(() => { const openSearchHandler = (event: KeyboardEvent) => { const specialKey = isMac ? event.metaKey : event.ctrlKey; - if (event.key === `\\` && specialKey) { + if (event.key === `/` && specialKey) { event.preventDefault(); if (isOpen) { onClose(); @@ -108,7 +108,7 @@ export const NetworkMenu = observer(() => { - \ + / diff --git a/src/lib/layout/network-selector/NetworkMenuBody.tsx b/src/lib/layout/network-selector/NetworkMenuBody.tsx index 3c1445f70..7f196c0fa 100644 --- a/src/lib/layout/network-selector/NetworkMenuBody.tsx +++ b/src/lib/layout/network-selector/NetworkMenuBody.tsx @@ -5,7 +5,6 @@ import { AccordionIcon, AccordionItem, AccordionPanel, - Box, Divider, Flex, FormControl, @@ -24,10 +23,8 @@ import { } from "@dnd-kit/core"; import { SortableContext, - useSortable, verticalListSortingStrategy, } from "@dnd-kit/sortable"; -import { CSS } from "@dnd-kit/utilities"; import { observer } from "mobx-react-lite"; import { useCallback, useMemo, useState } from "react"; import type { KeyboardEvent as ReactKeyboardEvent } from "react"; @@ -40,7 +37,7 @@ import { useNetworkStore } from "lib/providers/store"; import type { Option } from "lib/types"; import { NetworkAccodion } from "./NetworkAccordion"; -import { NetworkCard } from "./NetworkCard"; +import { NetworkDragItemWrapper } from "./NetworkDragItemWrapper"; interface NetworkMenuBodyProps { currentChainId: string; @@ -74,58 +71,6 @@ const dropAnimationConfig: DropAnimation = { }), }; -const SortableItem = ({ - chainId, - currentChainId, - index, - cursor, - setCursor, -}: { - chainId: string; - currentChainId: string; - index?: number; - cursor: Option; - setCursor: (index: Option) => void; -}) => { - const { - attributes, - listeners, - setNodeRef, - transform, - transition, - isDragging, - } = useSortable({ - id: chainId, - }); - - const style = { - opacity: isDragging ? 0.7 : undefined, - cursor: isDragging ? "grabbing" : "grab", - transform: transform ? CSS.Transform.toString(transform) : "none", - transition: transition || "transform 250ms ease", - }; - - return ( - e.stopPropagation()} - > - - - ); -}; - export const NetworkMenuBody = observer( ({ currentChainId, onClose }: NetworkMenuBodyProps) => { const selectChain = useSelectChain(); @@ -204,13 +149,16 @@ export const NetworkMenuBody = observer( // Navigate with arrow keys + const formattedPinnedNetworks = filteredPinnedNetworks.map( + (x) => x.chainId + ); const allNetworks = useMemo( () => [ - ...filteredPinnedNetworks, + ...formattedPinnedNetworks, ...filteredMainnetChains, ...filteredTestnetChains, ], - [filteredPinnedNetworks, filteredMainnetChains, filteredTestnetChains] + [formattedPinnedNetworks, filteredMainnetChains, filteredTestnetChains] ); const handleOnKeyEnter = useCallback( @@ -287,24 +235,26 @@ export const NetworkMenuBody = observer( strategy={verticalListSortingStrategy} > {filteredPinnedNetworks.map((item, index) => ( - ))} {activeItem ? ( - ) : null} @@ -325,6 +275,7 @@ export const NetworkMenuBody = observer( cursor={cursor} setCursor={setCursor} startIndex={filteredPinnedNetworks.length} + onClose={onClose} /> {filteredMainnetChains.length > 0 && ( {areAllNetworksEmpty && ( From e8f4087ad5648cbe744a176dfdb6ca70ba2a21bf Mon Sep 17 00:00:00 2001 From: Jennie Alles Date: Wed, 17 Jul 2024 17:29:31 +0700 Subject: [PATCH 09/24] fix(components): fix image --- .../layout/network-selector/NetworkCard.tsx | 18 +++++--------- .../layout/network-selector/NetworkImage.tsx | 24 +++++++++++++++++++ src/lib/stores/networks.ts | 2 +- 3 files changed, 31 insertions(+), 13 deletions(-) create mode 100644 src/lib/layout/network-selector/NetworkImage.tsx diff --git a/src/lib/layout/network-selector/NetworkCard.tsx b/src/lib/layout/network-selector/NetworkCard.tsx index b36f38142..316852bce 100644 --- a/src/lib/layout/network-selector/NetworkCard.tsx +++ b/src/lib/layout/network-selector/NetworkCard.tsx @@ -1,4 +1,4 @@ -import { Box, Flex, Image, Text, useToast, useToken } from "@chakra-ui/react"; +import { Box, Flex, Text, useToast } from "@chakra-ui/react"; import { observer } from "mobx-react-lite"; import { useCallback } from "react"; @@ -8,6 +8,8 @@ import { CustomIcon } from "lib/components/icon"; import { useNetworkStore } from "lib/providers/store"; import type { Option } from "lib/types"; +import { NetworkImage } from "./NetworkImage"; + interface NetworkCommonProps { chainId: string; isSelected: boolean; @@ -132,8 +134,6 @@ export const NetworkCard = observer( onClose, }: NetworkCardProps) => { const selectChain = useSelectChain(); - const [secondaryDarker] = useToken("colors", ["secondary.darker"]); - const fallbackImage = `https://ui-avatars.com/api/?name=${CHAIN_CONFIGS[chainId]?.prettyName || chainId}&background=${secondaryDarker.replace("#", "")}&color=fff`; const { isNetworkPinned, pinNetwork, removeNetwork } = useNetworkStore(); const toast = useHandleToast(); @@ -144,14 +144,14 @@ export const NetworkCard = observer( pinNetwork({ name: CHAIN_CONFIGS[chainId]?.prettyName, chainId, - logo: image || fallbackImage, + logo: image, id: "", }); toast({ title: `Pinned \u2018${CHAIN_CONFIGS[chainId]?.prettyName}\u2019 successfully`, }); }, - [pinNetwork, image, chainId, toast, fallbackImage] + [pinNetwork, image, chainId, toast] ); const handleRemove = useCallback( @@ -207,13 +207,7 @@ export const NetworkCard = observer( left="0px" /> - + {CHAIN_CONFIGS[chainId]?.prettyName || chainId} diff --git a/src/lib/layout/network-selector/NetworkImage.tsx b/src/lib/layout/network-selector/NetworkImage.tsx new file mode 100644 index 000000000..aba283af7 --- /dev/null +++ b/src/lib/layout/network-selector/NetworkImage.tsx @@ -0,0 +1,24 @@ +import { Image, useToken } from "@chakra-ui/react"; + +import { CHAIN_CONFIGS } from "config/chain"; + +export const NetworkImage = ({ + chainId, + image, +}: { + chainId: string; + image?: string; +}) => { + const [secondaryDarker] = useToken("colors", ["secondary.darker"]); + const fallbackImage = `https://ui-avatars.com/api/?name=${CHAIN_CONFIGS[chainId]?.prettyName || chainId}&background=${secondaryDarker.replace("#", "")}&color=fff`; + + return ( + + ); +}; diff --git a/src/lib/stores/networks.ts b/src/lib/stores/networks.ts index 2a737798b..5216b3b1f 100644 --- a/src/lib/stores/networks.ts +++ b/src/lib/stores/networks.ts @@ -8,7 +8,7 @@ import type { Dict } from "lib/types"; export interface Network { name: string; chainId: string; - logo: string; + logo?: string; id: string; } From 04229b0623af2d9fcf848a0ffbdd554dea88e8d6 Mon Sep 17 00:00:00 2001 From: songwongtp <16089160+songwongtp@users.noreply.github.com> Date: Thu, 18 Jul 2024 15:12:46 +0700 Subject: [PATCH 10/24] fix: move layer to extra --- src/config/chain/default.ts | 1 - src/config/chain/initia.ts | 25 ++++++++++--------- src/config/chain/neutron.ts | 2 -- src/config/chain/osmosis.ts | 3 --- src/config/chain/sei.ts | 2 -- src/config/chain/stargaze.ts | 2 -- src/config/chain/terra.ts | 3 --- src/config/chain/types.ts | 2 +- .../layout/network-menu/NetworkAccordion.tsx | 4 +-- 9 files changed, 16 insertions(+), 28 deletions(-) diff --git a/src/config/chain/default.ts b/src/config/chain/default.ts index 170d330c5..2f783eb98 100644 --- a/src/config/chain/default.ts +++ b/src/config/chain/default.ts @@ -6,7 +6,6 @@ export const DEFAULT_CHAIN_CONFIG: ChainConfig = { registryChainName: "", prettyName: "", networkType: "testnet", - layer: null, lcd: "", rpc: "", indexer: "", diff --git a/src/config/chain/initia.ts b/src/config/chain/initia.ts index 63e9644a3..414b6ac49 100644 --- a/src/config/chain/initia.ts +++ b/src/config/chain/initia.ts @@ -14,7 +14,6 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "minimovetestnet", prettyName: "Minimove", networkType: "testnet", - layer: "2", logoUrl: "https://assets.alleslabs.dev/integrations/chains/minimove.png", lcd: "https://lcd.minimove-1.initia.xyz", rpc: "https://rpc.minimove-1.initia.xyz", @@ -57,6 +56,7 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { }, extra: { isValidatorExternalLink: null, + layer: "2", }, }, "minimove-1-sequencer": { @@ -65,7 +65,6 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "minimovetestnet", prettyName: "Minimove", networkType: "testnet", - layer: "2", logoUrl: "https://assets.alleslabs.dev/integrations/chains/minimove.png", lcd: "https://lcd.minimove-1.initia.xyz", rpc: "https://rpc.minimove-1.initia.xyz", @@ -108,6 +107,7 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { }, extra: { isValidatorExternalLink: null, + layer: "2", }, }, "initiation-1": { @@ -116,7 +116,6 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "initiatestnet", prettyName: "Initia", networkType: "testnet", - layer: "1", logoUrl: "https://assets.alleslabs.dev/integrations/chains/initia.png", lcd: "https://lcd.initiation-1.initia.xyz", rpc: "https://rpc.initiation-1.initia.xyz", @@ -160,7 +159,9 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { gasAdjustment: 1.5, maxGasLimit: 200_000_000, }, - extra: {}, + extra: { + layer: "1", + }, }, "minimove-1": { tier: "full", @@ -168,7 +169,6 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "minimovetestnet", prettyName: "Minimove", networkType: "testnet", - layer: "2", logoUrl: "https://assets.alleslabs.dev/integrations/chains/minimove.png", lcd: "https://lcd.minimove-1.initia.xyz", rpc: "https://rpc.minimove-1.initia.xyz", @@ -211,6 +211,7 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { }, extra: { isValidatorExternalLink: null, + layer: "2", }, }, "miniwasm-1": { @@ -219,7 +220,6 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "miniwasmtestnet", prettyName: "Miniwasm", networkType: "testnet", - layer: "2", logoUrl: "https://assets.alleslabs.dev/integrations/chains/miniwasm.png", lcd: "https://lcd.miniwasm-1.initia.xyz", rpc: "https://rpc.miniwasm-1.initia.xyz:443", @@ -261,6 +261,7 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { }, extra: { isValidatorExternalLink: null, + layer: "2", }, }, "tomcat-1": { @@ -269,7 +270,6 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "blackwingtestnet", prettyName: "Blackwing", networkType: "testnet", - layer: "2", logoUrl: "https://assets.alleslabs.dev/integrations/chains/blackwing.png", lcd: "https://maze-rest-18bdff44-3aa4-425e-9bc0-06a2afa40af8.ase1-prod.newmetric.xyz", rpc: "https://maze-rpc-18bdff44-3aa4-425e-9bc0-06a2afa40af8.ase1-prod.newmetric.xyz", @@ -312,6 +312,7 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { }, extra: { isValidatorExternalLink: null, + layer: "2", }, }, "init-ai-1": { @@ -320,7 +321,6 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "initaitestnet", prettyName: "INIT AI", networkType: "testnet", - layer: "2", logoUrl: "https://assets.alleslabs.dev/integrations/chains/initai.png", lcd: "https://maze-rest-617bacff-7d34-4eb8-87f4-ee16fb4e0ac7.ue1-prod.newmetric.xyz", rpc: "https://maze-rpc-617bacff-7d34-4eb8-87f4-ee16fb4e0ac7.ue1-prod.newmetric.xyz", @@ -363,6 +363,7 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { }, extra: { isValidatorExternalLink: null, + layer: "2", }, }, "burrito-1": { @@ -371,7 +372,6 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "noontestnet", prettyName: "Noon", networkType: "testnet", - layer: "2", logoUrl: "https://assets.alleslabs.dev/integrations/chains/noon.png", lcd: "https://burrito-1-lcd.lunchlunch.xyz", rpc: "https://burrito-1-rpc.lunchlunch.xyz", @@ -414,6 +414,7 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { }, extra: { isValidatorExternalLink: null, + layer: "2", }, }, "birdee-1": { @@ -422,7 +423,6 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "tucanatestnet", prettyName: "Tucana", networkType: "testnet", - layer: "2", logoUrl: "https://assets.alleslabs.dev/integrations/chains/tucana.png", lcd: "https://maze-rest-c9796789-107d-49ab-b6de-059724d2a91d.ue1-prod.newmetric.xyz", rpc: "https://maze-rpc-c9796789-107d-49ab-b6de-059724d2a91d.ue1-prod.newmetric.xyz", @@ -464,6 +464,7 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { }, extra: { isValidatorExternalLink: null, + layer: "2", }, }, "landlord-1": { @@ -472,7 +473,6 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "civitiatestnet", prettyName: "Civitia", networkType: "testnet", - layer: "2", logoUrl: "https://assets.alleslabs.dev/integrations/chains/civitia.png", lcd: "https://maze-rest-sequencer-beab9b6f-d96d-435e-9caf-5679296d8172.ue1-prod.newmetric.xyz", rpc: "https://maze-rpc-sequencer-beab9b6f-d96d-435e-9caf-5679296d8172.ue1-prod.newmetric.xyz", @@ -515,6 +515,7 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { }, extra: { isValidatorExternalLink: null, + layer: "2", }, }, "glados-1": { @@ -523,7 +524,6 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "milkywaytestnet", prettyName: "Milkyway", networkType: "testnet", - layer: "2", lcd: "https://lcd.testnet.milkyway.zone", rpc: "https://rpc.testnet.milkyway.zone", indexer: "", @@ -564,6 +564,7 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { }, extra: { isValidatorExternalLink: null, + layer: "2", }, }, }; diff --git a/src/config/chain/neutron.ts b/src/config/chain/neutron.ts index 64ef1b038..a21d76d80 100644 --- a/src/config/chain/neutron.ts +++ b/src/config/chain/neutron.ts @@ -9,7 +9,6 @@ export const NEUTRON_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "neutron", prettyName: "Neutron", networkType: "mainnet", - layer: null, logoUrl: "https://assets.alleslabs.dev/integrations/chains/neutron.png", lcd: "https://rest-kralum.neutron-1.neutron.org", rpc: "https://rpc-kralum.neutron-1.neutron.org:443", @@ -58,7 +57,6 @@ export const NEUTRON_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "neutrontestnet", prettyName: "Neutron Testnet", networkType: "testnet", - layer: null, logoUrl: "https://assets.alleslabs.dev/integrations/chains/neutron.png", lcd: "https://rest-palvus.pion-1.ntrn.tech:443", rpc: "https://rpc-palvus.pion-1.ntrn.tech:443", diff --git a/src/config/chain/osmosis.ts b/src/config/chain/osmosis.ts index d4476108c..55ca2aab6 100644 --- a/src/config/chain/osmosis.ts +++ b/src/config/chain/osmosis.ts @@ -11,7 +11,6 @@ export const OSMOSIS_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "osmosis", prettyName: "Osmosis", networkType: "mainnet", - layer: null, logoUrl: OSMOSIS_LOGO, lcd: "https://lcd.osmosis.zone", rpc: "https://rpc.osmosis.zone:443", @@ -62,7 +61,6 @@ export const OSMOSIS_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "osmosistestnet", prettyName: "Osmosis Testnet", networkType: "testnet", - layer: null, logoUrl: OSMOSIS_LOGO, lcd: "https://lcd.osmotest5.osmosis.zone", rpc: "https://osmosis-testnet-rpc.polkachu.com:443", @@ -114,7 +112,6 @@ export const OSMOSIS_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "localosmosis", prettyName: "Local Osmosis", networkType: "testnet", - layer: null, logoUrl: OSMOSIS_LOGO, lcd: "http://localhost/rest", rpc: "http://localhost:80/rpc/", diff --git a/src/config/chain/sei.ts b/src/config/chain/sei.ts index f72734504..cc5b95876 100644 --- a/src/config/chain/sei.ts +++ b/src/config/chain/sei.ts @@ -15,7 +15,6 @@ export const SEI_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "sei", prettyName: "Sei", networkType: "mainnet", - layer: null, logoUrl: "https://assets.alleslabs.dev/integrations/chains/sei.png", lcd: "https://sei-api.polkachu.com", rpc: "https://sei-rpc.polkachu.com:443", @@ -66,7 +65,6 @@ export const SEI_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "seitestnet2", prettyName: "Sei Testnet2", networkType: "testnet", - layer: null, logoUrl: "https://assets.alleslabs.dev/integrations/chains/sei.png", lcd: "https://rest.atlantic-2.seinetwork.io", rpc: "https://rpc.atlantic-2.seinetwork.io:443", diff --git a/src/config/chain/stargaze.ts b/src/config/chain/stargaze.ts index 681d93be3..bf6dd29c8 100644 --- a/src/config/chain/stargaze.ts +++ b/src/config/chain/stargaze.ts @@ -9,7 +9,6 @@ export const STARGAZE_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "stargaze", prettyName: "Stargaze", networkType: "mainnet", - layer: null, logoUrl: "https://assets.alleslabs.dev/integrations/chains/neutron.png", lcd: "https://rest.stargaze-apis.com", rpc: "https://rpc.stargaze-apis.com:443", @@ -57,7 +56,6 @@ export const STARGAZE_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "stargazetestnet", prettyName: "Stargaze Testnet", networkType: "testnet", - layer: null, lcd: "https://rest.elgafar-1.stargaze-apis.com", rpc: "https://rpc.elgafar-1.stargaze-apis.com", indexer: "https://elgafar-1-graphql.alleslabs.dev/v1/graphql", diff --git a/src/config/chain/terra.ts b/src/config/chain/terra.ts index c4f847c70..980eec837 100644 --- a/src/config/chain/terra.ts +++ b/src/config/chain/terra.ts @@ -12,7 +12,6 @@ export const TERRA_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "terra2testnet", prettyName: "Terra Testnet Lite", networkType: "testnet", - layer: null, logoUrl: TERRA_LOGO, lcd: "https://terra-testnet-api.polkachu.com", rpc: "https://terra-testnet-rpc.polkachu.com:443", @@ -61,7 +60,6 @@ export const TERRA_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "terra2", prettyName: "Terra", networkType: "mainnet", - layer: null, logoUrl: TERRA_LOGO, lcd: "https://phoenix-lcd.terra.dev:443", rpc: "https://terra2-rpc.lavenderfive.com:443", @@ -110,7 +108,6 @@ export const TERRA_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "terra2testnet", prettyName: "Terra Testnet", networkType: "testnet", - layer: null, logoUrl: TERRA_LOGO, lcd: "https://terra-testnet-api.polkachu.com", rpc: "https://terra-testnet-rpc.polkachu.com:443", diff --git a/src/config/chain/types.ts b/src/config/chain/types.ts index 3265ac4f7..525fcfb97 100644 --- a/src/config/chain/types.ts +++ b/src/config/chain/types.ts @@ -59,7 +59,6 @@ export interface ChainConfig { prettyName: string; logoUrl?: string; networkType: "mainnet" | "testnet"; - layer: "1" | "2" | null; lcd: string; rpc: string; indexer: string; @@ -84,6 +83,7 @@ export interface ChainConfig { extra: { disableAnyOfAddresses?: boolean; isValidatorExternalLink?: Nullable; + layer?: "1" | "2"; }; } diff --git a/src/lib/layout/network-menu/NetworkAccordion.tsx b/src/lib/layout/network-menu/NetworkAccordion.tsx index a5bef29e8..412aab6d6 100644 --- a/src/lib/layout/network-menu/NetworkAccordion.tsx +++ b/src/lib/layout/network-menu/NetworkAccordion.tsx @@ -55,10 +55,10 @@ export const NetworkAccodion = ({ }: NetworkAccodionProps) => { const isInitia = useInitia(); const l1Networks = normalNetworks.filter( - (chainId) => CHAIN_CONFIGS[chainId]?.layer === "1" + (chainId) => CHAIN_CONFIGS[chainId]?.extra.layer === "1" ); const l2Networks = normalNetworks.filter( - (chainId) => CHAIN_CONFIGS[chainId]?.layer === "2" + (chainId) => CHAIN_CONFIGS[chainId]?.extra.layer === "2" ); return ( From 0ffe51ce1ecb6290dedc580ad19dc5b3210c34db Mon Sep 17 00:00:00 2001 From: songwongtp <16089160+songwongtp@users.noreply.github.com> Date: Thu, 18 Jul 2024 15:24:39 +0700 Subject: [PATCH 11/24] fix: set network store userkey --- src/lib/providers/network-guard/index.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib/providers/network-guard/index.tsx b/src/lib/providers/network-guard/index.tsx index 51a553f1b..8a5c2dfc9 100644 --- a/src/lib/providers/network-guard/index.tsx +++ b/src/lib/providers/network-guard/index.tsx @@ -10,6 +10,7 @@ import { useAccountStore, useCodeStore, useContractStore, + useNetworkStore, usePublicProjectStore, } from "lib/providers/store"; import { formatUserKey } from "lib/utils"; @@ -29,6 +30,7 @@ export const NetworkGuard = observer(({ children }: NetworkGuardProps) => { const { setAccountUserKey, isAccountUserKeyExist } = useAccountStore(); const { setCodeUserKey, isCodeUserKeyExist } = useCodeStore(); const { setContractUserKey, isContractUserKeyExist } = useContractStore(); + const { setNetworkUserKey, isNetworkUserKeyExist } = useNetworkStore(); const { setProjectUserKey, isProjectUserKeyExist } = usePublicProjectStore(); useEffect(() => { @@ -38,6 +40,8 @@ export const NetworkGuard = observer(({ children }: NetworkGuardProps) => { setCodeUserKey(userKey); setContractUserKey(userKey); setProjectUserKey(userKey); + + setNetworkUserKey(DEFAULT_ADDRESS); } }, [ isHydrated, @@ -45,6 +49,7 @@ export const NetworkGuard = observer(({ children }: NetworkGuardProps) => { setAccountUserKey, setCodeUserKey, setContractUserKey, + setNetworkUserKey, setProjectUserKey, ]); @@ -56,6 +61,7 @@ export const NetworkGuard = observer(({ children }: NetworkGuardProps) => { !isAccountUserKeyExist() || !isCodeUserKeyExist() || !isContractUserKeyExist() || + !isNetworkUserKeyExist() || !isProjectUserKeyExist() ) return ; From ddb93485d18c31d8e650d7806f0cef73dc6c2a1e Mon Sep 17 00:00:00 2001 From: songwongtp <16089160+songwongtp@users.noreply.github.com> Date: Thu, 18 Jul 2024 15:41:34 +0700 Subject: [PATCH 12/24] fix: button --- .../layout/network-menu/NetworkAccordion.tsx | 89 ++++++++++--------- src/lib/layout/network-menu/index.tsx | 21 +++-- 2 files changed, 58 insertions(+), 52 deletions(-) diff --git a/src/lib/layout/network-menu/NetworkAccordion.tsx b/src/lib/layout/network-menu/NetworkAccordion.tsx index 412aab6d6..bd9e3fc1f 100644 --- a/src/lib/layout/network-menu/NetworkAccordion.tsx +++ b/src/lib/layout/network-menu/NetworkAccordion.tsx @@ -10,7 +10,6 @@ import { } from "@chakra-ui/react"; import { CHAIN_CONFIGS } from "config/chain"; -import { useInitia } from "lib/app-provider"; import type { Option } from "lib/types"; import { NetworkCard } from "./NetworkCard"; @@ -53,7 +52,9 @@ export const NetworkAccodion = ({ startIndex, onClose, }: NetworkAccodionProps) => { - const isInitia = useInitia(); + const nonInitiaNetworks = normalNetworks.filter( + (chainId) => CHAIN_CONFIGS[chainId]?.extra.layer === undefined + ); const l1Networks = normalNetworks.filter( (chainId) => CHAIN_CONFIGS[chainId]?.extra.layer === "1" ); @@ -64,7 +65,7 @@ export const NetworkAccodion = ({ return ( + {filteredPinnedNetworks.length > 0 && ( + - - {allNetworks.length === 0 && ( - + {filteredMainnetChains.length > 0 && ( + )} - - ); - } -); + + + {allNetworks.length === 0 && ( + + )} + + ); +}); diff --git a/src/lib/layout/network-menu/NetworkSubsection.tsx b/src/lib/layout/network-menu/NetworkSubsection.tsx new file mode 100644 index 000000000..3a010af9c --- /dev/null +++ b/src/lib/layout/network-menu/NetworkSubsection.tsx @@ -0,0 +1,54 @@ +import { Badge, Flex, Text } from "@chakra-ui/react"; + +import { CHAIN_CONFIGS } from "config/chain"; +import { useCelatoneApp } from "lib/app-provider"; +import type { Option } from "lib/types"; + +import { NetworkCard } from "./NetworkCard"; + +interface NetworkSubsectionProps { + title?: string; + networks: string[]; + cursor: Option; + setCursor: (index: Option) => void; + subsectionStartIndex: number; + onClose: () => void; +} + +export const NetworkSubsection = ({ + title, + networks, + cursor, + setCursor, + subsectionStartIndex, + onClose, +}: NetworkSubsectionProps) => { + const { currentChainId } = useCelatoneApp(); + + return ( + + {title !== undefined && ( + + + {title} + + + {networks.length} + + + )} + {networks.map((chainId, index) => ( + + ))} + + ); +}; diff --git a/src/lib/layout/network-menu/index.tsx b/src/lib/layout/network-menu/index.tsx index a3380737a..3c28d9cbe 100644 --- a/src/lib/layout/network-menu/index.tsx +++ b/src/lib/layout/network-menu/index.tsx @@ -23,13 +23,8 @@ import { CustomIcon } from "lib/components/icon"; import { NetworkMenuBody } from "./NetworkMenuBody"; -const NetworkButton = ({ - isMobile, - currentChainId, -}: { - isMobile: boolean; - currentChainId: string; -}) => { +const NetworkButton = ({ isMobile }: { isMobile: boolean }) => { + const { currentChainId } = useCelatoneApp(); const width = isMobile ? "220px" : "170px"; return ( { const isMobile = useMobile(); const isMac = useIsMac(); - const { currentChainId } = useCelatoneApp(); const { isOpen, onClose, onOpen } = useDisclosure(); useEffect(() => { @@ -96,7 +90,7 @@ export const NetworkMenu = observer(() => { }} autoSelect={!isMobile} > - + @@ -122,10 +116,7 @@ export const NetworkMenu = observer(() => { - + From b06e44182d4c4543063e4d687abd94c5e8200816 Mon Sep 17 00:00:00 2001 From: songwongtp <16089160+songwongtp@users.noreply.github.com> Date: Thu, 18 Jul 2024 18:24:41 +0700 Subject: [PATCH 15/24] fix: refactor dnd comp --- .../network-menu/NetworkAccordionPinned.tsx | 138 ++++++++++++++++++ ...emWrapper.tsx => NetworkCardDraggable.tsx} | 12 +- .../layout/network-menu/NetworkMenuBody.tsx | 132 ++--------------- src/lib/layout/network-menu/index.tsx | 4 +- 4 files changed, 159 insertions(+), 127 deletions(-) create mode 100644 src/lib/layout/network-menu/NetworkAccordionPinned.tsx rename src/lib/layout/network-menu/{NetworkDragItemWrapper.tsx => NetworkCardDraggable.tsx} (85%) diff --git a/src/lib/layout/network-menu/NetworkAccordionPinned.tsx b/src/lib/layout/network-menu/NetworkAccordionPinned.tsx new file mode 100644 index 000000000..c2f7a55b8 --- /dev/null +++ b/src/lib/layout/network-menu/NetworkAccordionPinned.tsx @@ -0,0 +1,138 @@ +import { + AccordionButton, + AccordionIcon, + AccordionItem, + AccordionPanel, + Flex, + Heading, +} from "@chakra-ui/react"; +import type { Active, DragEndEvent } from "@dnd-kit/core"; +import { + closestCenter, + defaultDropAnimationSideEffects, + DndContext, + DragOverlay, + MouseSensor, + TouchSensor, + useSensor, + useSensors, +} from "@dnd-kit/core"; +import { + SortableContext, + verticalListSortingStrategy, +} from "@dnd-kit/sortable"; +import { useMemo, useState } from "react"; + +import { useCelatoneApp } from "lib/app-provider"; +import { useNetworkStore } from "lib/providers/store"; +import type { Network } from "lib/stores/networks"; +import type { Nullable, Option } from "lib/types"; + +import { NetworkCardDraggable } from "./NetworkCardDraggable"; + +interface NetworkAccodionPinnedProps { + pinnedNetworks: Network[]; + cursor: Option; + setCursor: (index: Option) => void; + onClose: () => void; +} + +export const NetworkAccodionPinned = ({ + pinnedNetworks, + cursor, + setCursor, + onClose, +}: NetworkAccodionPinnedProps) => { + const { currentChainId } = useCelatoneApp(); + const { setPinnedNetworks } = useNetworkStore(); + const [dndActive, setDndActive] = useState>(null); + + // Drag and drop feature + const activeItem = useMemo( + () => pinnedNetworks.find((item) => item.id === dndActive?.id), + [dndActive, pinnedNetworks] + ); + + const sensors = useSensors( + useSensor(MouseSensor, { + activationConstraint: { + delay: 100, + tolerance: 5, + }, + }), + useSensor(TouchSensor) + ); + + const handleDragEnd = (event: DragEndEvent) => { + const { active, over } = event; + + if (active.id !== over?.id) { + setPinnedNetworks(active.id.toString(), over?.id.toString()); + } + }; + + return ( + + ); +}; diff --git a/src/lib/layout/network-menu/NetworkDragItemWrapper.tsx b/src/lib/layout/network-menu/NetworkCardDraggable.tsx similarity index 85% rename from src/lib/layout/network-menu/NetworkDragItemWrapper.tsx rename to src/lib/layout/network-menu/NetworkCardDraggable.tsx index c122b9920..f1d14a034 100644 --- a/src/lib/layout/network-menu/NetworkDragItemWrapper.tsx +++ b/src/lib/layout/network-menu/NetworkCardDraggable.tsx @@ -7,22 +7,22 @@ import type { Option } from "lib/types"; import { NetworkCard } from "./NetworkCard"; -interface NetworkDragItemWrapperProps { +interface NetworkCardDraggableProps { chainId: string; - currentChainId: string; + isSelected: boolean; index?: number; cursor: Option; setCursor: (index: Option) => void; onClose: () => void; } -export const NetworkDragItemWrapper = ({ +export const NetworkCardDraggable = ({ chainId, - currentChainId, + isSelected, index, cursor, setCursor, onClose, -}: NetworkDragItemWrapperProps) => { +}: NetworkCardDraggableProps) => { const { attributes, listeners, @@ -53,7 +53,7 @@ export const NetworkDragItemWrapper = ({ isDraggable image={CHAIN_CONFIGS[chainId]?.logoUrl} chainId={chainId} - isSelected={chainId === currentChainId} + isSelected={isSelected} index={index} cursor={cursor} setCursor={setCursor} diff --git a/src/lib/layout/network-menu/NetworkMenuBody.tsx b/src/lib/layout/network-menu/NetworkMenuBody.tsx index b6f2ac5b3..125f65f4e 100644 --- a/src/lib/layout/network-menu/NetworkMenuBody.tsx +++ b/src/lib/layout/network-menu/NetworkMenuBody.tsx @@ -1,42 +1,18 @@ /* eslint-disable sonarjs/cognitive-complexity */ -import { - Accordion, - AccordionButton, - AccordionIcon, - AccordionItem, - AccordionPanel, - Divider, - Flex, - Heading, -} from "@chakra-ui/react"; -import type { Active, DragEndEvent } from "@dnd-kit/core"; -import { - closestCenter, - defaultDropAnimationSideEffects, - DndContext, - DragOverlay, - MouseSensor, - TouchSensor, - useSensor, - useSensors, -} from "@dnd-kit/core"; -import { - SortableContext, - verticalListSortingStrategy, -} from "@dnd-kit/sortable"; +import { Accordion, Divider, Flex } from "@chakra-ui/react"; import { observer } from "mobx-react-lite"; -import { useCallback, useMemo, useState } from "react"; import type { KeyboardEvent as ReactKeyboardEvent } from "react"; +import { useCallback, useMemo, useState } from "react"; import { CHAIN_CONFIGS } from "config/chain"; import { useCelatoneApp, useSelectChain } from "lib/app-provider"; import InputWithIcon from "lib/components/InputWithIcon"; import { EmptyState } from "lib/components/state"; import { useNetworkStore } from "lib/providers/store"; -import type { Nullable, Option } from "lib/types"; +import type { Option } from "lib/types"; import { NetworkAccordion } from "./NetworkAccordion"; -import { NetworkDragItemWrapper } from "./NetworkDragItemWrapper"; +import { NetworkAccodionPinned } from "./NetworkAccordionPinned"; interface NetworkMenuBodyProps { onClose: () => void; @@ -77,12 +53,11 @@ const getNextCursor = ( export const NetworkMenuBody = observer(({ onClose }: NetworkMenuBodyProps) => { const selectChain = useSelectChain(); - const { availableChainIds, currentChainId } = useCelatoneApp(); - const { getPinnedNetworks, setPinnedNetworks } = useNetworkStore(); + const { availableChainIds } = useCelatoneApp(); + const { getPinnedNetworks } = useNetworkStore(); const [cursor, setCursor] = useState(); const [keyword, setKeyword] = useState(""); - const [dndActive, setDndActive] = useState>(null); // Get chains info const pinnedNetworks = getPinnedNetworks(); @@ -114,31 +89,6 @@ export const NetworkMenuBody = observer(({ onClose }: NetworkMenuBodyProps) => { [filteredPinnedNetworks, filteredMainnetChains, filteredTestnetChains] ); - // Drag and drop feature - - const activeItem = useMemo( - () => filteredPinnedNetworks.find((item) => item.id === dndActive?.id), - [dndActive, filteredPinnedNetworks] - ); - - const sensors = useSensors( - useSensor(MouseSensor, { - activationConstraint: { - delay: 100, - tolerance: 5, - }, - }), - useSensor(TouchSensor) - ); - - const handleDragEnd = (event: DragEndEvent) => { - const { active, over } = event; - - if (active.id !== over?.id) { - setPinnedNetworks(active.id.toString(), over?.id.toString()); - } - }; - // Navigate with arrow keys const handleOnKeyEnter = useCallback( (e: ReactKeyboardEvent) => { @@ -170,17 +120,7 @@ export const NetworkMenuBody = observer(({ onClose }: NetworkMenuBodyProps) => { ); return ( - { - setDndActive(active); - }} - onDragEnd={handleDragEnd} - onDragCancel={() => { - setDndActive(null); - }} - > + { width="full" p={0} > - + {filteredPinnedNetworks.length > 0 && ( { Please check your keyword." /> )} - + ); }); diff --git a/src/lib/layout/network-menu/index.tsx b/src/lib/layout/network-menu/index.tsx index 3c28d9cbe..0497ea795 100644 --- a/src/lib/layout/network-menu/index.tsx +++ b/src/lib/layout/network-menu/index.tsx @@ -115,9 +115,7 @@ export const NetworkMenu = observer(() => { - - - + From 3066d71de4bf0f134ad2ed57e5b3114fb4dcdf63 Mon Sep 17 00:00:00 2001 From: songwongtp <16089160+songwongtp@users.noreply.github.com> Date: Thu, 18 Jul 2024 18:39:03 +0700 Subject: [PATCH 16/24] fix: refactor network card --- .../layout/network-menu/NetworkAccordion.tsx | 8 +- .../network-menu/NetworkAccordionPinned.tsx | 2 +- ...ion.tsx => NetworkAccordionSubsection.tsx} | 8 +- src/lib/layout/network-menu/NetworkCard.tsx | 231 ------------------ .../network-menu/network-card/NetworkCard.tsx | 121 +++++++++ .../NetworkCardDraggable.tsx | 0 .../network-card/NetworkIcons.tsx | 105 ++++++++ .../layout/network-menu/network-card/index.ts | 2 + 8 files changed, 237 insertions(+), 240 deletions(-) rename src/lib/layout/network-menu/{NetworkSubsection.tsx => NetworkAccordionSubsection.tsx} (87%) delete mode 100644 src/lib/layout/network-menu/NetworkCard.tsx create mode 100644 src/lib/layout/network-menu/network-card/NetworkCard.tsx rename src/lib/layout/network-menu/{ => network-card}/NetworkCardDraggable.tsx (100%) create mode 100644 src/lib/layout/network-menu/network-card/NetworkIcons.tsx create mode 100644 src/lib/layout/network-menu/network-card/index.ts diff --git a/src/lib/layout/network-menu/NetworkAccordion.tsx b/src/lib/layout/network-menu/NetworkAccordion.tsx index 0656dc16b..bc53f7295 100644 --- a/src/lib/layout/network-menu/NetworkAccordion.tsx +++ b/src/lib/layout/network-menu/NetworkAccordion.tsx @@ -10,7 +10,7 @@ import { import { CHAIN_CONFIGS } from "config/chain"; import type { Option } from "lib/types"; -import { NetworkSubsection } from "./NetworkSubsection"; +import { NetworkAccordionSubsection } from "./NetworkAccordionSubsection"; interface NetworkAccordionProps { title: string; @@ -50,7 +50,7 @@ export const NetworkAccordion = ({ - {l1Networks.length > 0 && ( - )} {l2Networks.length > 0 && ( - ; @@ -15,14 +15,14 @@ interface NetworkSubsectionProps { onClose: () => void; } -export const NetworkSubsection = ({ +export const NetworkAccordionSubsection = ({ title, networks, cursor, setCursor, subsectionStartIndex, onClose, -}: NetworkSubsectionProps) => { +}: NetworkAccordionSubsectionProps) => { const { currentChainId } = useCelatoneApp(); return ( diff --git a/src/lib/layout/network-menu/NetworkCard.tsx b/src/lib/layout/network-menu/NetworkCard.tsx deleted file mode 100644 index 316852bce..000000000 --- a/src/lib/layout/network-menu/NetworkCard.tsx +++ /dev/null @@ -1,231 +0,0 @@ -import { Box, Flex, Text, useToast } from "@chakra-ui/react"; -import { observer } from "mobx-react-lite"; -import { useCallback } from "react"; - -import { CHAIN_CONFIGS } from "config/chain"; -import { useSelectChain } from "lib/app-provider"; -import { CustomIcon } from "lib/components/icon"; -import { useNetworkStore } from "lib/providers/store"; -import type { Option } from "lib/types"; - -import { NetworkImage } from "./NetworkImage"; - -interface NetworkCommonProps { - chainId: string; - isSelected: boolean; - isDraggable?: boolean; -} - -interface NetworkCardProps extends NetworkCommonProps { - image?: string; - index?: number; - cursor: Option; - setCursor: (index: Option) => void; - onClose: () => void; -} - -interface NetworkIconsProps extends NetworkCommonProps { - handleSave: (e: React.MouseEvent) => void; - handleRemove: (e: React.MouseEvent) => void; - isNetworkPinned: (chainId: string) => boolean; -} - -const customTransition = "opacity 0.1s ease-in-out"; - -const iconProps = { - cursor: "pointer", - className: "icon-container", - align: "center", - padding: 1, -}; - -const getCardBackground = ( - index?: number, - cursor?: Option, - isSelected?: boolean -) => { - if (index === cursor) { - return "gray.800"; - } - if (isSelected) { - return "gray.700"; - } - return "transparent"; -}; - -const getDisplayCursor = (isDraggable: boolean, isSelected: boolean) => { - if (isDraggable) { - return "inherit"; - } - if (isSelected) { - return "default"; - } - return "pointer"; -}; - -const useHandleToast = () => { - return useToast({ - status: "success", - duration: 5000, - isClosable: false, - position: "bottom-right", - icon: , - }); -}; - -const NetworkIcons = ({ - chainId, - isSelected, - isDraggable, - handleSave, - handleRemove, - isNetworkPinned, -}: NetworkIconsProps) => ( - - {isDraggable && ( - - - - )} - {isNetworkPinned(chainId) ? ( - - - - ) : ( - - - - )} - -); - -export const NetworkCard = observer( - ({ - image, - chainId, - isSelected, - isDraggable = false, - index, - cursor, - setCursor, - onClose, - }: NetworkCardProps) => { - const selectChain = useSelectChain(); - const { isNetworkPinned, pinNetwork, removeNetwork } = useNetworkStore(); - - const toast = useHandleToast(); - - const handleSave = useCallback( - (e: React.MouseEvent) => { - e.stopPropagation(); - pinNetwork({ - name: CHAIN_CONFIGS[chainId]?.prettyName, - chainId, - logo: image, - id: "", - }); - toast({ - title: `Pinned \u2018${CHAIN_CONFIGS[chainId]?.prettyName}\u2019 successfully`, - }); - }, - [pinNetwork, image, chainId, toast] - ); - - const handleRemove = useCallback( - (e: React.MouseEvent) => { - e.stopPropagation(); - removeNetwork(chainId); - toast({ - title: `\u2018${CHAIN_CONFIGS[chainId]?.prettyName}\u2019 is removed from Pinned Networks`, - }); - }, - [removeNetwork, chainId, toast] - ); - - const handleClick = useCallback( - (e: React.MouseEvent) => { - e.stopPropagation(); - selectChain(chainId); - onClose(); - }, - [selectChain, chainId, onClose] - ); - - return ( - index !== cursor && setCursor(index)} - _hover={{ - background: isSelected ? "gray.700" : "gray.800", - "> .icon-wrapper > .icon-container": { - opacity: 1, - }, - }} - > - - - - - - {CHAIN_CONFIGS[chainId]?.prettyName || chainId} - - - {chainId} - - - - - - ); - } -); diff --git a/src/lib/layout/network-menu/network-card/NetworkCard.tsx b/src/lib/layout/network-menu/network-card/NetworkCard.tsx new file mode 100644 index 000000000..ad5a711b9 --- /dev/null +++ b/src/lib/layout/network-menu/network-card/NetworkCard.tsx @@ -0,0 +1,121 @@ +import { Box, Flex, Text } from "@chakra-ui/react"; +import { observer } from "mobx-react-lite"; +import { useCallback } from "react"; + +import { NetworkImage } from "../NetworkImage"; +import { CHAIN_CONFIGS } from "config/chain"; +import { useSelectChain } from "lib/app-provider"; +import type { Option } from "lib/types"; + +import { NetworkIcons } from "./NetworkIcons"; + +interface NetworkCardProps { + chainId: string; + isSelected: boolean; + isDraggable?: boolean; + image?: string; + index?: number; + cursor: Option; + setCursor: (index: Option) => void; + onClose: () => void; +} + +const getCardBackground = ( + index?: number, + cursor?: Option, + isSelected?: boolean +) => { + if (index === cursor) { + return "gray.800"; + } + if (isSelected) { + return "gray.700"; + } + return "transparent"; +}; + +const getDisplayCursor = (isDraggable: boolean, isSelected: boolean) => { + if (isDraggable) { + return "inherit"; + } + if (isSelected) { + return "default"; + } + return "pointer"; +}; + +export const NetworkCard = observer( + ({ + image, + chainId, + isSelected, + isDraggable = false, + index, + cursor, + setCursor, + onClose, + }: NetworkCardProps) => { + const selectChain = useSelectChain(); + + const handleClick = useCallback( + (e: React.MouseEvent) => { + e.stopPropagation(); + selectChain(chainId); + onClose(); + }, + [selectChain, chainId, onClose] + ); + + return ( + index !== cursor && setCursor(index)} + _hover={{ + background: isSelected ? "gray.700" : "gray.800", + "> .icon-wrapper > .icon-container": { + opacity: 1, + }, + }} + > + + + + + + {CHAIN_CONFIGS[chainId]?.prettyName || chainId} + + + {chainId} + + + + + + ); + } +); diff --git a/src/lib/layout/network-menu/NetworkCardDraggable.tsx b/src/lib/layout/network-menu/network-card/NetworkCardDraggable.tsx similarity index 100% rename from src/lib/layout/network-menu/NetworkCardDraggable.tsx rename to src/lib/layout/network-menu/network-card/NetworkCardDraggable.tsx diff --git a/src/lib/layout/network-menu/network-card/NetworkIcons.tsx b/src/lib/layout/network-menu/network-card/NetworkIcons.tsx new file mode 100644 index 000000000..8ec12e79e --- /dev/null +++ b/src/lib/layout/network-menu/network-card/NetworkIcons.tsx @@ -0,0 +1,105 @@ +import { Flex, useToast } from "@chakra-ui/react"; +import { observer } from "mobx-react-lite"; +import { useCallback } from "react"; + +import { CHAIN_CONFIGS } from "config/chain"; +import { CustomIcon } from "lib/components/icon"; +import { useNetworkStore } from "lib/providers/store"; + +const customTransition = "opacity 0.1s ease-in-out"; + +const iconProps = { + cursor: "pointer", + className: "icon-container", + align: "center", + padding: 1, +}; + +interface NetworkIconsProps { + chainId: string; + isSelected: boolean; + image?: string; + isDraggable?: boolean; +} + +export const NetworkIcons = observer( + ({ chainId, isSelected, image, isDraggable }: NetworkIconsProps) => { + const { isNetworkPinned, pinNetwork, removeNetwork } = useNetworkStore(); + const toast = useToast({ + status: "success", + duration: 5000, + isClosable: false, + position: "bottom-right", + icon: , + }); + + const handleSave = useCallback( + (e: React.MouseEvent) => { + e.stopPropagation(); + pinNetwork({ + name: CHAIN_CONFIGS[chainId]?.prettyName, + chainId, + logo: image, + id: "", + }); + toast({ + title: `Pinned \u2018${CHAIN_CONFIGS[chainId]?.prettyName}\u2019 successfully`, + }); + }, + [pinNetwork, image, chainId, toast] + ); + + const handleRemove = useCallback( + (e: React.MouseEvent) => { + e.stopPropagation(); + removeNetwork(chainId); + toast({ + title: `\u2018${CHAIN_CONFIGS[chainId]?.prettyName}\u2019 is removed from Pinned Networks`, + }); + }, + [removeNetwork, chainId, toast] + ); + + return ( + + {isDraggable && ( + + + + )} + {isNetworkPinned(chainId) ? ( + + + + ) : ( + + + + )} + + ); + } +); diff --git a/src/lib/layout/network-menu/network-card/index.ts b/src/lib/layout/network-menu/network-card/index.ts new file mode 100644 index 000000000..918387710 --- /dev/null +++ b/src/lib/layout/network-menu/network-card/index.ts @@ -0,0 +1,2 @@ +export * from "./NetworkCard"; +export * from "./NetworkCardDraggable"; From 8f26f673d1e28bc3fd8e3fba847b21646396616a Mon Sep 17 00:00:00 2001 From: songwongtp <16089160+songwongtp@users.noreply.github.com> Date: Thu, 18 Jul 2024 19:12:10 +0700 Subject: [PATCH 17/24] fix: missing query in move lite --- src/lib/layout/mobile/utils.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/layout/mobile/utils.ts b/src/lib/layout/mobile/utils.ts index 74996de5d..2762a9089 100644 --- a/src/lib/layout/mobile/utils.ts +++ b/src/lib/layout/mobile/utils.ts @@ -47,6 +47,11 @@ export const getNavDrawerLite = ( slug: "/accounts/0x1", icon: "hex" as IconKeys, }, + { + name: "View Module", + slug: "/interact", + icon: "query" as IconKeys, + }, ] : []), ], From 2bb05ec09294cbf2af1d1897122b34391ffffd8f Mon Sep 17 00:00:00 2001 From: songwongtp <16089160+songwongtp@users.noreply.github.com> Date: Fri, 19 Jul 2024 13:54:54 +0700 Subject: [PATCH 18/24] fix: refactor network button --- src/lib/layout/network-menu/NetworkButton.tsx | 41 +++++++++++++ src/lib/layout/network-menu/index.tsx | 60 ++++--------------- 2 files changed, 52 insertions(+), 49 deletions(-) create mode 100644 src/lib/layout/network-menu/NetworkButton.tsx diff --git a/src/lib/layout/network-menu/NetworkButton.tsx b/src/lib/layout/network-menu/NetworkButton.tsx new file mode 100644 index 000000000..0d455a621 --- /dev/null +++ b/src/lib/layout/network-menu/NetworkButton.tsx @@ -0,0 +1,41 @@ +import { Flex, Text } from "@chakra-ui/react"; + +import { useCelatoneApp } from "lib/app-provider"; +import { CustomIcon } from "lib/components/icon"; + +interface NetworkButtonProps { + isMobile: boolean; + onClick: () => void; +} + +export const NetworkButton = ({ isMobile, onClick }: NetworkButtonProps) => { + const { currentChainId } = useCelatoneApp(); + const width = isMobile ? "220px" : "170px"; + return ( + + + {currentChainId} + + + + ); +}; diff --git a/src/lib/layout/network-menu/index.tsx b/src/lib/layout/network-menu/index.tsx index 0497ea795..2033fa23e 100644 --- a/src/lib/layout/network-menu/index.tsx +++ b/src/lib/layout/network-menu/index.tsx @@ -9,8 +9,6 @@ import { Flex, Heading, Kbd, - Menu, - MenuButton, Text, useDisclosure, } from "@chakra-ui/react"; @@ -18,47 +16,11 @@ import { observer } from "mobx-react-lite"; import { useEffect } from "react"; import { AmpEvent } from "lib/amplitude"; -import { useCelatoneApp, useIsMac, useMobile } from "lib/app-provider"; -import { CustomIcon } from "lib/components/icon"; +import { useIsMac, useMobile } from "lib/app-provider"; +import { NetworkButton } from "./NetworkButton"; import { NetworkMenuBody } from "./NetworkMenuBody"; -const NetworkButton = ({ isMobile }: { isMobile: boolean }) => { - const { currentChainId } = useCelatoneApp(); - const width = isMobile ? "220px" : "170px"; - return ( - - - - {currentChainId} - - - - - ); -}; - export const NetworkMenu = observer(() => { const isMobile = useMobile(); const isMac = useIsMac(); @@ -83,14 +45,14 @@ export const NetworkMenu = observer(() => { }, [isMac, isOpen, onClose, onOpen]); return ( - { - track(AmpEvent.USE_SELECT_NETWORK); - onOpen(); - }} - autoSelect={!isMobile} - > - + <> + { + track(AmpEvent.USE_SELECT_NETWORK); + onOpen(); + }} + /> @@ -119,6 +81,6 @@ export const NetworkMenu = observer(() => { - + ); }); From 7ad2e8b065d77eefae64604dbb141c2412e41f37 Mon Sep 17 00:00:00 2001 From: songwongtp <16089160+songwongtp@users.noreply.github.com> Date: Fri, 19 Jul 2024 14:18:58 +0700 Subject: [PATCH 19/24] fix: misc optimize --- .../network-menu/NetworkAccordionPinned.tsx | 17 +++++++---------- src/lib/stores/networks.ts | 5 ++--- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/lib/layout/network-menu/NetworkAccordionPinned.tsx b/src/lib/layout/network-menu/NetworkAccordionPinned.tsx index ffa05b13c..7a86713bf 100644 --- a/src/lib/layout/network-menu/NetworkAccordionPinned.tsx +++ b/src/lib/layout/network-menu/NetworkAccordionPinned.tsx @@ -6,7 +6,7 @@ import { Flex, Heading, } from "@chakra-ui/react"; -import type { Active, DragEndEvent } from "@dnd-kit/core"; +import type { Active } from "@dnd-kit/core"; import { closestCenter, defaultDropAnimationSideEffects, @@ -63,14 +63,6 @@ export const NetworkAccodionPinned = ({ useSensor(TouchSensor) ); - const handleDragEnd = (event: DragEndEvent) => { - const { active, over } = event; - - if (active.id !== over?.id) { - setPinnedNetworks(active.id.toString(), over?.id.toString()); - } - }; - return ( diff --git a/src/lib/layout/network-menu/NetworkAccordionSubsection.tsx b/src/lib/layout/network-menu/NetworkAccordionSubsection.tsx index 2a5dd57b1..b7d008018 100644 --- a/src/lib/layout/network-menu/NetworkAccordionSubsection.tsx +++ b/src/lib/layout/network-menu/NetworkAccordionSubsection.tsx @@ -1,7 +1,5 @@ import { Badge, Flex, Text } from "@chakra-ui/react"; -import { CHAIN_CONFIGS } from "config/chain"; -import { useCelatoneApp } from "lib/app-provider"; import type { Option } from "lib/types"; import { NetworkCard } from "./network-card"; @@ -22,33 +20,27 @@ export const NetworkAccordionSubsection = ({ setCursor, subsectionStartIndex, onClose, -}: NetworkAccordionSubsectionProps) => { - const { currentChainId } = useCelatoneApp(); - - return ( - - {title !== undefined && ( - - - {title} - - - {networks.length} - - - )} - {networks.map((chainId, index) => ( - - ))} - - ); -}; +}: NetworkAccordionSubsectionProps) => ( + + {title !== undefined && ( + + + {title} + + + {networks.length} + + + )} + {networks.map((chainId, index) => ( + + ))} + +); diff --git a/src/lib/layout/network-menu/NetworkImage.tsx b/src/lib/layout/network-menu/NetworkImage.tsx index aba283af7..8337cdb20 100644 --- a/src/lib/layout/network-menu/NetworkImage.tsx +++ b/src/lib/layout/network-menu/NetworkImage.tsx @@ -2,14 +2,14 @@ import { Image, useToken } from "@chakra-ui/react"; import { CHAIN_CONFIGS } from "config/chain"; -export const NetworkImage = ({ - chainId, - image, -}: { +interface NetworkImageProps { chainId: string; - image?: string; -}) => { +} + +export const NetworkImage = ({ chainId }: NetworkImageProps) => { const [secondaryDarker] = useToken("colors", ["secondary.darker"]); + + const image = CHAIN_CONFIGS[chainId]?.logoUrl; const fallbackImage = `https://ui-avatars.com/api/?name=${CHAIN_CONFIGS[chainId]?.prettyName || chainId}&background=${secondaryDarker.replace("#", "")}&color=fff`; return ( @@ -19,6 +19,7 @@ export const NetworkImage = ({ borderRadius="full" src={image} fallbackSrc={fallbackImage} + fallbackStrategy="onError" /> ); }; diff --git a/src/lib/layout/network-menu/index.tsx b/src/lib/layout/network-menu/index.tsx index 2033fa23e..d047d43ef 100644 --- a/src/lib/layout/network-menu/index.tsx +++ b/src/lib/layout/network-menu/index.tsx @@ -61,18 +61,20 @@ export const NetworkMenu = observer(() => { Select Network - - - - {isMac ? "⌘" : "Ctrl"} - - - - - / - - - + {!isMobile && ( + + + + {isMac ? "⌘" : "Ctrl"} + + + + + / + + + + )} diff --git a/src/lib/layout/network-menu/network-card/NetworkCard.tsx b/src/lib/layout/network-menu/network-card/NetworkCard.tsx index ad5a711b9..fac07d1af 100644 --- a/src/lib/layout/network-menu/network-card/NetworkCard.tsx +++ b/src/lib/layout/network-menu/network-card/NetworkCard.tsx @@ -4,20 +4,18 @@ import { useCallback } from "react"; import { NetworkImage } from "../NetworkImage"; import { CHAIN_CONFIGS } from "config/chain"; -import { useSelectChain } from "lib/app-provider"; +import { useCelatoneApp, useSelectChain } from "lib/app-provider"; import type { Option } from "lib/types"; import { NetworkIcons } from "./NetworkIcons"; interface NetworkCardProps { chainId: string; - isSelected: boolean; - isDraggable?: boolean; - image?: string; index?: number; cursor: Option; setCursor: (index: Option) => void; onClose: () => void; + isDraggable?: boolean; } const getCardBackground = ( @@ -46,15 +44,14 @@ const getDisplayCursor = (isDraggable: boolean, isSelected: boolean) => { export const NetworkCard = observer( ({ - image, chainId, - isSelected, - isDraggable = false, index, cursor, setCursor, onClose, + isDraggable = false, }: NetworkCardProps) => { + const { currentChainId } = useCelatoneApp(); const selectChain = useSelectChain(); const handleClick = useCallback( @@ -66,6 +63,7 @@ export const NetworkCard = observer( [selectChain, chainId, onClose] ); + const isSelected = chainId === currentChainId; return ( - + {CHAIN_CONFIGS[chainId]?.prettyName || chainId} @@ -112,7 +110,6 @@ export const NetworkCard = observer( diff --git a/src/lib/layout/network-menu/network-card/NetworkCardDraggable.tsx b/src/lib/layout/network-menu/network-card/NetworkCardDraggable.tsx index f1d14a034..5675e0921 100644 --- a/src/lib/layout/network-menu/network-card/NetworkCardDraggable.tsx +++ b/src/lib/layout/network-menu/network-card/NetworkCardDraggable.tsx @@ -2,14 +2,12 @@ import { Box } from "@chakra-ui/react"; import { useSortable } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; -import { CHAIN_CONFIGS } from "config/chain"; import type { Option } from "lib/types"; import { NetworkCard } from "./NetworkCard"; interface NetworkCardDraggableProps { chainId: string; - isSelected: boolean; index?: number; cursor: Option; setCursor: (index: Option) => void; @@ -17,7 +15,6 @@ interface NetworkCardDraggableProps { } export const NetworkCardDraggable = ({ chainId, - isSelected, index, cursor, setCursor, @@ -35,29 +32,21 @@ export const NetworkCardDraggable = ({ }); const style = { - opacity: isDragging ? 0.7 : undefined, + opacity: isDragging ? 0 : 1, cursor: isDragging ? "grabbing" : "grab", - transform: transform ? CSS.Transform.toString(transform) : "none", - transition: transition || "transform 250ms ease", + transform: CSS.Transform.toString(transform), + transition, }; return ( - e.stopPropagation()} - > + ); diff --git a/src/lib/layout/network-menu/network-card/NetworkIcons.tsx b/src/lib/layout/network-menu/network-card/NetworkIcons.tsx index 8ec12e79e..b1e265d12 100644 --- a/src/lib/layout/network-menu/network-card/NetworkIcons.tsx +++ b/src/lib/layout/network-menu/network-card/NetworkIcons.tsx @@ -18,12 +18,11 @@ const iconProps = { interface NetworkIconsProps { chainId: string; isSelected: boolean; - image?: string; isDraggable?: boolean; } export const NetworkIcons = observer( - ({ chainId, isSelected, image, isDraggable }: NetworkIconsProps) => { + ({ chainId, isSelected, isDraggable }: NetworkIconsProps) => { const { isNetworkPinned, pinNetwork, removeNetwork } = useNetworkStore(); const toast = useToast({ status: "success", @@ -39,14 +38,13 @@ export const NetworkIcons = observer( pinNetwork({ name: CHAIN_CONFIGS[chainId]?.prettyName, chainId, - logo: image, - id: "", + logo: CHAIN_CONFIGS[chainId]?.logoUrl, }); toast({ title: `Pinned \u2018${CHAIN_CONFIGS[chainId]?.prettyName}\u2019 successfully`, }); }, - [pinNetwork, image, chainId, toast] + [pinNetwork, chainId, toast] ); const handleRemove = useCallback( diff --git a/src/lib/stores/networks.ts b/src/lib/stores/networks.ts index 23ce7224b..5a7d0483b 100644 --- a/src/lib/stores/networks.ts +++ b/src/lib/stores/networks.ts @@ -9,7 +9,6 @@ export interface Network { name: string; chainId: string; logo?: string; - id: string; } export class NetworkStore { From e1f25a763c462e8235ef30d979ca8707f120e525 Mon Sep 17 00:00:00 2001 From: songwongtp <16089160+songwongtp@users.noreply.github.com> Date: Fri, 19 Jul 2024 17:21:17 +0700 Subject: [PATCH 21/24] fix: mobile logic --- src/config/chain/initia.ts | 1 + src/lib/layout/network-menu/NetworkImage.tsx | 2 +- .../layout/network-menu/NetworkMenuBody.tsx | 5 +- .../network-menu/network-card/NetworkCard.tsx | 19 ++++--- .../network-card/NetworkIcons.tsx | 56 ++++++++----------- 5 files changed, 41 insertions(+), 42 deletions(-) diff --git a/src/config/chain/initia.ts b/src/config/chain/initia.ts index 9d2d9104d..9d0faa7de 100644 --- a/src/config/chain/initia.ts +++ b/src/config/chain/initia.ts @@ -524,6 +524,7 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { registryChainName: "milkywaytestnet", prettyName: "Milkyway", networkType: "testnet", + logoUrl: "https://assets.alleslabs.dev/integrations/chains/milkyway.png", lcd: "https://lcd.testnet.milkyway.zone", rpc: "https://rpc.testnet.milkyway.zone", indexer: "", diff --git a/src/lib/layout/network-menu/NetworkImage.tsx b/src/lib/layout/network-menu/NetworkImage.tsx index 8337cdb20..fa7c47c19 100644 --- a/src/lib/layout/network-menu/NetworkImage.tsx +++ b/src/lib/layout/network-menu/NetworkImage.tsx @@ -17,7 +17,7 @@ export const NetworkImage = ({ chainId }: NetworkImageProps) => { w={6} h={6} borderRadius="full" - src={image} + src={image ?? fallbackImage} fallbackSrc={fallbackImage} fallbackStrategy="onError" /> diff --git a/src/lib/layout/network-menu/NetworkMenuBody.tsx b/src/lib/layout/network-menu/NetworkMenuBody.tsx index 125f65f4e..e817ed46a 100644 --- a/src/lib/layout/network-menu/NetworkMenuBody.tsx +++ b/src/lib/layout/network-menu/NetworkMenuBody.tsx @@ -5,7 +5,7 @@ import type { KeyboardEvent as ReactKeyboardEvent } from "react"; import { useCallback, useMemo, useState } from "react"; import { CHAIN_CONFIGS } from "config/chain"; -import { useCelatoneApp, useSelectChain } from "lib/app-provider"; +import { useCelatoneApp, useMobile, useSelectChain } from "lib/app-provider"; import InputWithIcon from "lib/components/InputWithIcon"; import { EmptyState } from "lib/components/state"; import { useNetworkStore } from "lib/providers/store"; @@ -52,6 +52,7 @@ const getNextCursor = ( }; export const NetworkMenuBody = observer(({ onClose }: NetworkMenuBodyProps) => { + const isMobile = useMobile(); const selectChain = useSelectChain(); const { availableChainIds } = useCelatoneApp(); const { getPinnedNetworks } = useNetworkStore(); @@ -125,7 +126,7 @@ export const NetworkMenuBody = observer(({ onClose }: NetworkMenuBodyProps) => { placeholder="Search by Name or Chain ID" size="md" value={keyword} - autoFocus + autoFocus={!isMobile} onChange={(e) => setKeyword(e.target.value)} onKeyDown={handleOnKeyEnter} amptrackSection="network-search" diff --git a/src/lib/layout/network-menu/network-card/NetworkCard.tsx b/src/lib/layout/network-menu/network-card/NetworkCard.tsx index fac07d1af..f3fa78562 100644 --- a/src/lib/layout/network-menu/network-card/NetworkCard.tsx +++ b/src/lib/layout/network-menu/network-card/NetworkCard.tsx @@ -4,7 +4,7 @@ import { useCallback } from "react"; import { NetworkImage } from "../NetworkImage"; import { CHAIN_CONFIGS } from "config/chain"; -import { useCelatoneApp, useSelectChain } from "lib/app-provider"; +import { useCelatoneApp, useMobile, useSelectChain } from "lib/app-provider"; import type { Option } from "lib/types"; import { NetworkIcons } from "./NetworkIcons"; @@ -51,6 +51,7 @@ export const NetworkCard = observer( onClose, isDraggable = false, }: NetworkCardProps) => { + const isMobile = useMobile(); const { currentChainId } = useCelatoneApp(); const selectChain = useSelectChain(); @@ -79,12 +80,16 @@ export const NetworkCard = observer( background={getCardBackground(index, cursor, isSelected)} cursor={getDisplayCursor(isDraggable, isSelected)} onMouseMove={() => index !== cursor && setCursor(index)} - _hover={{ - background: isSelected ? "gray.700" : "gray.800", - "> .icon-wrapper > .icon-container": { - opacity: 1, - }, - }} + _hover={ + isMobile + ? undefined + : { + background: isSelected ? "gray.700" : "gray.800", + "> .icon-wrapper > .icon-container": { + opacity: 1, + }, + } + } > { + const isMobile = useMobile(); const { isNetworkPinned, pinNetwork, removeNetwork } = useNetworkStore(); const toast = useToast({ status: "success", @@ -58,41 +51,40 @@ export const NetworkIcons = observer( [removeNetwork, chainId, toast] ); + const opacityStyle = { + opacity: isMobile ? 1 : 0, + }; + + const pinIconStyles = { + cursor: "pointer", + className: "icon-container", + align: "center", + padding: 1, + _hover: isMobile + ? undefined + : { + background: isSelected ? "gray.800" : "gray.900", + borderRadius: 4, + }, + }; + return ( {isDraggable && ( - + )} {isNetworkPinned(chainId) ? ( - + ) : ( From e4f6cc0514940c13ac0cfef34df5bcf5aeba8f89 Mon Sep 17 00:00:00 2001 From: songwongtp <16089160+songwongtp@users.noreply.github.com> Date: Fri, 19 Jul 2024 17:28:44 +0700 Subject: [PATCH 22/24] fix: minitswap --- src/lib/layout/AppMenu.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/layout/AppMenu.tsx b/src/lib/layout/AppMenu.tsx index fbdb51074..8116ef813 100644 --- a/src/lib/layout/AppMenu.tsx +++ b/src/lib/layout/AppMenu.tsx @@ -20,6 +20,11 @@ const appList = [ logo: "https://assets.alleslabs.dev/integrations/initia/app-logo/scan.svg", link: "https://scan.testnet.initia.xyz/", }, + { + name: "minitswap", + logo: "https://assets.alleslabs.dev/integrations/initia/app-logo/minitswap.svg", + link: "https://minitswap.testnet.initia.xyz/", + }, { name: "usernames", logo: "https://assets.alleslabs.dev/integrations/initia/app-logo/usernames.svg", From c4e2bd1072fc835fbc7ddfaee0654cdd57940fcc Mon Sep 17 00:00:00 2001 From: songwongtp <16089160+songwongtp@users.noreply.github.com> Date: Fri, 19 Jul 2024 18:05:26 +0700 Subject: [PATCH 23/24] fix: grab icon --- .../network-menu/network-card/NetworkCardDraggable.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/lib/layout/network-menu/network-card/NetworkCardDraggable.tsx b/src/lib/layout/network-menu/network-card/NetworkCardDraggable.tsx index 5675e0921..d6d3f2310 100644 --- a/src/lib/layout/network-menu/network-card/NetworkCardDraggable.tsx +++ b/src/lib/layout/network-menu/network-card/NetworkCardDraggable.tsx @@ -33,13 +33,18 @@ export const NetworkCardDraggable = ({ const style = { opacity: isDragging ? 0 : 1, - cursor: isDragging ? "grabbing" : "grab", transform: CSS.Transform.toString(transform), transition, }; return ( - + Date: Tue, 23 Jul 2024 17:00:41 +0700 Subject: [PATCH 24/24] fix: comments --- .../network-menu/NetworkAccordionPinned.tsx | 25 ++++--- .../layout/network-menu/NetworkMenuBody.tsx | 69 +++++++++---------- src/lib/layout/network-menu/index.tsx | 5 +- .../network-menu/network-card/NetworkCard.tsx | 4 +- .../{NetworkIcons.tsx => NetworkCardCta.tsx} | 12 ++-- src/lib/stores/networks.ts | 33 +++------ 6 files changed, 65 insertions(+), 83 deletions(-) rename src/lib/layout/network-menu/network-card/{NetworkIcons.tsx => NetworkCardCta.tsx} (89%) diff --git a/src/lib/layout/network-menu/NetworkAccordionPinned.tsx b/src/lib/layout/network-menu/NetworkAccordionPinned.tsx index e4c58013b..38a9a4075 100644 --- a/src/lib/layout/network-menu/NetworkAccordionPinned.tsx +++ b/src/lib/layout/network-menu/NetworkAccordionPinned.tsx @@ -18,6 +18,7 @@ import { useSensors, } from "@dnd-kit/core"; import { + arrayMove, SortableContext, verticalListSortingStrategy, } from "@dnd-kit/sortable"; @@ -25,13 +26,12 @@ import { useMemo, useState } from "react"; import { createPortal } from "react-dom"; import { useNetworkStore } from "lib/providers/store"; -import type { Network } from "lib/stores/networks"; import type { Nullable, Option } from "lib/types"; import { NetworkCardDraggable } from "./network-card"; interface NetworkAccodionPinnedProps { - pinnedNetworks: Network[]; + pinnedNetworks: string[]; cursor: Option; setCursor: (index: Option) => void; onClose: () => void; @@ -43,12 +43,12 @@ export const NetworkAccodionPinned = ({ setCursor, onClose, }: NetworkAccodionPinnedProps) => { - const { setPinnedNetworks } = useNetworkStore(); + const { getPinnedNetworks, setPinnedNetworks } = useNetworkStore(); const [dndActive, setDndActive] = useState>(null); // Drag and drop feature const activeItem = useMemo( - () => pinnedNetworks.find((item) => item.chainId === dndActive?.id), + () => pinnedNetworks.find((item) => item === dndActive?.id), [dndActive, pinnedNetworks] ); @@ -86,7 +86,12 @@ export const NetworkAccodionPinned = ({ }} onDragEnd={({ active, over }) => { if (over && active.id !== over.id) { - setPinnedNetworks(active.id.toString(), over.id.toString()); + const pinnedChainIds = getPinnedNetworks(); + const activeIndex = pinnedChainIds.indexOf(active.id.toString()); + const overIndex = pinnedChainIds.indexOf(over.id.toString()); + setPinnedNetworks( + arrayMove(pinnedChainIds, activeIndex, overIndex) + ); } setDndActive(null); }} @@ -95,13 +100,13 @@ export const NetworkAccodionPinned = ({ }} > item.chainId)} + items={pinnedNetworks} strategy={verticalListSortingStrategy} > {pinnedNetworks.map((item, index) => ( {activeItem ? ( - chainIds - .filter((chain) => CHAIN_CONFIGS[chain]?.networkType === type) - .filter( - (network) => - !keyword || - CHAIN_CONFIGS[network]?.prettyName - .toLowerCase() - .includes(keyword.toLowerCase()) || - network.toLowerCase().includes(keyword.toLowerCase()) - ); + type?: ChainConfig["networkType"] +) => { + const chainIdsByType = type + ? chainIds.filter((chainId) => CHAIN_CONFIGS[chainId]?.networkType === type) + : chainIds; + return chainIdsByType.filter( + (chainId) => + !keyword || + CHAIN_CONFIGS[chainId]?.prettyName + .toLowerCase() + .includes(keyword.toLowerCase()) || + chainId.toLowerCase().includes(keyword.toLowerCase()) + ); +}; const getNextCursor = ( key: string, @@ -62,32 +65,24 @@ export const NetworkMenuBody = observer(({ onClose }: NetworkMenuBodyProps) => { // Get chains info const pinnedNetworks = getPinnedNetworks(); - const filteredPinnedNetworks = useMemo(() => { - if (!keyword) return [...pinnedNetworks]; - return pinnedNetworks.filter( - (network) => - CHAIN_CONFIGS[network.chainId]?.prettyName - .toLowerCase() - .includes(keyword.toLowerCase()) || - network.chainId.toLowerCase().includes(keyword.toLowerCase()) - ); - }, [pinnedNetworks, keyword]); - const [filteredMainnetChains, filteredTestnetChains] = useMemo( - () => [ - filterChains(availableChainIds, keyword, "mainnet"), - filterChains(availableChainIds, keyword, "testnet"), - ], - [availableChainIds, keyword] - ); + const [filteredPinnedChains, filteredMainnetChains, filteredTestnetChains] = + useMemo( + () => [ + filterChains(pinnedNetworks, keyword), + filterChains(availableChainIds, keyword, "mainnet"), + filterChains(availableChainIds, keyword, "testnet"), + ], + [availableChainIds, keyword, pinnedNetworks] + ); const allNetworks = useMemo( () => [ - ...filteredPinnedNetworks.map((network) => network.chainId), + ...filteredPinnedChains, ...filteredMainnetChains, ...filteredTestnetChains, ], - [filteredPinnedNetworks, filteredMainnetChains, filteredTestnetChains] + [filteredPinnedChains, filteredMainnetChains, filteredTestnetChains] ); // Navigate with arrow keys @@ -139,15 +134,15 @@ export const NetworkMenuBody = observer(({ onClose }: NetworkMenuBodyProps) => { p={0} > - {filteredPinnedNetworks.length > 0 && ( + {filteredPinnedChains.length > 0 && ( )} @@ -156,13 +151,13 @@ export const NetworkMenuBody = observer(({ onClose }: NetworkMenuBodyProps) => { networks={filteredMainnetChains} cursor={cursor} setCursor={setCursor} - startIndex={filteredPinnedNetworks.length} + startIndex={filteredPinnedChains.length} onClose={onClose} /> {filteredMainnetChains.length > 0 && ( )} @@ -172,7 +167,7 @@ export const NetworkMenuBody = observer(({ onClose }: NetworkMenuBodyProps) => { cursor={cursor} setCursor={setCursor} startIndex={ - filteredPinnedNetworks.length + filteredMainnetChains.length + filteredPinnedChains.length + filteredMainnetChains.length } onClose={onClose} /> diff --git a/src/lib/layout/network-menu/index.tsx b/src/lib/layout/network-menu/index.tsx index d047d43ef..8bab62665 100644 --- a/src/lib/layout/network-menu/index.tsx +++ b/src/lib/layout/network-menu/index.tsx @@ -39,9 +39,8 @@ export const NetworkMenu = observer(() => { } }; document.addEventListener("keydown", openSearchHandler); - return () => { - document.removeEventListener("keydown", openSearchHandler); - }; + + return () => document.removeEventListener("keydown", openSearchHandler); }, [isMac, isOpen, onClose, onOpen]); return ( diff --git a/src/lib/layout/network-menu/network-card/NetworkCard.tsx b/src/lib/layout/network-menu/network-card/NetworkCard.tsx index f3fa78562..5bfceba65 100644 --- a/src/lib/layout/network-menu/network-card/NetworkCard.tsx +++ b/src/lib/layout/network-menu/network-card/NetworkCard.tsx @@ -7,7 +7,7 @@ import { CHAIN_CONFIGS } from "config/chain"; import { useCelatoneApp, useMobile, useSelectChain } from "lib/app-provider"; import type { Option } from "lib/types"; -import { NetworkIcons } from "./NetworkIcons"; +import { NetworkCardCta } from "./NetworkCardCta"; interface NetworkCardProps { chainId: string; @@ -112,7 +112,7 @@ export const NetworkCard = observer( - { +export const NetworkCardCta = observer( + ({ chainId, isSelected, isDraggable }: NetworkCardCtaProps) => { const isMobile = useMobile(); const { isNetworkPinned, pinNetwork, removeNetwork } = useNetworkStore(); const toast = useToast({ @@ -28,11 +28,7 @@ export const NetworkIcons = observer( const handleSave = useCallback( (e: React.MouseEvent) => { e.stopPropagation(); - pinNetwork({ - name: CHAIN_CONFIGS[chainId]?.prettyName, - chainId, - logo: CHAIN_CONFIGS[chainId]?.logoUrl, - }); + pinNetwork(chainId); toast({ title: `Pinned \u2018${CHAIN_CONFIGS[chainId]?.prettyName}\u2019 successfully`, }); diff --git a/src/lib/stores/networks.ts b/src/lib/stores/networks.ts index 5a7d0483b..5c04befbb 100644 --- a/src/lib/stores/networks.ts +++ b/src/lib/stores/networks.ts @@ -1,20 +1,12 @@ -import { arrayMove } from "@dnd-kit/sortable"; -import { findIndex } from "lodash"; import { makeAutoObservable } from "mobx"; import { isHydrated, makePersistable } from "mobx-persist-store"; import type { Dict } from "lib/types"; -export interface Network { - name: string; - chainId: string; - logo?: string; -} - export class NetworkStore { private userKey: string; - networks: Dict; + networks: Dict; constructor() { this.userKey = ""; @@ -40,32 +32,27 @@ export class NetworkStore { this.userKey = userKey; } - getPinnedNetworks(): Network[] { + getPinnedNetworks(): string[] { return this.networks[this.userKey] ?? []; } isNetworkPinned(chainId: string): boolean { - const networkByUserKey = this.getPinnedNetworks(); - - return networkByUserKey.findIndex((x) => x.chainId === chainId) > -1; + return this.getPinnedNetworks().findIndex((item) => item === chainId) > -1; } - pinNetwork(newNetwork: Network): void { - if (!this.isNetworkPinned(newNetwork.chainId)) { - this.networks[this.userKey] = [...this.getPinnedNetworks(), newNetwork]; + pinNetwork(chainId: string): void { + if (!this.isNetworkPinned(chainId)) { + this.networks[this.userKey] = [...this.getPinnedNetworks(), chainId]; } } removeNetwork(chainId: string): void { - this.networks[this.userKey] = this.networks[this.userKey]?.filter( - (each) => each.chainId !== chainId + this.networks[this.userKey] = this.getPinnedNetworks().filter( + (item) => item !== chainId ); } - setPinnedNetworks(activeId: string, overId: string): void { - const items = this.networks[this.userKey] ?? []; - const oldIndex = findIndex(items, (item) => item.chainId === activeId); - const newIndex = findIndex(items, (item) => item.chainId === overId); - this.networks[this.userKey] = arrayMove(items, oldIndex, newIndex); + setPinnedNetworks(chainIds: string[]): void { + this.networks[this.userKey] = chainIds; } }