diff --git a/README.md b/README.md index 1b51bccbf..819055957 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Gnoswap Interface +# Gnoswap Interface Welcome to the open source interface for Gnoswap, the first decentralized exchange (DEX) powered by Gnoland, designed to simplify concentrated liquidity experience and increase capital efficiency for traders. _Note: Gnoswap is in active development and not yet in production, and we welcome your contributions! Please check our [contribution guidelines](https://github.com/gnoswap-labs/gnoswap-interface#contributing--support) and the [latest release](https://github.com/gnoswap-labs/gnoswap-interface/releases) to see the current development status._ diff --git a/packages/web/public/gnos.svg b/packages/web/public/gnos.svg index 77cf26cee..09d7ae10a 100644 --- a/packages/web/public/gnos.svg +++ b/packages/web/public/gnos.svg @@ -1,13 +1,13 @@ - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + diff --git a/packages/web/src/common/utils/test-util.ts b/packages/web/src/common/utils/test-util.ts index bf48f0381..b5fae5e1b 100644 --- a/packages/web/src/common/utils/test-util.ts +++ b/packages/web/src/common/utils/test-util.ts @@ -61,7 +61,7 @@ export const generateToken0 = () => { export const generateToken1 = () => { return { token_id: `${generateAddress()}`, - name: "GNOS", + name: "GNS", symbol: "GNOSWAP", amount: { value: generateNumberPlus(), @@ -106,7 +106,7 @@ export const generateTokenMetas = () => { "https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xB98d4C97425d9908E66E53A6fDf673ACcA0BE986/logo.png"; return { images: [image0, image1], - names: ["GNOT", "GNOS"], + names: ["GNOT", "GNS"], }; }; diff --git a/packages/web/src/components/common/bar-graph/BarGraph.styles.ts b/packages/web/src/components/common/bar-graph/BarGraph.styles.ts index dddbfbe3c..abd62e43b 100644 --- a/packages/web/src/components/common/bar-graph/BarGraph.styles.ts +++ b/packages/web/src/components/common/bar-graph/BarGraph.styles.ts @@ -17,8 +17,8 @@ export const BarGraphWrapper = styled.div` width: 100%; height: auto; overflow: visible; - pointer-events: none; & > svg { + transform: translateY(24px); display: flex; flex-direction: column; width: 100%; @@ -28,8 +28,13 @@ export const BarGraphWrapper = styled.div` pointer-events: auto; &:hover { fill: ${(props) => - props.svgColor === "default" ? ({ theme }) => theme.color.point : ""}; + props.svgColor === "default" ? ({ hoverColor }) => hoverColor : ""}; opacity: ${(props) => (props.svgColor === "incentivized" ? 0.4 : 1)}; + + path { + fill: ${(props) => + props.svgColor === "default" ? ({ hoverColor }) => hoverColor : ""}; + opacity: ${(props) => (props.svgColor === "incentivized" ? 0.4 : 1)}; + } } } } @@ -43,26 +48,15 @@ export const BarLineWrapper = styled.line` } `; -interface BarGraphTooltipWrapperProps { - x: number; - y: number; -} - -export const BarGraphTooltipWrapper = styled.div` - position: absolute; - top: ${(props) => `${props.y - 51}px`}; - left: ${(props) => `${props.x}px`}; +export const BarGraphTooltipWrapper = styled.div` display: flex; flex-direction: column; - min-width: 220px; + min-width: 126px; height: auto; - padding: 6px 8px; background: ${({ theme }) => theme.color.background02}; border-radius: 4px; - box-shadow: 2px 2px 12px 0px rgba(0, 0, 0, 0.15); - overflow: visible; - gap: 5px; - transform: translateX(-48%); + gap: 8px; + ${fonts.p4}; & .tooltip-header { @@ -71,33 +65,27 @@ export const BarGraphTooltipWrapper = styled.div` width: 100%; height: auto; justify-content: space-between; - font-size: 16px; - font-weight: 700; - line-height: 19px; - color: ${({ theme }) => theme.color.point}; + ${fonts.body9} + color: ${({ theme }) => theme.color.text02}; } & .tooltip-body { - ${fonts.p4}; + ${fonts.body12}; color: ${({ theme }) => theme.color.text04}; - .date { - margin-right: 3px; + .time { + margin-left: 40px; + ${media.tablet} { + display: none; + } } } `; -export const IncentivizeGraphTooltipWrapper = styled.div` +export const IncentivizeGraphTooltipWrapper = styled.div` ${mixins.flexbox("column", "flex-start", "flex-start")}; - position: absolute; - top: ${(props) => `${props.y - 140}px`}; - left: ${(props) => `${props.x}px`}; - padding: 16px; background: ${({ theme }) => theme.color.background02}; - border-radius: 8px; - box-shadow: 8px 8px 20px 0px rgba(0, 0, 0, 0.2); gap: 8px; z-index: ${Z_INDEX.modalTooltip}; - transform: translateX(-50%); .row { ${mixins.flexbox("row", "flex-start", "flex-start")}; .token, diff --git a/packages/web/src/components/common/bar-graph/BarGraph.tsx b/packages/web/src/components/common/bar-graph/BarGraph.tsx index 2ea11f29b..b317fe5cd 100644 --- a/packages/web/src/components/common/bar-graph/BarGraph.tsx +++ b/packages/web/src/components/common/bar-graph/BarGraph.tsx @@ -2,6 +2,9 @@ import BigNumber from "bignumber.js"; import React, { useCallback, useMemo, useState } from "react"; import { BarGraphTooltipWrapper, BarGraphWrapper, IncentivizeGraphTooltipWrapper } from "./BarGraph.styles"; import { useColorGraph } from "@hooks/common/use-color-graph"; +import { Global, css } from "@emotion/react"; +import FloatingTooltip from "../tooltip/FloatingTooltip"; + export interface BarGraphProps { className?: string; color?: string; @@ -15,6 +18,9 @@ export interface BarGraphProps { tooltipOption?: string; svgColor?: string; currentIndex?: number; + customData?: { height: number, locationTooltip: number}; + times?: string[]; + radiusBorder?: number; } interface Point { @@ -25,6 +31,38 @@ interface Point { const VIEWPORT_DEFAULT_WIDTH = 400; const VIEWPORT_DEFAULT_HEIGHT = 200; +function parseTime(time: string) { + const dateObject = new Date(time); + const month = dateObject.toLocaleString("en-US", { month: "short" }); + const day = dateObject.getDate(); + const year = dateObject.getFullYear(); + const hours = dateObject.getHours(); + const minutes = dateObject.getMinutes(); + const isPM = hours >= 12; + const formattedHours = hours % 12 || 12; + return { + date: `${month} ${day}, ${year}`, + time: `${formattedHours.toString().padStart(2, "0")}:${minutes + .toString() + .padStart(2, "0")} ${isPM ? "PM" : "AM"}`, + }; +} + +const ChartGlobalTooltip = () => { + return ( + css` + .chart-tooltip { + > div { + padding: 10px; + box-shadow: 2px 2px 12px 0px rgba(0, 0, 0, 0.15); + } + } + `} + /> + ); +}; + const BarGraph: React.FC = ({ className = "", color, @@ -37,20 +75,23 @@ const BarGraph: React.FC = ({ height = VIEWPORT_DEFAULT_HEIGHT, tooltipOption = "default", svgColor = "default", - currentIndex, + customData = { height: 0, locationTooltip: 0}, + times = [], + radiusBorder = 0, }) => { const [activated, setActivated] = useState(false); const [currentPoint, setCurrentPoint] = useState(); const [currentPointIndex, setCurrentPointIndex] = useState(-1); const { redColor, greenColor } = useColorGraph(); + const { height: customHeight = 0, locationTooltip = 0 } = customData; + const [chartPoint, setChartPoint] = useState(); const getStrokeWidth = useCallback(() => { const maxStorkeWidth = BigNumber( width - (datas.length - 1) * minGap, ).dividedBy(datas.length); - return maxStorkeWidth.isLessThan(strokeWidth) - ? maxStorkeWidth.toNumber() - : strokeWidth; + + return maxStorkeWidth.toNumber(); }, [width, datas.length, minGap, strokeWidth]); const getGraphPoints = useCallback(() => { @@ -114,16 +155,20 @@ const BarGraph: React.FC = ({ return getGraphPoints()[currentTick]; }, [currentTick, getGraphPoints]); - const onMouseMove = (event: React.MouseEvent) => { + const onMouseMove = (event: React.MouseEvent | React.TouchEvent) => { event.preventDefault(); event.stopPropagation(); + const isTouch = event.type.startsWith("touch"); + const touch = isTouch ? (event as React.TouchEvent).touches[0] : null; + const clientX = isTouch ? touch?.clientX : (event as React.MouseEvent).clientX; + const clientY = isTouch ? touch?.clientY : (event as React.MouseEvent).clientY; if (!activated) { setCurrentPointIndex(-1); return; } - const { clientX, currentTarget } = event; - const { left } = currentTarget.getBoundingClientRect(); - const positionX = clientX - left; + const { currentTarget } = event; + const { left, top } = currentTarget.getBoundingClientRect(); + const positionX = (clientX || 0) - left; const clientWidth = currentTarget.clientWidth; const xPosition = new BigNumber(positionX) .multipliedBy(width) @@ -144,26 +189,35 @@ const BarGraph: React.FC = ({ minDistance = distance; setCurrentPointIndex(currentPointIndex); } - } - - if (currentPoint) { - setCurrentPoint(currentPoint); + if (currentPoint) { + setChartPoint({ x: positionX, y: (clientY || 0) - top}); + setCurrentPoint(currentPoint); + } } }; - const locationHovertooltip = useMemo(() => { - const temp = currentPoint?.x || 0; - if (typeof window !== "undefined" && window?.innerWidth <= 1440) { - if (currentIndex !== undefined && currentIndex === 0 && temp < 120) { - return 120; + const locationTooltipPosition = useMemo(() => { + if ((chartPoint?.y || 0) > customHeight + height - 25) { + if (width < (currentPoint?.x || 0) + locationTooltip) { + return "top-end"; + } else { + return "top-start"; } - if (currentIndex === 3 && temp > 120) { - return 130; - } - } - return temp; - }, [currentIndex, currentPoint]); + } + if (width < (currentPoint?.x || 0) + locationTooltip) return "left"; + return "right"; + }, [currentPoint, width, locationTooltip, height, chartPoint, customHeight]); + + + const onTouchMove = (event: React.MouseEvent | React.TouchEvent) => { + onMouseMove(event); + }; + const onTouchStart = (event: React.MouseEvent | React.TouchEvent) => { + event.preventDefault(); + onMouseMove(event); + }; + return ( = ({ onMouseEnter={() => setActivated(true)} onMouseLeave={() => setActivated(false)} svgColor={svgColor} + onTouchMove={onTouchMove} + onTouchStart={onTouchStart} > - - - - - - - - - - - - {getGraphPoints().map((point, index) => ( - - ))} - {currentPosition && ( - - )} - - {tooltipOption === "default" && currentPointIndex > -1 && activated && ( - width - 40 - ? currentPoint?.x - 40 - : currentPoint?.x || 0 - } - y={currentPoint?.y || 0} - > -
- $98,412,880 -
+ -1 && activated ? +
- Aug 03, 2023 09:00 PM - 10:00 PM + + {parseTime(times[currentPointIndex]).date} + +
+
+ {`$${Number(BigNumber( + datas[currentPointIndex], + )).toLocaleString()}`}
-
- )} - {tooltipOption === "incentivized" && currentPointIndex > -1 && activated && ( - +
: + tooltipOption === "incentivized" && currentPointIndex > -1 && activated ? +
Token
Amount
@@ -250,8 +267,51 @@ const BarGraph: React.FC = ({
Amount
0.000046 - 0.000051 BTC
-
- )} + : null + }> + + + + + + + + + + + + {radiusBorder && getGraphPoints().map((point, index) => ( + + ))} + {getGraphPoints().map((point, index) => ( + + ))} + {currentPosition && ( + + )} + + +
); }; diff --git a/packages/web/src/components/common/button/Button.tsx b/packages/web/src/components/common/button/Button.tsx index 6bb2a1f62..c4b089528 100644 --- a/packages/web/src/components/common/button/Button.tsx +++ b/packages/web/src/components/common/button/Button.tsx @@ -8,6 +8,7 @@ interface ButtonProps { style: ButtonStyleProps; onClick?: (e: React.MouseEvent) => void; disabled?: boolean; + buttonRef?: React.RefObject; } export enum ButtonHierarchy { @@ -24,9 +25,11 @@ const Button = ({ style, onClick, disabled, + buttonRef, }: ButtonProps) => { return ( theme.color.background04Hover}; + } + } ${media.mobile} { padding: 16px 12px; width: 328px; diff --git a/packages/web/src/components/common/connect-wallet-modal/ConnectWalletModal.tsx b/packages/web/src/components/common/connect-wallet-modal/ConnectWalletModal.tsx index 9b9085106..c4e2e19df 100644 --- a/packages/web/src/components/common/connect-wallet-modal/ConnectWalletModal.tsx +++ b/packages/web/src/components/common/connect-wallet-modal/ConnectWalletModal.tsx @@ -4,13 +4,15 @@ import IconClose from "../icons/IconCancel"; import Button, { ButtonHierarchy } from "../button/Button"; import IconAdenaLogo from "@components/common/icons/defaultIcon/IconAdenaLogo"; import IconWalletConnect from "../icons/defaultIcon/IconWalletConnect"; +import LoadingSpinner from "../loading-spinner/LoadingSpinner"; interface Props { close: () => void; connect: () => void; + loadingConnect: string; } -const ConnectWalletModal: React.FC = ({ close, connect }) => { +const ConnectWalletModal: React.FC = ({ close, connect, loadingConnect }) => { const onClickClose = useCallback(() => { close(); }, [close]); @@ -19,7 +21,7 @@ const ConnectWalletModal: React.FC = ({ close, connect }) => {
-
Connect Wallet
+
Wallet Login
@@ -27,8 +29,8 @@ const ConnectWalletModal: React.FC = ({ close, connect }) => {
diff --git a/packages/web/src/components/common/connect-wallet-status-modal/ConnectWalletStatusModal.styles.ts b/packages/web/src/components/common/connect-wallet-status-modal/ConnectWalletStatusModal.styles.ts new file mode 100644 index 000000000..e5fda4426 --- /dev/null +++ b/packages/web/src/components/common/connect-wallet-status-modal/ConnectWalletStatusModal.styles.ts @@ -0,0 +1,86 @@ +import mixins from "@styles/mixins"; +import { fonts } from "@constants/font.constant"; +import styled from "@emotion/styled"; +import { media } from "@styles/media"; + +export const ConnectWalletStatusModalWrapper = styled.div` + ${mixins.flexbox("column", "flex-start", "flex-start")}; + width: 460px; + padding: 23px; + .modal-body { + width: 100%; + ${mixins.flexbox("column", "center", "flex-start")}; + gap: 24px; + .header { + ${mixins.flexbox("row", "center", "flex-end")}; + width: 100%; + .close-wrap { + ${mixins.flexbox("row", "center", "center")}; + cursor: pointer; + width: 24px; + height: 24px; + .close-icon { + width: 24px; + height: 24px; + * { + fill: ${({ theme }) => theme.color.icon01}; + } + &:hover { + * { + fill: ${({ theme }) => theme.color.icon07}; + } + } + } + } + } + .icon-wrapper { + display: contents; + .fail-icon { + width: 72px; + height: auto; + ${media.mobile} { + width: 60px; + } + } + } + .content { + ${mixins.flexbox("column", "center", "center")}; + gap: 8px; + h5 { + ${fonts.body7} + color: ${({ theme }) => theme.color.text02}; + ${media.mobile} { + ${fonts.body9} + } + } + div { + ${fonts.body12} + color: ${({ theme }) => theme.color.text04}; + text-align: center; + ${media.mobile} { + ${fonts.p2} + } + } + } + .button-wrapper { + width: 100%; + .button-try { + width: 100%; + height: 57px; + span { + ${fonts.body7} + } + ${media.mobile} { + height: 41px; + span { + ${fonts.body9} + } + } + } + } + } + ${media.mobile} { + width: 328px; + padding: 15px 11px; + } +`; diff --git a/packages/web/src/components/common/connect-wallet-status-modal/ConnectWalletStatusModal.tsx b/packages/web/src/components/common/connect-wallet-status-modal/ConnectWalletStatusModal.tsx new file mode 100644 index 000000000..f86a5a8aa --- /dev/null +++ b/packages/web/src/components/common/connect-wallet-status-modal/ConnectWalletStatusModal.tsx @@ -0,0 +1,52 @@ +import { ConnectWalletStatusModalWrapper } from "./ConnectWalletStatusModal.styles"; +import React, { useCallback } from "react"; +import IconClose from "../icons/IconCancel"; +import Button, { ButtonHierarchy } from "../button/Button"; +import IconFailed from "../icons/IconFailed"; + +interface Props { + close: () => void; + connect: () => void; +} + +const ConnectWalletStatusModal: React.FC = ({ close, connect }) => { + const onClickClose = useCallback(() => { + close(); + }, [close]); + + return ( + +
+
+
+ +
+
+
+ +
+
+
+ Error Connecting +
+
+ The connection attempt has failed. Please try again. +
+
+
+
+
+
+ ); +}; + +export default ConnectWalletStatusModal; diff --git a/packages/web/src/components/common/footer/Footer.tsx b/packages/web/src/components/common/footer/Footer.tsx index e9bab3dcb..dd3a9a48f 100644 --- a/packages/web/src/components/common/footer/Footer.tsx +++ b/packages/web/src/components/common/footer/Footer.tsx @@ -1,6 +1,6 @@ import React from "react"; -import IconLogoWhite from "../icons/IconLogoWhite"; -import IconLogoPrimary from "../icons/IconLogoPrimary"; +import IconFooterDarkLogo from "../icons/IconFooterDarkLogo"; +import IconFooterLightLogo from "../icons/IconFooterLightLogo"; import { useAtomValue } from "jotai"; import { ThemeState } from "@states/index"; import Link from "next/link"; @@ -51,9 +51,9 @@ const Footer: React.FC = () => { {themeKey === "dark" ? ( - + ) : ( - + )}

{FOOTER_LEFT_NAV.content}

diff --git a/packages/web/src/components/common/header-side-menu-modal/HeaderSideMenuModal.styles.ts b/packages/web/src/components/common/header-side-menu-modal/HeaderSideMenuModal.styles.ts index 1e5d273be..31843bb64 100644 --- a/packages/web/src/components/common/header-side-menu-modal/HeaderSideMenuModal.styles.ts +++ b/packages/web/src/components/common/header-side-menu-modal/HeaderSideMenuModal.styles.ts @@ -9,16 +9,20 @@ export const HeaderSideMenuModalWrapper = styled.div` ${media.tablet} { top: 44px; } - ${media.mobile} { - top: -210px; - right: -10px; - } + right: 0px; width: 240px; border-radius: 8px; background-color: ${({ theme }) => theme.color.background01}; border: 1px solid ${({ theme }) => theme.color.border02}; box-shadow: 8px 8px 20px 0px rgba(0, 0, 0, 0.2); + ${media.mobile} { + width: 100%; + left: 0; + bottom: 0; + position: fixed; + top: initial; + } `; export const Navigation = styled.nav` diff --git a/packages/web/src/components/common/header-side-menu-modal/HeaderSideMenuModal.tsx b/packages/web/src/components/common/header-side-menu-modal/HeaderSideMenuModal.tsx index a5c761218..85b2a94a2 100644 --- a/packages/web/src/components/common/header-side-menu-modal/HeaderSideMenuModal.tsx +++ b/packages/web/src/components/common/header-side-menu-modal/HeaderSideMenuModal.tsx @@ -12,7 +12,6 @@ import { } from "./HeaderSideMenuModal.styles"; import IconOpenLink from "@components/common/icons/IconOpenLink"; import IconAccountUser from "../icons/IconAccountUser"; -import IconShoppingBag from "../icons/IconShoppingBag"; interface HeaderSideMenuModalProps { onSideMenuToggle: () => void; @@ -42,16 +41,6 @@ const HeaderSideMenuModal: React.FC = ({
    -
  • - - - {}}> - - - {SIDE_MENU_NAV.AIRDROP.title} - - -
  • diff --git a/packages/web/src/components/common/header/Header.styles.ts b/packages/web/src/components/common/header/Header.styles.ts index fa9e1c21c..7a4c7167b 100644 --- a/packages/web/src/components/common/header/Header.styles.ts +++ b/packages/web/src/components/common/header/Header.styles.ts @@ -137,7 +137,7 @@ export const RightSection = styled.div` ${mixins.flexbox("row", "center", "flex-end")}; max-width: 255px; width: 100%; - gap: 16px; + gap: 10px; ${media.tablet} { max-width: 231px; gap: 8px; @@ -146,7 +146,7 @@ export const RightSection = styled.div` export const SearchContainer = styled.div` ${mixins.flexbox("row", "center", "flex-start")}; - gap: 18px; + gap: 10px; ${media.tablet} { gap: 10px; } @@ -156,11 +156,12 @@ export const SearchButton = styled.button` ${mixins.flexbox("row", "center", "flex-start")}; border-radius: 4px; transition: all 0.3s ease; + margin-right: 2px; .search-icon { width: 32.5px; height: 32.5px; * { - fill: ${({ theme }) => theme.color.icon05}; + fill: ${({ theme }) => theme.color.icon03}; } ${media.tablet} { width: 29px; @@ -173,3 +174,24 @@ export const SearchButton = styled.button` } } `; + +export const DepositButton = styled.button` + ${mixins.flexbox("row", "center", "center")}; + color: #E0E8F4; + gap: 8px; + ${fonts.p1} + padding: 10px 16px 10px 14px; + background-color: ${({ theme }) => theme.color.background04}; + border-radius: 8px; + width: 101px; + svg { + width: 15px; + height: 15px; + * { + fill: #E0E8F4; + } + } + &:hover { + background-color: ${({ theme }) => theme.color.background04Hover}; + } +`; diff --git a/packages/web/src/components/common/header/Header.tsx b/packages/web/src/components/common/header/Header.tsx index b25e6411c..af460b15f 100644 --- a/packages/web/src/components/common/header/Header.tsx +++ b/packages/web/src/components/common/header/Header.tsx @@ -14,6 +14,7 @@ import { BottomNavWrapper, BottomNavContainer, BottomNavItem, + DepositButton, } from "./Header.styles"; import NotificationButton from "@components/common/notification-button/NotificationButton"; import { HEADER_NAV } from "@constants/header.constant"; @@ -23,6 +24,7 @@ import { DEVICE_TYPE } from "@styles/media"; import SubMenuButton from "../sub-menu-button/SubMenuButton"; import SearchMenuModal from "../search-menu-modal/SearchMenuModal"; import { AccountModel } from "@models/account/account-model"; +import IconDownload from "../icons/IconDownload"; interface HeaderProps { pathname?: string; @@ -43,6 +45,7 @@ interface HeaderProps { disconnectWallet: () => void; switchNetwork: () => void; isSwitchNetwork: boolean; + loadingConnect: string; } const Header: React.FC = ({ @@ -63,6 +66,7 @@ const Header: React.FC = ({ disconnectWallet, switchNetwork, isSwitchNetwork, + loadingConnect, }) => { return ( <> @@ -105,6 +109,10 @@ const Header: React.FC = ({ + {connected && breakpoint !== DEVICE_TYPE.MOBILE && + + Deposit + } = ({ disconnectWallet={disconnectWallet} switchNetwork={switchNetwork} isSwitchNetwork={isSwitchNetwork} + loadingConnect={loadingConnect} /> diff --git a/packages/web/src/components/common/icons/IconFooterDarkLogo.tsx b/packages/web/src/components/common/icons/IconFooterDarkLogo.tsx new file mode 100644 index 000000000..54828c7f8 --- /dev/null +++ b/packages/web/src/components/common/icons/IconFooterDarkLogo.tsx @@ -0,0 +1,53 @@ +const IconFooterDarkLogo = ({ className }: { className?: string }) => ( + + + + + + + + + + + + +); + +export default IconFooterDarkLogo; diff --git a/packages/web/src/components/common/icons/IconFooterLightLogo.tsx b/packages/web/src/components/common/icons/IconFooterLightLogo.tsx new file mode 100644 index 000000000..66850ebc7 --- /dev/null +++ b/packages/web/src/components/common/icons/IconFooterLightLogo.tsx @@ -0,0 +1,53 @@ +const IconFooterLightLogo = ({ className }: { className?: string }) => ( + + + + + + + + + + + + +); + +export default IconFooterLightLogo; diff --git a/packages/web/src/components/common/icons/IconLogoGnot.tsx b/packages/web/src/components/common/icons/IconLogoGnot.tsx new file mode 100644 index 000000000..61870bdf2 --- /dev/null +++ b/packages/web/src/components/common/icons/IconLogoGnot.tsx @@ -0,0 +1,22 @@ +const IconLogoGnot = ({ className }: { className?: string }) => ( + + + + + +); + +export default IconLogoGnot; diff --git a/packages/web/src/components/common/icons/IconLogoWhite.tsx b/packages/web/src/components/common/icons/IconLogoWhite.tsx index 32231c367..78f9f7a2a 100644 --- a/packages/web/src/components/common/icons/IconLogoWhite.tsx +++ b/packages/web/src/components/common/icons/IconLogoWhite.tsx @@ -1,35 +1,59 @@ const IconLogoWhite = ({ className }: { className?: string }) => ( + + + + + + + fill="#fff" + d="M55.047 44.081L71.99 33l34.012 22.07-16.918 11.072-34.037-22.06z" + > + d="M55.395 71.45l16.943-11.082 33.66 21.734-16.605 11.27L55.396 71.45z" + > + d="M38.055 87.328l16.993-11.082 33.937 22.112-16.994 10.978-33.936-22.008z" + > + fill="#fff" + d="M38.004 55.069L55.046 44.05v32.2L38.004 87.352V55.07z" + > + d="M89.399 93.372l16.606-11.27v5.167L89.047 98.33l.352-4.957z" + > ); diff --git a/packages/web/src/components/common/icons/IconSuccess.tsx b/packages/web/src/components/common/icons/IconSuccess.tsx index 7582c8450..8e3b70efa 100644 --- a/packages/web/src/components/common/icons/IconSuccess.tsx +++ b/packages/web/src/components/common/icons/IconSuccess.tsx @@ -11,7 +11,7 @@ const IconSuccess = ({ className }: { className?: string }) => ( fillRule="evenodd" clipRule="evenodd" d="M36 72C55.8823 72 72 55.8823 72 36C72 16.1177 55.8823 0 36 0C16.1177 0 0 16.1177 0 36C0 55.8823 16.1177 72 36 72ZM52.9713 30.4713C54.1429 29.2997 54.1429 27.4003 52.9713 26.2287C51.7997 25.0571 49.9003 25.0571 48.7287 26.2287L32.4 42.5574L23.7213 33.8787C22.5497 32.7071 20.6503 32.7071 19.4787 33.8787C18.3071 35.0503 18.3071 36.9497 19.4787 38.1213L30.2787 48.9213C30.8413 49.4839 31.6044 49.8 32.4 49.8C33.1956 49.8 33.9587 49.4839 34.5213 48.9213L52.9713 30.4713Z" - fill="#0059FF" + fill="#16C78A" /> ); diff --git a/packages/web/src/components/common/line-graph/LineGraph.styles.ts b/packages/web/src/components/common/line-graph/LineGraph.styles.ts index 0fb46f9a5..a2cd22452 100644 --- a/packages/web/src/components/common/line-graph/LineGraph.styles.ts +++ b/packages/web/src/components/common/line-graph/LineGraph.styles.ts @@ -7,42 +7,32 @@ export const LineGraphWrapper = styled.div` display: flex; flex-direction: column; width: 100%; - height: 319px; + height: 321px; overflow: visible; ${media.mobile} { - height: 263px; + height: 252px; } & svg { display: flex; flex-direction: column; width: 100%; - height: 319px; + height: 321px; overflow: visible; ${media.mobile} { - height: 263px; + height: 252px; } } `; -interface LineGraphTooltipWrapperProps { - x: number; - y: number; -} - -export const LineGraphTooltipWrapper = styled.div` - position: absolute; - top: ${props => `${props.y}px`}; - left: ${props => `${props.x}px`}; +export const LineGraphTooltipWrapper = styled.div` display: flex; flex-direction: column; - min-width: 148px; + min-width: 126px; height: auto; - padding: 6px 8px; background: ${({ theme }) => theme.color.background02}; border-radius: 4px; - box-shadow: 2px 2px 12px 0px rgba(0, 0, 0, 0.15); overflow: visible; - gap: 5px; + gap: 8px; ${fonts.p4}; & .tooltip-header { @@ -51,17 +41,18 @@ export const LineGraphTooltipWrapper = styled.div` width: 100%; height: auto; justify-content: space-between; - font-size: 16px; - font-weight: 700; - line-height: 19px; - color: ${({ theme }) => theme.color.point}; + ${fonts.body9} + color: ${({ theme }) => theme.color.text02}; } & .tooltip-body { - ${fonts.p4}; + ${fonts.body12}; color: ${({ theme }) => theme.color.text04}; - .date { - margin-right: 3px; + .time { + margin-left: 40px; + ${media.tablet} { + display: none; + } } } `; diff --git a/packages/web/src/components/common/line-graph/LineGraph.tsx b/packages/web/src/components/common/line-graph/LineGraph.tsx index 3cc86211f..1847a03dc 100644 --- a/packages/web/src/components/common/line-graph/LineGraph.tsx +++ b/packages/web/src/components/common/line-graph/LineGraph.tsx @@ -1,6 +1,8 @@ import BigNumber from "bignumber.js"; import React, { useCallback, useEffect, useState, useMemo } from "react"; import { LineGraphTooltipWrapper, LineGraphWrapper } from "./LineGraph.styles"; +import FloatingTooltip from "../tooltip/FloatingTooltip"; +import { Global, css } from "@emotion/react"; function calculateSmoothing(pointA: Point, pointB: Point) { const lengthX = pointB.x - pointA.x; @@ -63,6 +65,7 @@ export interface LineGraphProps { point?: boolean; firstPointColor?: string; typeOfChart?: string; + customData?: { height: number, locationTooltip: number}; } interface Point { @@ -103,6 +106,21 @@ function parseTimeTVL(time: string) { const VIEWPORT_DEFAULT_WIDTH = 400; const VIEWPORT_DEFAULT_HEIGHT = 200; +const ChartGlobalTooltip = () => { + return ( + css` + .chart-tooltip { + > div { + padding: 10px; + box-shadow: 2px 2px 12px 0px rgba(0, 0, 0, 0.15); + } + } + `} + /> + ); +}; + const LineGraph: React.FC = ({ className = "", cursor, @@ -117,12 +135,15 @@ const LineGraph: React.FC = ({ point, firstPointColor, typeOfChart, + customData = { height: 0, locationTooltip: 0}, }) => { const COMPONENT_ID = (Math.random() * 100000).toString(); const [activated, setActivated] = useState(false); const [currentPoint, setCurrentPoint] = useState(); + const [chartPoint, setChartPoint] = useState(); const [currentPointIndex, setCurrentPointIndex] = useState(-1); const [points, setPoints] = useState([]); + const { height: customHeight = 0, locationTooltip } = customData; const isFocus = useCallback(() => { return activated && cursor; @@ -174,17 +195,21 @@ const LineGraph: React.FC = ({ setPoints(points); }; - const onMouseMove = (event: React.MouseEvent) => { + const onMouseMove = (event: React.MouseEvent | React.TouchEvent) => { event.preventDefault(); event.stopPropagation(); + const isTouch = event.type.startsWith("touch"); + const touch = isTouch ? (event as React.TouchEvent).touches[0] : null; + const clientX = isTouch ? touch?.clientX : (event as React.MouseEvent).clientX; + const clientY = isTouch ? touch?.clientY : (event as React.MouseEvent).clientY; if (!isFocus) { setCurrentPointIndex(-1); return; } - const { clientX, currentTarget } = event; - const { left } = currentTarget.getBoundingClientRect(); - const positionX = clientX - left; + const { currentTarget } = event; + const { left, top } = currentTarget.getBoundingClientRect(); + const positionX = (clientX || 0) - left; const clientWidth = currentTarget.clientWidth; const xPosition = new BigNumber(positionX) .multipliedBy(width) @@ -194,23 +219,25 @@ const LineGraph: React.FC = ({ let minDistance = -1; let currentPointIndex = -1; + for (const point of points) { - const distance = Math.abs(point.x - xPosition); + const distance = xPosition - point.x; currentPointIndex += 1; - if (minDistance < 0) { + if (minDistance < 0 && distance >= 0) { minDistance = distance; } - if (distance < minDistance + 10) { + if (distance >= 0 && distance < minDistance + 1) { currentPoint = point; minDistance = distance; setCurrentPointIndex(currentPointIndex); } } if (currentPoint) { + setChartPoint({ x: positionX, y: (clientY || 0) - top}); setCurrentPoint(currentPoint); } }; - + const getGraphLine = useCallback( (smooth?: boolean, fill?: boolean) => { function mappedPoint(point: Point, index: number, points: Point[]) { @@ -244,110 +271,125 @@ const LineGraph: React.FC = ({ } return points[0]; }, [points]); + const locationTooltipPosition = useMemo(() => { + if ((chartPoint?.y || 0) > customHeight + height - 25) { + if (width < (currentPoint?.x || 0) + locationTooltip) { + return "top-end"; + } else { + return "top-start"; + } + } + if (width < (currentPoint?.x || 0) + locationTooltip) return "left"; + return "right"; + }, [currentPoint, width, locationTooltip, height, chartPoint, customHeight]); + + const onTouchMove = (event: React.MouseEvent | React.TouchEvent) => { + onMouseMove(event); + }; + const onTouchStart = (event: React.MouseEvent | React.TouchEvent) => { + event.preventDefault(); + onMouseMove(event); + }; + return ( setActivated(true)} onMouseLeave={() => setActivated(false)} + onTouchMove={onTouchMove} + onTouchStart={onTouchStart} > - - - - - - - - - - - {point && - points.map((point, index) => ( - - ))} - - { - - {firstPointColor && } - {isFocus() && currentPoint && ( - - )} - {isFocus() && currentPoint && ( - - )} - - } - - {isFocus() && currentPointIndex > -1 && ( - width / 2 - ? currentPoint?.x - 150 - : currentPoint?.x || 0 - } - y={currentPoint?.y || 0} - > -
    - {`$${BigNumber( - datas[currentPointIndex].value, - ).toString()}`} -
    + -1 ? +
    {typeOfChart === "tvl" ? parseTimeTVL(datas[currentPointIndex].time).date : parseTime(datas[currentPointIndex].time).date} - - {typeOfChart === "tvl" - ? parseTimeTVL(datas[currentPointIndex].time).time - : parseTime(datas[currentPointIndex].time).time} - + {typeOfChart !== "tvl" && + {parseTime(datas[currentPointIndex].time).time} + } +
    +
    + {`$${Number(BigNumber( + datas[currentPointIndex].value, + )).toLocaleString()}`}
    -
    - )} +
    : null + }> + + + + + + + + + + + {point && + points.map((point, index) => ( + + ))} + + { + + {firstPointColor && } + {isFocus() && currentPoint && ( + + )} + {isFocus() && currentPoint && ( + + )} + + } + + +
    ); }; diff --git a/packages/web/src/components/common/loading-spinner/LoadingSpinner.styles.ts b/packages/web/src/components/common/loading-spinner/LoadingSpinner.styles.ts index 378360537..1ea4e833c 100644 --- a/packages/web/src/components/common/loading-spinner/LoadingSpinner.styles.ts +++ b/packages/web/src/components/common/loading-spinner/LoadingSpinner.styles.ts @@ -19,7 +19,7 @@ export const wrapper = (theme: Theme) => css` animation: ${spin} 1s linear infinite; background: conic-gradient( from 0deg at 50% 50.63%, - #000000 0deg, + ${theme.color.bgLoading} 0deg, #233DBD 360deg ); @@ -30,7 +30,7 @@ export const wrapper = (theme: Theme) => css` width: 60px; height: 60px; border-radius: 100%; - box-shadow: 8px 8px 20px 0px rgba(0, 0, 0, 0.2); + box-shadow: ${theme.color.shadow02}; } ${media.mobile} { width: 60px; diff --git a/packages/web/src/components/common/loading-spinner/LoadingSpinner.tsx b/packages/web/src/components/common/loading-spinner/LoadingSpinner.tsx index 911c5ca72..baf213d9e 100644 --- a/packages/web/src/components/common/loading-spinner/LoadingSpinner.tsx +++ b/packages/web/src/components/common/loading-spinner/LoadingSpinner.tsx @@ -1,7 +1,7 @@ import { wrapper } from "./LoadingSpinner.styles"; -const LoadingSpinner = () => { - return
    ; +const LoadingSpinner = ({className} : {className?: string}) => { + return
    ; }; export default LoadingSpinner; diff --git a/packages/web/src/components/common/modal/Modal.tsx b/packages/web/src/components/common/modal/Modal.tsx index d91f364df..9c4e1e00b 100644 --- a/packages/web/src/components/common/modal/Modal.tsx +++ b/packages/web/src/components/common/modal/Modal.tsx @@ -4,7 +4,7 @@ import { ModalWrapper, Overlay, } from "./Modal.styles"; -import { useRef } from "react"; +import React, { useRef, cloneElement } from "react"; interface ModalProps { hasLeftArrow?: boolean; @@ -23,12 +23,15 @@ const Modal: React.FC = ({ children, }) => { const modalRef = useRef(null); + const cloneChildren = () => + React.Children.map(children, (child) => + cloneElement(child as React.ReactElement, { modalRef }) + ); usePositionModal(modalRef); - return ( <> - {children} + {cloneChildren()} diff --git a/packages/web/src/components/common/notice/NoticeToast.styles.ts b/packages/web/src/components/common/notice/NoticeToast.styles.ts index 9fedd45ae..0217b73d0 100644 --- a/packages/web/src/components/common/notice/NoticeToast.styles.ts +++ b/packages/web/src/components/common/notice/NoticeToast.styles.ts @@ -3,38 +3,67 @@ import styled from "@emotion/styled"; import { media } from "@styles/media"; import mixins from "@styles/mixins"; import { Z_INDEX } from "@styles/zIndex"; +import { keyframes } from "@emotion/react"; -export const NoticeContextWrapper = styled.div` +const toastInRight = keyframes` + from { + transform: translateX(100%); + } + to { + transform: translateX(0); + } +`; + +const toastOutRightClose = keyframes` + from { + transform: translateX(0); + } + to { + transform: translateX(100%); + } +`; + +export const NoticeUIList = styled.div` display: block; position: fixed; - top: 0px; - bottom: 0px; - left: 0px; - right: 0px; - width: 100%; - height: 100lvh; - z-index: ${Z_INDEX.modalTooltip}; + top: 82px; + right: 30px; + width: 380px; + .toast-item { + animation: ${toastInRight} 500ms; + } + z-index: ${Z_INDEX.modalOverlay}; + ${media.tablet} { + top: 70px; + right: 30px; + } + ${media.mobile} { + width: 328px; + right: 16px; + top: -16px; + } `; + export const NoticeUIWrapper = styled.div` - position: absolute; - box-shadow: 8px 8px 20px 0px rgba(0, 0, 0, 0.2); - padding: 16px 24px; + box-shadow: ${({ theme }) => theme.color.shadow}; + padding: 15px 23px; color: ${({ theme }) => theme.color.text02}; - background-color: ${({ theme }) => theme.color.background02}; + background-color: ${({ theme }) => theme.color.background06}; border: 1px solid ${({ theme }) => theme.color.border02}; - width: 380px; - bottom: 38px; - right: 36px; border-radius: 8px; - transition: all 0.3s ease-in-out; + margin-top: 10px; + position: relative; + &.closing { + animation: ${toastOutRightClose} 500ms; + } ${media.mobile} { - width: 90%; + width: 100%; top: 16px; left: 50%; transform: translateX(-50%); height: fit-content; - padding: 16px; + padding: 15px; } .icon-close { width: 24px; @@ -46,6 +75,14 @@ export const NoticeUIWrapper = styled.div` svg { width: 24px; height: 24px; + * { + fill: ${({ theme }) => theme.color.icon03}; + } + &:hover { + * { + fill: ${({ theme }) => theme.color.icon07}; + } + } } ${media.mobile} { right: 16px; @@ -63,13 +100,30 @@ export const NoticeUIWrapper = styled.div` height: 24px; } } + .loading-icon { + width: 36px; + height: 36px; + &:before { + width: 24px; + height: 24px; + } + ${media.mobile} { + width: 24px; + height: 24px; + &:before { + width: 15.4px; + height: 15.4px; + } + } + } > div { ${mixins.flexbox("column", "flex-start", "flex-start")}; gap: 8px; h5 { - ${fonts.body5}; + ${fonts.body7}; color: ${({ theme }) => theme.color.text02}; + line-height: 23px; ${media.mobile} { ${fonts.body7} } @@ -90,9 +144,16 @@ export const NoticeUIWrapper = styled.div` ${media.mobile} { ${fonts.p2} } + &:hover { + color: ${({ theme }) => theme.color.icon07}; + svg * { + fill: ${({ theme }) => theme.color.icon07}; + } + } svg { width: 16px; height: 16px; + } } ${media.mobile} { diff --git a/packages/web/src/components/common/notice/NoticeToast.tsx b/packages/web/src/components/common/notice/NoticeToast.tsx index 59ce90c3b..659136fce 100644 --- a/packages/web/src/components/common/notice/NoticeToast.tsx +++ b/packages/web/src/components/common/notice/NoticeToast.tsx @@ -5,15 +5,17 @@ import { TNoticeType, } from "src/context/NoticeContext"; import IconClose from "../icons/IconCancel"; +import IconFailed from "../icons/IconFailed"; import IconNewTab from "../icons/IconNewTab"; import IconSuccess from "../icons/IconSuccess"; -import { NoticeContextWrapper, NoticeUIWrapper } from "./NoticeToast.styles"; +import LoadingSpinner from "../loading-spinner/LoadingSpinner"; +import { NoticeUIList, NoticeUIWrapper } from "./NoticeToast.styles"; interface NoticeProps { closeable?: boolean; - onClose?: () => void; - type?: TNoticeType; - children?: React.ReactNode; + onClose?: (id: number) => void; + type: TNoticeType; + id: number; } const TEMP_URL = @@ -34,43 +36,94 @@ const SuccessContent: FC = () => { ); }; -const NoticeUI: FC = ({ - children, - closeable = true, - onClose, - type = "success", -}) => { +const PendingContent: FC = () => { return ( - +
    + +
    +
    Broadcasting Transaction
    +

    Waiting for Transaction Confirmation

    + + View transaction + +
    +
    + ); +}; + +const FailContent: FC = () => { + return ( +
    + +
    +
    Swap - Failure!
    +

    Failed swapping 0.1 GNOT for 0.12 GNS

    + + View transaction + +
    +
    + ); +}; + +const NoticeUIItem: FC = ({ onClose, type = "success", id }) => { + const [typeAnimation, setTypeAnimation] = useState< + "toast-item" | "closing" | "" + >("toast-item"); + + const handleClose = useCallback(() => { + setTypeAnimation("closing"); + const timeout = setTimeout(() => { + onClose?.(id); + setTypeAnimation(""); + }, 500); + return () => clearTimeout(timeout); + }, [onClose]); + + useEffect(() => { + const autoCloseTimeout = setTimeout(() => { + setTypeAnimation("closing"); + const animationTimeout = setTimeout(() => { + onClose?.(id); + }, 500); + return () => clearTimeout(animationTimeout); + }, 6000); + + return () => { + clearTimeout(autoCloseTimeout); + }; + }, [onClose]); + + return ( + {type === "success" && } - {children} - {closeable && ( -
    - -
    - )} + {type === "error" && } + {type === "pending" && } +
    + +
    ); }; const Notice: FC<{ children: React.ReactNode }> = ({ children }) => { const [currentNotice, setCurrentNotice] = useState< - (NoticeProps & { timeout?: number }) | null + (NoticeProps & { timeout: number }) | null >(null); + const [list, setList] = useState<{ type: TNoticeType; id: number }[]>([]); const setNotice = useCallback( - (content, options) => { + (_, options) => { setCurrentNotice({ - children: content, ...options, }); + setList(prev => [...prev, { type: options.type, id: options.id }]); }, - [] + [list], ); const onCloseNotice = useCallback(() => { setCurrentNotice(null); - currentNotice?.onClose?.(); }, []); const contextValue = useMemo( @@ -78,7 +131,7 @@ const Notice: FC<{ children: React.ReactNode }> = ({ children }) => { setNotice, onCloseNotice, }), - [setNotice, onCloseNotice] + [setNotice, onCloseNotice], ); useEffect(() => { @@ -95,20 +148,28 @@ const Notice: FC<{ children: React.ReactNode }> = ({ children }) => { }; }, [currentNotice]); + const handleClose = useCallback( + (id: number) => { + setList(prev => prev.filter(item => item.id !== id)); + }, + [setList], + ); + return ( - {currentNotice && ( - - { - setCurrentNotice(null); - currentNotice.onClose?.(); - }} - > - {currentNotice.children} - - + {list.length > 0 && ( + + {list.map(item_ => { + return ( + + ); + })} + )} {children} diff --git a/packages/web/src/components/common/notification-button/NotificationButton.styles.ts b/packages/web/src/components/common/notification-button/NotificationButton.styles.ts index 933146b19..d62a8941b 100644 --- a/packages/web/src/components/common/notification-button/NotificationButton.styles.ts +++ b/packages/web/src/components/common/notification-button/NotificationButton.styles.ts @@ -13,6 +13,7 @@ export const AlertButton = styled.button` padding: 3px 4px 5px 4px; border-radius: 4px; transition: all 0.3s ease; + position: relative; ${media.tablet} { width: 32px; height: 32px; @@ -25,7 +26,7 @@ export const AlertButton = styled.button` height: 24px; } * { - fill: ${({ theme }) => theme.color.icon05}; + fill: ${({ theme }) => theme.color.icon03}; } } &:hover { @@ -33,4 +34,13 @@ export const AlertButton = styled.button` fill: ${({ theme }) => theme.color.icon07}; } } + .point-unread { + position: absolute; + width: 8px; + height: 8px; + border-radius: 50%; + background-color: ${({ theme }) => theme.color.red01}; + top: 5.5px; + right: 5.5px; + } `; diff --git a/packages/web/src/components/common/notification-button/NotificationButton.tsx b/packages/web/src/components/common/notification-button/NotificationButton.tsx index 1f5bcb117..7fb966d77 100644 --- a/packages/web/src/components/common/notification-button/NotificationButton.tsx +++ b/packages/web/src/components/common/notification-button/NotificationButton.tsx @@ -10,6 +10,7 @@ import { AlertButton, NotificationWrapper } from "./NotificationButton.styles"; import { useAtom } from "jotai"; import { CommonState } from "@states/index"; import { DEVICE_TYPE } from "@styles/media"; +import useEscCloseModal from "@hooks/common/use-esc-close-modal"; export interface TransactionGroupsType { title: string; @@ -18,6 +19,19 @@ export interface TransactionGroupsType { const NotificationButton = ({ breakpoint }: { breakpoint: DEVICE_TYPE }) => { const [toggle, setToggle] = useAtom(CommonState.headerToggle); + const handleESC = () => { + setToggle(prev => { + if (prev.notification) { + return { + ...prev, + notification: false, + }; + } + return prev; + }); + }; + + useEscCloseModal(() => handleESC()); const onListToggle = () => { setToggle(prev => ({ @@ -38,6 +52,7 @@ const NotificationButton = ({ breakpoint }: { breakpoint: DEVICE_TYPE }) => { +
    {toggle.notification && ( ` border: 1px solid ${({ theme }) => theme.color.border02}; box-shadow: 8px 8px 20px rgba(0, 0, 0, 0.2); border-radius: 8px; + z-index: ${Z_INDEX.modal}; right: ${({ width }) => { return width && width > 1920 ? "-250px" : width && width > 1440 ? `-${(width-1440)/2 + 10}px` : "-10px"; }}; @@ -36,7 +37,6 @@ export const NotificationListWrapper = styled.div` width: 100%; height: 426px; top: calc(100vh - 426px); - z-index: ${Z_INDEX.modal}; padding: 24px 0px; min-width: 360px; } @@ -71,6 +71,9 @@ export const NotificationHeader = styled.div` export const ClearButton = styled.button` ${fonts.body11}; color: ${({ theme }) => theme.color.text04}; + &:hover { + color: ${({ theme }) => theme.color.text03}; + } `; export const TxsListItem = styled.div` @@ -120,13 +123,13 @@ export const TxsSummaryItem = styled.div` margin-left: auto; } .success-icon * { - fill: ${({ theme }) => theme.color.point}; + fill: ${({ theme }) => theme.color.green01}; } .failed-icon * { - fill: ${({ theme }) => theme.color.icon03}; + fill: ${({ theme }) => theme.color.red01}; } .pending-icon * { - fill: ${({ theme }) => theme.color.icon06}; + fill: ${({ theme }) => theme.color.text24}; } `; @@ -175,13 +178,13 @@ export const TransactionItemsWrap = styled.div` } } .success-icon * { - fill: ${({ theme }) => theme.color.point}; + fill: ${({ theme }) => theme.color.green01}; } .failed-icon * { - fill: ${({ theme }) => theme.color.icon03}; + fill: ${({ theme }) => theme.color.red01}; } .pending-icon * { - fill: ${({ theme }) => theme.color.icon06}; + fill: ${({ theme }) => theme.color.text24}; } ${media.mobile} { margin-top: 4px; @@ -199,3 +202,15 @@ export const DoubleLogo = styled.div` margin-left: -12px; } `; + +export const Overlay = styled.div` + position: fixed; + top: 0px; + bottom: 0px; + left: 0px; + right: 0px; + width: 100%; + height: 100%; + overflow: hidden; + z-index: ${Z_INDEX.modalOverlay}; +`; diff --git a/packages/web/src/components/common/notification-list/NotificationList.tsx b/packages/web/src/components/common/notification-list/NotificationList.tsx index 6ff3fde02..eda37c97a 100644 --- a/packages/web/src/components/common/notification-list/NotificationList.tsx +++ b/packages/web/src/components/common/notification-list/NotificationList.tsx @@ -1,9 +1,10 @@ -import React, { useEffect, useRef } from "react"; +import React, { useRef } from "react"; import { ClearButton, NoDataText, NotificationHeader, NotificationListWrapper, + Overlay, } from "./NotificationList.styles"; import TransactionItems from "./TransactionItems"; import { TransactionGroupsType } from "@components/common/notification-button/NotificationButton"; @@ -22,47 +23,35 @@ const NotificationList: React.FC = ({ }) => { const listRef = useRef(null); - useEffect(() => { - const closeList = (e: MouseEvent) => { - if (listRef.current && listRef.current.contains(e.target as Node)) { - return; - } else { - e.stopPropagation(); - onListToggle(); - } - }; - window.addEventListener("click", closeList, true); - return () => { - window.removeEventListener("click", closeList, true); - }; - }, [listRef, onListToggle]); - return ( - - - Notification - {txsGroupsInformation.length > 0 && ( - Clear All - )} - - {txsGroupsInformation && txsGroupsInformation.length > 0 ? ( -
    -
    - {txsGroupsInformation.map(groups => ( - - ))} + <> + + + Notification + {txsGroupsInformation.length > 0 && ( + Clear All + )} + + {txsGroupsInformation && txsGroupsInformation.length > 0 ? ( +
    +
    + {txsGroupsInformation.map(groups => ( + + ))} +
    -
    - ) : ( - <> - No notifications found - - )} - + ) : ( + <> + No notifications found + + )} + + + ); }; diff --git a/packages/web/src/components/common/search-menu-modal/SearchMenuModal.styles.ts b/packages/web/src/components/common/search-menu-modal/SearchMenuModal.styles.ts index 33b2afac8..3a5993bc5 100644 --- a/packages/web/src/components/common/search-menu-modal/SearchMenuModal.styles.ts +++ b/packages/web/src/components/common/search-menu-modal/SearchMenuModal.styles.ts @@ -5,15 +5,7 @@ import { Z_INDEX } from "@styles/zIndex"; import { media } from "@styles/media"; export const SearchModalBackground = styled.div` - position: fixed; - top: 0px; - bottom: 0px; - left: 0px; - right: 0px; - width: 100%; - height: 100%; - background: rgba(10, 14, 23, 0.7); - z-index: ${Z_INDEX.modalOverlay}; + z-index: ${Z_INDEX.modal}; `; export const SearchContainer = styled.div` @@ -47,7 +39,7 @@ export const SearchWrapper = styled.div` background-color: ${({ theme }) => theme.color.background06}; color: ${({ theme }) => theme.color.text14}; .search-icon * { - fill: ${({ theme }) => theme.color.icon03}; + fill: ${({ theme }) => theme.color.icon08}; } box-shadow: 10px 14px 48px 0px rgba(0, 0, 0, 0.12); &:focus-within { @@ -130,25 +122,30 @@ export const ModalContainer = styled.div` height: 24px; } .token-name { - ${fonts.body8}; + ${fonts.body10}; color: ${({ theme }) => theme.color.text02}; ${media.mobile} { ${fonts.body12} } } .token-symbol { - ${fonts.body8}; + ${fonts.body12}; color: ${({ theme }) => theme.color.text04}; ${media.mobile} { - ${fonts.body12} + ${fonts.p4} } } .token-price { - ${fonts.body7}; + ${mixins.flexbox("row", "center", "flex-end")}; + ${fonts.body9}; color: ${({ theme }) => theme.color.text02}; ${media.mobile} { ${fonts.body11} } + svg { + height: 20px; + width: auto; + } } .negative { ${fonts.body12}; @@ -161,8 +158,21 @@ export const ModalContainer = styled.div` ${fonts.body12}; color: ${({ theme }) => theme.color.green01}; ${media.mobile} { - ${fonts.p2} + ${fonts.p4} } } } `; + +export const Overlay = styled.div` + position: fixed; + top: 0px; + bottom: 0px; + left: 0px; + right: 0px; + width: 100%; + height: 100%; + overflow: hidden; + z-index: ${Z_INDEX.modalOverlay}; + background: rgba(10, 14, 23, 0.7); +`; diff --git a/packages/web/src/components/common/search-menu-modal/SearchMenuModal.tsx b/packages/web/src/components/common/search-menu-modal/SearchMenuModal.tsx index a51388da7..9121b0eb2 100644 --- a/packages/web/src/components/common/search-menu-modal/SearchMenuModal.tsx +++ b/packages/web/src/components/common/search-menu-modal/SearchMenuModal.tsx @@ -1,16 +1,19 @@ // TODO : remove eslint-disable after work /* eslint-disable */ import { Token } from "@containers/header-container/HeaderContainer"; -import React, { useEffect, useRef } from "react"; +import React, { useRef } from "react"; import { SearchModalBackground, SearchContainer, SearchWrapper, InputStyle, ModalContainer, + Overlay, } from "./SearchMenuModal.styles"; import IconSearch from "@components/common/icons/IconSearch"; import Badge, { BADGE_TYPE } from "../badge/Badge"; +import DoubleLogo from "../double-logo/DoubleLogo"; +import IconStar from "../icons/IconStar"; interface SearchMenuModalProps { onSearchMenuToggle: () => void; search: (e: React.ChangeEvent) => void; @@ -33,147 +36,157 @@ const SearchMenuModal: React.FC = ({ location.href = "/tokens/" + symbol; }; - useEffect(() => { - const closeMenu = (e: MouseEvent) => { - if (menuRef.current && menuRef.current.contains(e.target as Node)) { - return; - } else { - e.stopPropagation(); - onSearchMenuToggle(); - } - }; - window.addEventListener("click", closeMenu, true); - return () => { - window.removeEventListener("click", closeMenu, true); - }; - }, [menuRef, onSearchMenuToggle]); - return ( - -
    - - - - - - - -
      - {tokens.length === 0 && isFetched && ( -
      No data found
      - )} - {(tokens.length !== 0 || !isFetched) && ( - <> -
      - {!keyword ? "Recent Searches" : "Tokens"} -
      - {tokens - .filter(item => item.searchType === "recent") - .map((item, idx) => ( -
    • onClickItem(item.token.symbol)} - > -
      - token logo - {item.token.name} - - {item.token.symbol} - -
      - {item.price} - {item.priceOf1d.status === "POSITIVE" ? ( - - +{item.priceOf1d.value} - - ) : ( - - -{item.priceOf1d.value} - - )} -
    • - ))} -
      - {!keyword ? "Popular Tokens" : "Pools"} -
      - {tokens - .filter(item => item.searchType === "popular") - .map((item, idx) => ( -
    • onClickItem(item.token.symbol)} - > -
      - token logo - {item.token.name} - - {item.token.symbol} - -
      - {item.price} - {item.priceOf1d.status === "POSITIVE" ? ( - - +{item.priceOf1d.value} - + <> + +
      + + + + + + + +
        + {tokens.length === 0 && isFetched && ( +
        No data found
        + )} + {(tokens.length !== 0 || !isFetched) && ( + <> +
        + {!keyword ? "Recent Searches" : "Tokens"} +
        + {tokens + .filter(item => item.searchType === "recent") + .map((item, idx) => ( + !item.isLiquid ? ( +
      • onClickItem(item.token.symbol)} + > +
        + token logo + {item.token.name} + + {item.token.symbol} + +
        + {item.price} + {item.priceOf1d.status === "POSITIVE" ? ( + + +{item.priceOf1d.value}% + + ) : ( + + -{item.priceOf1d.value}% + + )} +
      • ) : ( - - -{item.priceOf1d.value} +
      • onClickItem(item.token.symbol)} + > +
        + + + {item.token.name}/{item?.tokenB?.name} + + +
        + + {Number(item.priceOf1d.value) >= 100 && }{item.priceOf1d.value}% {item.token.symbol} - )} -
      • - ))} - {!keyword && ( - <> -
        Most Liquid Pools
        - {tokens - .filter(item => item.searchType === "recent") - .map((item, idx) => ( -
      • onClickItem(item.token.symbol)} - > -
        - token logo - - {item.token.name} - - - {item.token.symbol} - - -
        - - {item.priceOf1d.value} {item.token.symbol} +
      • + ) + ))} +
        + {!keyword ? "Popular Tokens" : "Pools"} +
        + {tokens + .filter(item => item.searchType === "popular") + .map((item, idx) => ( +
      • onClickItem(item.token.symbol)} + > +
        + token logo + {item.token.name} + + {item.token.symbol} + +
        + {item.price} + {item.priceOf1d.status === "POSITIVE" ? ( + + +{item.priceOf1d.value} -
      • - ))} - - )} - - )} -
      -
      -
      -
      + ) : ( + + -{item.priceOf1d.value} + + )} +
    • + ))} + {!keyword && ( + <> +
      Most Liquid Pools
      + {tokens + .filter(item => item.searchType === "recent") + .map((item, idx) => ( +
    • onClickItem(item.token.symbol)} + > +
      + + + {item.token.name}/{item?.tokenB?.name} + + +
      + + {Number(item.priceOf1d.value) >= 100 && }{item.priceOf1d.value}% {item.token.symbol} + +
    • + ))} + + )} + + )} +
    +
    +
    +
    + + ); }; diff --git a/packages/web/src/components/common/select-pair-button/SelectPairButton.tsx b/packages/web/src/components/common/select-pair-button/SelectPairButton.tsx index d562b3073..b1c70ef3d 100644 --- a/packages/web/src/components/common/select-pair-button/SelectPairButton.tsx +++ b/packages/web/src/components/common/select-pair-button/SelectPairButton.tsx @@ -37,7 +37,7 @@ const SelectPairButton: React.FC = ({
    {token ? (
    diff --git a/packages/web/src/components/common/select-token/SelectToken.styles.ts b/packages/web/src/components/common/select-token/SelectToken.styles.ts index 33fed0412..8644e16b6 100644 --- a/packages/web/src/components/common/select-token/SelectToken.styles.ts +++ b/packages/web/src/components/common/select-token/SelectToken.styles.ts @@ -179,22 +179,22 @@ export const SelectTokenWrapper = styled.div` gap: 8px; .token-name { color: ${({ theme }) => theme.color.text02}; - ${fonts.body8} + ${fonts.body10} ${media.mobile} { ${fonts.body12} } } .token-symbol { color: ${({ theme }) => theme.color.text04}; - ${fonts.body8} + ${fonts.body12} ${media.mobile} { - ${fonts.body12} + ${fonts.p4} } } } .token-balance { color: ${({ theme }) => theme.color.text02}; - ${fonts.body7} + ${fonts.body9} ${media.mobile} { ${fonts.body11} } diff --git a/packages/web/src/components/common/select-token/SelectToken.tsx b/packages/web/src/components/common/select-token/SelectToken.tsx index 68e5fd469..f2505a2c0 100644 --- a/packages/web/src/components/common/select-token/SelectToken.tsx +++ b/packages/web/src/components/common/select-token/SelectToken.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from "react"; +import React, { useCallback, useRef, useEffect, useState } from "react"; import { SelectTokenWrapper } from "./SelectToken.styles"; import IconSearch from "@components/common/icons/IconSearch"; import IconClose from "@components/common/icons/IconCancel"; @@ -13,6 +13,7 @@ export interface SelectTokenProps { changeToken: (token: TokenModel) => void; close: () => void; themeKey: "dark" | "light"; + modalRef?: React.RefObject; } const SelectToken: React.FC = ({ @@ -24,7 +25,11 @@ const SelectToken: React.FC = ({ changeToken, close, themeKey, + modalRef, }) => { + const myElementRef = useRef(null); + const [positionTop, setPositionTop] = useState(0); + const getTokenPrice = useCallback((token: TokenModel) => { const tokenPrice = tokenPrices[token.priceId]; if (tokenPrice === null || Number.isNaN(tokenPrice)) { @@ -47,8 +52,25 @@ const SelectToken: React.FC = ({ changeKeyword(searchKeyword); }, [changeKeyword]); + useEffect(() => { + const getPositionTop = () => { + const element = myElementRef.current; + if (element) { + const rect = element.getBoundingClientRect(); + const temp = Math.max(positionTop, rect.top); + if (modalRef && modalRef.current) { + modalRef.current.style.top = `${temp}px`; + modalRef.current.style.transform = "translate(-50%, 0)"; + } + setPositionTop(temp); + + } + }; + getPositionTop(); + }, [positionTop]); + return ( - +
    Select a token diff --git a/packages/web/src/components/common/tooltip/FloatingTooltip.tsx b/packages/web/src/components/common/tooltip/FloatingTooltip.tsx index 2ccd6d9c7..c3c9b53fe 100644 --- a/packages/web/src/components/common/tooltip/FloatingTooltip.tsx +++ b/packages/web/src/components/common/tooltip/FloatingTooltip.tsx @@ -17,11 +17,12 @@ interface TooltipProps { position: FloatingPosition; content: React.ReactNode; className?: string; + isHiddenArrow?: boolean; children?: any; } const FloatingTooltip = forwardRef, TooltipProps>( - ({ children, content, className }, ref) => { + ({ children, content, className, isHiddenArrow, position = "top" as FloatingPosition, offset = 20 }, ref) => { const { handleMouseMove, x, @@ -34,14 +35,15 @@ const FloatingTooltip = forwardRef, TooltipProps>( floating, setOpened, } = useFloatingTooltip({ - offset: 20, - position: "top", + offset: offset, + position: position, }); + const theme = useTheme(); const targetRef = useMergedRef(boundaryRef, (children as any).ref, ref); - const onMouseEnter = (event: React.MouseEvent) => { + const onMouseEnter = (event: React.MouseEvent | React.TouchEvent) => { children.props.onMouseEnter?.(event); handleMouseMove(event); setOpened(true); @@ -52,6 +54,12 @@ const FloatingTooltip = forwardRef, TooltipProps>( setOpened(false); }; + const onTouchStart = (event: React.TouchEvent) => { + children.props.onPointerEnter?.(event); + handleMouseMove(event); + setOpened(true); + }; + return ( <> {cloneElement(children, { @@ -59,6 +67,8 @@ const FloatingTooltip = forwardRef, TooltipProps>( ref: targetRef, onMouseEnter, onMouseLeave, + onTouchStart: onTouchStart, + onTouchMove: onTouchStart, })} @@ -72,16 +82,18 @@ const FloatingTooltip = forwardRef, TooltipProps>( zIndex: Z_INDEX.modalTooltip, }} className={className} + onTouchMove={onTouchStart} + onTouchStart={onTouchStart} > - - {content} + />} + {content && {content}}
    diff --git a/packages/web/src/components/common/wallet-connector-button/WalletConnectorButton.styles.ts b/packages/web/src/components/common/wallet-connector-button/WalletConnectorButton.styles.ts index 5dfd64f20..c4a7cc458 100644 --- a/packages/web/src/components/common/wallet-connector-button/WalletConnectorButton.styles.ts +++ b/packages/web/src/components/common/wallet-connector-button/WalletConnectorButton.styles.ts @@ -21,6 +21,17 @@ export const WalletConnectorButtonWrapper = styled.div` .switch-network { margin: 16px 0; } + .loading-button { + width: 20px; + height: 20px; + background: conic-gradient(from 0deg at 50% 50.63%, #FFFFFF 0deg, #233DBD 360deg); + margin: auto; + &::before { + width: 14px; + height: 14px; + background-color: ${({ theme }) => theme.color.background04Hover}; + } + } `; export const FailNetworkTooltipContentWrap = styled.div` diff --git a/packages/web/src/components/common/wallet-connector-button/WalletConnectorButton.tsx b/packages/web/src/components/common/wallet-connector-button/WalletConnectorButton.tsx index 03aee480b..90c270049 100644 --- a/packages/web/src/components/common/wallet-connector-button/WalletConnectorButton.tsx +++ b/packages/web/src/components/common/wallet-connector-button/WalletConnectorButton.tsx @@ -14,6 +14,8 @@ import { AccountModel } from "@models/account/account-model"; import IconFailed from "../icons/IconFailed"; import Tooltip from "../tooltip/Tooltip"; import { Global, css } from "@emotion/react"; +import LoadingSpinner from "../loading-spinner/LoadingSpinner"; +import useEscCloseModal from "@hooks/common/use-esc-close-modal"; interface WalletConnectProps { account: AccountModel | null; @@ -23,6 +25,7 @@ interface WalletConnectProps { disconnectWallet: () => void; switchNetwork: () => void; isSwitchNetwork: boolean; + loadingConnect: string; } const ToolTipGlobalStyle = () => { @@ -58,8 +61,22 @@ const WalletConnectorButton: React.FC = ({ disconnectWallet, switchNetwork, isSwitchNetwork, + loadingConnect, }) => { const [toggle, setToggle] = useAtom(CommonState.headerToggle); + const handleESC = () => { + setToggle(prev => { + if (prev.walletConnect) { + return { + ...prev, + walletConnect: false, + }; + } + return prev; + }); + }; + + useEscCloseModal(() => handleESC()); const address = useMemo(() => { if (account === null) { @@ -75,6 +92,10 @@ const WalletConnectorButton: React.FC = ({ })); }; + const isLoading = useMemo(() => { + return loadingConnect === "loading"; + }, [loadingConnect]); + return ( {connected ? ( @@ -105,19 +126,20 @@ const WalletConnectorButton: React.FC = ({ arrowColor: "text18", padding: "10px 16px", gap: "8px", + height: 36 }} onClick={onMenuToggle} /> ) : (
    + ) : ( +
    - ) : ( -
    -
    + )} +
    + + Theme + +
    - )} -
    - - Theme - - -
    - + + + ); }; diff --git a/packages/web/src/components/dashboard/activity-info/ActivityInfo.styles.ts b/packages/web/src/components/dashboard/activity-info/ActivityInfo.styles.ts index d19ebda6b..c4410ad4d 100644 --- a/packages/web/src/components/dashboard/activity-info/ActivityInfo.styles.ts +++ b/packages/web/src/components/dashboard/activity-info/ActivityInfo.styles.ts @@ -15,7 +15,6 @@ export const TokenInfoWrapper = styled.div` export const HoverSection = styled.div` ${mixins.flexbox("row", "center", "center", false)}; - background-color: ${({ theme }) => theme.color.background01}; transition: background-color 0.3s ease; height: 100%; &:hover { @@ -50,6 +49,9 @@ export const TableColumn = styled.div<{ tdWidth: number }>` ${fonts.body11}; color: ${({ theme }) => theme.color.text02}; } + .tooltip-label { + cursor: default; + } .symbol-text { font-weight: 700; } diff --git a/packages/web/src/components/dashboard/activity-info/ActivityInfo.tsx b/packages/web/src/components/dashboard/activity-info/ActivityInfo.tsx index 2bd58bfc5..6b300e81f 100644 --- a/packages/web/src/components/dashboard/activity-info/ActivityInfo.tsx +++ b/packages/web/src/components/dashboard/activity-info/ActivityInfo.tsx @@ -74,7 +74,7 @@ const ActivityInfo: React.FC = ({ item, idx, key }) => { } > - {account} + {account} @@ -86,7 +86,7 @@ const ActivityInfo: React.FC = ({ item, idx, key }) => { } > - {time} + {time} diff --git a/packages/web/src/components/dashboard/activity-list-header/ActivityListHeader.styles.ts b/packages/web/src/components/dashboard/activity-list-header/ActivityListHeader.styles.ts index f599d2e49..df354cd38 100644 --- a/packages/web/src/components/dashboard/activity-list-header/ActivityListHeader.styles.ts +++ b/packages/web/src/components/dashboard/activity-list-header/ActivityListHeader.styles.ts @@ -7,7 +7,9 @@ export const ActivityListHeaderwrapper = styled.div` ${mixins.flexbox("row", "center", "flex-start")}; width: 100%; gap: 36px; - + ${media.tabletMiddle} { + gap: 36px; + } ${media.mobile} { flex-direction: column; justify-content: center; @@ -16,6 +18,7 @@ export const ActivityListHeaderwrapper = styled.div` } h2 { ${fonts.h5}; + flex: 0 0 auto; ${media.mobile} { ${fonts.h6}; } @@ -25,6 +28,9 @@ export const ActivityListHeaderwrapper = styled.div` > div { padding: 1px; } + ${media.tabletMiddle} { + overflow-x: scroll; + } ${media.mobile} { max-width: ${ContainerWidth.MOBILE_CONTAINER}; width: 100%; diff --git a/packages/web/src/components/dashboard/activity-list-header/ActivityListHeader.tsx b/packages/web/src/components/dashboard/activity-list-header/ActivityListHeader.tsx index 4f0f05f75..9789816bf 100644 --- a/packages/web/src/components/dashboard/activity-list-header/ActivityListHeader.tsx +++ b/packages/web/src/components/dashboard/activity-list-header/ActivityListHeader.tsx @@ -13,7 +13,7 @@ const ActivityListHeader: React.FC = ({ changeActivityType, }) => ( -

    Activities

    +

    Onchain Activities

    theme.color.point}; - width: 36px; - height: 36px; - border-radius: 100%; - ${media.mobile} { - width: 25px; - height: 25px; + svg { + width: 36px; + height: 36px; + ${media.mobile} { + width: 25px; + height: 25px; + } } } .gnos-image { @@ -60,13 +60,13 @@ export const GnotLogoWrapper = styled.div` gap: 8px; .gnot-image-wrapper { ${mixins.flexbox("row", "center", "center")}; - background-color: ${({ theme }) => theme.color.icon09}; - width: 36px; - height: 36px; - border-radius: 100%; - ${media.mobile} { - width: 25px; - height: 25px; + svg { + width: 36px; + height: 36px; + ${media.mobile} { + width: 25px; + height: 25px; + } } } .gnot-symbol { diff --git a/packages/web/src/components/dashboard/dashboard-info-title/DashboardInfoTitle.tsx b/packages/web/src/components/dashboard/dashboard-info-title/DashboardInfoTitle.tsx index c00d72de7..c6544e211 100644 --- a/packages/web/src/components/dashboard/dashboard-info-title/DashboardInfoTitle.tsx +++ b/packages/web/src/components/dashboard/dashboard-info-title/DashboardInfoTitle.tsx @@ -8,6 +8,7 @@ import { import IconLogoWhite from "../../common/icons/IconLogoWhite"; import { DashboardTokenInfo } from "@containers/dashboard-info-container/DashboardInfoContainer"; import { DEVICE_TYPE } from "@styles/media"; +import IconLogoGnot from "@components/common/icons/IconLogoGnot"; interface DashboardInfoTitleProps { dashboardTokenInfo: DashboardTokenInfo; @@ -24,7 +25,7 @@ const DashboardInfoTitle: React.FC = ({
    -
    GNOS
    +
    GNS
    {dashboardTokenInfo.gnosAmount}
    @@ -32,7 +33,7 @@ const DashboardInfoTitle: React.FC = ({
    - +
    GNOT
    diff --git a/packages/web/src/components/dashboard/dashboard-info/DashboardInfo.styles.ts b/packages/web/src/components/dashboard/dashboard-info/DashboardInfo.styles.ts index 797b09ca9..7c66a9392 100644 --- a/packages/web/src/components/dashboard/dashboard-info/DashboardInfo.styles.ts +++ b/packages/web/src/components/dashboard/dashboard-info/DashboardInfo.styles.ts @@ -7,12 +7,21 @@ export const DashboardInfoWrapper = styled.div` ${mixins.flexbox("column", "flex-start", "flex-start")}; max-width: ${ContainerWidth.WEB_CONTAINER}; width: 100%; - padding: 24px; + padding: 23px; gap: 24px; background-color: ${({ theme }) => theme.color.background03}; border: 1px solid ${({ theme }) => theme.color.border01}; border-radius: 10px; box-shadow: 8px 8px 20px 0px rgba(0, 0, 0, 0.08); + .loading-spining { + padding: 8px 0; + width: 100%; + span { + height: 20px; + display: block; + max-width: 100%; + } + } ${media.tablet} { max-width: ${ContainerWidth.TABLET_CONTAINER}; } @@ -20,5 +29,8 @@ export const DashboardInfoWrapper = styled.div` max-width: ${ContainerWidth.MOBILE_CONTAINER}; padding: 0px; gap: 0px; + .loading-spining { + padding: 30px 0 30px 11px; + } } `; diff --git a/packages/web/src/components/dashboard/dashboard-info/DashboardInfo.tsx b/packages/web/src/components/dashboard/dashboard-info/DashboardInfo.tsx index 630e7a00c..073b04f9d 100644 --- a/packages/web/src/components/dashboard/dashboard-info/DashboardInfo.tsx +++ b/packages/web/src/components/dashboard/dashboard-info/DashboardInfo.tsx @@ -7,12 +7,14 @@ import { SupplyOverviewInfo, } from "@containers/dashboard-info-container/DashboardInfoContainer"; import { DEVICE_TYPE } from "@styles/media"; +import { SHAPE_TYPES, skeletonTokenDetail } from "@constants/skeleton.constant"; interface DashboardInfoProps { dashboardTokenInfo: DashboardTokenInfo; supplyOverviewInfo: SupplyOverviewInfo; governenceOverviewInfo: GovernenceOverviewInfo; breakpoint: DEVICE_TYPE; + loading: boolean; } const DashboardInfo: React.FC = ({ @@ -20,16 +22,23 @@ const DashboardInfo: React.FC = ({ supplyOverviewInfo, governenceOverviewInfo, breakpoint, + loading, }) => ( - + />} + {loading &&
    + +
    }
    ); diff --git a/packages/web/src/components/dashboard/dashboard-label/DashboardLabel.tsx b/packages/web/src/components/dashboard/dashboard-label/DashboardLabel.tsx index 0d9f0b019..a222df070 100644 --- a/packages/web/src/components/dashboard/dashboard-label/DashboardLabel.tsx +++ b/packages/web/src/components/dashboard/dashboard-label/DashboardLabel.tsx @@ -4,7 +4,7 @@ import Tooltip from "@components/common/tooltip/Tooltip"; import { WalletBalanceDetailInfoTooltipContent } from "./DashboardLabel.styles"; interface WalletBalanceDetailInfoProps { - tooltip: string; + tooltip: React.ReactNode; } const DashboardLabel: React.FC = ({ diff --git a/packages/web/src/components/dashboard/dashboard-overview/DashboardOverview.tsx b/packages/web/src/components/dashboard/dashboard-overview/DashboardOverview.tsx index bb3d8ad89..733493eae 100644 --- a/packages/web/src/components/dashboard/dashboard-overview/DashboardOverview.tsx +++ b/packages/web/src/components/dashboard/dashboard-overview/DashboardOverview.tsx @@ -15,22 +15,24 @@ interface DashboardOverviewProps { supplyOverviewInfo: SupplyOverviewInfo; governenceOverviewInfo: GovernenceOverviewInfo; breakpoint: DEVICE_TYPE; + loading: boolean; } const DashboardOverview: React.FC = ({ supplyOverviewInfo, governenceOverviewInfo, breakpoint, + loading, }) => ( {breakpoint === DEVICE_TYPE.MOBILE && } - + {breakpoint !== DEVICE_TYPE.MOBILE ? ( ) : ( )} - + ); diff --git a/packages/web/src/components/dashboard/governance-overview/GovernanceOverview.styles.ts b/packages/web/src/components/dashboard/governance-overview/GovernanceOverview.styles.ts index acfe626a4..e0e9fa48b 100644 --- a/packages/web/src/components/dashboard/governance-overview/GovernanceOverview.styles.ts +++ b/packages/web/src/components/dashboard/governance-overview/GovernanceOverview.styles.ts @@ -7,10 +7,10 @@ export const GovernanceOverviewWrapper = styled.div` ${mixins.flexbox("column", "flex-start", "flex-start")}; width: 100%; color: ${({ theme }) => theme.color.text02}; - padding: 24px; + padding: 23px; gap: 24px; ${media.mobile} { - padding: 12px 12px 16px 12px; + padding: 11px 11px 15px 11px; gap: 16px; } `; @@ -87,7 +87,6 @@ export const GovernanceWrapper = styled.div` ${fonts.body12} } svg { - cursor: pointer; margin: 1.5px 0px; width: 18px; height: 18px; @@ -163,3 +162,14 @@ export const GovernanceWrapper = styled.div` } } `; + +export const LoadingTextWrapper = styled.div` + padding: 2.5px 0; + span { + height: 20px; + display: block; + } + ${media.mobile} { + padding: 1px 0; + } +`; \ No newline at end of file diff --git a/packages/web/src/components/dashboard/governance-overview/GovernanceOverview.tsx b/packages/web/src/components/dashboard/governance-overview/GovernanceOverview.tsx index d6ee4d709..aed5b178a 100644 --- a/packages/web/src/components/dashboard/governance-overview/GovernanceOverview.tsx +++ b/packages/web/src/components/dashboard/governance-overview/GovernanceOverview.tsx @@ -6,15 +6,29 @@ import { GovernanceWrapper, IconButton, LabelIconButton, + LoadingTextWrapper, } from "./GovernanceOverview.styles"; import { GovernenceOverviewInfo } from "@containers/dashboard-info-container/DashboardInfoContainer"; +import { SHAPE_TYPES, skeletonTokenDetail } from "@constants/skeleton.constant"; interface GovernanceOverviewProps { governenceOverviewInfo: GovernenceOverviewInfo; + loading: boolean; } +const LoadingText = () => { + return ( + + + + ); +}; + const GovernanceOverview: React.FC = ({ governenceOverviewInfo, + loading, }) => ( @@ -30,31 +44,33 @@ const GovernanceOverview: React.FC = ({
    -
    Total xGNOS Issued
    - +
    Total xGNS Issued
    +
    -
    {governenceOverviewInfo.totalXgnosIssued}
    + {!loading ?
    {governenceOverviewInfo.totalXgnosIssued}
    : }
    Holders
    - +
    -
    {governenceOverviewInfo.holders}
    + {!loading ?
    {governenceOverviewInfo.holders}
    : }
    Passed Proposals
    -
    {governenceOverviewInfo.passedProposals}
    + {!loading ?
    {governenceOverviewInfo.passedProposals}
    : } +
    Active Proposals
    -
    {governenceOverviewInfo.activeProposals}
    - {governenceOverviewInfo.activeProposals}
    : } + + {!loading && { alert("open Link"); }} @@ -62,15 +78,15 @@ const GovernanceOverview: React.FC = ({
    -
    + }
    Community Pool
    - +
    -
    {governenceOverviewInfo.communityPool}
    + {!loading ?
    {governenceOverviewInfo.communityPool}
    : }
    diff --git a/packages/web/src/components/dashboard/supply-overview/SupplyOverview.styles.ts b/packages/web/src/components/dashboard/supply-overview/SupplyOverview.styles.ts index 33d4ebf68..b286cb5b9 100644 --- a/packages/web/src/components/dashboard/supply-overview/SupplyOverview.styles.ts +++ b/packages/web/src/components/dashboard/supply-overview/SupplyOverview.styles.ts @@ -7,7 +7,7 @@ export const SupplyOverviewWrapper = styled.div` ${mixins.flexbox("column", "flex-start", "flex-start")} width: 100%; color: ${({ theme }) => theme.color.text02}; - padding: 24px; + padding: 23px; gap: 24px; .supply-overview { ${fonts.body9}; @@ -16,7 +16,7 @@ export const SupplyOverviewWrapper = styled.div` } } ${media.mobile} { - padding: 12px; + padding: 11px; gap: 16px; } `; @@ -41,7 +41,6 @@ export const SupplyInfoWrapper = styled.div` ${fonts.body12} } svg { - cursor: pointer; margin: 1.5px 0px; width: 18px; height: 18px; @@ -104,11 +103,31 @@ export const SupplyInfoWrapper = styled.div` ${mixins.flexbox("column", "flex-end", "center")}; width: 100%; gap: 4px; + .loading-text-wrapper { + &:last-of-type { + padding: 0; + margin-bottom: -4px; + } + } ${media.tabletMiddle} { flex-direction: column; align-items: flex-start; gap: 4px; } + ${media.mobile} { + flex-direction: row; + align-items: center; + justify-content: space-between; + gap: 4px; + .loading-text-wrapper { + span { + width: 100px; + } + &:last-of-type { + margin-bottom: 0px; + } + } + } } .staked-ratio-title { ${fonts.p2} @@ -120,7 +139,7 @@ export const SupplyInfoWrapper = styled.div` ${mixins.flexbox("row", "center", "flex-start")}; gap: 4px; svg { - cursor: pointer; + cursor: default; margin: 3.5px 0px; width: 18px; height: 18px; @@ -150,3 +169,53 @@ export const ProgressBar = styled.div` background-color: ${({ theme }) => theme.color.point}; } `; + +export const LoadingTextWrapper = styled.div` + padding: 2.5px 0; + span { + height: 20px; + display: block; + } + ${media.mobile} { + padding: 1px 0; + } +`; +export const LoadingProgressWrapper = styled.div` + width: 100%; + span { + height: 12px; + display: block; + } +`; + +export const BlockEmissionsWrapper = styled.div` + width: 268px; + ${mixins.flexbox("column", "flex-start", "flex-start")}; + gap: 8px; + ${media.mobile} { + gap: 4px; + } + h5 { + color: ${({ theme }) => theme.color.text04}; + ${fonts.body12} + } + .content { + height: 28px; + width: 100%; + ${mixins.flexbox("row", "center", "space-between")}; + + .label, .value { + color: ${({ theme }) => theme.color.text02}; + ${fonts.body12} + } + .value { + ${mixins.flexbox("row", "center", "flex-end")}; + gap: 8px; + img { + display: block; + width: 20px; + height: 20px; + } + } + } +`; diff --git a/packages/web/src/components/dashboard/supply-overview/SupplyOverview.tsx b/packages/web/src/components/dashboard/supply-overview/SupplyOverview.tsx index 00e2fe3d3..1feb0b919 100644 --- a/packages/web/src/components/dashboard/supply-overview/SupplyOverview.tsx +++ b/packages/web/src/components/dashboard/supply-overview/SupplyOverview.tsx @@ -4,13 +4,67 @@ import { SupplyOverviewWrapper, SupplyInfoWrapper, ProgressBar, + LoadingTextWrapper, + LoadingProgressWrapper, + BlockEmissionsWrapper, } from "./SupplyOverview.styles"; +import { SHAPE_TYPES, skeletonTokenDetail } from "@constants/skeleton.constant"; + interface SupplyOverviewInfoProps { supplyOverviewInfo: SupplyOverviewInfo; + loading: boolean; } +const LoadingText = () => { + return ( + + + + ); +}; + +const LoadingProgress = () => { + return ( + + + + ); +}; + +const BlockEmissions = () => { + return ( +
    Block Emissions
    +
    +
    Liquidity Staking
    +
    + logo +
    123.44 / day
    +
    +
    +
    +
    DevOps
    +
    + logo +
    53.44 / day
    +
    +
    +
    +
    Community
    +
    + logo +
    12.44 / day
    +
    +
    +
    ); +}; + const SupplyOverview: React.FC = ({ supplyOverviewInfo, + loading, }) => (
    Supply Overview
    @@ -18,42 +72,42 @@ const SupplyOverview: React.FC = ({
    Total Supply - +
    -
    {supplyOverviewInfo.totalSupply}
    + {!loading ?
    {supplyOverviewInfo.totalSupply}
    : }
    Circulating Supply
    - +
    -
    {supplyOverviewInfo.circulatingSupply}
    + {!loading ?
    {supplyOverviewInfo.circulatingSupply}
    : }
    - + {!loading ?
    - + : }
    Daily Block Emissions
    - +
    -
    {supplyOverviewInfo.dailyBlockEmissions}
    - + {!loading ?
    {supplyOverviewInfo.dailyBlockEmissions}
    : } + {!loading && } />}
    Total Staked
    - +
    -
    {supplyOverviewInfo.totalStaked}
    -
    + {!loading ?
    {supplyOverviewInfo.totalStaked}
    : } + {!loading ?
    Staking Ratio: {supplyOverviewInfo.stakingRatio} -
    +
    : }
    diff --git a/packages/web/src/components/dashboard/tvl-chart-graph/TvlChartGraph.styles.ts b/packages/web/src/components/dashboard/tvl-chart-graph/TvlChartGraph.styles.ts index 649bda37d..8f0031d63 100644 --- a/packages/web/src/components/dashboard/tvl-chart-graph/TvlChartGraph.styles.ts +++ b/packages/web/src/components/dashboard/tvl-chart-graph/TvlChartGraph.styles.ts @@ -6,7 +6,7 @@ import mixins from "@styles/mixins"; export const TvlChartGraphWrapper = styled.div` ${mixins.flexbox("column", "flex-start", "flex-start")}; width: 100%; - background-color: ${({ theme }) => theme.color.background01}; + background-color: ${({ theme }) => theme.color.background15}; border-radius: 8px; padding: 0 0 12px 0; ${media.mobile} { diff --git a/packages/web/src/components/dashboard/tvl-chart-graph/TvlChartGraph.tsx b/packages/web/src/components/dashboard/tvl-chart-graph/TvlChartGraph.tsx index 0982b51d7..91fa75e21 100644 --- a/packages/web/src/components/dashboard/tvl-chart-graph/TvlChartGraph.tsx +++ b/packages/web/src/components/dashboard/tvl-chart-graph/TvlChartGraph.tsx @@ -1,8 +1,10 @@ import LineGraph from "@components/common/line-graph/LineGraph"; import { useTheme } from "@emotion/react"; import useComponentSize from "@hooks/common/use-component-size"; -import React, { useCallback } from "react"; +import React, { useCallback, useMemo } from "react"; import { TvlChartGraphWrapper } from "./TvlChartGraph.styles"; +import { useWindowSize } from "@hooks/common/use-window-size"; +import { DEVICE_TYPE } from "@styles/media"; export interface TvlChartGraphProps { datas: { @@ -22,6 +24,7 @@ const TvlChartGraph: React.FC = ({ }) => { const theme = useTheme(); const [componentRef, size] = useComponentSize(); + const { breakpoint } = useWindowSize(); const getDatas = useCallback(() => { return datas.map(data => ({ @@ -29,7 +32,12 @@ const TvlChartGraph: React.FC = ({ time: data.time, })); }, [datas]); - + + const countXAxis = useMemo(() => { + if (breakpoint !== DEVICE_TYPE.MOBILE) + return Math.floor((((size.width || 0) + 20) - 25) / 100); + return Math.floor((((size.width || 0) + 20) - 8) / 80); + }, [size.width, breakpoint]); return (
    @@ -38,15 +46,19 @@ const TvlChartGraph: React.FC = ({ cursor className="graph" width={size.width} - height={size.height} - color={theme.color.point} + height={size.height - 36} + color={theme.color.background04Hover} strokeWidth={1} datas={getDatas()} typeOfChart="tvl" + customData={{ + height: 36, + locationTooltip: 170, + }} />
    - {xAxisLabels.map((label, index) => ( + {xAxisLabels.slice(0, Math.min(countXAxis, 8)).map((label, index) => ( {label} ))}
    diff --git a/packages/web/src/components/dashboard/tvl-chart/TvlChart.styles.ts b/packages/web/src/components/dashboard/tvl-chart/TvlChart.styles.ts index adccc1830..712f9a76b 100644 --- a/packages/web/src/components/dashboard/tvl-chart/TvlChart.styles.ts +++ b/packages/web/src/components/dashboard/tvl-chart/TvlChart.styles.ts @@ -7,10 +7,10 @@ export const TvlChartWrapper = styled.div` background-color: ${({ theme }) => theme.color.background06}; border: 1px solid ${({ theme }) => theme.color.border02}; border-radius: 8px; - padding: 24px; + padding: 23px; gap: 31px; ${media.mobile} { - padding: 12px 24px 12px 12px; + padding: 11px 23px 11px 11px; gap: 16px; } `; @@ -23,3 +23,30 @@ export const ChartWrapper = styled.div` gap: 12px; } `; + +export const LoadingTVLChart = styled.div` + ${mixins.flexbox("row", "center", "center")} + width: 100%; + height: 246px; + background-color: ${({ theme }) => theme.color.background15}; + border-radius: 8px; + > div { + width: 60px; + height: 60px; + &::before { + width: 48px; + height: 48px; + } + &::after { + ${mixins.positionCenter()}; + content: ""; + border-radius: 50%; + width: 48px; + height: 48px; + background-color: ${({ theme }) => theme.color.background15}; + } + } + ${media.mobile} { + height: 224px; + } +`; \ No newline at end of file diff --git a/packages/web/src/components/dashboard/tvl-chart/TvlChart.tsx b/packages/web/src/components/dashboard/tvl-chart/TvlChart.tsx index 8f1bc6fee..6b1d05e67 100644 --- a/packages/web/src/components/dashboard/tvl-chart/TvlChart.tsx +++ b/packages/web/src/components/dashboard/tvl-chart/TvlChart.tsx @@ -6,12 +6,14 @@ import { import TvlChartGraph from "../tvl-chart-graph/TvlChartGraph"; import TvlChartPriceInfo from "../tvl-chart-price-info/TvlChartPriceInfo"; import TvlChartSelectTab from "../tvl-chart-select-tab/TvlChartSelectTab"; -import { ChartWrapper, TvlChartWrapper } from "./TvlChart.styles"; +import { ChartWrapper, LoadingTVLChart, TvlChartWrapper } from "./TvlChart.styles"; +import LoadingSpinner from "@components/common/loading-spinner/LoadingSpinner"; interface TvlChartItemProps { tvlChartType: CHART_TYPE; tvlPriceInfo: TvlPriceInfo; tvlChartInfo: TvlChartInfo; + loading: boolean; changeTvlChartType: (newType: string) => void; } @@ -20,6 +22,7 @@ const TvlChart: React.FC = ({ tvlPriceInfo, tvlChartInfo, changeTvlChartType, + loading, }) => { return ( @@ -29,10 +32,13 @@ const TvlChart: React.FC = ({ tvlChartType={tvlChartType} changeTvlChartType={changeTvlChartType} /> - + />} + {loading && + + } ); diff --git a/packages/web/src/components/dashboard/volume-chart-graph/VolumeChartGraph.styles.ts b/packages/web/src/components/dashboard/volume-chart-graph/VolumeChartGraph.styles.ts index c1886767d..1fb0c25cc 100644 --- a/packages/web/src/components/dashboard/volume-chart-graph/VolumeChartGraph.styles.ts +++ b/packages/web/src/components/dashboard/volume-chart-graph/VolumeChartGraph.styles.ts @@ -6,7 +6,7 @@ import mixins from "@styles/mixins"; export const VolumeChartGraphWrapper = styled.div` ${mixins.flexbox("column", "flex-start", "flex-start")}; width: 100%; - background-color: ${({ theme }) => theme.color.background01}; + background-color: ${({ theme }) => theme.color.background15}; border-radius: 8px; padding: 0 0 12px 0; ${media.mobile} { @@ -21,7 +21,6 @@ export const VolumeChartGraphWrapper = styled.div` } .graph { - cursor: pointer; height: 204px; border-bottom: 1px solid ${({ theme }) => theme.color.border02}; & svg { diff --git a/packages/web/src/components/dashboard/volume-chart-graph/VolumeChartGraph.tsx b/packages/web/src/components/dashboard/volume-chart-graph/VolumeChartGraph.tsx index 5bba4295e..06f54c2ad 100644 --- a/packages/web/src/components/dashboard/volume-chart-graph/VolumeChartGraph.tsx +++ b/packages/web/src/components/dashboard/volume-chart-graph/VolumeChartGraph.tsx @@ -1,20 +1,37 @@ import BarGraph from "@components/common/bar-graph/BarGraph"; import { useTheme } from "@emotion/react"; import useComponentSize from "@hooks/common/use-component-size"; -import React from "react"; +import React, { useMemo } from "react"; import { VolumeChartGraphWrapper } from "./VolumeChartGraph.styles"; +import { useWindowSize } from "@hooks/common/use-window-size"; +import { DEVICE_TYPE } from "@styles/media"; export interface VolumeChartGraphProps { datas: string[]; + times: string[]; xAxisLabels: string[]; } const VolumeChartGraph: React.FC = ({ datas, xAxisLabels, + times, }) => { const theme = useTheme(); const [componentRef, size] = useComponentSize(); + const { breakpoint } = useWindowSize(); + + const countXAxis = useMemo(() => { + if (breakpoint !== DEVICE_TYPE.MOBILE) + return Math.floor((((size.width || 0) + 20) - 25) / 100); + return Math.floor((((size.width || 0) + 20) - 8) / 80); + }, [size.width, breakpoint]); + + const minGap = useMemo(() => { + if (datas.length === 8) return 16; + if (datas.length === 31) return 6; + if (datas.length === 91) return 2; + }, [datas.length]); return ( @@ -22,16 +39,23 @@ const VolumeChartGraph: React.FC = ({
    - {xAxisLabels.map((label, index) => ( + {xAxisLabels.slice(0, Math.min(countXAxis, 8)).map((label, index) => ( {label} diff --git a/packages/web/src/components/dashboard/volume-chart/VolumeChart.stories.tsx b/packages/web/src/components/dashboard/volume-chart/VolumeChart.stories.tsx index 6bb39af1e..028980b11 100644 --- a/packages/web/src/components/dashboard/volume-chart/VolumeChart.stories.tsx +++ b/packages/web/src/components/dashboard/volume-chart/VolumeChart.stories.tsx @@ -21,5 +21,6 @@ Default.args = { volumeChartInfo: { datas: Array.from({ length: 24 }, (_, index) => `${index + 1}`), xAxisLabels: ["09:00", "12:00", "15:00", "18:00", "21:00", "24:00"], + times: [], }, }; diff --git a/packages/web/src/components/dashboard/volume-chart/VolumeChart.styles.ts b/packages/web/src/components/dashboard/volume-chart/VolumeChart.styles.ts index 5dfc28e3c..8dd1b457a 100644 --- a/packages/web/src/components/dashboard/volume-chart/VolumeChart.styles.ts +++ b/packages/web/src/components/dashboard/volume-chart/VolumeChart.styles.ts @@ -7,9 +7,9 @@ export const VolumeChartWrapper = styled.div` background-color: ${({ theme }) => theme.color.background06}; border: 1px solid ${({ theme }) => theme.color.border02}; border-radius: 8px; - padding: 24px; + padding: 23px; ${media.mobile} { - padding: 12px 12px 12px 12px; + padding: 11px 11px 11px 11px; } `; @@ -21,3 +21,31 @@ export const ChartWrapper = styled.div` gap: 12px; } `; + +export const LoadingVolumnChart = styled.div` + ${mixins.flexbox("row", "center", "center")} + width: 100%; + height: 246px; + background-color: ${({ theme }) => theme.color.background15}; + border-radius: 8px; + > div { + width: 60px; + height: 60px; + &::before { + width: 48px; + height: 48px; + background-color: ${({ theme }) => theme.color.background01}; + } + &::after { + ${mixins.positionCenter()}; + content: ""; + border-radius: 50%; + width: 48px; + height: 48px; + background-color: ${({ theme }) => theme.color.background15}; + } + } + ${media.mobile} { + height: 224px; + } +`; \ No newline at end of file diff --git a/packages/web/src/components/dashboard/volume-chart/VolumeChart.tsx b/packages/web/src/components/dashboard/volume-chart/VolumeChart.tsx index 3293d393c..d0a0da159 100644 --- a/packages/web/src/components/dashboard/volume-chart/VolumeChart.tsx +++ b/packages/web/src/components/dashboard/volume-chart/VolumeChart.tsx @@ -6,14 +6,16 @@ import { } from "@containers/volume-chart-container/VolumeChartContainer"; import VolumeChartPriceInfo from "../volume-chart-price-info/VolumeChartPriceInfo"; import VolumeChartSelectTab from "../volume-chart-select-tab/VolumeChartSelectTab"; -import { VolumeChartWrapper, ChartWrapper } from "./VolumeChart.styles"; +import { VolumeChartWrapper, ChartWrapper, LoadingVolumnChart } from "./VolumeChart.styles"; import VolumeChartGraph from "../volume-chart-graph/VolumeChartGraph"; +import LoadingSpinner from "@components/common/loading-spinner/LoadingSpinner"; interface VolumeChartItemProps { volumeChartType: CHART_TYPE; changeVolumeChartType: (newType: string) => void; volumePriceInfo: VolumePriceInfo; volumeChartInfo: VolumeChartInfo; + loading: boolean; } const VolumeChart: React.FC = ({ @@ -21,6 +23,7 @@ const VolumeChart: React.FC = ({ changeVolumeChartType, volumePriceInfo, volumeChartInfo, + loading, }) => ( @@ -29,10 +32,14 @@ const VolumeChart: React.FC = ({ volumeChartType={volumeChartType} changeVolumeChartType={changeVolumeChartType} /> - + times={volumeChartInfo.times} + />} + {loading && + + } ); diff --git a/packages/web/src/components/earn-add/earn-add-liquidity/EarnAddLiquidity.tsx b/packages/web/src/components/earn-add/earn-add-liquidity/EarnAddLiquidity.tsx index 27ca78f80..39efcef5a 100644 --- a/packages/web/src/components/earn-add/earn-add-liquidity/EarnAddLiquidity.tsx +++ b/packages/web/src/components/earn-add/earn-add-liquidity/EarnAddLiquidity.tsx @@ -43,7 +43,7 @@ interface EarnAddLiquidityProps { submit: () => void; isEarnAdd: boolean; connected: boolean; - slippage: number; + slippage: string; changeSlippage: (value: string) => void; handleClickOneStaking?: () => void; openModal: () => void; diff --git a/packages/web/src/components/earn/earn-my-positions-header/EarnMyPositionsHeader.tsx b/packages/web/src/components/earn/earn-my-positions-header/EarnMyPositionsHeader.tsx index 4eaa4f4f0..c7cd38f2e 100644 --- a/packages/web/src/components/earn/earn-my-positions-header/EarnMyPositionsHeader.tsx +++ b/packages/web/src/components/earn/earn-my-positions-header/EarnMyPositionsHeader.tsx @@ -4,19 +4,21 @@ import { PositionsWrapper } from "./EarnMyPositionsHeader.styles"; export interface EarnMyPositionsHeaderProps { connected: boolean; + isSwitchNetwork: boolean; moveEarnAdd: () => void; moveEarnStake: () => void; } const EarnMyPositionsHeader: React.FC = ({ connected, + isSwitchNetwork, moveEarnAdd, moveEarnStake, }) => { const disabledNewPosition = useMemo(() => { - return !connected; - }, [connected]); + return !connected || isSwitchNetwork; + }, [connected, isSwitchNetwork]); const onClickNewPosition = useCallback(() => { moveEarnAdd(); diff --git a/packages/web/src/components/earn/earn-my-positions-no-liquidity/EarnMyPositionNoLiquidity.tsx b/packages/web/src/components/earn/earn-my-positions-no-liquidity/EarnMyPositionNoLiquidity.tsx index 4360cbb32..f7c4ad1f4 100644 --- a/packages/web/src/components/earn/earn-my-positions-no-liquidity/EarnMyPositionNoLiquidity.tsx +++ b/packages/web/src/components/earn/earn-my-positions-no-liquidity/EarnMyPositionNoLiquidity.tsx @@ -19,7 +19,7 @@ const EarnMyPositionNoLiquidity: React.FC<

    Add liquidity to Gnoswap’s pools to earn trading fees on every swap. You - may also stake LP tokens to earn GNOS token rewards. + may also stake LP tokens to earn GNS token rewards. ); diff --git a/packages/web/src/components/earn/earn-my-positions/EarnMyPositions.tsx b/packages/web/src/components/earn/earn-my-positions/EarnMyPositions.tsx index 3e10d83e6..6de428b97 100644 --- a/packages/web/src/components/earn/earn-my-positions/EarnMyPositions.tsx +++ b/packages/web/src/components/earn/earn-my-positions/EarnMyPositions.tsx @@ -44,6 +44,7 @@ const EarnMyPositions: React.FC = ({ connected={connected} moveEarnAdd={moveEarnAdd} moveEarnStake={moveEarnStake} + isSwitchNetwork={isSwitchNetwork} /> = ({ @@ -99,20 +101,7 @@ const CreateProposalModal: React.FC = ({ } }; - useEffect(() => { - const closeModal = (e: MouseEvent) => { - if (modalRef.current && modalRef.current.contains(e.target as Node)) { - return; - } else { - e.stopPropagation(); - setIsShowCreateProposal(true); - } - }; - window.addEventListener("click", closeModal, true); - return () => { - window.removeEventListener("click", closeModal, true); - }; - }, [modalRef, setIsShowCreateProposal]); + useEscCloseModal(() => setIsShowCreateProposal(false)); useEffect(() => { handleResize(); @@ -181,151 +170,154 @@ const CreateProposalModal: React.FC = ({ }, [isDirty, isValid, errors]); return ( - - {}}> - -
    -
    -
    Create Proposal
    -
    setIsShowCreateProposal(false)} - > - -
    -
    - -
    - {ProposalOption.map((item, index) => ( -
    setType(ProposalOption[index])} - > - {ProposalOption[index]} -
    - ))} + <> + + {}}> + +
    +
    +
    Create Proposal
    +
    setIsShowCreateProposal(false)} + > + +
    - - - - - - {type === ProposalOption[1] && ( - + +
    + {ProposalOption.map((item, index) => ( +
    setType(ProposalOption[index])} + > + {ProposalOption[index]} +
    + ))} +
    +
    + + -
    + + {type === ProposalOption[1] && ( + -
    - token logo - {TOKEN.currency} +
    + +
    + token logo + {TOKEN.currency} +
    -
    -
    - )} - {type === ProposalOption[2] && ( - - {fields.map((item, index) => ( -
    -
    - + + )} + {type === ProposalOption[2] && ( + + {fields.map((item, index) => ( +
    +
    + - - + + +
    + handleClickFormFieldArray(index)} + > + {index === fields.length - 1 ? ( + + ) : ( + + )} + +
    + ))} +
    + )} + +
    +
    +
    + token logo + {TOKEN.currency}
    - handleClickFormFieldArray(index)} - > - {index === fields.length - 1 ? ( - - ) : ( - - )} - -
    - ))} - - )} - -
    -
    -
    - token logo - {TOKEN.currency}
    + {TOKEN.value}
    - {TOKEN.value} -
    -
    -
    -
    +
    + + + setIsShowProposalModal(false)}/> + ); }; diff --git a/packages/web/src/components/home/banner/Banner.styled.ts b/packages/web/src/components/home/banner/Banner.styled.ts index b5a2d8625..291c81638 100644 --- a/packages/web/src/components/home/banner/Banner.styled.ts +++ b/packages/web/src/components/home/banner/Banner.styled.ts @@ -61,11 +61,14 @@ export const BannerContent = styled.div` height: 20px; } svg * { - fill: ${({ theme }) => theme.color.text01}; + fill: ${({ theme }) => theme.color.border07}; } &:hover { + span { + color: #C3D2EA; + } svg * { - fill: ${({ theme }) => theme.color.text01}; + fill: #C3D2EA !important; } background-color: ${({ theme }) => theme.color.background04Hover}; } diff --git a/packages/web/src/components/home/banner/Banner.tsx b/packages/web/src/components/home/banner/Banner.tsx index 98c5f71ea..0dfdb20a5 100644 --- a/packages/web/src/components/home/banner/Banner.tsx +++ b/packages/web/src/components/home/banner/Banner.tsx @@ -17,7 +17,7 @@ const Banner: React.FC = () => { bgColor: "background04", fontType: "body11", padding: "16px 40px", - textColor: "text09", + textColor: "border07", }} className="banner-button" /> diff --git a/packages/web/src/components/home/card-list/CardList.tsx b/packages/web/src/components/home/card-list/CardList.tsx index a91024395..4fe9256b9 100644 --- a/packages/web/src/components/home/card-list/CardList.tsx +++ b/packages/web/src/components/home/card-list/CardList.tsx @@ -121,8 +121,8 @@ const CardListTokenItem: React.FC = ({ index, item, onCl }, [item.upDown]); const onClick = useCallback(() => { - onClickItem(item.token.path); - }, [onClickItem, item.token.path]); + onClickItem(item.token.symbol + `?tokenB=${item.token.path}&direction=EXACT_IN`); + }, [onClickItem, item.token.symbol]); return ( diff --git a/packages/web/src/components/home/gnoswap-brand/GnoswapBrand.styles.ts b/packages/web/src/components/home/gnoswap-brand/GnoswapBrand.styles.ts index 2479ccd0e..12acc1197 100644 --- a/packages/web/src/components/home/gnoswap-brand/GnoswapBrand.styles.ts +++ b/packages/web/src/components/home/gnoswap-brand/GnoswapBrand.styles.ts @@ -16,6 +16,7 @@ export const GnoswapBrandWrapper = styled.div` max-width: 328px; align-items: center; gap: 32px; + padding: 10px 0 0 0; } .sns { diff --git a/packages/web/src/components/home/highest-aprs-card-list/HighestAprsCardList.styles.ts b/packages/web/src/components/home/highest-aprs-card-list/HighestAprsCardList.styles.ts index 48183fe62..8bb122489 100644 --- a/packages/web/src/components/home/highest-aprs-card-list/HighestAprsCardList.styles.ts +++ b/packages/web/src/components/home/highest-aprs-card-list/HighestAprsCardList.styles.ts @@ -8,18 +8,26 @@ interface SkeletonStyleProps { tdWidth?: CSSProperties["width"]; } -export const HighestAprsCardListwrapper = styled.div` +interface Props { + loading: boolean; +} + +export const HighestAprsCardListwrapper = styled.div` ${mixins.flexbox("column", "flex-start", "center")} width: 100%; background-color: ${({ theme }) => theme.color.background06}; border: 1px solid ${({ theme }) => theme.color.border02}; box-shadow: 8px 8px 20px 0px rgba(0, 0, 0, 0.08); border-radius: 10px; - padding: 16px 0px 12px; - gap: 12px; + padding: ${({ loading }) => { + return loading ? "15px 0px 15px" : "15px 0px 11px"; + }}; + gap: ${({ loading }) => { + return loading ? "8px" : "12px"; + }}; ${media.mobile} { - padding: 16px 0px; + padding: 15px 0px; gap: 16px; } h2 { @@ -43,22 +51,9 @@ export const HighestAprsCardListwrapper = styled.div` export const SkeletonItem = styled.div` width: ${({ tdWidth }) => `${tdWidth}`}; height: 100%; - padding: 8px 24px; + padding: 4px 24px; ${mixins.flexbox("row", "center", "flex-start")}; -`; - -export const LoadingWrapper = styled.div` - ${mixins.flexbox("column", "flex-start", "flex-start")} - width: 100%; - background-color: ${({ theme }) => theme.color.background06}; - border: 1px solid ${({ theme }) => theme.color.border02}; - box-shadow: 8px 8px 20px 0px rgba(0, 0, 0, 0.08); - border-radius: 10px; - padding: 16px 0px 12px; - & > div:first-of-type { - padding: 0 24px 13px 24px; + &:first-of-type { + padding: 0px 24px 5px 24px; } - ${media.mobile} { - padding: 16px 0px; - } -`; \ No newline at end of file +`; diff --git a/packages/web/src/components/home/highest-aprs-card-list/HighestAprsCardList.tsx b/packages/web/src/components/home/highest-aprs-card-list/HighestAprsCardList.tsx index 697c5a922..042051dae 100644 --- a/packages/web/src/components/home/highest-aprs-card-list/HighestAprsCardList.tsx +++ b/packages/web/src/components/home/highest-aprs-card-list/HighestAprsCardList.tsx @@ -1,10 +1,16 @@ import React, { useMemo } from "react"; import CardList from "@components/home/card-list/CardList"; -import { HighestAprsCardListwrapper, LoadingWrapper, SkeletonItem } from "./HighestAprsCardList.styles"; +import { + HighestAprsCardListwrapper, + SkeletonItem, +} from "./HighestAprsCardList.styles"; import IconDiamond from "@components/common/icons/IconDiamond"; import { DEVICE_TYPE } from "@styles/media"; import { CardListPoolInfo } from "@models/common/card-list-item-info"; -import { SHAPE_TYPES, skeletonTrendingStyle } from "@constants/skeleton.constant"; +import { + SHAPE_TYPES, + skeletonTrendingStyle, +} from "@constants/skeleton.constant"; interface HighestAprsCardListProps { list: Array; @@ -23,29 +29,42 @@ const HighestAprsCardList: React.FC = ({ return device !== DEVICE_TYPE.MOBILE; }, [device]); - if (loading) { - return ( - - - - - - - - - - - - - ); - } - return visible ? ( - -

    - Highest APRs -

    - + + {loading ? ( + + + + ) : ( +
    +

    + Highest APRs +

    +
    + )} + {loading ? ( + <> + + + + + + + + + + + ) : ( + + )}
    ) : null; }; diff --git a/packages/web/src/components/home/home-swap/HomeSwap.stories.tsx b/packages/web/src/components/home/home-swap/HomeSwap.stories.tsx index e5045e708..2e681776e 100644 --- a/packages/web/src/components/home/home-swap/HomeSwap.stories.tsx +++ b/packages/web/src/components/home/home-swap/HomeSwap.stories.tsx @@ -47,7 +47,7 @@ Default.args = { tokenBUSD: 0, tokenBUSDStr: "0", direction: "EXACT_IN", - slippage: 0, + slippage: "0", }, swapNow: action("swapNow"), }; diff --git a/packages/web/src/components/home/home-swap/HomeSwap.styles.ts b/packages/web/src/components/home/home-swap/HomeSwap.styles.ts index 018999f68..b4c9985c1 100644 --- a/packages/web/src/components/home/home-swap/HomeSwap.styles.ts +++ b/packages/web/src/components/home/home-swap/HomeSwap.styles.ts @@ -36,11 +36,14 @@ export const wrapper = (theme: Theme) => css` width: 100%; height: 100%; position: relative; + .from, .to { ${mixins.flexbox("row", "center", "space-between")}; flex-wrap: wrap; - + &:focus-within { + border: 1px solid ${theme.color.border15}; + } width: 100%; padding: 9px 23px; @@ -56,11 +59,13 @@ export const wrapper = (theme: Theme) => css` } .token { - width: 112px; - height: 30px; cursor: default; + > div { + padding: 5px 12px 5px 6px; + height: 34px; + } span { - font-size: 15px; + font-size: 16px; line-height: 19px; } } @@ -77,12 +82,15 @@ export const wrapper = (theme: Theme) => css` line-height: 38px; color: ${theme.color.text01}; margin-right: 30px; + &::placeholder { + color: ${theme.color.text01}; + } } .price-text, .balance-text { ${fonts.p2}; - color: ${theme.color.text10}; + color: ${theme.color.text04}; } .token { diff --git a/packages/web/src/components/home/home-swap/HomeSwap.tsx b/packages/web/src/components/home/home-swap/HomeSwap.tsx index 37d5b5ca5..524b471f2 100644 --- a/packages/web/src/components/home/home-swap/HomeSwap.tsx +++ b/packages/web/src/components/home/home-swap/HomeSwap.tsx @@ -7,6 +7,7 @@ import { SwapTokenInfo } from "@models/swap/swap-token-info"; import { useWindowSize } from "@hooks/common/use-window-size"; interface HomeSwapProps { + changeTokenAAmount: (value: string) => void; swapTokenInfo: SwapTokenInfo; swapNow: () => void; onSubmitSwapValue: () => void; @@ -17,7 +18,7 @@ function isAmount(str: string) { return regex.test(str); } -const HomeSwap: React.FC = ({ swapTokenInfo, swapNow, onSubmitSwapValue }) => { +const HomeSwap: React.FC = ({ swapTokenInfo, swapNow, onSubmitSwapValue, changeTokenAAmount}) => { const { breakpoint } = useWindowSize(); const [fromAmount, setFromAmount] = useState("0"); const [toAmount, setToAmount] = useState("0"); @@ -27,7 +28,9 @@ const HomeSwap: React.FC = ({ swapTokenInfo, swapNow, onSubmitSwa const value = e.target.value; if (value !== "" && !isAmount(value)) return; - setFromAmount(value); + const temp = value.replace(/^0+(?=\d)|(\.\d*)$/g, "$1"); + setFromAmount(temp); + changeTokenAAmount(temp); // TODO // - mapT0AmountToT0Price // - mapT0AmpuntT1Amount @@ -41,7 +44,7 @@ const HomeSwap: React.FC = ({ swapTokenInfo, swapNow, onSubmitSwa const value = e.target.value; if (value !== "" && !isAmount(value)) return; - setToAmount(value); + setToAmount(value.replace(/^0+(?=\d)|(\.\d*)$/g, "$1")); // TODO // - mapT1AmountToT1Price // - mapT1AmpuntT0Amount diff --git a/packages/web/src/components/home/recently-added-card-list/RecentlyAddedCardList.styles.ts b/packages/web/src/components/home/recently-added-card-list/RecentlyAddedCardList.styles.ts index d585e7fc7..e347a47ab 100644 --- a/packages/web/src/components/home/recently-added-card-list/RecentlyAddedCardList.styles.ts +++ b/packages/web/src/components/home/recently-added-card-list/RecentlyAddedCardList.styles.ts @@ -7,18 +7,26 @@ interface SkeletonStyleProps { tdWidth?: CSSProperties["width"]; } -export const RecentlyAddedCardListwrapper = styled.div` +interface Props { + loading: boolean; +} + +export const RecentlyAddedCardListwrapper = styled.div` ${mixins.flexbox("column", "flex-start", "center")} width: 100%; background-color: ${({ theme }) => theme.color.background06}; border: 1px solid ${({ theme }) => theme.color.border02}; box-shadow: 8px 8px 20px 0px rgba(0, 0, 0, 0.08); border-radius: 10px; - padding: 16px 0px 12px; - gap: 12px; + padding: ${({ loading }) => { + return loading ? "15px 0px 15px" : "15px 0px 11px"; + }}; + gap: ${({ loading }) => { + return loading ? "8px" : "12px"; + }}; ${media.mobile} { - padding: 16px 0px; + padding: 15px 0px; gap: 16px; } h2 { @@ -42,23 +50,9 @@ export const RecentlyAddedCardListwrapper = styled.div` export const SkeletonItem = styled.div` width: ${({ tdWidth }) => `${tdWidth}`}; height: 100%; - padding: 8px 24px; + padding: 4px 24px; ${mixins.flexbox("row", "center", "flex-start")}; -`; - - -export const LoadingWrapper = styled.div` - ${mixins.flexbox("column", "flex-start", "flex-start")} - width: 100%; - background-color: ${({ theme }) => theme.color.background06}; - border: 1px solid ${({ theme }) => theme.color.border02}; - box-shadow: 8px 8px 20px 0px rgba(0, 0, 0, 0.08); - border-radius: 10px; - padding: 16px 0px 12px; - & > div:first-of-type { - padding: 0 24px 13px 24px; + &:first-of-type { + padding: 0px 24px 5px 24px; } - ${media.mobile} { - padding: 16px 0px; - } -`; \ No newline at end of file +`; diff --git a/packages/web/src/components/home/recently-added-card-list/RecentlyAddedCardList.tsx b/packages/web/src/components/home/recently-added-card-list/RecentlyAddedCardList.tsx index 286b06fbd..31e7185d4 100644 --- a/packages/web/src/components/home/recently-added-card-list/RecentlyAddedCardList.tsx +++ b/packages/web/src/components/home/recently-added-card-list/RecentlyAddedCardList.tsx @@ -1,10 +1,16 @@ import React, { useMemo } from "react"; import CardList from "@components/home/card-list/CardList"; -import { LoadingWrapper, RecentlyAddedCardListwrapper, SkeletonItem } from "./RecentlyAddedCardList.styles"; +import { + RecentlyAddedCardListwrapper, + SkeletonItem, +} from "./RecentlyAddedCardList.styles"; import IconClock from "@components/common/icons/IconClock"; import { DEVICE_TYPE } from "@styles/media"; import { CardListTokenInfo } from "@models/common/card-list-item-info"; -import { SHAPE_TYPES, skeletonTrendingStyle } from "@constants/skeleton.constant"; +import { + SHAPE_TYPES, + skeletonTrendingStyle, +} from "@constants/skeleton.constant"; interface RecentlyAddedCardListProps { list: Array; device: DEVICE_TYPE; @@ -22,28 +28,42 @@ const RecentlyAddedCardList: React.FC = ({ return device !== DEVICE_TYPE.MOBILE; }, [device]); - if (loading) { - return ( - - - - - - - - - - - - - ); - } return visible ? ( - -

    - New Listings -

    - + + {loading ? ( + + + + ) : ( +
    +

    + New Listings +

    +
    + )} + {loading ? ( + <> + + + + + + + + + + + ) : ( + + )}
    ) : null; }; diff --git a/packages/web/src/components/home/token-info/TokenInfo.tsx b/packages/web/src/components/home/token-info/TokenInfo.tsx index 8a6b31850..d89a86a42 100644 --- a/packages/web/src/components/home/token-info/TokenInfo.tsx +++ b/packages/web/src/components/home/token-info/TokenInfo.tsx @@ -48,7 +48,9 @@ const TokenInfo: React.FC = ({ item, idx }) => { } = item; const onClickItem = (symbol: string) => { - location.href = "/tokens/" + symbol; + console.log(symbol); + + location.href = "/tokens/" + "FOO" + "?tokenB=gno.land/r/foo&direction=EXACT_IN"; }; return ( diff --git a/packages/web/src/components/home/token-list-header/TokenListHeader.styles.ts b/packages/web/src/components/home/token-list-header/TokenListHeader.styles.ts index a36849747..32a212076 100644 --- a/packages/web/src/components/home/token-list-header/TokenListHeader.styles.ts +++ b/packages/web/src/components/home/token-list-header/TokenListHeader.styles.ts @@ -41,16 +41,20 @@ export const TokenTitleWrapper = styled.div` } } - .search-icon { - width: 24px; - height: 24px; - * { - fill: ${({ theme }) => theme.color.icon08}; + .icon-wrap { + height: 100%; + .search-icon { + width: 24px; + height: 24px; + margin-right: 13px; + * { + fill: ${({ theme }) => theme.color.icon08}; + } } - } - .search-icon:hover { - * { - fill: ${({ theme }) => theme.color.icon02}; + .search-icon:hover { + * { + fill: ${({ theme }) => theme.color.icon02}; + } } } `; diff --git a/packages/web/src/components/home/token-list-header/TokenListHeader.tsx b/packages/web/src/components/home/token-list-header/TokenListHeader.tsx index 535864b9e..037e8abbe 100644 --- a/packages/web/src/components/home/token-list-header/TokenListHeader.tsx +++ b/packages/web/src/components/home/token-list-header/TokenListHeader.tsx @@ -17,6 +17,7 @@ interface TokenListHeaderProps { breakpoint: DEVICE_TYPE; searchIcon: boolean; onTogleSearch: () => void; + searchRef: React.RefObject; } const TokenListHeader: React.FC = ({ @@ -27,6 +28,7 @@ const TokenListHeader: React.FC = ({ breakpoint, searchIcon, onTogleSearch, + searchRef, }) => ( @@ -38,13 +40,15 @@ const TokenListHeader: React.FC = ({ onClick={changeTokenType} /> ) : searchIcon ? ( - +
    }> + +
    ) : (
    diff --git a/packages/web/src/components/home/token-list/TokenList.tsx b/packages/web/src/components/home/token-list/TokenList.tsx index 70d870bed..a54e56ced 100644 --- a/packages/web/src/components/home/token-list/TokenList.tsx +++ b/packages/web/src/components/home/token-list/TokenList.tsx @@ -30,6 +30,7 @@ interface TokenItem { breakpoint: DEVICE_TYPE; searchIcon: boolean; onTogleSearch: () => void; + searchRef: React.RefObject; } const TokenList: React.FC = ({ @@ -49,6 +50,7 @@ const TokenList: React.FC = ({ breakpoint, searchIcon, onTogleSearch, + searchRef, }) => { return (
    @@ -60,6 +62,7 @@ const TokenList: React.FC = ({ breakpoint={breakpoint} searchIcon={searchIcon} onTogleSearch={onTogleSearch} + searchRef={searchRef} /> ` ${mixins.flexbox("column", "flex-start", "flex-start")} width: 100%; background-color: ${({ theme }) => theme.color.background06}; border: 1px solid ${({ theme }) => theme.color.border02}; box-shadow: 8px 8px 20px 0px rgba(0, 0, 0, 0.08); border-radius: 10px; - padding: 16px 0px 12px; - gap: 12px; + padding: ${({ loading }) => { + return loading ? "15px 0px 15px" : "15px 0px 11px"; + }}; + gap: ${({ loading }) => { + return loading ? "8px" : "12px"; + }}; ${media.mobile} { - padding: 16px 0px; + padding: 15px 0px; gap: 16px; } h2 { @@ -42,23 +49,9 @@ export const TrendingCardListwrapper = styled.div` export const SkeletonItem = styled.div` width: ${({ tdWidth }) => `${tdWidth}`}; height: 100%; - padding: 8px 24px; + padding: 4px 24px; ${mixins.flexbox("row", "center", "flex-start")}; -`; - - -export const LoadingWrapper = styled.div` - ${mixins.flexbox("column", "flex-start", "flex-start")} - width: 100%; - background-color: ${({ theme }) => theme.color.background06}; - border: 1px solid ${({ theme }) => theme.color.border02}; - box-shadow: 8px 8px 20px 0px rgba(0, 0, 0, 0.08); - border-radius: 10px; - padding: 16px 0px 12px; - & > div:first-of-type { - padding: 0 24px 13px 24px; + &:first-of-type { + padding: 0px 24px 5px 24px; } - ${media.mobile} { - padding: 16px 0px; - } -`; \ No newline at end of file +`; diff --git a/packages/web/src/components/home/trending-card-list/TrendingCardList.tsx b/packages/web/src/components/home/trending-card-list/TrendingCardList.tsx index 40a3bcca5..8876da2e7 100644 --- a/packages/web/src/components/home/trending-card-list/TrendingCardList.tsx +++ b/packages/web/src/components/home/trending-card-list/TrendingCardList.tsx @@ -1,10 +1,16 @@ import React, { useMemo } from "react"; import CardList from "@components/home/card-list/CardList"; -import { LoadingWrapper, SkeletonItem, TrendingCardListwrapper } from "./TrendingCardList.styles"; +import { + SkeletonItem, + TrendingCardListwrapper, +} from "./TrendingCardList.styles"; import IconFlame from "@components/common/icons/IconFlame"; import { DEVICE_TYPE } from "@styles/media"; import { CardListTokenInfo } from "@models/common/card-list-item-info"; -import { SHAPE_TYPES, skeletonTrendingStyle } from "@constants/skeleton.constant"; +import { + SHAPE_TYPES, + skeletonTrendingStyle, +} from "@constants/skeleton.constant"; interface TrendingCardListProps { list: Array; device: DEVICE_TYPE; @@ -18,35 +24,48 @@ const TrendingCardList: React.FC = ({ onClickItem, loading, }) => { + const visible = useMemo(() => { return device !== DEVICE_TYPE.MOBILE; }, [device]); - - if (loading) { - return ( - - - - - - - - - - - - - ); - } - return visible ? ( - -

    - - Trending -

    - + + {loading ? ( + + + + ) : ( +
    +

    + + Trending +

    +
    + )} + {loading ? ( + <> + + + + + + + + + + + ) : ( + + )}
    ) : null; }; diff --git a/packages/web/src/components/pool/learn-more-modal/LearnMoreModal.styles.ts b/packages/web/src/components/pool/learn-more-modal/LearnMoreModal.styles.ts index 24d28b1bf..5a1490986 100644 --- a/packages/web/src/components/pool/learn-more-modal/LearnMoreModal.styles.ts +++ b/packages/web/src/components/pool/learn-more-modal/LearnMoreModal.styles.ts @@ -12,11 +12,12 @@ export const LearnMoreModalBackground = styled.div` right: 0px; width: 100%; height: 100lvh; - background: rgba(10, 14, 23, 0.7); - z-index: ${Z_INDEX.modalOverlay}; + z-index: ${Z_INDEX.modal}; + pointer-events: none; `; export const LearnMoreModalWrapper = styled.div` + pointer-events: initial; ${mixins.flexbox("column", "flex-start", "flex-start")}; position: absolute; overflow: hidden; diff --git a/packages/web/src/components/pool/learn-more-modal/LearnMoreModal.tsx b/packages/web/src/components/pool/learn-more-modal/LearnMoreModal.tsx index 0110879a8..1a3ed902d 100644 --- a/packages/web/src/components/pool/learn-more-modal/LearnMoreModal.tsx +++ b/packages/web/src/components/pool/learn-more-modal/LearnMoreModal.tsx @@ -1,6 +1,8 @@ import Button from "@components/common/button/Button"; import IconStrokeArrowLeft from "@components/common/icons/IconStrokeArrowLeft"; import IconStrokeArrowRight from "@components/common/icons/IconStrokeArrowRight"; +import { Overlay } from "@components/common/modal/Modal.styles"; +import useEscCloseModal from "@hooks/common/use-esc-close-modal"; import { useWindowSize } from "@hooks/common/use-window-size"; import { ThemeState } from "@states/index"; import { DEVICE_TYPE } from "@styles/media"; @@ -107,20 +109,7 @@ const LearnMoreModal: React.FC = ({ setIsShowLearnMoreModal }) => { } }; - useEffect(() => { - const closeModal = (e: MouseEvent) => { - if (modalRef.current && modalRef.current.contains(e.target as Node)) { - return; - } else { - e.stopPropagation(); - setIsShowLearnMoreModal(false); - } - }; - window.addEventListener("click", closeModal, true); - return () => { - window.removeEventListener("click", closeModal, true); - }; - }, [modalRef, setIsShowLearnMoreModal]); + useEscCloseModal(() => setIsShowLearnMoreModal(false)); useEffect(() => { handleResize(); @@ -131,65 +120,68 @@ const LearnMoreModal: React.FC = ({ setIsShowLearnMoreModal }) => { }, [modalRef]); return ( - - - - {LEARN_MORE_DATA.map((_, i) => ( -
    = i ? "active-progress" : ""}`} - >
    - ))} -
    - - e.preventDefault()}> - <h3>{LEARN_MORE_DATA[index].title}</h3> - <div>{LEARN_MORE_DATA[index].description}</div> - - - e.preventDefault()} - onMouseDown={e => e.preventDefault()} - draggable="false" - src={ - themeKey === "dark" - ? LEARN_MORE_DATA[index].darkImageUrl - : LEARN_MORE_DATA[index].lightImageURL - } - alt="logo img" - /> - - setIndex( - index < LEARN_MORE_DATA.length - 1 - ? index + 1 - : LEARN_MORE_DATA.length - 1, - ) - } - className={`slide-icon right-icon ${ - index < LEARN_MORE_DATA.length - 1 ? "active-icon" : "" - }`} - /> - setIndex(index > 0 ? index - 1 : 0)} - className={`slide-icon left-icon ${ - index > 0 ? "active-icon" : "" - }`} - /> - - -
    - - )} -
    - - +
    +
    + + )} +
    + + + + ); }; @@ -228,7 +221,7 @@ const ConfirmSwapResult: React.FC = ({
    Waiting for Confirmation - Swapping 0.1 GNOS for 0.12 GNOT + Swapping 0.1 GNS for 0.12 GNOT
    Confirm this transaction in your wallet @@ -263,7 +256,6 @@ const ConfirmSwapResult: React.FC = ({ text="Close" style={{ fullWidth: true, - height: 57, fontType: "body7", hierarchy: ButtonHierarchy.Primary, }} diff --git a/packages/web/src/components/swap/setting-menu-modal/SettingMenuModal.stories.tsx b/packages/web/src/components/swap/setting-menu-modal/SettingMenuModal.stories.tsx index 901f8c6da..2b5e11cbc 100644 --- a/packages/web/src/components/swap/setting-menu-modal/SettingMenuModal.stories.tsx +++ b/packages/web/src/components/swap/setting-menu-modal/SettingMenuModal.stories.tsx @@ -19,7 +19,7 @@ const Template: ComponentStory = args => ( export const Default = Template.bind({}); Default.args = { - slippage: 0, + slippage: "0", changeSlippage: action("changeSlippage"), close: action("close"), }; diff --git a/packages/web/src/components/swap/setting-menu-modal/SettingMenuModal.styles.ts b/packages/web/src/components/swap/setting-menu-modal/SettingMenuModal.styles.ts index ad1b0b5fb..a64ab827f 100644 --- a/packages/web/src/components/swap/setting-menu-modal/SettingMenuModal.styles.ts +++ b/packages/web/src/components/swap/setting-menu-modal/SettingMenuModal.styles.ts @@ -8,7 +8,7 @@ export const SettingMenuModalWrapper = styled.div` z-index: ${Z_INDEX.modal}; position: absolute; width: 236px; - padding: 16px 0px; + padding: 15px 0px; gap: 16px; border-radius: 8px; background: ${({ theme }) => theme.color.background06}; @@ -20,7 +20,7 @@ export const SettingMenuModalWrapper = styled.div` .modal-body { ${mixins.flexbox("column", "flex-start", "flex-start")}; width: 100%; - padding: 0px 16px; + padding: 0px 15px; gap: 16px; .modal-header { @@ -62,7 +62,6 @@ export const SettingMenuModalWrapper = styled.div` ${mixins.flexbox("row", "center", "center")}; width: 16px; height: 16px; - cursor: pointer; .info-icon { * { fill: ${({ theme }) => theme.color.icon03}; @@ -85,11 +84,17 @@ export const SettingMenuModalWrapper = styled.div` color: ${({ theme }) => theme.color.text03}; border: 1px solid ${({ theme }) => theme.color.border02}; background: ${({ theme }) => theme.color.background01}; + &:focus-within { + border: 1px solid ${({ theme }) => theme.color.border15}; + } } .amount-text { ${mixins.flexbox("row", "center", "flex-end")}; text-align: right; width: 100%; + &::placeholder { + color: ${({ theme }) => theme.color.text03}; + } } } } @@ -104,3 +109,15 @@ export const ModalTooltipWrap = styled.div` ${mixins.flexbox("column", "flex-start", "flex-start")}; } `; + +export const Overlay = styled.div` + position: fixed; + top: 0px; + bottom: 0px; + left: 0px; + right: 0px; + width: 100%; + height: 100%; + overflow: hidden; + z-index: ${Z_INDEX.modalOverlay}; +`; diff --git a/packages/web/src/components/swap/setting-menu-modal/SettingMenuModal.tsx b/packages/web/src/components/swap/setting-menu-modal/SettingMenuModal.tsx index 88bf81589..eb16cd83f 100644 --- a/packages/web/src/components/swap/setting-menu-modal/SettingMenuModal.tsx +++ b/packages/web/src/components/swap/setting-menu-modal/SettingMenuModal.tsx @@ -2,15 +2,18 @@ import Button, { ButtonHierarchy } from "@components/common/button/Button"; import IconClose from "@components/common/icons/IconCancel"; import IconInfo from "@components/common/icons/IconInfo"; import Tooltip from "@components/common/tooltip/Tooltip"; -import React, { useCallback, useRef } from "react"; +import React, { useCallback, useRef, useState } from "react"; import { ModalTooltipWrap, + Overlay, SettingMenuModalWrapper, } from "./SettingMenuModal.styles"; -import useModalCloseEvent from "@hooks/common/use-modal-close-event"; +import useEscCloseModal from "@hooks/common/use-esc-close-modal"; +import { isAmount } from "@common/utils/data-check-util"; +import { DEFAULT_SLIPPAGE } from "@constants/option.constant"; interface SettingMenuModalProps { - slippage: number; + slippage: string; changeSlippage: (value: string) => void; close: () => void; className?: string; @@ -22,9 +25,31 @@ const SettingMenuModal: React.FC = ({ close, className, }) => { + const [previos, setPrevios] = useState(slippage); const settingMenuRef = useRef(null); - useModalCloseEvent(settingMenuRef, close); + const inputRef = useRef(null); + const buttonRef = useRef(null); + const wrapperInputRef = useRef(null); + const closeRef = useRef(null); + + const handleClose = useCallback(() => { + changeSlippage(previos); + close(); + }, [close, changeSlippage, previos]); + useEscCloseModal(handleClose); + const handleCloseExit = useCallback(() => { + if (inputRef && inputRef.current) { + if (Number(inputRef.current.value) > 30) { + changeSlippage("30"); + } else { + changeSlippage(inputRef.current.value); + } + } + close(); + }, [close, changeSlippage, previos]); + useEscCloseModal(handleClose); + const TooltipFloatingContent = (
    @@ -39,54 +64,107 @@ const SettingMenuModal: React.FC = ({ const onChangeSlippage = useCallback((event: React.ChangeEvent) => { const value = event.target.value; - changeSlippage(value); + if (value !== "" && !isAmount(value)) return; + if (/^\d{0,10}(\.\d{0,2})?$/.test(value)) { + changeSlippage(value.replace(/^0+(?=\d)|(\.\d*)$/g, "$1")); + } }, [changeSlippage]); const onClickReset = useCallback(() => { - changeSlippage("0.5"); + changeSlippage(DEFAULT_SLIPPAGE); }, [changeSlippage]); + + const handleClickWrapper = useCallback((event: React.MouseEvent) => { + if (buttonRef && buttonRef.current && buttonRef.current.contains(event.target as Node)) { + setPrevios(DEFAULT_SLIPPAGE); + changeSlippage(DEFAULT_SLIPPAGE); + return; + } + if (closeRef && closeRef.current && closeRef.current.contains(event.target as Node)) { + changeSlippage(previos); + return; + } + if (inputRef && inputRef.current && !inputRef.current.contains(event.target as Node)) { + const value = inputRef.current.value; + if (value === "") { + changeSlippage("0"); + setPrevios("0"); + } else if (Number(value) > 30) { + changeSlippage("30"); + setPrevios("30"); + } else { + setPrevios(Number(value).toString()); + changeSlippage(Number(value).toString()); + } + } + }, [changeSlippage, setPrevios, previos]); + const handleKeyDown = useCallback((event: React.KeyboardEvent) => { + if (event.keyCode === 13) { + const value = event.currentTarget.value; + if (inputRef && inputRef.current) { + inputRef.current.blur(); + } + if (value === "") { + changeSlippage("0"); + setPrevios("0"); + } else if (Number(value) > 30) { + changeSlippage("30"); + setPrevios("30"); + } else { + setPrevios(Number(value).toString()); + changeSlippage(Number(value).toString()); + } + } + }, [changeSlippage, setPrevios]); + return ( - -
    -
    - Settings -
    - -
    -
    -
    - Slippage tolerance - -
    - + <> + +
    +
    + Settings +
    +
    - -
    -
    -
    -
    - + + + ); }; diff --git a/packages/web/src/components/swap/swap-button-tooltip/SwapButtonTooltip.styles.ts b/packages/web/src/components/swap/swap-button-tooltip/SwapButtonTooltip.styles.ts index 245682fb7..36fd07470 100644 --- a/packages/web/src/components/swap/swap-button-tooltip/SwapButtonTooltip.styles.ts +++ b/packages/web/src/components/swap/swap-button-tooltip/SwapButtonTooltip.styles.ts @@ -22,12 +22,11 @@ export const SwapButtonTooltipWrap = styled.div` export const IconWrap = styled.div` ${mixins.flexbox("row", "center", "flex-start")}; - cursor: pointer; .icon-info { width: 16px; height: 16px; * { - fill: ${({ theme }) => theme.color.background02}; + fill: ${({ theme }) => theme.color.icon03}; } } `; diff --git a/packages/web/src/components/swap/swap-card-auto-router/SwapCardAutoRouter.styles.ts b/packages/web/src/components/swap/swap-card-auto-router/SwapCardAutoRouter.styles.ts index 6c6dcf56a..d78cc0fac 100644 --- a/packages/web/src/components/swap/swap-card-auto-router/SwapCardAutoRouter.styles.ts +++ b/packages/web/src/components/swap/swap-card-auto-router/SwapCardAutoRouter.styles.ts @@ -32,7 +32,7 @@ export const AutoRouterWrapper = styled.div` ${mixins.flexbox("row", "center", "flex-start")}; height: 28px; padding: 0px 4px 0px 4px; - gap: 2px; + gap: 4px; border-radius: 4px; border: 1px solid ${({ theme }) => theme.color.border02}; background: ${({ theme }) => theme.color.background02}; @@ -44,13 +44,13 @@ export const AutoRouterWrapper = styled.div` gap: 4px; border-radius: 4px; ${fonts.p3}; - color: ${({ theme }) => theme.color.text03}; + color: ${({ theme }) => theme.color.text10}; border: 1px solid ${({ theme }) => theme.color.border08}; background: ${({ theme }) => theme.color.background05}; } span { ${fonts.p3}; - color: ${({ theme }) => theme.color.text03}; + color: ${({ theme }) => theme.color.text10}; } } @@ -58,13 +58,13 @@ export const AutoRouterWrapper = styled.div` ${mixins.flexbox("row", "center", "center")}; height: 28px; padding: 0px 6px; - gap: 2px; + gap: 4px; border-radius: 4px; border: 1px solid ${({ theme }) => theme.color.border02}; background: ${({ theme }) => theme.color.background02}; h1 { ${fonts.p3}; - color: ${({ theme }) => theme.color.text03}; + color: ${({ theme }) => theme.color.text10}; } .coin-logo { ${mixins.flexbox("row", "flex-start", "flex-start")}; diff --git a/packages/web/src/components/swap/swap-card-content-detail/SwapCardContentDetail.styles.ts b/packages/web/src/components/swap/swap-card-content-detail/SwapCardContentDetail.styles.ts index f577d474a..602a5c98e 100644 --- a/packages/web/src/components/swap/swap-card-content-detail/SwapCardContentDetail.styles.ts +++ b/packages/web/src/components/swap/swap-card-content-detail/SwapCardContentDetail.styles.ts @@ -13,7 +13,7 @@ export const DetailWrapper = styled.div` border-radius: ${({ opened }) => { return opened ? "8px 8px 0px 0px" : "8px"; }}; - background: ${({ theme }) => theme.color.background01}; + background: ${({ theme }) => theme.color.background20}; border: 1px solid ${({ theme }) => theme.color.border02}; .exchange-section { @@ -25,12 +25,14 @@ export const DetailWrapper = styled.div` .loading-change { ${mixins.flexbox("row", "center", "flex-start")}; gap: 8px; + color: ${({ theme }) => theme.color.text10}; > div { width: 16px; height: 16px; &::before { width: 12px; height: 12px; + background-color: ${({ theme }) => theme.color.background01}; } } } @@ -52,6 +54,7 @@ export const DetailWrapper = styled.div` } .swap-rate { cursor: pointer; + color: ${({ theme }) => theme.color.text10}; } .exchange-price { color: ${({ theme }) => theme.color.text04}; @@ -60,7 +63,7 @@ export const DetailWrapper = styled.div` width: 16px; height: 16px; * { - fill: ${({ theme }) => theme.color.icon05}; + fill: ${({ theme }) => theme.color.icon03}; } } } @@ -91,7 +94,7 @@ export const FeelWrapper = styled.div` border-radius: ${({ opened }) => { return opened ? "0px 0px 8px 8px" : "8px"; }}; - background: ${({ theme }) => theme.color.background01}; + background: ${({ theme }) => theme.color.background20}; border-left: 1px solid ${({ theme }) => theme.color.border02}; border-right: 1px solid ${({ theme }) => theme.color.border02}; border-bottom: 1px solid ${({ theme }) => theme.color.border02}; @@ -115,5 +118,5 @@ export const SwapDivider = styled.div` width: 100%; height: 1px; align-self: stretch; - background: ${({ theme }) => theme.color.border02}; + background: ${({ theme }) => theme.color.background20}; `; diff --git a/packages/web/src/components/swap/swap-card-content-detail/SwapCardContentDetail.tsx b/packages/web/src/components/swap/swap-card-content-detail/SwapCardContentDetail.tsx index 694ac4c7c..7ed4572de 100644 --- a/packages/web/src/components/swap/swap-card-content-detail/SwapCardContentDetail.tsx +++ b/packages/web/src/components/swap/swap-card-content-detail/SwapCardContentDetail.tsx @@ -63,7 +63,7 @@ const SwapCardContentDetail: React.FC = ({ const handleSwapRate = useCallback(() => { setSwapRateAction((prev) => (prev === "ATOB" ? "BTOA" : "ATOB")); }, [swapRateAction]); - + return ( <> @@ -82,7 +82,7 @@ const SwapCardContentDetail: React.FC = ({ )} {isLoading && (
    - Fetching Best Price.. + Fetching Best Price...
    )}
    diff --git a/packages/web/src/components/swap/swap-card-content/SwapCardContent.stories.tsx b/packages/web/src/components/swap/swap-card-content/SwapCardContent.stories.tsx index 8628a5851..335db64d5 100644 --- a/packages/web/src/components/swap/swap-card-content/SwapCardContent.stories.tsx +++ b/packages/web/src/components/swap/swap-card-content/SwapCardContent.stories.tsx @@ -37,7 +37,7 @@ const swapTokenInfo: SwapTokenInfo = { tokenBUSD: 0, tokenBUSDStr: "0", direction: "EXACT_IN", - slippage: 10 + slippage: "10" }; export default { diff --git a/packages/web/src/components/swap/swap-card-content/SwapCardContent.styles.ts b/packages/web/src/components/swap/swap-card-content/SwapCardContent.styles.ts index 84c673401..abe78dab7 100644 --- a/packages/web/src/components/swap/swap-card-content/SwapCardContent.styles.ts +++ b/packages/web/src/components/swap/swap-card-content/SwapCardContent.styles.ts @@ -13,14 +13,17 @@ export const ContentWrapper = styled.div` ${mixins.flexbox("column", "flex-start", "flex-start")}; position: relative; width: 100%; - padding: 16px 24px; + padding: 15px 23px; gap: 8px; align-self: stretch; border-radius: 8px; border: 1px solid ${({ theme }) => theme.color.border02}; background: ${({ theme }) => theme.color.background20}; + &:focus-within { + border: 1px solid ${({ theme }) => theme.color.border15}; + } ${media.mobile} { - padding: 12px; + padding: 11px; } } @@ -45,7 +48,16 @@ export const ContentWrapper = styled.div` .token-selector { display: block; - height: 32px; + height: 34px; + .selected-token { + padding: 5px 10px 5px 6px; + } + .not-selected-token { + padding: 5px 10px 5px 12px + } + .token-symbol { + height: 21px; + } } .amount-info { @@ -58,7 +70,7 @@ export const ContentWrapper = styled.div` ${media.mobile} { ${fonts.p2}; } - color: ${({ theme }) => theme.color.text10}; + color: ${({ theme }) => theme.color.text04}; } .balance-text-disabled { cursor: pointer; @@ -67,14 +79,17 @@ export const ContentWrapper = styled.div` .second-section { ${mixins.flexbox("column", "flex-start", "flex-start")}; - padding: 16px 24px; + padding: 15px 23px; gap: 8px; align-self: stretch; border-radius: 8px; background: ${({ theme }) => theme.color.background20}; border: 1px solid ${({ theme }) => theme.color.border02}; + &:focus-within { + border: 1px solid ${({ theme }) => theme.color.border15}; + } ${media.mobile} { - padding: 12px; + padding: 11px; } } .arrow { diff --git a/packages/web/src/components/swap/swap-card-content/SwapCardContent.tsx b/packages/web/src/components/swap/swap-card-content/SwapCardContent.tsx index a7584fdc5..59312ac2f 100644 --- a/packages/web/src/components/swap/swap-card-content/SwapCardContent.tsx +++ b/packages/web/src/components/swap/swap-card-content/SwapCardContent.tsx @@ -14,9 +14,9 @@ interface ContentProps { swapSummaryInfo: SwapSummaryInfo | null; swapRouteInfos: SwapRouteInfo[]; changeTokenA: (token: TokenModel) => void; - changeTokenAAmount: (value: string) => void; + changeTokenAAmount: (value: string, none?: boolean) => void; changeTokenB: (token: TokenModel) => void; - changeTokenBAmount: (value: string) => void; + changeTokenBAmount: (value: string, none?: boolean) => void; switchSwapDirection: () => void; connectedWallet: boolean; isLoading: boolean; @@ -41,6 +41,9 @@ const SwapCardContent: React.FC = ({ const onChangeTokenAAmount = useCallback( (e: React.ChangeEvent) => { const value = e.target.value; + if (value === "") { + changeTokenAAmount("", true); + } if (value !== "" && !isAmount(value)) return; changeTokenAAmount(value.replace(/^0+(?=\d)|(\.\d*)$/g, "$1")); }, @@ -50,6 +53,9 @@ const SwapCardContent: React.FC = ({ const onChangeTokenBAmount = useCallback( (e: React.ChangeEvent) => { const value = e.target.value; + if (value === "") { + changeTokenBAmount("", true); + } if (value !== "" && !isAmount(value)) return; changeTokenBAmount(value.replace(/^0+(?=\d)|(\.\d*)$/g, "$1")); }, diff --git a/packages/web/src/components/swap/swap-card-fee-info/SwapCardFeeInfo.styles.ts b/packages/web/src/components/swap/swap-card-fee-info/SwapCardFeeInfo.styles.ts index 60d363f94..e94c66967 100644 --- a/packages/web/src/components/swap/swap-card-fee-info/SwapCardFeeInfo.styles.ts +++ b/packages/web/src/components/swap/swap-card-fee-info/SwapCardFeeInfo.styles.ts @@ -21,9 +21,12 @@ export const FeeWrapper = styled.div` .gray-text { color: ${({ theme }) => theme.color.text04}; + &:last-of-type { + margin-left: 4px; + } } .white-text { - color: ${({ theme }) => theme.color.text03}; + color: ${({ theme }) => theme.color.text10}; } .received, .gas-fee, diff --git a/packages/web/src/components/swap/swap-card-header/SwapCardHeader.stories.tsx b/packages/web/src/components/swap/swap-card-header/SwapCardHeader.stories.tsx index 819765365..e63aea1ed 100644 --- a/packages/web/src/components/swap/swap-card-header/SwapCardHeader.stories.tsx +++ b/packages/web/src/components/swap/swap-card-header/SwapCardHeader.stories.tsx @@ -21,7 +21,7 @@ export const Default = Template.bind({}); Default.args = { copied: false, copyURL: action("copyURL"), - slippage: 0, + slippage: "0", changeSlippage: action("changeSlippage"), }; diff --git a/packages/web/src/components/swap/swap-card-header/SwapCardHeader.styles.ts b/packages/web/src/components/swap/swap-card-header/SwapCardHeader.styles.ts index caa5e12b1..f887f3c31 100644 --- a/packages/web/src/components/swap/swap-card-header/SwapCardHeader.styles.ts +++ b/packages/web/src/components/swap/swap-card-header/SwapCardHeader.styles.ts @@ -46,10 +46,11 @@ export const SettingMenuButton = styled.div` export const CopyTooltip = styled.div` ${mixins.flexbox("column", "center", "flex-start")}; position: absolute; + cursor: default; top: -65px; .box { ${mixins.flexbox("column", "flex-start", "flex-start")}; - width: 115px; + width: 155px; padding: 16px; gap: 8px; flex-shrink: 0; diff --git a/packages/web/src/components/swap/swap-card-header/SwapCardHeader.tsx b/packages/web/src/components/swap/swap-card-header/SwapCardHeader.tsx index 35b811fda..452eab1ef 100644 --- a/packages/web/src/components/swap/swap-card-header/SwapCardHeader.tsx +++ b/packages/web/src/components/swap/swap-card-header/SwapCardHeader.tsx @@ -11,7 +11,7 @@ import { interface SwapCardHeaderProps { copied: boolean; copyURL: () => void; - slippage: number; + slippage: string; changeSlippage: (value: string) => void; themeKey: "dark" | "light"; } @@ -45,7 +45,7 @@ const SwapCardHeader: React.FC = ({ {copied && (
    - URL Copied! + Swap URL Copied!
    diff --git a/packages/web/src/components/swap/swap-card/SwapCard.styles.ts b/packages/web/src/components/swap/swap-card/SwapCard.styles.ts index b0527155f..5c252fd12 100644 --- a/packages/web/src/components/swap/swap-card/SwapCard.styles.ts +++ b/packages/web/src/components/swap/swap-card/SwapCard.styles.ts @@ -5,7 +5,7 @@ import mixins from "@styles/mixins"; export const SwapCardWrapper = styled.div` ${mixins.flexbox("column", "center", "flex-start")}; width: 100%; - padding: 24px; + padding: 23px; gap: 16px; border-radius: 8px; @@ -14,8 +14,8 @@ export const SwapCardWrapper = styled.div` box-shadow: 8px 8px 20px 0px rgba(0, 0, 0, 0.08); ${media.mobile} { - padding: 16px; - gap: 8px; + padding: 15px; + gap: 12px; align-self: stretch; } @@ -26,6 +26,12 @@ export const SwapCardWrapper = styled.div` ${mixins.flexbox("row", "flex-start", "flex-start")}; position: relative; width: 100%; + button { + height: 57px; + ${media.mobile} { + height: 41px; + } + } } .tooltip { } diff --git a/packages/web/src/components/swap/swap-card/SwapCard.tsx b/packages/web/src/components/swap/swap-card/SwapCard.tsx index 850691337..b69804bd6 100644 --- a/packages/web/src/components/swap/swap-card/SwapCard.tsx +++ b/packages/web/src/components/swap/swap-card/SwapCard.tsx @@ -27,9 +27,9 @@ interface SwapCardProps { isLoading: boolean; changeTokenA: (token: TokenModel) => void; - changeTokenAAmount: (value: string) => void; + changeTokenAAmount: (value: string, none?: boolean) => void; changeTokenB: (token: TokenModel) => void; - changeTokenBAmount: (value: string) => void; + changeTokenBAmount: (value: string, none?: boolean) => void; changeSlippage: (value: string) => void; switchSwapDirection: () => void; @@ -143,7 +143,6 @@ const SwapButton: React.FC = ({ fullWidth: true, fontType: "body7" as FontsKey, hierarchy: ButtonHierarchy.Primary, - height: 56, }; if (!connectedWallet) { diff --git a/packages/web/src/components/swap/swap-liquidity/SwapLiquidity.styles.ts b/packages/web/src/components/swap/swap-liquidity/SwapLiquidity.styles.ts index 53fc6b098..81343ebc0 100644 --- a/packages/web/src/components/swap/swap-liquidity/SwapLiquidity.styles.ts +++ b/packages/web/src/components/swap/swap-liquidity/SwapLiquidity.styles.ts @@ -6,11 +6,11 @@ import mixins from "@styles/mixins"; export const SwapLiquidityWrapper = styled.div` ${mixins.flexbox("column", "flex-start", "flex-start")}; width: 100%; - padding: 24px 0px; - gap: 24px; + padding: 23px 0px; + gap: 16px; ${media.mobile} { - padding: 16px 0px; - gap: 16px; + padding: 15px 0px; + gap: 12px; align-self: stretch; } @@ -22,12 +22,12 @@ export const SwapLiquidityWrapper = styled.div` .box-header { ${mixins.flexbox("row", "center", "flex-start")}; width: 100%; - padding: 0px 24px; + padding: 0px 23px; gap: 8px; color: ${({ theme }) => theme.color.text02}; ${fonts.body7}; ${media.mobile} { - padding: 0px 16px; + padding: 0px 15px; } .coin-pair { @@ -88,6 +88,9 @@ export const SwapLiquidityWrapper = styled.div` color: ${({ theme }) => theme.color.text04}; ${media.mobile} { padding: 0px 16px; + .volume { + min-width: 83px; + } } .feetier { @@ -100,16 +103,21 @@ export const SwapLiquidityWrapper = styled.div` flex: 1 0 0; text-align: right; } + } .fee-info { ${mixins.flexbox("row", "center", "flex-start")}; width: 100%; - padding: 8px 24px; + padding: 7px 23px; gap: 8px; align-self: stretch; ${media.mobile} { - padding: 8px 16px; + padding: 7px 15px; + gap: 4px; + .volume { + min-width: 83px; + } } &:hover { background-color: ${({ theme }) => theme.color.background05Hover}; diff --git a/packages/web/src/components/token/best-pool-card-list/BestPoolCardList.styles.ts b/packages/web/src/components/token/best-pool-card-list/BestPoolCardList.styles.ts index b6e2aa7fc..6098113bb 100644 --- a/packages/web/src/components/token/best-pool-card-list/BestPoolCardList.styles.ts +++ b/packages/web/src/components/token/best-pool-card-list/BestPoolCardList.styles.ts @@ -79,3 +79,21 @@ export const wrapper = (theme: Theme) => css` } } `; + +export const loadingWrapper = (theme: Theme) => css` + ${mixins.flexbox("row", "flex-start", "center")} + width: 100%; + height: 196px; + background-color: ${theme.color.background01}; + border-radius: 8px; + padding-top: 53px; + > div { + width: 48px; + height: 48px; + &::before { + background-color: ${theme.color.background01}; + width: 38px; + height: 38px; + } + } +`; \ No newline at end of file diff --git a/packages/web/src/components/token/best-pool-card-list/BestPoolCardList.tsx b/packages/web/src/components/token/best-pool-card-list/BestPoolCardList.tsx index 2820c8518..a3c9042ae 100644 --- a/packages/web/src/components/token/best-pool-card-list/BestPoolCardList.tsx +++ b/packages/web/src/components/token/best-pool-card-list/BestPoolCardList.tsx @@ -2,17 +2,19 @@ import React from "react"; import DoubleLogo from "@components/common/double-logo/DoubleLogo"; import { type BestPool } from "@containers/best-pools-container/BestPoolsContainer"; import { tokenPairSymbolToOneCharacter } from "@utils/string-utils"; -import { wrapper } from "./BestPoolCardList.styles"; +import { loadingWrapper, wrapper } from "./BestPoolCardList.styles"; import Link from "next/link"; import { SwapFeeTierInfoMap } from "@constants/option.constant"; +import LoadingSpinner from "@components/common/loading-spinner/LoadingSpinner"; interface BestPoolCardListProps { list: BestPool[]; + loading: boolean; } const LIST_TITLE = ["Pair", "TVL", "APR"]; -const BestPoolCardList: React.FC = ({ list }) => { +const BestPoolCardList: React.FC = ({ list, loading }) => { return (
    @@ -22,7 +24,10 @@ const BestPoolCardList: React.FC = ({ list }) => { ))}
    -
      + {loading &&
      + +
      } + {!loading &&
        {list.map((info, idx) => (
      • @@ -42,7 +47,7 @@ const BestPoolCardList: React.FC = ({ list }) => {
      • ))} -
      +
    }
    ); }; diff --git a/packages/web/src/components/token/best-pools/BestPools.stories.tsx b/packages/web/src/components/token/best-pools/BestPools.stories.tsx index 1062b36a8..db90497d4 100644 --- a/packages/web/src/components/token/best-pools/BestPools.stories.tsx +++ b/packages/web/src/components/token/best-pools/BestPools.stories.tsx @@ -15,6 +15,6 @@ const Template: ComponentStory = args => ( export const Default = Template.bind({}); Default.args = { - titleSymbol: "GNOS", + titleSymbol: "GNS", cardList: bestPoolListInit, }; diff --git a/packages/web/src/components/token/best-pools/BestPools.styles.ts b/packages/web/src/components/token/best-pools/BestPools.styles.ts index 8f09ec4a5..5aa22337e 100644 --- a/packages/web/src/components/token/best-pools/BestPools.styles.ts +++ b/packages/web/src/components/token/best-pools/BestPools.styles.ts @@ -9,19 +9,19 @@ export const wrapper = (theme: Theme) => css` background-color: ${theme.color.background01}; border: 1px solid ${theme.color.border02}; border-radius: 8px; - padding: 24px 0px; + padding: 23px 0px; gap: 12px; h2 { ${fonts.body9}; color: ${theme.color.text01}; - padding: 0px 24px; + padding: 0px 23px; } @media (max-width: 1180px) { gap: 12px; - padding: 16px 0; + padding: 15px 0; h2 { - padding: 0 16px; + padding: 0 15px; } } ${media.mobile} { diff --git a/packages/web/src/components/token/best-pools/BestPools.tsx b/packages/web/src/components/token/best-pools/BestPools.tsx index 031187168..bb392aec9 100644 --- a/packages/web/src/components/token/best-pools/BestPools.tsx +++ b/packages/web/src/components/token/best-pools/BestPools.tsx @@ -6,13 +6,14 @@ import { wrapper } from "./BestPools.styles"; interface BestPoolsProps { titleSymbol: string; cardList: BestPool[]; + loading: boolean; } -const BestPools: React.FC = ({ titleSymbol, cardList }) => { +const BestPools: React.FC = ({ titleSymbol, cardList, loading }) => { return (

    {`Best Pools for ${titleSymbol}`}

    - +
    ); }; diff --git a/packages/web/src/components/token/gainer-and-loser/CardListCommonStyle.styles.ts b/packages/web/src/components/token/gainer-and-loser/CardListCommonStyle.styles.ts index fccc31855..715506b89 100644 --- a/packages/web/src/components/token/gainer-and-loser/CardListCommonStyle.styles.ts +++ b/packages/web/src/components/token/gainer-and-loser/CardListCommonStyle.styles.ts @@ -9,7 +9,7 @@ export const cardStyle = (theme: Theme) => css` color: ${theme.color.text02}; width: 100%; gap: 4px; - padding: 24px 0px; + padding: 23px 0px; &:first-of-type { border-bottom: 1px solid ${theme.color.border02}; } @@ -17,7 +17,7 @@ export const cardStyle = (theme: Theme) => css` ${fonts.body9}; color: ${theme.color.text01}; width: 100%; - padding: 0px 24px; + padding: 0px 23px; margin-bottom: 8px; ${media.mobile} { margin-bottom: 4px; @@ -37,7 +37,7 @@ export const cardStyle = (theme: Theme) => css` } width: 100%; height: 36px; - padding: 0px 24px; + padding: 0px 23px; cursor: pointer; transition: background-color 0.3s ease; &:hover { @@ -70,15 +70,33 @@ export const cardStyle = (theme: Theme) => css` } } @media (max-width: 1180px) { - padding: 16px 0; + padding: 15px 0; .card-title { - padding: 0 16px; + padding: 0 15px; } .card-wrap { - padding: 0 16px; + padding: 0 15px; } } ${media.mobile} { gap: 4px; } `; + +export const loadingWrapper = (theme: Theme) => css` + ${mixins.flexbox("row", "flex-start", "center")} + width: 100%; + height: 118px; + background-color: ${theme.color.background01}; + border-radius: 8px; + padding-top: 16px; + > div { + width: 48px; + height: 48px; + &::before { + background-color: ${theme.color.background01}; + width: 38px; + height: 38px; + } + } +`; diff --git a/packages/web/src/components/token/gainer-and-loser/GainerAndLoser.tsx b/packages/web/src/components/token/gainer-and-loser/GainerAndLoser.tsx index a06512e3d..38e76a14e 100644 --- a/packages/web/src/components/token/gainer-and-loser/GainerAndLoser.tsx +++ b/packages/web/src/components/token/gainer-and-loser/GainerAndLoser.tsx @@ -6,13 +6,15 @@ import { wrapper } from "./GainerAndLoser.styles"; interface GainerAndLoserProps { gainers: any[]; losers: any[]; + loadingGain: boolean; + loadingLose: boolean; } -const GainerAndLoser: React.FC = ({ gainers, losers }) => { +const GainerAndLoser: React.FC = ({ gainers, losers, loadingLose, loadingGain }) => { return (
    - - + +
    ); }; diff --git a/packages/web/src/components/token/gainer-card-list/GainerCardList.tsx b/packages/web/src/components/token/gainer-card-list/GainerCardList.tsx index 5e9b48a0c..7e035ae95 100644 --- a/packages/web/src/components/token/gainer-card-list/GainerCardList.tsx +++ b/packages/web/src/components/token/gainer-card-list/GainerCardList.tsx @@ -1,18 +1,23 @@ import { MATH_NEGATIVE_TYPE } from "@constants/option.constant"; import React from "react"; import { cx } from "@emotion/css"; -import { cardStyle } from "@components/token/gainer-and-loser/CardListCommonStyle.styles"; +import { cardStyle, loadingWrapper } from "@components/token/gainer-and-loser/CardListCommonStyle.styles"; import Link from "next/link"; +import LoadingSpinner from "@components/common/loading-spinner/LoadingSpinner"; interface GainerCardListProps { gainers: any[]; + loadingGain: boolean; } -const GainerCardList: React.FC = ({ gainers }) => { +const GainerCardList: React.FC = ({ gainers, loadingGain }) => { return (

    Top 3 Gainers

    - {gainers.map((gainer, idx) => ( + {loadingGain &&
    + +
    } + {!loadingGain && gainers.map((gainer, idx) => (
    diff --git a/packages/web/src/components/token/loser-card-list/LoserCardList.tsx b/packages/web/src/components/token/loser-card-list/LoserCardList.tsx index b4bb9b68c..2e2d73701 100644 --- a/packages/web/src/components/token/loser-card-list/LoserCardList.tsx +++ b/packages/web/src/components/token/loser-card-list/LoserCardList.tsx @@ -1,18 +1,23 @@ import { cx } from "@emotion/css"; import React from "react"; import { MATH_NEGATIVE_TYPE } from "@constants/option.constant"; -import { cardStyle } from "@components/token/gainer-and-loser/CardListCommonStyle.styles"; +import { cardStyle, loadingWrapper } from "@components/token/gainer-and-loser/CardListCommonStyle.styles"; import Link from "next/link"; +import LoadingSpinner from "@components/common/loading-spinner/LoadingSpinner"; interface LoserCardListProps { losers: any[]; + loadingLose: boolean; } -const LoserCard: React.FC = ({ losers }) => { +const LoserCard: React.FC = ({ losers, loadingLose }) => { return (

    Top 3 Losers

    - {losers.map((loser, idx) => ( + {loadingLose &&
    + +
    } + {!loadingLose && losers.map((loser, idx) => (
    diff --git a/packages/web/src/components/token/market-information-list/MarketInformationList.styles.ts b/packages/web/src/components/token/market-information-list/MarketInformationList.styles.ts index 7159204dd..bcc8af875 100644 --- a/packages/web/src/components/token/market-information-list/MarketInformationList.styles.ts +++ b/packages/web/src/components/token/market-information-list/MarketInformationList.styles.ts @@ -16,11 +16,17 @@ export const wrapper = (theme: Theme) => css` background-color: ${theme.color.backgroundOpacity3}; border: 1px solid ${theme.color.border02}; border-radius: 8px; - padding: 16px; + padding: 15px; gap: 16px; .title { ${fonts.body12}; color: ${theme.color.text04}; + ${media.tablet} { + ${fonts.p4}; + } + ${media.tabletMiddle} { + ${fonts.body12}; + } } .market-info-value { ${fonts.body10}; @@ -28,6 +34,9 @@ export const wrapper = (theme: Theme) => css` ${fonts.body12}; } } + .loading-value { + max-width: 200px; + } } @media (max-width: 1180px) { grid-gap: 8px; diff --git a/packages/web/src/components/token/market-information-list/MarketInformationList.tsx b/packages/web/src/components/token/market-information-list/MarketInformationList.tsx index f4315d8a6..267f95ce5 100644 --- a/packages/web/src/components/token/market-information-list/MarketInformationList.tsx +++ b/packages/web/src/components/token/market-information-list/MarketInformationList.tsx @@ -1,20 +1,27 @@ +import { SHAPE_TYPES, skeletonTokenDetail } from "@constants/skeleton.constant"; import { wrapper } from "./MarketInformationList.styles"; interface MarketInformationListProps { list: any; + loading: boolean; } const TITLE_LIST = ["Popularity", "TVL", "Volume (24h)", "Fees (24h)"]; const MarketInformationList: React.FC = ({ list, + loading, }) => { return (
    {Object.values(list).map((item: any, idx: number) => (
    {TITLE_LIST[idx]} - {item} + {!loading && {item}} + {loading && }
    ))}
    diff --git a/packages/web/src/components/token/market-information/MarketInformation.styles.ts b/packages/web/src/components/token/market-information/MarketInformation.styles.ts index dbbfb1be4..ae5dc3c40 100644 --- a/packages/web/src/components/token/market-information/MarketInformation.styles.ts +++ b/packages/web/src/components/token/market-information/MarketInformation.styles.ts @@ -9,10 +9,10 @@ export const wrapper = (theme: Theme) => css` width: 100%; color: ${theme.color.text01}; gap: 12px; - padding: 24px; + padding: 23px; @media (max-width: 1180px) { - padding: 16px; + padding: 15px; } ${media.mobile} { ${fonts.body9}; diff --git a/packages/web/src/components/token/market-information/MarketInformation.tsx b/packages/web/src/components/token/market-information/MarketInformation.tsx index 623b88fcc..a625dd3e2 100644 --- a/packages/web/src/components/token/market-information/MarketInformation.tsx +++ b/packages/web/src/components/token/market-information/MarketInformation.tsx @@ -3,13 +3,14 @@ import { wrapper } from "./MarketInformation.styles"; interface MarketInformationProps { info: any; + loading: boolean; } -const MarketInformation: React.FC = ({ info }) => { +const MarketInformation: React.FC = ({ info, loading }) => { return (

    Market Information

    - +
    ); }; diff --git a/packages/web/src/components/token/price-information-list/PriceInformationList.styles.ts b/packages/web/src/components/token/price-information-list/PriceInformationList.styles.ts index f97a3e530..105f15fd6 100644 --- a/packages/web/src/components/token/price-information-list/PriceInformationList.styles.ts +++ b/packages/web/src/components/token/price-information-list/PriceInformationList.styles.ts @@ -19,7 +19,7 @@ export const wrapper = (theme: Theme) => css` background-color: ${theme.color.backgroundOpacity3}; border: 1px solid ${theme.color.border02}; border-radius: 8px; - padding: 16px; + padding: 15px; gap: 16px; br { display: none; @@ -40,13 +40,16 @@ export const wrapper = (theme: Theme) => css` ${fonts.body12}; } } + .loading-value { + max-width: 200px; + } .negative { color: ${theme.color.red01}; } } @media (max-width: 1360px) and (min-width: 930px) { .information-wrap { - padding: 16px 0 16px 16px; + padding: 15px 0 15px 15px; .title { br { display: block; diff --git a/packages/web/src/components/token/price-information-list/PriceInformationList.tsx b/packages/web/src/components/token/price-information-list/PriceInformationList.tsx index da32f76ba..e1e35ffef 100644 --- a/packages/web/src/components/token/price-information-list/PriceInformationList.tsx +++ b/packages/web/src/components/token/price-information-list/PriceInformationList.tsx @@ -1,9 +1,11 @@ import { MATH_NEGATIVE_TYPE } from "@constants/option.constant"; import { cx } from "@emotion/css"; import { wrapper } from "./PriceInformationList.styles"; +import { SHAPE_TYPES, skeletonTokenDetail } from "@constants/skeleton.constant"; interface PriceInformationListProps { list: any; + loading: boolean; } const TITLE_LIST = [ @@ -27,19 +29,24 @@ const TITLE_LIST = [ const PriceInformationList: React.FC = ({ list, + loading, }) => { return (
    {Object.values(list).map((item: any, idx: number) => (
    {TITLE_LIST[idx]}
    - {item.value} - + } + {loading && }
    ))}
    diff --git a/packages/web/src/components/token/price-information/PriceInformation.styles.ts b/packages/web/src/components/token/price-information/PriceInformation.styles.ts index 3acf2fb0d..99a256128 100644 --- a/packages/web/src/components/token/price-information/PriceInformation.styles.ts +++ b/packages/web/src/components/token/price-information/PriceInformation.styles.ts @@ -9,9 +9,9 @@ export const wrapper = (theme: Theme) => css` width: 100%; color: ${theme.color.text01}; gap: 12px; - padding: 24px; + padding: 23px; @media (max-width: 1180px) { - padding: 16px; + padding: 15px; } ${media.mobile} { ${fonts.body9}; diff --git a/packages/web/src/components/token/price-information/PriceInformation.tsx b/packages/web/src/components/token/price-information/PriceInformation.tsx index 76c2e4c6d..e5ef9d516 100644 --- a/packages/web/src/components/token/price-information/PriceInformation.tsx +++ b/packages/web/src/components/token/price-information/PriceInformation.tsx @@ -3,13 +3,14 @@ import { wrapper } from "./PriceInformation.styles"; interface PriceInformationProps { info: any; + loading: boolean; } -const PriceInformation: React.FC = ({ info }) => { +const PriceInformation: React.FC = ({ info, loading }) => { return (

    Price Information

    - +
    ); }; diff --git a/packages/web/src/components/token/price-performance-list/PricePerformanceList.styles.ts b/packages/web/src/components/token/price-performance-list/PricePerformanceList.styles.ts index 6cfa43af7..04bc6a7db 100644 --- a/packages/web/src/components/token/price-performance-list/PricePerformanceList.styles.ts +++ b/packages/web/src/components/token/price-performance-list/PricePerformanceList.styles.ts @@ -11,8 +11,12 @@ export const wrapper = (theme: Theme) => css` background-color: ${theme.color.backgroundOpacity3}; border: 1px solid ${theme.color.border02}; border-radius: 8px; - padding: 16px; + padding: 15px; gap: 16px; + ${media.mobile} { + gap: 12px; + height: auto; + } .title-wrap, .performance-list { @@ -34,9 +38,15 @@ export const wrapper = (theme: Theme) => css` ${mixins.flexbox("column", "center", "center")}; width: 100%; gap: 16px; + ${media.mobile} { + gap: 12px; + } } .performance-list { height: 25px; + ${media.mobile} { + height: auto; + } span { ${fonts.body10}; width: 200px; diff --git a/packages/web/src/components/token/price-performance-list/PricePerformanceList.tsx b/packages/web/src/components/token/price-performance-list/PricePerformanceList.tsx index 4c9338605..bb47ce866 100644 --- a/packages/web/src/components/token/price-performance-list/PricePerformanceList.tsx +++ b/packages/web/src/components/token/price-performance-list/PricePerformanceList.tsx @@ -2,15 +2,18 @@ import React from "react"; import { MATH_NEGATIVE_TYPE } from "@constants/option.constant"; import { cx } from "@emotion/css"; import { wrapper } from "./PricePerformanceList.styles"; +import { SHAPE_TYPES, skeletonTokenDetail } from "@constants/skeleton.constant"; interface PricePerformanceListProps { list: any[]; + loading: boolean; } const TITLE_LIST = ["Period", "Amount", "Change"]; const PricePerformanceList: React.FC = ({ list, + loading, }) => { return (
    @@ -25,20 +28,26 @@ const PricePerformanceList: React.FC = ({ {list.map((item, idx) => (
    {item.createdAt} - {item.amount.value} - - } + {loading && } + {loading && } + {!loading && {item.change.value} - + }
    ))}
    diff --git a/packages/web/src/components/token/price-performance/PricePerformance.styles.ts b/packages/web/src/components/token/price-performance/PricePerformance.styles.ts index 3acf2fb0d..99a256128 100644 --- a/packages/web/src/components/token/price-performance/PricePerformance.styles.ts +++ b/packages/web/src/components/token/price-performance/PricePerformance.styles.ts @@ -9,9 +9,9 @@ export const wrapper = (theme: Theme) => css` width: 100%; color: ${theme.color.text01}; gap: 12px; - padding: 24px; + padding: 23px; @media (max-width: 1180px) { - padding: 16px; + padding: 15px; } ${media.mobile} { ${fonts.body9}; diff --git a/packages/web/src/components/token/price-performance/PricePerformance.tsx b/packages/web/src/components/token/price-performance/PricePerformance.tsx index fb534b136..0c92efc22 100644 --- a/packages/web/src/components/token/price-performance/PricePerformance.tsx +++ b/packages/web/src/components/token/price-performance/PricePerformance.tsx @@ -3,13 +3,14 @@ import { wrapper } from "./PricePerformance.styles"; interface PricePerformanceProps { info: any; + loading: boolean; } -const PricePerformance: React.FC = ({ info }) => { +const PricePerformance: React.FC = ({ info, loading }) => { return (

    Price Performance

    - +
    ); }; diff --git a/packages/web/src/components/token/token-chart-info/TokenChartInfo.styles.ts b/packages/web/src/components/token/token-chart-info/TokenChartInfo.styles.ts index f259d7f89..6c9fda5cb 100644 --- a/packages/web/src/components/token/token-chart-info/TokenChartInfo.styles.ts +++ b/packages/web/src/components/token/token-chart-info/TokenChartInfo.styles.ts @@ -27,8 +27,8 @@ export const TokenChartInfoWrapper = styled.div` } .token-symbol { - color: ${({ theme }) => theme.color.text05}; - ${fonts.body4}; + color: ${({ theme }) => theme.color.text04}; + ${fonts.body6}; } } diff --git a/packages/web/src/components/token/token-chart-info/TokenChartInfo.tsx b/packages/web/src/components/token/token-chart-info/TokenChartInfo.tsx index 4ef22d9cf..6dbbb8f04 100644 --- a/packages/web/src/components/token/token-chart-info/TokenChartInfo.tsx +++ b/packages/web/src/components/token/token-chart-info/TokenChartInfo.tsx @@ -16,11 +16,13 @@ export interface TokenChartInfoProps { }; changedRate: number; }; + loading: boolean; } const TokenChartInfo: React.FC = ({ token, - priceInfo + priceInfo, + loading, }) => { const isIncreasePrice = useCallback(() => { @@ -39,15 +41,16 @@ const TokenChartInfo: React.FC = ({
    - {`$ ${priceInfo.amount.value}`} -
    + {{loading ? "-" : `$${priceInfo.amount.value}`}} + {!loading &&
    { isIncreasePrice() ? : } {priceInfo.changedRate}% -
    +
    } + {loading &&
     
    }
    diff --git a/packages/web/src/components/token/token-chart/TokenChart.styles.ts b/packages/web/src/components/token/token-chart/TokenChart.styles.ts index c208a41b5..7f3f0e3ad 100644 --- a/packages/web/src/components/token/token-chart/TokenChart.styles.ts +++ b/packages/web/src/components/token/token-chart/TokenChart.styles.ts @@ -1,5 +1,6 @@ import styled from "@emotion/styled"; import { media } from "@styles/media"; +import mixins from "@styles/mixins"; export const TokenChartWrapper = styled.div` display: flex; @@ -9,7 +10,7 @@ export const TokenChartWrapper = styled.div` border: 1px solid ${({ theme }) => theme.color.border02}; width: 100%; height: auto; - padding: 24px; + padding: 23px; border-radius: 8px; ${media.mobile} { background-color: transparent; @@ -23,3 +24,28 @@ export const TokenChartWrapper = styled.div` .chart-graph-wrapper { } `; + + +export const LoadingChart = styled.div` + ${mixins.flexbox("row", "center", "center")} + width: 100%; + height: 361px; + background-color: ${({ theme }) => theme.color.background15}; + border-radius: 8px; + > div { + &::before { + background-color: ${({ theme }) => theme.color.background01}; + } + &::after { + ${mixins.positionCenter()}; + content: ""; + border-radius: 50%; + width: 60px; + height: 60px; + background-color: ${({ theme }) => theme.color.background15}; + } + } + ${media.mobile} { + height: 252px; + } +`; diff --git a/packages/web/src/components/token/token-chart/TokenChart.tsx b/packages/web/src/components/token/token-chart/TokenChart.tsx index 2437052e1..a5ffd0d48 100644 --- a/packages/web/src/components/token/token-chart/TokenChart.tsx +++ b/packages/web/src/components/token/token-chart/TokenChart.tsx @@ -3,7 +3,8 @@ import React from "react"; import TokenChartInfo from "../token-chart-info/TokenChartInfo"; import TokenChartGraphTab from "./token-chart-graph-tab/TokenChartGraphTab"; import TokenChartGraph from "./token-chart-graph/TokenChartGraph"; -import { TokenChartWrapper } from "./TokenChart.styles"; +import { LoadingChart, TokenChartWrapper } from "./TokenChart.styles"; +import LoadingSpinner from "@components/common/loading-spinner/LoadingSpinner"; export interface TokenChartProps { @@ -11,6 +12,7 @@ export interface TokenChartProps { chartInfo?: ChartInfo; currentTab: TokenChartGraphPeriodType; changeTab: (tab: string) => void; + loading: boolean; } const TokenChart: React.FC = ({ @@ -18,19 +20,26 @@ const TokenChart: React.FC = ({ chartInfo, currentTab, changeTab, + loading, }) => { + return ( - + - + + } + {!loading && + currentTab={currentTab} + />} ); }; diff --git a/packages/web/src/components/token/token-chart/token-chart-graph-tab/TokenChartGraphTab.styles.ts b/packages/web/src/components/token/token-chart/token-chart-graph-tab/TokenChartGraphTab.styles.ts index 4031cb400..d7fefc081 100644 --- a/packages/web/src/components/token/token-chart/token-chart-graph-tab/TokenChartGraphTab.styles.ts +++ b/packages/web/src/components/token/token-chart/token-chart-graph-tab/TokenChartGraphTab.styles.ts @@ -15,6 +15,7 @@ export const TokenChartGraphTabWrapper = styled.div` } .select-tab-wrapper { border: none; + padding: 2px; } @media (max-width: 1180px) { .chart-select-button { diff --git a/packages/web/src/components/token/token-chart/token-chart-graph/TokenChartGraph.styles.ts b/packages/web/src/components/token/token-chart/token-chart-graph/TokenChartGraph.styles.ts index c49a8a27b..476de1e8f 100644 --- a/packages/web/src/components/token/token-chart/token-chart-graph/TokenChartGraph.styles.ts +++ b/packages/web/src/components/token/token-chart/token-chart-graph/TokenChartGraph.styles.ts @@ -10,7 +10,6 @@ export const TokenChartGraphWrapper = styled.div` background-color: ${({ theme }) => theme.color.background15}; border-radius: 8px; justify-content: space-between; - padding: 0 5px 12px 0; .data-wrapper { display: flex; @@ -28,10 +27,10 @@ export const TokenChartGraphWrapper = styled.div` .xaxis-wrapper { display: flex; flex-direction: row; + height: 40px; width: 100%; - margin: 12px 0 0 0; - padding-right: 13px; - padding-left: 12px; + align-items: center; + padding: 0 12px; justify-content: space-between; ${fonts.body12}; color: ${({ theme }) => theme.color.text04}; @@ -40,13 +39,12 @@ export const TokenChartGraphWrapper = styled.div` ${fonts.body12}; color: ${({ theme }) => theme.color.text04}; ${media.mobile} { - ${fonts.p7}; + ${fonts.p4}; } } ${media.mobile} { - padding-right: 4px; - padding-left: 4px; - margin-top: 4px; + height: 30px; + padding: 0 4px; } } } @@ -55,24 +53,41 @@ export const TokenChartGraphWrapper = styled.div` text-align: center; display: flex; flex-direction: column-reverse; - width: 24px; - padding: 8px 0 9px 5px; - margin-bottom: 30px; + min-width: 50px; + margin-bottom: 40px; + padding: 8px 0; justify-content: space-between; span { ${fonts.body12}; color: ${({ theme }) => theme.color.text04}; } + .large-text { + ${fonts.body12}; + } + .medium-text { + ${fonts.p4} + } + .small-text { + ${fonts.p6} + font-size: 11px; + } ${media.mobile} { ${fonts.p7}; - padding-left: 5px; - width: 14px; + margin-bottom: 30px; + min-width: 40px; + padding: 4px 0; span { ${fonts.p7}; } + .large-text { + ${fonts.p4}; + } + .medium-text { + ${fonts.p6} + } + .small-text { + ${fonts.p7} + } } } - ${media.mobile} { - padding: 0 5px 4px 0; - } `; diff --git a/packages/web/src/components/token/token-chart/token-chart-graph/TokenChartGraph.tsx b/packages/web/src/components/token/token-chart/token-chart-graph/TokenChartGraph.tsx index f3a9d2195..2168d2374 100644 --- a/packages/web/src/components/token/token-chart/token-chart-graph/TokenChartGraph.tsx +++ b/packages/web/src/components/token/token-chart/token-chart-graph/TokenChartGraph.tsx @@ -1,7 +1,10 @@ import LineGraph from "@components/common/line-graph/LineGraph"; import { useTheme } from "@emotion/react"; -import React, { useRef, useState, useEffect } from "react"; +import React, { useMemo, useRef, useState, useEffect } from "react"; import { TokenChartGraphWrapper } from "./TokenChartGraph.styles"; +import { useWindowSize } from "@hooks/common/use-window-size"; +import { DEVICE_TYPE } from "@styles/media"; +import { TokenChartGraphPeriodType, TokenChartGraphPeriods } from "@containers/token-chart-container/TokenChartContainer"; export interface TokenChartGraphProps { datas: { @@ -13,25 +16,29 @@ export interface TokenChartGraphProps { }[]; xAxisLabels: string[]; yAxisLabels: string[]; + currentTab: TokenChartGraphPeriodType; } const TokenChartGraph: React.FC = ({ datas, xAxisLabels, yAxisLabels, + currentTab, }) => { const theme = useTheme(); + const { breakpoint } = useWindowSize(); const wrapperRef = useRef(null); const [width, setWidth] = useState(undefined); const [height, setHeight] = useState(undefined); - + useEffect(() => { const updateWidth = () => { if (wrapperRef.current) { const newWidth = wrapperRef.current.getBoundingClientRect().width; const newHeight = wrapperRef.current.getBoundingClientRect().height; + setWidth(newWidth); - setHeight(newHeight - 30); + setHeight(newHeight - (breakpoint !== DEVICE_TYPE.MOBILE ? 40 : 30)); } }; updateWidth(); @@ -40,8 +47,32 @@ const TokenChartGraph: React.FC = ({ return () => { window.removeEventListener("resize", updateWidth); }; + }, [breakpoint]); + const countXAxis = useMemo(() => { + if (breakpoint !== DEVICE_TYPE.MOBILE) + return Math.floor((((width || 0) + 20) - 25) / (currentTab === TokenChartGraphPeriods[0] ? 60: 100)); + return Math.floor((((width || 0) + 20) - 8) / 80); + }, [width, breakpoint, currentTab]); + const customData = useMemo(() => { + const temp = 47.55; + return { + height: temp, + locationTooltip: 198, + }; }, []); + const typeYAxis = useMemo(() => { + if (yAxisLabels.length > 0) { + const leng = yAxisLabels[0].length; + if (leng > 0) { + if (leng <=3 ) return "large-text"; + if (leng === 4) return "medium-text"; + return "small-text"; + } + } + return "small-text"; + }, [yAxisLabels]); + return (
    @@ -49,17 +80,18 @@ const TokenChartGraph: React.FC = ({ cursor className="graph" width={width} - height={height} - color={theme.color.point} + height={(height || 0) - customData.height} + color="#192EA2" strokeWidth={1} datas={datas.map(data => ({ value: data.amount.value, time: data.time, }))} firstPointColor={theme.color.border05} + customData={customData} />
    - {xAxisLabels.map((label, index) => ( + {xAxisLabels.slice(0, Math.min(countXAxis, 7)).map((label, index) => ( {label} @@ -68,8 +100,8 @@ const TokenChartGraph: React.FC = ({
    {yAxisLabels.map((label, index) => ( - - {label} + + ${label} ))}
    diff --git a/packages/web/src/components/token/token-description-content/TokenDescriptionContent.stories.tsx b/packages/web/src/components/token/token-description-content/TokenDescriptionContent.stories.tsx index 0b61e8c8d..7a2018217 100644 --- a/packages/web/src/components/token/token-description-content/TokenDescriptionContent.stories.tsx +++ b/packages/web/src/components/token/token-description-content/TokenDescriptionContent.stories.tsx @@ -15,5 +15,5 @@ const Template: ComponentStory = args => ( export const Default = Template.bind({}); Default.args = { - content: descriptionInit.desc, + content: descriptionInit.token.description, }; diff --git a/packages/web/src/components/token/token-description-links/TokenDescriptionLinks.styles.ts b/packages/web/src/components/token/token-description-links/TokenDescriptionLinks.styles.ts index 16ed0bee0..b8d62feb1 100644 --- a/packages/web/src/components/token/token-description-links/TokenDescriptionLinks.styles.ts +++ b/packages/web/src/components/token/token-description-links/TokenDescriptionLinks.styles.ts @@ -15,7 +15,7 @@ export const wrapper = (theme: Theme) => css` ${fonts.p4}; gap: 4px; height: 24px; - background-color: ${theme.color.background06}; + background-color: ${theme.color.background11}; border-radius: 4px; padding: 0px 8px; color: ${theme.color.text05}; diff --git a/packages/web/src/components/token/token-description/TokenDescription.stories.tsx b/packages/web/src/components/token/token-description/TokenDescription.stories.tsx index a3d2a9edf..3f9e996e2 100644 --- a/packages/web/src/components/token/token-description/TokenDescription.stories.tsx +++ b/packages/web/src/components/token/token-description/TokenDescription.stories.tsx @@ -17,6 +17,6 @@ export const Default = Template.bind({}); Default.args = { tokenName: descriptionInit.token.name, tokenSymbol: descriptionInit.token.symbol, - content: descriptionInit.desc, + content: descriptionInit.token.description, links: descriptionInit.links, }; diff --git a/packages/web/src/components/token/token-description/TokenDescription.styles.ts b/packages/web/src/components/token/token-description/TokenDescription.styles.ts index a1f6ef81b..cbc671a86 100644 --- a/packages/web/src/components/token/token-description/TokenDescription.styles.ts +++ b/packages/web/src/components/token/token-description/TokenDescription.styles.ts @@ -11,21 +11,25 @@ export const wrapper = (theme: Theme) => css` border-radius: 8px; color: ${theme.color.text01}; gap: 12px; - padding: 24px; + padding: 23px; h2 { ${fonts.body7}; } @media (max-width: 1180px) { - padding: 16px; + padding: 15px; } ${media.mobile} { h2 { ${fonts.body9}; } margin-top: 16px; + margin-bottom: 16px; ${fonts.body9}; border: none; gap: 8px; padding: 0; + .loading-value { + height: 20px; + } } `; diff --git a/packages/web/src/components/token/token-description/TokenDescription.tsx b/packages/web/src/components/token/token-description/TokenDescription.tsx index 1009891e3..73f07b4c0 100644 --- a/packages/web/src/components/token/token-description/TokenDescription.tsx +++ b/packages/web/src/components/token/token-description/TokenDescription.tsx @@ -2,11 +2,13 @@ import React from "react"; import TokenDescriptionContent from "@components/token/token-description-content/TokenDescriptionContent"; import TokenDescriptionLinks from "@components/token/token-description-links/TokenDescriptionLinks"; import { wrapper } from "./TokenDescription.styles"; +import { SHAPE_TYPES, skeletonTokenDetail } from "@constants/skeleton.constant"; interface TokenDescriptionProps { tokenName: string; tokenSymbol: string; content: string; links: any; + loading: boolean; } const TokenDescription: React.FC = ({ @@ -14,11 +16,23 @@ const TokenDescription: React.FC = ({ tokenSymbol, content, links, + loading, }) => { return (

    {`About ${tokenName} (${tokenSymbol})`}

    - + {!loading && } + {loading && <> + + + } +
    ); diff --git a/packages/web/src/components/token/token-info-content/TokenInfoContent.tsx b/packages/web/src/components/token/token-info-content/TokenInfoContent.tsx index dc25d046d..f8ae4ed0c 100644 --- a/packages/web/src/components/token/token-info-content/TokenInfoContent.tsx +++ b/packages/web/src/components/token/token-info-content/TokenInfoContent.tsx @@ -8,18 +8,24 @@ interface TokenInfoContentProps { performance: any; priceInfo: any; marketInfo: any; + loadingPricePerform: boolean; + loadingPriceInfo: boolean; + loadingMarketInfo: boolean; } const TokenInfoContent: React.FC = ({ performance, priceInfo, marketInfo, + loadingPricePerform, + loadingPriceInfo, + loadingMarketInfo, }) => { return (
    - - - + + +
    ); }; diff --git a/packages/web/src/components/token/token-swap/TokenSwap.styles.ts b/packages/web/src/components/token/token-swap/TokenSwap.styles.ts index b2f72cc39..7907800fe 100644 --- a/packages/web/src/components/token/token-swap/TokenSwap.styles.ts +++ b/packages/web/src/components/token/token-swap/TokenSwap.styles.ts @@ -5,12 +5,11 @@ import { media } from "@styles/media"; import styled from "@emotion/styled"; export const wrapper = (theme: Theme) => css` - padding: 24px; + padding: 23px; background-color: ${theme.color.background06}; border: 1px solid ${theme.color.border02}; box-shadow: 8px 8px 20px 0px rgba(0, 0, 0, 0.08); border-radius: 8px; - .header { ${mixins.flexbox("row", "center", "space-between")}; width: 100%; @@ -74,13 +73,14 @@ export const wrapper = (theme: Theme) => css` .to { ${mixins.flexbox("row", "center", "space-between")}; flex-wrap: wrap; - width: 100%; padding: 16px 24px; - background-color: ${theme.color.background01}; border: 1px solid ${theme.color.border02}; border-radius: 8px; + &:focus-within { + border: 1px solid ${theme.color.border15}; + } } .amount { @@ -89,17 +89,6 @@ export const wrapper = (theme: Theme) => css` margin-bottom: 8px; } - .token { - width: auto; - height: 30px; - cursor: default; - span { - font-size: 15px; - line-height: 19px; - margin: 0px; - } - } - .info { ${mixins.flexbox("row", "center", "space-between")}; width: 100%; @@ -112,12 +101,15 @@ export const wrapper = (theme: Theme) => css` line-height: 38px; color: ${theme.color.text01}; margin-right: 30px; + &::placeholder { + color: ${theme.color.text02}; + } } .price-text, .balance-text { ${fonts.p2}; - color: ${theme.color.text10}; + color: ${theme.color.text04}; } .balance-text-disabled { cursor: pointer; @@ -126,12 +118,18 @@ export const wrapper = (theme: Theme) => css` .token { ${mixins.flexbox("row", "center", "center")} width: auto; - height: 30px; - font-size: 15px; - font-weight: 500; - line-height: 19px; border-radius: 36px; color: ${theme.color.text01}; + height: 34px; + .selected-token { + padding: 5px 10px 5px 6px; + } + .not-selected-token { + padding: 5px 10px 5px 12px + } + .token-symbol { + height: 21px; + } } .arrow { @@ -164,6 +162,27 @@ export const wrapper = (theme: Theme) => css` ${mixins.flexbox("row", "center", "space-between")}; width: 100%; padding-top: 16px; + .confirm-button { + height: 57px; + span { + ${fonts.body7} + } + } + } + ${media.mobile} { + padding: 15px; + .header { + padding-bottom: 12px; + } + .footer { + padding-top: 12px; + .confirm-button { + height: 41px; + span { + ${fonts.body9} + } + } + } } `; diff --git a/packages/web/src/components/token/token-swap/TokenSwap.tsx b/packages/web/src/components/token/token-swap/TokenSwap.tsx index bd674fc20..d907fe403 100644 --- a/packages/web/src/components/token/token-swap/TokenSwap.tsx +++ b/packages/web/src/components/token/token-swap/TokenSwap.tsx @@ -29,9 +29,9 @@ export interface TokenSwapProps { handleCopied: () => void; connectWallet: () => void; changeTokenA: (token: TokenModel) => void; - changeTokenAAmount: (value: string) => void; + changeTokenAAmount: (value: string, none?: boolean) => void; changeTokenB: (token: TokenModel) => void; - changeTokenBAmount: (value: string) => void; + changeTokenBAmount: (value: string, none?: boolean) => void; switchSwapDirection: () => void; } @@ -67,6 +67,9 @@ const TokenSwap: React.FC = ({ const onChangeTokenAAmount = useCallback( (e: React.ChangeEvent) => { const value = e.target.value; + if (value === "") { + changeTokenAAmount("", true); + } if (value !== "" && !isAmount(value)) return; changeTokenAAmount(value.replace(/^0+(?=\d)|(\.\d*)$/g, "$1")); }, @@ -76,6 +79,9 @@ const TokenSwap: React.FC = ({ const onChangeTokenBAmount = useCallback( (e: React.ChangeEvent) => { const value = e.target.value; + if (value === "") { + changeTokenBAmount("", true); + } if (value !== "" && !isAmount(value)) return; changeTokenBAmount(value.replace(/^0+(?=\d)|(\.\d*)$/g, "$1")); }, @@ -186,12 +192,11 @@ const TokenSwap: React.FC = ({ text={swapButtonText} style={{ fullWidth: true, - height: 57, - fontType: "body7", hierarchy: ButtonHierarchy.Primary, }} disabled={!isAvailSwap} onClick={onClickConfirm} + className="confirm-button" />
    diff --git a/packages/web/src/components/token/trending-crypto-card-list/TrendingCryptoCardList.styles.ts b/packages/web/src/components/token/trending-crypto-card-list/TrendingCryptoCardList.styles.ts index 33a8a5e27..cbbc77dfa 100644 --- a/packages/web/src/components/token/trending-crypto-card-list/TrendingCryptoCardList.styles.ts +++ b/packages/web/src/components/token/trending-crypto-card-list/TrendingCryptoCardList.styles.ts @@ -1,6 +1,6 @@ -import { css } from "@emotion/react"; import { media } from "@styles/media"; import mixins from "@styles/mixins"; +import { css, type Theme } from "@emotion/react"; export const wrapper = css` ${mixins.flexbox("column", "flex-start", "flex-start")}; @@ -13,3 +13,20 @@ export const wrapper = css` gap: 4px; } `; + +export const loadingWrapper = (theme: Theme) => css` + ${mixins.flexbox("row", "flex-start", "center")} + width: 100%; + height: 196px; + background-color: ${theme.color.background01}; + border-radius: 8px; + padding-top: 64px; + > div { + width: 48px; + height: 48px; + &::before { + background-color: ${theme.color.background01}; + width: 38px; + height: 38px; + } +`; diff --git a/packages/web/src/components/token/trending-crypto-card-list/TrendingCryptoCardList.tsx b/packages/web/src/components/token/trending-crypto-card-list/TrendingCryptoCardList.tsx index f8c696bbd..ce315a966 100644 --- a/packages/web/src/components/token/trending-crypto-card-list/TrendingCryptoCardList.tsx +++ b/packages/web/src/components/token/trending-crypto-card-list/TrendingCryptoCardList.tsx @@ -1,14 +1,21 @@ import React from "react"; import TrendingCryptoCard from "@components/token/trending-crypto-card/TrendingCryptoCard"; -import { wrapper } from "./TrendingCryptoCardList.styles"; +import { loadingWrapper, wrapper } from "./TrendingCryptoCardList.styles"; +import LoadingSpinner from "@components/common/loading-spinner/LoadingSpinner"; interface TrendingCryptoCardListProps { list: any[]; + loading: boolean; } const TrendingCryptoCardList: React.FC = ({ list, + loading, }) => { + if (loading) return( +
    + +
    ); return (
    {list.map((item, idx) => ( diff --git a/packages/web/src/components/token/trending-cryptos/TrendingCryptos.styles.ts b/packages/web/src/components/token/trending-cryptos/TrendingCryptos.styles.ts index ca22b29db..2256c2d08 100644 --- a/packages/web/src/components/token/trending-cryptos/TrendingCryptos.styles.ts +++ b/packages/web/src/components/token/trending-cryptos/TrendingCryptos.styles.ts @@ -9,19 +9,19 @@ export const wrapper = (theme: Theme) => css` background-color: ${theme.color.background01}; border: 1px solid ${theme.color.border02}; border-radius: 8px; - padding: 24px 0px; + padding: 23px 0px; gap: 12px; h2 { ${fonts.body9}; color: ${theme.color.text01}; width: 100%; - padding: 0px 24px; + padding: 0px 23px; } @media (max-width: 1180px) { gap: 12px; - padding: 16px 0; + padding: 15px 0; h2 { - padding: 0 16px; + padding: 0 15px; } } ${media.mobile} { diff --git a/packages/web/src/components/unstake/select-unstake-result/SelectUnstakeResult.tsx b/packages/web/src/components/unstake/select-unstake-result/SelectUnstakeResult.tsx index 6d164b34d..8f3f67c36 100644 --- a/packages/web/src/components/unstake/select-unstake-result/SelectUnstakeResult.tsx +++ b/packages/web/src/components/unstake/select-unstake-result/SelectUnstakeResult.tsx @@ -36,7 +36,7 @@ const SelectUnstakeResult: React.FC = ({
  • pooled tokenA logo -

    Unclaimed
    GNOS Rewards

    +

    Unclaimed
    GNS Rewards

    1,140.058845
    $5,564.48 diff --git a/packages/web/src/components/wallet/asset-list-header/AssetListHeader.styles.ts b/packages/web/src/components/wallet/asset-list-header/AssetListHeader.styles.ts index 6febedda3..205ca4bcb 100644 --- a/packages/web/src/components/wallet/asset-list-header/AssetListHeader.styles.ts +++ b/packages/web/src/components/wallet/asset-list-header/AssetListHeader.styles.ts @@ -22,6 +22,7 @@ export const AssetListHeaderWrapper = styled.div` gap: 36px; ${media.tabletMiddle} { gap: 24px; + height: 24px; ${mixins.flexbox("column", "flex-start", "flex-start")}; } ${media.mobile} { @@ -36,6 +37,7 @@ export const AssetListHeaderWrapper = styled.div` .icon-wrap { ${mixins.flexbox("row", "center", "center")}; cursor: pointer; + margin-right: 13px; } .search-icon { width: 24px; diff --git a/packages/web/src/components/wallet/deposit-modal/DepositModal.styles.ts b/packages/web/src/components/wallet/deposit-modal/DepositModal.styles.ts index 5d1f0237d..738d7946c 100644 --- a/packages/web/src/components/wallet/deposit-modal/DepositModal.styles.ts +++ b/packages/web/src/components/wallet/deposit-modal/DepositModal.styles.ts @@ -4,16 +4,7 @@ import { media } from "@styles/media"; import mixins from "@styles/mixins"; import { Z_INDEX } from "@styles/zIndex"; export const DepositModalBackground = styled.div` - position: fixed; - overflow: scroll; - top: 0px; - bottom: 0px; - left: 0px; - right: 0px; - width: 100%; - height: 100lvh; - background: rgba(10, 14, 23, 0.7); - z-index: ${Z_INDEX.modalOverlay}; + z-index: ${Z_INDEX.modal}; `; export const DepositModalWrapper = styled.div` @@ -30,6 +21,7 @@ export const DepositModalWrapper = styled.div` box-shadow: 10px 14px 60px 0px rgba(0, 0, 0, 0.4); border: 1px solid ${({ theme }) => theme.color.border02}; background-color: ${({ theme }) => theme.color.background06}; + z-index: ${Z_INDEX.modal}; ${media.mobile} { width: 328px; padding: 16px 11px; diff --git a/packages/web/src/components/wallet/deposit-modal/DepositModal.tsx b/packages/web/src/components/wallet/deposit-modal/DepositModal.tsx index 19c038bd8..44556d488 100644 --- a/packages/web/src/components/wallet/deposit-modal/DepositModal.tsx +++ b/packages/web/src/components/wallet/deposit-modal/DepositModal.tsx @@ -2,8 +2,9 @@ import Button, { ButtonHierarchy } from "@components/common/button/Button"; import IconClose from "@components/common/icons/IconCancel"; import IconFailed from "@components/common/icons/IconFailed"; import IconStrokeArrowRight from "@components/common/icons/IconStrokeArrowRight"; +import { Overlay } from "@components/common/modal/Modal.styles"; import SelectPairButton from "@components/common/select-pair-button/SelectPairButton"; -import useModalCloseEvent from "@hooks/common/use-modal-close-event"; +import useEscCloseModal from "@hooks/common/use-esc-close-modal"; import { usePositionModal } from "@hooks/common/use-postion-modal"; import { TokenModel } from "@models/token/token-model"; import { DEVICE_TYPE } from "@styles/media"; @@ -47,7 +48,7 @@ const DepositModal: React.FC = ({ const modalRef = useRef(null); const [amount, setAmount] = useState("0"); - useModalCloseEvent(modalRef, close); + useEscCloseModal(close); usePositionModal(modalRef); const onChangeAmount = useCallback( (e: React.ChangeEvent) => { @@ -64,92 +65,95 @@ const DepositModal: React.FC = ({ ); return ( - - -
    -
    -
    IBC Deposit
    -
    - + <> + + +
    +
    +
    IBC Deposit
    +
    + +
    -
    - -
    -
    - -
    - +
    +
    + +
    + +
    +
    +
    + {!Number(amount) ? "-" : `$${amount}`} + Available: -
    -
    - {!Number(amount) ? "-" : `$${amount}`} - Available: - -
    -
    - - -
    -
    From
    -
    - token logo - {fromToken.symbol} + + +
    +
    From
    +
    + token logo + {fromToken.symbol} +
    +

    {formatAddress(fromToken.address)}

    -

    {formatAddress(fromToken.address)}

    -
    - - - -
    -
    To
    -
    - token logo - {toToken.symbol} + + + +
    +
    To
    +
    + token logo + {toToken.symbol} +
    +

    {formatAddress(toToken.address)}

    -

    {formatAddress(toToken.address)}

    -
    - - - -

    This feature will be available once Gnoland enables IBC.

    -
    -
    - - + + + +

    This feature will be available once Gnoland enables IBC.

    +
    +
    + + + + ); }; diff --git a/packages/web/src/components/wallet/withdraw-modal/WithDrawModal.styles.ts b/packages/web/src/components/wallet/withdraw-modal/WithDrawModal.styles.ts index 27809f7d5..0518465e9 100644 --- a/packages/web/src/components/wallet/withdraw-modal/WithDrawModal.styles.ts +++ b/packages/web/src/components/wallet/withdraw-modal/WithDrawModal.styles.ts @@ -4,15 +4,6 @@ import { media } from "@styles/media"; import mixins from "@styles/mixins"; import { Z_INDEX } from "@styles/zIndex"; export const WithDrawModalBackground = styled.div` - position: fixed; - overflow: scroll; - top: 0px; - bottom: 0px; - left: 0px; - right: 0px; - width: 100%; - height: 100lvh; - background: rgba(10, 14, 23, 0.7); z-index: ${Z_INDEX.modalOverlay}; `; @@ -30,6 +21,7 @@ export const WithDrawModalWrapper = styled.div` box-shadow: 10px 14px 60px 0px rgba(0, 0, 0, 0.4); border: 1px solid ${({ theme }) => theme.color.border02}; background-color: ${({ theme }) => theme.color.background06}; + z-index: ${Z_INDEX.modal}; ${media.mobile} { width: 328px; padding: 16px 11px; diff --git a/packages/web/src/components/wallet/withdraw-modal/WithDrawModal.tsx b/packages/web/src/components/wallet/withdraw-modal/WithDrawModal.tsx index 3e525d7ff..e1c80c60f 100644 --- a/packages/web/src/components/wallet/withdraw-modal/WithDrawModal.tsx +++ b/packages/web/src/components/wallet/withdraw-modal/WithDrawModal.tsx @@ -2,8 +2,9 @@ import Button, { ButtonHierarchy } from "@components/common/button/Button"; import IconClose from "@components/common/icons/IconCancel"; import IconFailed from "@components/common/icons/IconFailed"; import IconStrokeArrowRight from "@components/common/icons/IconStrokeArrowRight"; +import { Overlay } from "@components/common/modal/Modal.styles"; import SelectPairButton from "@components/common/select-pair-button/SelectPairButton"; -import useModalCloseEvent from "@hooks/common/use-modal-close-event"; +import useEscCloseModal from "@hooks/common/use-esc-close-modal"; import { usePositionModal } from "@hooks/common/use-postion-modal"; import { TokenModel } from "@models/token/token-model"; import { DEVICE_TYPE } from "@styles/media"; @@ -47,7 +48,7 @@ const WithDrawModal: React.FC = ({ const modalRef = useRef(null); const [amount, setAmount] = useState("0"); - useModalCloseEvent(modalRef, close); + useEscCloseModal(close); usePositionModal(modalRef); const onChangeAmount = useCallback( @@ -65,92 +66,95 @@ const WithDrawModal: React.FC = ({ ); return ( - - -
    -
    -
    IBC Withdraw
    -
    - + <> + + +
    +
    +
    IBC Withdraw
    +
    + +
    -
    - -
    -
    - -
    - +
    +
    + +
    + +
    +
    +
    + {!Number(amount) ? "-" : `$${amount}`} + Available: -
    -
    - {!Number(amount) ? "-" : `$${amount}`} - Available: - -
    -
    - - -
    -
    From
    -
    - token logo - {fromToken.symbol} + + +
    +
    From
    +
    + token logo + {fromToken.symbol} +
    +

    {formatAddress(fromToken.address)}

    -

    {formatAddress(fromToken.address)}

    -
    - - - -
    -
    To
    -
    - token logo - {toToken.symbol} + + + +
    +
    To
    +
    + token logo + {toToken.symbol} +
    +

    {formatAddress(toToken.address)}

    -

    {formatAddress(toToken.address)}

    -
    - - - -

    This feature will be available once Gnoland enables IBC.

    -
    -
    - - + + + +

    This feature will be available once Gnoland enables IBC.

    +
    +
    + + + + ); }; diff --git a/packages/web/src/constants/footer.constant.tsx b/packages/web/src/constants/footer.constant.tsx index a6c050187..178a2f7eb 100644 --- a/packages/web/src/constants/footer.constant.tsx +++ b/packages/web/src/constants/footer.constant.tsx @@ -46,7 +46,7 @@ export const FOOTER_RIGHT_NAV = [ newTab: false, }, { - title: "Add Liquidity", + title: "Create Position", path: "/earn/add", newTab: false, }, @@ -60,11 +60,6 @@ export const FOOTER_RIGHT_NAV = [ path: "/earn/pool/1/incentivize", newTab: false, }, - { - title: "Airdrop", - path: "/airdrop", - newTab: false, - }, ], }, { @@ -81,12 +76,7 @@ export const FOOTER_RIGHT_NAV = [ newTab: true, }, { - title: "Discord Support", - path: "/", - newTab: true, - }, - { - title: "Twitter Support", + title: "Twitter (X)", path: "/", newTab: true, }, diff --git a/packages/web/src/constants/header.constant.ts b/packages/web/src/constants/header.constant.ts index 362e35b11..b0b43cf3e 100644 --- a/packages/web/src/constants/header.constant.ts +++ b/packages/web/src/constants/header.constant.ts @@ -24,10 +24,6 @@ export const SIDE_MENU_NAV = { title: "Governance", path: "/governance", }, - AIRDROP: { - title: "Airdrop", - path: "/airdrop", - }, HELPCENTER: { title: "Help Center", path: "/helpcenter", diff --git a/packages/web/src/constants/option.constant.ts b/packages/web/src/constants/option.constant.ts index db3d378e5..20739c0ef 100644 --- a/packages/web/src/constants/option.constant.ts +++ b/packages/web/src/constants/option.constant.ts @@ -108,7 +108,7 @@ export const CHART_TYPE = { "7D": "7D", "1M": "1M", "1Y": "1Y", - YTD: "YTD", + ALL: "ALL", } as const; export type CHART_TYPE = ValuesType; @@ -126,7 +126,7 @@ export const PriceRangeTooltip: { Custom: undefined, }; -export const DEFAULT_SLIPPAGE = 0.5; +export const DEFAULT_SLIPPAGE = "0.5"; export type AddLiquiditySubmitType = | "CREATE_POOL" diff --git a/packages/web/src/constants/skeleton.constant.ts b/packages/web/src/constants/skeleton.constant.ts index 917d4e77b..2a6a9b4fd 100644 --- a/packages/web/src/constants/skeleton.constant.ts +++ b/packages/web/src/constants/skeleton.constant.ts @@ -1,4 +1,5 @@ import { css, keyframes, Theme } from "@emotion/react"; +import { media } from "@styles/media"; import { CSSProperties } from "react"; import { ValuesType } from "utility-types"; @@ -39,14 +40,14 @@ export const skeletonStyle = export const skeletonTrendingStyle = -(skeletonWidth: CSSProperties["width"], type: SHAPE_TYPES) => +(skeletonWidth: CSSProperties["width"], type: SHAPE_TYPES, seconds?: number) => (theme: Theme) => css` position: relative; width: ${typeof skeletonWidth === "number" ? `${skeletonWidth}px` : skeletonWidth}; - height: 24px; + height: 25px; background: ${theme.color.background23}; overflow: hidden; border-radius: ${type === SHAPE_TYPES.CIRCLE ? "50%" : "2px"}; @@ -63,7 +64,35 @@ export const skeletonTrendingStyle = ${theme.color.backgroundGradient} 0%, ${theme.color.backgroundGradient} 100% ); - animation: ${skeletonAni} 2s ease infinite; + animation: ${skeletonAni} ${seconds ? seconds : "3"}s ease infinite; + } + `; + +export const skeletonTokenDetail = +(skeletonWidth: CSSProperties["width"], type: SHAPE_TYPES, seconds?: number) => +(theme: Theme) => + css` + position: relative; + width: ${typeof skeletonWidth === "number" + ? `${skeletonWidth}px` + : skeletonWidth}; + height: 22px; + overflow: hidden; + border-radius: ${type === SHAPE_TYPES.CIRCLE ? "50%" : "2px"}; + z-index: 1; + &::after { + content: ""; + position: absolute; + left: 0%; + top: 0; + transform: translateX(-100%); + width: 100%; + height: 100%; + background: ${theme.color.backgroundGradient6}; + animation: ${skeletonAni} ${seconds ? seconds : "3"}s ease infinite; + } + ${media.mobile} { + height: 18px; } `; diff --git a/packages/web/src/constants/theme.constant.ts b/packages/web/src/constants/theme.constant.ts index fcf279dc4..e370e3bcc 100644 --- a/packages/web/src/constants/theme.constant.ts +++ b/packages/web/src/constants/theme.constant.ts @@ -45,6 +45,8 @@ const palette = { gradient4: "linear-gradient(to left, #536CD7, #A7B9F8)", gradient5: "linear-gradient(to right, #59678240, #59678200)", gradient6: "linear-gradient(0, #E0E8F4 0%, #E0E8F4 100%)", + gradient7: "linear-gradient(90deg, rgba(89, 103, 130, 0.25) 0%, rgba(89, 103, 130, 0) 99.91%)", + gradient8: "linear-gradient(90deg, #E0E8F4 0%, rgba(224, 232, 244, 0) 100%)", whiteGradient4: "linear-gradient(to left, #536CD7, #233DBD)", redDark: "#ff2e2e66", greenDark: "#2eff8266", @@ -146,6 +148,9 @@ export const DARK_THEME_COLORS: ThemeColorType = { background21: colors.dark.brand900BG, background22: colors.global.none, background23: colors.dark.gradientBG, + background24: colors.global.brand500, + background25: colors.dark.blackBG, + bgLoading: colors.global.black2, backgroundOpacity: colors.dark.blackOpacity07BG, backgroundOpacity2: colors.dark.blackOpacity07BG, backgroundOpacity3: colors.global.gray600Opacity, @@ -156,6 +161,7 @@ export const DARK_THEME_COLORS: ThemeColorType = { backgroundGradient3: colors.global.gradient3, backgroundGradient4: colors.global.gradient4, backgroundGradient5: colors.global.gradient5, + backgroundGradient6: colors.global.gradient7, border01: colors.dark.gray600Border1, border02: colors.dark.gray500Border2, border03: colors.dark.gray400Border3, @@ -229,6 +235,7 @@ export const DARK_THEME_COLORS: ThemeColorType = { greenDark: colors.global.greenDark, shadow: colors.global.shadowDark, shadow01: colors.global.shadowDark1, + shadow02: colors.global.shadowDark, }; export const LIGHT_THEME_COLORS: ThemeColorType = { @@ -257,6 +264,9 @@ export const LIGHT_THEME_COLORS: ThemeColorType = { background21: colors.global.whiteGradient4, background22: colors.global.brand500, background23: colors.white.whiteBG, + background24: colors.global.brand600, + background25: colors.white.gray100BG, + bgLoading: colors.white.whiteBG, backgroundOpacity: colors.white.gray100Opacity05BG, backgroundOpacity2: colors.white.whiteOpacity07BG, backgroundOpacity3: colors.white.gray100Opacity05BG, @@ -267,6 +277,7 @@ export const LIGHT_THEME_COLORS: ThemeColorType = { backgroundGradient3: colors.white.whiteBG, backgroundGradient4: colors.global.whiteGradient4, backgroundGradient5: colors.global.gradient6, + backgroundGradient6: colors.global.gradient8, border01: colors.white.gray100Border1, border02: colors.white.gray100Border1, border03: colors.white.gray200Border2, @@ -340,4 +351,5 @@ export const LIGHT_THEME_COLORS: ThemeColorType = { greenDark: colors.global.greenDark, shadow: colors.global.shadowLight, shadow01: colors.global.shadowLight, + shadow02: colors.global.none, }; diff --git a/packages/web/src/containers/asset-list-container/AssetListContainer.tsx b/packages/web/src/containers/asset-list-container/AssetListContainer.tsx index 066cc72c7..3c80f3a0b 100644 --- a/packages/web/src/containers/asset-list-container/AssetListContainer.tsx +++ b/packages/web/src/containers/asset-list-container/AssetListContainer.tsx @@ -77,12 +77,12 @@ export const dummyAssetList: Asset[] = [ balance: "0.1", }, { - id: "GNOS", + id: "GNS", logoUri: "https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xB98d4C97425d9908E66E53A6fDf673ACcA0BE986/logo.png", type: ASSET_TYPE.GRC20, name: "Gnoswap", - symbol: "GNOS", + symbol: "GNS", chain: "Gnoland", balance: "0.000000", }, diff --git a/packages/web/src/containers/best-pools-container/BestPoolsContainer.tsx b/packages/web/src/containers/best-pools-container/BestPoolsContainer.tsx index acf8ac709..0ed697573 100644 --- a/packages/web/src/containers/best-pools-container/BestPoolsContainer.tsx +++ b/packages/web/src/containers/best-pools-container/BestPoolsContainer.tsx @@ -1,7 +1,8 @@ -import React from "react"; +import React, { useState, useEffect } from "react"; import BestPools from "@components/token/best-pools/BestPools"; import { SwapFeeTierType } from "@constants/option.constant"; import { type TokenPairInfo } from "@models/token/token-pair-info"; +import { useRouter } from "next/router"; export interface BestPool { tokenPair: TokenPairInfo; @@ -40,7 +41,17 @@ export const bestPoolListInit: BestPool[] = [ ]; const BestPoolsContainer: React.FC = () => { - return ; + const [loading, setLoading] = useState(true); + const router = useRouter(); + + useEffect(() => { + const timeout = setTimeout(() => { + setLoading(false); + }, 2000); + return () => clearTimeout(timeout); + }, []); + + return ; }; export default BestPoolsContainer; diff --git a/packages/web/src/containers/connect-wallet-container/ConnectWalletContainer.tsx b/packages/web/src/containers/connect-wallet-container/ConnectWalletContainer.tsx index a05f94c0c..5542223ec 100644 --- a/packages/web/src/containers/connect-wallet-container/ConnectWalletContainer.tsx +++ b/packages/web/src/containers/connect-wallet-container/ConnectWalletContainer.tsx @@ -1,22 +1,32 @@ import ConnectWalletModal from "@components/common/connect-wallet-modal/ConnectWalletModal"; import { useClearModal } from "@hooks/common/use-clear-modal"; +import { useConnectWalletStatusModal } from "@hooks/wallet/use-connect-status-wallet-modal"; import { useWallet } from "@hooks/wallet/use-wallet"; -import React, { useCallback } from "react"; +import React, { useCallback, useEffect } from "react"; const ConnectWalletContainer = () => { const clearModal = useClearModal(); - const { connectAdenaClient } = useWallet(); + const { connectAdenaClient, loadingConnect } = useWallet(); + + const { openModal } = useConnectWalletStatusModal(); const close = useCallback(() => { clearModal(); }, [clearModal]); + + useEffect(() => { + if (loadingConnect === "error") { + openModal(); + } else if (loadingConnect === "done") { + close(); + } + }, [loadingConnect, close, openModal]); const connect = useCallback(() => { connectAdenaClient(); - close(); }, [connectAdenaClient, close]); - return ; + return ; }; export default ConnectWalletContainer; diff --git a/packages/web/src/containers/connect-wallet-status-modal-container/ConnectWalletStatusModalContainer.tsx b/packages/web/src/containers/connect-wallet-status-modal-container/ConnectWalletStatusModalContainer.tsx new file mode 100644 index 000000000..0d9fa500f --- /dev/null +++ b/packages/web/src/containers/connect-wallet-status-modal-container/ConnectWalletStatusModalContainer.tsx @@ -0,0 +1,27 @@ +import ConnectWalletStatusModal from "@components/common/connect-wallet-status-modal/ConnectWalletStatusModal"; +import { useClearModal } from "@hooks/common/use-clear-modal"; +import { useWallet } from "@hooks/wallet/use-wallet"; +import React, { useCallback, useEffect } from "react"; + +const ConnectWalletStatusModalContainer = () => { + const clearModal = useClearModal(); + const { connectAdenaClient, loadingConnect } = useWallet(); + + const close = useCallback(() => { + clearModal(); + }, [clearModal]); + + useEffect(() => { + if (loadingConnect === "done") { + close(); + } + }, [loadingConnect, close]); + + const connect = useCallback(() => { + connectAdenaClient(); + }, [connectAdenaClient, close]); + + return ; +}; + +export default ConnectWalletStatusModalContainer; diff --git a/packages/web/src/containers/dashboard-activities-container/DashboardActivitiesContainer.tsx b/packages/web/src/containers/dashboard-activities-container/DashboardActivitiesContainer.tsx index 6b2914d97..c4a7c9843 100644 --- a/packages/web/src/containers/dashboard-activities-container/DashboardActivitiesContainer.tsx +++ b/packages/web/src/containers/dashboard-activities-container/DashboardActivitiesContainer.tsx @@ -49,10 +49,10 @@ const SORT_PARAMS: { [key in TABLE_HEAD]: string } = { export const dummyTokenList: Activity[] = [ { - action: "Add GNOT and GNOS", + action: "Add GNOT and GNS", totalValue: "$12,090", tokenAmountOne: "100 ATOM", - tokenAmountTwo: "19 GNOS", + tokenAmountTwo: "19 GNS", account: "g129kua...ndsu12", time: "less than a minute ago", }, diff --git a/packages/web/src/containers/dashboard-info-container/DashboardInfoContainer.tsx b/packages/web/src/containers/dashboard-info-container/DashboardInfoContainer.tsx index ff7b91070..409f207f0 100644 --- a/packages/web/src/containers/dashboard-info-container/DashboardInfoContainer.tsx +++ b/packages/web/src/containers/dashboard-info-container/DashboardInfoContainer.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState, useEffect} from "react"; import DashboardInfo from "@components/dashboard/dashboard-info/DashboardInfo"; import { useWindowSize } from "@hooks/common/use-window-size"; @@ -22,11 +22,11 @@ export interface SupplyOverviewInfo { } const initialSupplyOverviewInfo: SupplyOverviewInfo = { - totalSupply: "1,000,000,000 GNOS", - circulatingSupply: "218,184,885 GNOS", - progressBar: "580 GNOS", - dailyBlockEmissions: "580 GNOS", - totalStaked: "152,412,148 GNOS", + totalSupply: "1,000,000,000 GNS", + circulatingSupply: "218,184,885 GNS", + progressBar: "580 GNS", + dailyBlockEmissions: "580 GNS", + totalStaked: "152,412,148 GNS", stakingRatio: "55.15%", }; @@ -43,11 +43,19 @@ const initialGovernenceOverviewInfo: GovernenceOverviewInfo = { holders: "14,072", passedProposals: "125", activeProposals: "2", - communityPool: "2,412,148 GNOS", + communityPool: "2,412,148 GNS", }; const DashboardInfoContainer: React.FC = () => { const { breakpoint } = useWindowSize(); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const timeout = setTimeout(() => { + setLoading(false); + }, 2000); + return () => clearTimeout(timeout); + }, []); return ( { supplyOverviewInfo={initialSupplyOverviewInfo} governenceOverviewInfo={initialGovernenceOverviewInfo} breakpoint={breakpoint} + loading={loading} /> ); }; diff --git a/packages/web/src/containers/earn-add-liquidity-container/EarnAddLiquidityContainer.tsx b/packages/web/src/containers/earn-add-liquidity-container/EarnAddLiquidityContainer.tsx index b194ec467..37343d959 100644 --- a/packages/web/src/containers/earn-add-liquidity-container/EarnAddLiquidityContainer.tsx +++ b/packages/web/src/containers/earn-add-liquidity-container/EarnAddLiquidityContainer.tsx @@ -260,11 +260,6 @@ const EarnAddLiquidityContainer: React.FC = () => { switchNetwork, ]); - const handleChangeSlippage = useCallback((vl: string) => { - - changeSlippage(Number(vl)); - }, []); - const handleClickOneStaking = useCallback(() => { if (!isEarnAdd) { setIsEarnAdd(true); @@ -296,7 +291,7 @@ const EarnAddLiquidityContainer: React.FC = () => { isEarnAdd={true} connected={connectedWallet} slippage={slippage} - changeSlippage={handleChangeSlippage} + changeSlippage={changeSlippage} handleClickOneStaking={handleClickOneStaking} openModal={openOneClickModal} /> diff --git a/packages/web/src/containers/gainer-and-loser-container/GainerAndLoserContainer.tsx b/packages/web/src/containers/gainer-and-loser-container/GainerAndLoserContainer.tsx index 109687d0b..c488d82af 100644 --- a/packages/web/src/containers/gainer-and-loser-container/GainerAndLoserContainer.tsx +++ b/packages/web/src/containers/gainer-and-loser-container/GainerAndLoserContainer.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState, useEffect } from "react"; import GainerAndLoser from "@components/token/gainer-and-loser/GainerAndLoser"; import { MATH_NEGATIVE_TYPE } from "@constants/option.constant"; @@ -79,7 +79,23 @@ export const losersInit = [ ]; const GainerAndLoserContainer: React.FC = () => { - return ; + const [loadingGain, setLoadingGain] = useState(true); + const [loadingLose, setLoadingLose] = useState(true); + + useEffect(() => { + const timeout = setTimeout(() => { + setLoadingGain(false); + setLoadingLose(false); + }, 2000); + return () => clearTimeout(timeout); + }, []); + + return ; }; export default GainerAndLoserContainer; diff --git a/packages/web/src/containers/header-container/HeaderContainer.tsx b/packages/web/src/containers/header-container/HeaderContainer.tsx index 80fd5c4e9..532aef652 100644 --- a/packages/web/src/containers/header-container/HeaderContainer.tsx +++ b/packages/web/src/containers/header-container/HeaderContainer.tsx @@ -13,6 +13,7 @@ import { CommonState, ThemeState } from "@states/index"; import { useAtom } from "jotai"; import { usePreventScroll } from "@hooks/common/use-prevent-scroll"; import { useConnectWalletModal } from "@hooks/wallet/use-connect-wallet-modal"; +import useEscCloseModal from "@hooks/common/use-esc-close-modal"; interface NegativeStatusType { status: MATH_NEGATIVE_TYPE; @@ -24,6 +25,8 @@ export interface Token { token: TokenInfo; price: string; priceOf1d: NegativeStatusType; + tokenB?: TokenInfo; + isLiquid?: boolean; } export const RecentdummyToken: Token[] = [ @@ -32,14 +35,63 @@ export const RecentdummyToken: Token[] = [ searchType: "recent", token: { path: "1", - name: "Bitcoin", - symbol: "BTC", - logoURI: "https://s2.coinmarketcap.com/static/img/coins/64x64/1.png", + name: "GNS", + symbol: "APR", + logoURI: "/gnos.svg", }, price: "$12,090.09", priceOf1d: { status: MATH_NEGATIVE_TYPE.POSITIVE, - value: "12.08%", + value: "52.4", + }, + tokenB: { + path: "1", + name: "GNOT", + symbol: "GNOT", + logoURI: "https://s2.coinmarketcap.com/static/img/coins/64x64/2.png", + }, + isLiquid: true, + }, + { + path: Math.floor(Math.random() * 50 + 1).toString(), + searchType: "recent", + token: { + path: "1", + name: "GNS", + symbol: "APR", + logoURI: "/gnos.svg", + }, + price: "$12,090.09", + priceOf1d: { + status: MATH_NEGATIVE_TYPE.POSITIVE, + value: "107.4", + }, + tokenB: { + path: "1", + name: "GNOT", + symbol: "GNOT", + logoURI: "https://s2.coinmarketcap.com/static/img/coins/64x64/2.png", + }, + }, + { + path: Math.floor(Math.random() * 50 + 1).toString(), + searchType: "recent", + token: { + path: "1", + name: "GNS", + symbol: "APR", + logoURI: "/gnos.svg", + }, + price: "$12,090.09", + priceOf1d: { + status: MATH_NEGATIVE_TYPE.POSITIVE, + value: "31.4", + }, + tokenB: { + path: "1", + name: "GNOT", + symbol: "GNOT", + logoURI: "https://s2.coinmarketcap.com/static/img/coins/64x64/2.png", }, }, ]; @@ -67,8 +119,6 @@ async function fetchTokens( ): Promise { return new Promise(resolve => setTimeout(resolve, 1500)).then(() => { const data = [ - ...RecentdummyToken, - ...RecentdummyToken, ...RecentdummyToken, ...PopulardummyToken, ...PopulardummyToken, @@ -85,10 +135,15 @@ const HeaderContainer: React.FC = () => { const [keyword, setKeyword] = useState(""); const { breakpoint } = useWindowSize(); const themeKey = useAtomValue(ThemeState.themeKey); - const { account, connected, disconnectWallet, switchNetwork, isSwitchNetwork } = useWallet(); + const { account, connected, disconnectWallet, switchNetwork, isSwitchNetwork, loadingConnect } = useWallet(); const { openModal } = useConnectWalletModal(); + const handleESC = () => { + setSearchMenuToggle(false); + } + useEscCloseModal(handleESC); + const { isFetched, error, @@ -136,6 +191,7 @@ const HeaderContainer: React.FC = () => { themeKey={themeKey} switchNetwork={switchNetwork} isSwitchNetwork={isSwitchNetwork} + loadingConnect={loadingConnect} /> ); }; diff --git a/packages/web/src/containers/home-swap-container/HomeSwapContainer.tsx b/packages/web/src/containers/home-swap-container/HomeSwapContainer.tsx index f88520059..6c1f72272 100644 --- a/packages/web/src/containers/home-swap-container/HomeSwapContainer.tsx +++ b/packages/web/src/containers/home-swap-container/HomeSwapContainer.tsx @@ -2,12 +2,15 @@ import { SwapDirectionType } from "@common/values"; import HomeSwap from "@components/home/home-swap/HomeSwap"; import { useSlippage } from "@hooks/common/use-slippage"; import { useTokenData } from "@hooks/token/use-token-data"; +import { useWallet } from "@hooks/wallet/use-wallet"; import { SwapTokenInfo } from "@models/swap/swap-token-info"; import { TokenModel } from "@models/token/token-model"; import { numberToUSD } from "@utils/number-utils"; import BigNumber from "bignumber.js"; import { useRouter } from "next/router"; import React, { useCallback, useMemo, useState } from "react"; +import { useAtom } from "jotai"; +import { SwapState } from "@states/index"; const TOKEN_A = { chainId: "dev", @@ -28,7 +31,7 @@ const TOKEN_B = { address: "g1sqaft388ruvsseu97r04w4rr4szxkh4nn6xpax", path: "gno.land/r/gnos", decimals: 4, - symbol: "GNOS", + symbol: "GNS", logoURI: "/gnos.svg", priceId: "gno.land/r/gnos", }; @@ -40,22 +43,27 @@ const HomeSwapContainer: React.FC = () => { const [tokenAAmount] = useState("1000"); const [tokenB, setTokenB] = useState(TOKEN_B); const [tokenBAmount] = useState("0"); - const [swapDirection] = useState("EXACT_IN"); + const [swapDirection, setSwapDirection] = useState("EXACT_IN"); const { slippage } = useSlippage(); + const { connected } = useWallet(); + const [, setSwapValue] = useAtom(SwapState.swap); + const tokenABalance = useMemo(() => { + if (!connected) return "-"; if (tokenA && balances[tokenA.priceId]) { return BigNumber(balances[tokenA.priceId] || 0).toFormat(); } return "0"; - }, [balances, tokenA]); + }, [connected ,balances, tokenA]); const tokenBBalance = useMemo(() => { + if (!connected) return "-"; if (tokenB && balances[tokenB.priceId]) { return BigNumber(balances[tokenB.priceId] || 0).toFormat(); } return "0"; - }, [balances, tokenB]); + }, [connected ,balances, tokenB]); const tokenAUSD = useMemo(() => { if (!tokenA || !tokenPrices[tokenA.priceId]) { @@ -104,19 +112,33 @@ const HomeSwapContainer: React.FC = () => { ]); const swapNow = useCallback(() => { - router.push("/swap?from=GNOT&to=GNOS"); - }, [router]); + if (swapDirection === "EXACT_IN") { + router.push("/swap?tokenA=gno.land/r/bar&tokenB=gno.land/r/foo&direction=EXACT_IN"); + } else { + router.push("/swap?tokenA=gno.land/r/foo&tokenB=gno.land/r/bar&direction=EXACT_IN"); + } + }, [router, swapDirection]); const onSubmitSwapValue = () => { setTokenA(tokenB); setTokenB(tokenA); + setSwapDirection(prev => prev === "EXACT_IN" ? "EXACT_OUT" : "EXACT_IN"); }; + const changeTokenAAmount = useCallback((value: string) => { + setSwapValue((prev) => ({ + ...prev, + tokenAAmount: value, + })); + }, []); + + return ( ); }; diff --git a/packages/web/src/containers/modal-container/ModalContainer.tsx b/packages/web/src/containers/modal-container/ModalContainer.tsx index f2cf39b07..152d504bf 100644 --- a/packages/web/src/containers/modal-container/ModalContainer.tsx +++ b/packages/web/src/containers/modal-container/ModalContainer.tsx @@ -1,12 +1,14 @@ import React, { useCallback, useMemo } from "react"; import Modal from "@components/common/modal/Modal"; import { useAtom } from "jotai"; -import { CommonState } from "@states/index"; +import { CommonState, WalletState } from "@states/index"; import { usePreventScroll } from "@hooks/common/use-prevent-scroll"; +import useEscCloseModal from "@hooks/common/use-esc-close-modal"; const ModalContainer: React.FC = () => { const [openedModal, setOpendModal] = useAtom(CommonState.openedModal); const [modalContent] = useAtom(CommonState.modalContent); + const [, setWalletAccount] = useAtom(WalletState.loadingConnect); const visible = useMemo(() => { return openedModal && modalContent !== null; @@ -16,7 +18,9 @@ const ModalContainer: React.FC = () => { const closeModal = useCallback(() => { setOpendModal(false); + setWalletAccount("initial"); }, [setOpendModal]); + useEscCloseModal(closeModal); return visible ? ( { const [currentPool, setCurrentPool] = useState(pools[0]); const [, setPool] = useAtom(EarnState.pool); - const { connected, connectAdenaClient } = useWallet(); + const { connected, connectAdenaClient, isSwitchNetwork } = useWallet(); const [currentToken, setCurrentToken] = useState(null); const [poolDetail, setPoolDetail] = useState(null); @@ -83,6 +83,9 @@ const PoolAddIncentivizeContainer: React.FC = () => { if (!connected) { return false; } + if (isSwitchNetwork) { + return false; + } if (!currentPool) { return true; } @@ -102,6 +105,9 @@ const PoolAddIncentivizeContainer: React.FC = () => { if (!connected) { return "Wallet Login"; } + if (isSwitchNetwork) { + return "Switch to Gnoland"; + } if (!currentPool) { return "Select Pool"; } @@ -115,7 +121,7 @@ const PoolAddIncentivizeContainer: React.FC = () => { return "Insufficient Balance"; } return "Incentivize Pool"; - }, [connected, currentPool, tokenAmountInput]); + }, [connected, currentPool, tokenAmountInput, isSwitchNetwork]); return ( { return; } }, [initialized, router, tokenA?.path, tokenB?.path, tokens]); - - const handleChangeSlippage = useCallback((vl: string) => { - changeSlippage(Number(vl)); - }, []); return ( { isEarnAdd={false} connected={connectedWallet} slippage={slippage} - changeSlippage={handleChangeSlippage} + changeSlippage={changeSlippage} openModal={openOneClickModal} /> ); diff --git a/packages/web/src/containers/pool-incentivize-container/PoolIncentivizeContainer.tsx b/packages/web/src/containers/pool-incentivize-container/PoolIncentivizeContainer.tsx index 8e2ea194e..a10617da3 100644 --- a/packages/web/src/containers/pool-incentivize-container/PoolIncentivizeContainer.tsx +++ b/packages/web/src/containers/pool-incentivize-container/PoolIncentivizeContainer.tsx @@ -26,7 +26,7 @@ const PoolIncentivizeContainer: React.FC = () => { const [, setDataModal] = useAtom(EarnState.dataModal); const [currentPool, setCurrentPool] = useAtom(EarnState.pool); - const { connected, connectAdenaClient } = useWallet(); + const { connected, connectAdenaClient, isSwitchNetwork } = useWallet(); const [currentToken, setCurrentToken] = useState(null); const [poolDetail, setPoolDetail] = useState(null); @@ -79,6 +79,9 @@ const PoolIncentivizeContainer: React.FC = () => { if (!connected) { return false; } + if (isSwitchNetwork) { + return false; + } if (!currentPool) { return true; } @@ -92,12 +95,15 @@ const PoolIncentivizeContainer: React.FC = () => { return true; } return false; - }, [connected, currentPool, tokenAmountInput]); + }, [connected, currentPool, tokenAmountInput, isSwitchNetwork]); const textBtn = useMemo(() => { if (!connected) { return "Wallet Login"; } + if (isSwitchNetwork) { + return "Switch to Gnoland"; + } if (!currentPool) { return "Select Pool"; } @@ -111,7 +117,7 @@ const PoolIncentivizeContainer: React.FC = () => { return "Insufficient Balance"; } return "Incentivize Pool"; - }, [connected, currentPool, tokenAmountInput]); + }, [connected, currentPool, tokenAmountInput, isSwitchNetwork]); return ( { const [isShowCancelled, toggleShowCancelled] = useState(false); const [isShowProposalModal, setIsShowProposalModal] = useState(false); const [isShowCreateProposal, setIsShowCreateProposal] = useState(false); - const { isSwitchNetwork, connected } = useWallet(); + const { isSwitchNetwork, connected, switchNetwork } = useWallet(); const { breakpoint } = useWindowSize(); + const { openModal } = useConnectWalletModal(); const { data: proposalList = [] } = useQuery({ queryKey: ["proposalList", isShowCancelled], @@ -112,6 +114,14 @@ const ProposalListContainer: React.FC = () => { setIsShowProposalModal(true); }; + const handleSelectVote = useCallback(() => { + if (!connected) { + openModal(); + } else if (isSwitchNetwork) { + switchNetwork(); + } + }, [switchNetwork, openModal, isSwitchNetwork, connected]); + return ( { setIsShowCreateProposal(!isShowCreateProposal) } isSwitchNetwork={isSwitchNetwork} + handleSelectVote={handleSelectVote} /> ); }; diff --git a/packages/web/src/containers/select-token-container/SelectTokenContainer.tsx b/packages/web/src/containers/select-token-container/SelectTokenContainer.tsx index 230a202c4..7b53b4bf4 100644 --- a/packages/web/src/containers/select-token-container/SelectTokenContainer.tsx +++ b/packages/web/src/containers/select-token-container/SelectTokenContainer.tsx @@ -10,11 +10,13 @@ import useEscCloseModal from "@hooks/common/use-esc-close-modal"; interface SelectTokenContainerProps { changeToken?: (token: TokenModel) => void; callback?: (value: boolean) => void; + modalRef?: React.RefObject; } const SelectTokenContainer: React.FC = ({ changeToken, callback, + modalRef, }) => { const { tokens, balances, updateTokens, updateBalances } = useTokenData(); const [keyword, setKeyword] = useState(""); @@ -71,6 +73,7 @@ const SelectTokenContainer: React.FC = ({ changeToken={selectToken} close={close} themeKey={themeKey} + modalRef={modalRef} /> ); }; diff --git a/packages/web/src/containers/swap-container/SwapContainer.tsx b/packages/web/src/containers/swap-container/SwapContainer.tsx index a08cfc5cb..bee4305f9 100644 --- a/packages/web/src/containers/swap-container/SwapContainer.tsx +++ b/packages/web/src/containers/swap-container/SwapContainer.tsx @@ -20,11 +20,12 @@ import { useRouter } from "next/router"; import { usePreventScroll } from "@hooks/common/use-prevent-scroll"; import { useNotice } from "@hooks/common/use-notice"; import { useConnectWalletModal } from "@hooks/wallet/use-connect-wallet-modal"; +import { TNoticeType } from "src/context/NoticeContext"; import { useSlippage } from "@hooks/common/use-slippage"; const SwapContainer: React.FC = () => { const [swapValue, setSwapValue] = useAtom(SwapState.swap); - const { tokenA = null, tokenB = null, type = "EXACT_IN" } = swapValue; + const { tokenA = null, tokenB = null, type = "EXACT_IN", tokenAAmount: defaultTokenAAmount } = swapValue; const themeKey = useAtomValue(ThemeState.themeKey); const router = useRouter(); const { setNotice } = useNotice(); @@ -33,13 +34,13 @@ const SwapContainer: React.FC = () => { const [initialized, setInitialized] = useState(false); const { connected: connectedWallet, isSwitchNetwork, switchNetwork } = useWallet(); const { tokens, tokenPrices, balances, updateTokens, updateTokenPrices, updateBalances, getTokenUSDPrice, getTokenPriceRate } = useTokenData(); - const [tokenAAmount, setTokenAAmount] = useState(""); + const [tokenAAmount, setTokenAAmount] = useState(defaultTokenAAmount ?? ""); const [tokenBAmount, setTokenBAmount] = useState(""); const [submitted, setSubmitted] = useState(false); const [copied, setCopied] = useState(false); const [swapResult, setSwapResult] = useState(null); - const { slippage: storedSlippage, changeSlippage: changeStoredSlippage } = useSlippage(); - const [slippage, setSlippage] = useState(storedSlippage); + const { slippage, changeSlippage } = useSlippage(); + const [gasFeeAmount] = useState({ amount: 0.000001, currency: "GNOT" @@ -111,7 +112,7 @@ const SwapContainer: React.FC = () => { const swapButtonText = useMemo(() => { if (!connectedWallet) { - return "Connect Wallet"; + return "Wallet Login"; } if (isSwitchNetwork) { return "Switch to Gnoland"; @@ -158,12 +159,13 @@ const SwapContainer: React.FC = () => { setSwapResult(null); setOpenedConfirModal(false); updateBalances(); - if (swapResult?.success) { - setNotice(null, { timeout: 2000 }); - } - }, [updateBalances, swapResult]); + }, [updateBalances]); - const changeTokenAAmount = useCallback((value: string) => { + const changeTokenAAmount = useCallback((value: string, none?: boolean) => { + if (none) { + setIsLoading(false); + return; + } if (!matchInputNumber(value)) { return; } @@ -180,7 +182,11 @@ const SwapContainer: React.FC = () => { setQuery({ ...query, direction: "EXACT_IN" }); }, [query, setTokenBAmount]); - const changeTokenBAmount = useCallback((value: string) => { + const changeTokenBAmount = useCallback((value: string, none?: boolean) => { + if (none) { + setIsLoading(false); + return; + } if (!matchInputNumber(value)) { return; } @@ -197,13 +203,6 @@ const SwapContainer: React.FC = () => { setQuery({ ...query, direction: "EXACT_OUT" }); }, [query]); - const changeSlippage = useCallback((value: string) => { - setSlippage(BigNumber(value || 0).toNumber()); - if (!Number.isNaN(value)) { - changeStoredSlippage(Number(value)); - } - }, []); - const swapTokenInfo: SwapTokenInfo = useMemo(() => { return { tokenA, @@ -220,7 +219,7 @@ const SwapContainer: React.FC = () => { slippage }; }, [slippage, type, tokenA, tokenAAmount, tokenABalance, tokenAUSD, tokenB, tokenBAmount, tokenBBalance, tokenBUSD]); - + const swapSummaryInfo: SwapSummaryInfo | null = useMemo(() => { if (!tokenA || !tokenB) { return null; @@ -367,18 +366,45 @@ const SwapContainer: React.FC = () => { setSubmitted(true); const swapAmount = type === "EXACT_IN" ? tokenAAmount : tokenBAmount; swap(estimatedRoutes, swapAmount).then(result => { + if (result !== false) { + setNotice(null, {timeout: 50000, type: "pending", closeable: true, id: Math.random() * 19999}); + setTimeout(() => { + if (!!result) { + setNotice(null, {timeout: 50000, type: "success" as TNoticeType, closeable: true, id: Math.random() * 19999}); + } else { + setNotice(null, {timeout: 50000, type: "error" as TNoticeType, closeable: true, id: Math.random() * 19999}); + } + }, 1000); + } setSwapResult({ success: result !== null, hash: (result as unknown as SwapResponse)?.tx_hash || "", }); + }).catch(() => { + setSwapResult({ + success: false, + hash: "", + }); }); } useEffect(() => { updateTokens(); updateTokenPrices(); + setSwapValue({ + tokenA: null, + tokenB: null, + type: "EXACT_IN", + }); }, []); + useEffect(() => { + if (defaultTokenAAmount) { + setIsLoading(true); + } + }, [defaultTokenAAmount]); + + useEffect(() => { if (!tokenA || !tokenB) { return; @@ -448,7 +474,7 @@ const SwapContainer: React.FC = () => { return; } }, [initialized, router, tokenA?.path, tokenB?.path, tokens]); - + return ( { const date = new Date(now); date.setDate(date.getDate() - 1 * index); - const monthStr = `${date.getMonth() + 1}`.padStart(2, "0"); - const dateStr = `${date.getDate()}`.padStart(2, "0"); - return `${monthStr}-${dateStr}`; + const monthStr = months[date.getMonth()]; + const dayStr = `${date.getDate()}`.padStart(2, "0"); + const yearStr = date.getFullYear(); + return `${monthStr} ${dayStr}, ${yearStr}`; }).reverse(); case "1M": return Array.from({ length: 8 }, (_, index) => { const date = new Date(now); date.setMonth(date.getMonth() - 1 * index); + const monthStr = months[date.getMonth()]; + const dayStr = `${date.getDate()}`.padStart(2, "0"); const yearStr = date.getFullYear(); - const monthStr = `${date.getMonth() + 1}`.padStart(2, "0"); - return `${yearStr}-${monthStr}`; + + return `${monthStr} ${dayStr}, ${yearStr}`; }).reverse(); case "1Y": return Array.from({ length: 8 }, (_, index) => { const date = new Date(now); date.setFullYear(date.getFullYear() - 1 * index); + const monthStr = months[date.getMonth()]; + const dayStr = `${date.getDate()}`.padStart(2, "0"); const yearStr = date.getFullYear(); - return `${yearStr}`; + + return `${monthStr} ${dayStr}, ${yearStr}`; }).reverse(); - case "YTD": + case "ALL": default: return Array.from({ length: 10 }, (_, index) => { const date = new Date(now); date.setMonth(date.getMonth() - 1 * index); + const monthStr = months[date.getMonth()]; + const dayStr = `${date.getDate()}`.padStart(2, "0"); const yearStr = date.getFullYear(); - const monthStr = `${date.getMonth() + 1}`.padStart(2, "0"); - return `${yearStr}-${monthStr}`; + + return `${monthStr} ${dayStr}, ${yearStr}`; }).reverse(); } } function createDummyAmountDatas() { - const length = 24; + const length = 55; return Array.from({ length }, (_, index) => { const date = new Date(); date.setHours(date.getHours() - index); @@ -108,8 +128,27 @@ function createDummyAmountDatas() { } const TokenChartContainer: React.FC = () => { - const [tokenInfo] = useState(dummyTokenInfo); + const [tokenInfo, setTokenInfo] = useState(dummyTokenInfo); const [currentTab, setCurrentTab] = useState("1D"); + const [loading, setLoading] = useState(true); + + const router = useRouter(); + useEffect(() => { + const currentToken = TOKEN_LIST.filter(item => item.symbol === router.query["token-path"])[0]; + setTokenInfo(prev => ({ + ...prev, + token: { + ...currentToken + } + })); + }, [router.query]); + + useEffect(() => { + const timeout = setTimeout(() => { + setLoading(false); + }, 2000); + return () => clearTimeout(timeout); + }, []); const changeTab = useCallback((tab: string) => { const currentTab = TokenChartGraphPeriods.find(period => `${period}` === tab) || "1D"; @@ -136,15 +175,19 @@ const TokenChartContainer: React.FC = () => { }; const getYAxisLabels = (): string[] => { - return ["1", "2", "3", "4", "5", "6", "7"]; + const fake1 = ["10", "20", "30", "40", "50", "60", "70"]; + const fake2 = ["100", "200", "300", "400", "500", "600", "700"]; + const fake3 = ["1000", "2000", "3000", "4000", "5000", "6000", "7000"]; + return [fake1, fake2, fake3][Math.floor(Math.random() * 3)]; }; - + return ( ); }; diff --git a/packages/web/src/containers/token-description-container/TokenDescriptionContainer.tsx b/packages/web/src/containers/token-description-container/TokenDescriptionContainer.tsx index fe5936060..2c4b07b75 100644 --- a/packages/web/src/containers/token-description-container/TokenDescriptionContainer.tsx +++ b/packages/web/src/containers/token-description-container/TokenDescriptionContainer.tsx @@ -1,28 +1,72 @@ -import React from "react"; +import React, { useState, useEffect } from "react"; import TokenDescription from "@components/token/token-description/TokenDescription"; +import { useRouter } from "next/router"; +import TOKEN_LIST from "@repositories/token/mock/assets.json"; -export const descriptionInit = { +export interface DescriptionInfo { token: { - path: "1", + name: string; + symbol: string; + image: string; + pkg_path: string + decimals: number + description: string + website_url: string + }; + links: { + Website: string; + Gnoscan: string; + }; +} + +export const descriptionInit: DescriptionInfo = { + token: { + pkg_path: "1", name: "Bitcoin", symbol: "BTC", - logoURI: "https://s2.coinmarketcap.com/static/img/coins/64x64/1.png", + image: "/gnos.svg", + decimals: 1, + description: "string", + website_url: "string", }, - desc: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nisi orci, ultrices sit amet mi eget, efficitur elementum tellus. Integer augue purus, rutrum eu pretium sit amet, varius in quam. In auctor gravida pretium. Maecenas est ante, pulvinar id accumsan eget, aliquam ac turpis. Donec vulputate nunc tellus. In sit amet mattis nisl. Mauris libero quam, hendrerit dapibus scelerisque eu, fringilla sit amet sapien. Proin ut semper risus. Vivamus fringilla eget lectus ut faucibus. Proin vitae mollis massa. Sed bibendum tortor eget aliquam commodo. Etiam et eleifend augue. Donec molestie placerat elit.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nisi orci, ultrices sit amet mi eget, efficitur elementum tellus. Integer augue purus, rutrum eu pretium sit amet, varius in quam. In auctor gravida pretium. Maecenas est ante, pulvinar id accumsan eget, aliquam ac turpis. Donec vulputate nunc tellus. In sit amet mattis nisl. Mauris libero quam, hendrerit dapibus scelerisque eu Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nisi orci, ultrices sit amet mi eget, efficitur elementum tellus. Integer augue purus, rutrum eu pretium sit amet, varius in quam. In auctor gravida pretium. Maecenas est ante, pulvinar id accumsan eget, aliquam ac turpis. Donec vulputate nunc tellus. In sit amet mattis nisl. Mauris libero quam, hendrerit dapibus scelerisque euetur adipiscing elit. Phasellus nisi orci, ultrices sit amet mi eget, efficitur elementum tellus. Integer augue purus, rutrum eu pretium sit amet, varius in quam. In auctor gravida pretium. Maecenas est ante, pulvinar id accumsan eget, aliquam ac turpis. Donec vulputate nunc tellus. In sit amet mattis nisl. Mauris libero quam, hendrerit dapibus scelerisque euetur adipiscing elit. Phasellus nisi orci, ultrices sit amet mi eget, efficitur elementum tellus. Integer augue purus, rutrum eu pretium sit amet, varius in quam. In auctor gravida pretium. Maecenas est ante, pulvinar id accumsan eget, aliquam ac turpis. Donec vulputate nunc tellus. In sit amet mattis nisl. Mauris libero quam, hendrerit dapibus scelerisque euetur adipiscing elit. Phasellus nisi orci, ultrices sit amet mi eget, efficitur elementum tellus. Integer augue purus, rutrum eu pretium sit amet, varius in quam. In auctor gravida pretium. Maecenas est ante, pulvinar id accumsan eget, aliquam ac turpis. Donec vulputate nunc tellus. In sit amet mattis nisl. Mauris libero quam, hendrerit dapibus scelerisque eu.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nisi orci, ultrices sit amet mi eget, efficitur elementum tellus. Integer augue purus, rutrum eu pretium sit amet, varius in quam. In auctor gravida pretium. Maecenas est ante, pulvinar id accumsan eget, aliquam ac turpis. Donec vulputate nunc tellus. In sit amet mattis nisl. Mauris libero quam, hendrerit dapibus scelerisque euetur adipiscing elit. Phasellus nisi orci, ultrices sit amet mi eget, efficitur elementum tellus. Integer augue purus, rutrum eu pretium sit amet, varius in quam. In auctor gravida pretium. Maecenas est ante, pulvinar id accumsan eget, aliquam ac turpis. Donec vulputate nunc tellus. In sit amet mattis nisl. Mauris libero quam, hendrerit dapibus scelerisque euetur adipiscing elit. Phasellus nisi orci, ultrices sit amet mi eget, efficitur elementum tellus. Integer augue purus, rutrum eu pretium sit amet, varius in quam. In auctor gravida pretium. Maecenas est ante, pulvinar id accumsan eget, aliquam ac turpis. Donec vulputate nunc tellus.", links: { - Gnoscan: "gnoscan.io/tokens/r_demo_grc20_GNOS", - Analytics: "info.gnoswap.io/tokens/r_demo_grc20_GNOS", Website: "gnoswap.io", + Gnoscan: "gnoscan.io/tokens/r_demo_grc20_GNOS", }, }; const TokenDescriptionContainer: React.FC = () => { + const [loading, setLoading] = useState(true); + const [descriptionInfo, setDescriptionInfo] = useState(descriptionInit); + const router = useRouter(); + useEffect(() => { + const current = TOKEN_LIST.filter(item => item.symbol === router.query["token-path"])[0]; + if (current) { + setDescriptionInfo(prev => ({ + ...prev, + token: { + ...current + }, + links: { + Website: current.website_url, + Gnoscan: "gnoscan.io/tokens/r_demo_grc20_GNOS", + } + })); + } + }, [router.query]); + useEffect(() => { + const timeout = setTimeout(() => { + setLoading(false); + }, 2000); + return () => clearTimeout(timeout); + }, []); return ( ); }; diff --git a/packages/web/src/containers/token-info-content-container/TokenInfoContentContainer.tsx b/packages/web/src/containers/token-info-content-container/TokenInfoContentContainer.tsx index ebd026ead..28bcb4fbd 100644 --- a/packages/web/src/containers/token-info-content-container/TokenInfoContentContainer.tsx +++ b/packages/web/src/containers/token-info-content-container/TokenInfoContentContainer.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState, useEffect } from "react"; import TokenInfoContent from "@components/token/token-info-content/TokenInfoContent"; import { MATH_NEGATIVE_TYPE } from "@constants/option.constant"; @@ -76,11 +76,27 @@ export const marketInformationInit = { }; const TokenInfoContentContainer: React.FC = () => { + const [loadingPricePerform, setLoadingPricePerform] = useState(true); + const [loadingPriceInfo, setLoadingPriceInfo] = useState(true); + const [loadingMarketInfo, setLoadingMarketInfo] = useState(true); + + useEffect(() => { + const timeout = setTimeout(() => { + setLoadingPricePerform(false); + setLoadingPriceInfo(false); + setLoadingMarketInfo(false); + }, 2000); + return () => clearTimeout(timeout); + }, []); + return ( ); }; diff --git a/packages/web/src/containers/token-list-container/TokenListContainer.tsx b/packages/web/src/containers/token-list-container/TokenListContainer.tsx index 72e2ad9c7..6e27675d9 100644 --- a/packages/web/src/containers/token-list-container/TokenListContainer.tsx +++ b/packages/web/src/containers/token-list-container/TokenListContainer.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState } from "react"; +import React, { useCallback, useState, useEffect } from "react"; import TokenList from "@components/home/token-list/TokenList"; import { MATH_NEGATIVE_TYPE } from "@constants/option.constant"; import { type TokenInfo } from "@models/token/token-info"; @@ -6,6 +6,7 @@ import { type TokenPairInfo } from "@models/token/token-pair-info"; import { useQuery } from "@tanstack/react-query"; import { ValuesType } from "utility-types"; import { useWindowSize } from "@hooks/common/use-window-size"; +import useClickOutside from "@hooks/common/use-click-outside"; interface NegativeStatusType { status: MATH_NEGATIVE_TYPE; value: string; @@ -164,8 +165,19 @@ const TokenListContainer: React.FC = () => { const [sortOption, setSortOption] = useState(); const { breakpoint } = useWindowSize(); const [searchIcon, setSearchIcon] = useState(false); + const [componentRef, isClickOutside, setIsInside] = useClickOutside(); + + useEffect(() => { + if (!keyword) { + if (isClickOutside) { + setSearchIcon(false); + } + } + }, [isClickOutside, keyword]); + const onTogleSearch = () => { setSearchIcon(prev => !prev); + setIsInside(true); }; const { @@ -253,6 +265,7 @@ const TokenListContainer: React.FC = () => { breakpoint={breakpoint} searchIcon={searchIcon} onTogleSearch={onTogleSearch} + searchRef={componentRef} /> ); }; diff --git a/packages/web/src/containers/token-swap-container/TokenSwapContainer.tsx b/packages/web/src/containers/token-swap-container/TokenSwapContainer.tsx index 0f6531e60..bde3c3ba6 100644 --- a/packages/web/src/containers/token-swap-container/TokenSwapContainer.tsx +++ b/packages/web/src/containers/token-swap-container/TokenSwapContainer.tsx @@ -12,90 +12,19 @@ import { useTokenData } from "@hooks/token/use-token-data"; import BigNumber from "bignumber.js"; import { SwapError } from "@common/errors/swap"; import { TokenModel } from "@models/token/token-model"; -import { DataTokenInfo } from "@models/token/token-swap-model"; import { matchInputNumber, numberToUSD } from "@utils/number-utils"; import { AmountModel } from "@models/common/amount-model"; -import { amountEmptyNumberInit } from "@common/values"; +import { SwapDirectionType, amountEmptyNumberInit } from "@common/values"; import { SwapRouteInfo } from "@models/swap/swap-route-info"; import { swapRouteInfos as tempSwapRouteInfos } from "@components/swap/swap-card/SwapCard.stories"; import { useConnectWalletModal } from "@hooks/wallet/use-connect-wallet-modal"; - -const swapSummaryInfoTemp: SwapSummaryInfo = { - tokenA: { - chainId: "test3", - address: "0x111111111117dC0aa78b770fA6A738034120C302", - path: "gno.land/r/demo/1inch", - name: "1inch", - symbol: "1INCH", - decimals: 6, - logoURI: - "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", - priceId: "1inch", - createdAt: "1999-01-01T00:00:01Z", - }, - tokenB: { - chainId: "test3", - address: "0x111111111117dC0aa78b770fA6A738034120C302", - path: "gno.land/r/demo/1inch", - name: "1inch", - symbol: "1INCH", - decimals: 6, - logoURI: - "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", - priceId: "1inch", - createdAt: "1999-01-01T00:00:01Z", - }, - swapDirection: "EXACT_IN", - swapRate: 1.14, - swapRateUSD: 1.14, - priceImpact: 0.3, - guaranteedAmount: { - amount: 45124, - currency: "GNOT", - }, - gasFee: { - amount: 0.000001, - currency: "GNOT", - }, - gasFeeUSD: 0.1, -}; - -const swapTokenInfo: SwapTokenInfo = { - tokenA: { - chainId: "test3", - address: "0x111111111117dC0aa78b770fA6A738034120C302", - path: "gno.land/r/demo/1inch", - name: "1inch", - symbol: "1INCH", - decimals: 6, - logoURI: - "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", - priceId: "1inch", - createdAt: "1999-01-01T00:00:01Z", - }, - tokenAAmount: "0", - tokenABalance: "0", - tokenAUSD: 0, - tokenAUSDStr: "0", - tokenB: { - chainId: "test3", - address: "0x111111111117dC0aa78b770fA6A738034120C302", - path: "gno.land/r/demo/1inch", - name: "1inch", - symbol: "1INCH", - decimals: 6, - logoURI: - "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", - priceId: "1inch", - createdAt: "1999-01-01T00:00:01Z", - }, - tokenBAmount: "0", - tokenBBalance: "0", - tokenBUSD: 0, - tokenBUSDStr: "0", - direction: "EXACT_IN", - slippage: 10, -}; +import { useSlippage } from "@hooks/common/use-slippage"; +import { usePreventScroll } from "@hooks/common/use-prevent-scroll"; +import { SwapResultInfo } from "@models/swap/swap-result-info"; +import { useNotice } from "@hooks/common/use-notice"; +import { SwapResponse } from "@repositories/swap"; +import { TNoticeType } from "src/context/NoticeContext"; +import { useRouter } from "next/router"; const TokenSwapContainer: React.FC = () => { const [swapValue, setSwapValue] = useAtom(TokenState.swap); @@ -111,15 +40,39 @@ const TokenSwapContainer: React.FC = () => { const [swapRate] = useState(100); const [gasFeeAmount] = useState(amountEmptyNumberInit); const [swapRouteInfos] = useState(tempSwapRouteInfos); - const [slippage, setSlippage] = useState(1); + const { slippage, changeSlippage } = useSlippage(); + const [submitted, setSubmitted] = useState(false); + const [swapResult, setSwapResult] = useState(null); + + const [initialized, setInitialized] = useState(false); + const [query, setQuery] = useState<{ [key in string]: string | null }>({}); + const router = useRouter(); - const { openModal } = useConnectWalletModal(); + const { setNotice } = useNotice(); + const { openModal } = useConnectWalletModal(); + + usePreventScroll(openedConfirmModal || submitted); + + useEffect(() => { + const queryValues = []; + for (const [key, value] of Object.entries(query)) { + if (value) { + queryValues.push(`${key}=${value}`); + } + } + // if (queryValues.length > 0) { + // const path = `/tokens/${router.query["token-path"]}?${queryValues.join("&")}`; + // router.replace(path); + // } + }, [query]); const { + tokens, tokenPrices, balances, updateTokens, updateTokenPrices, + updateBalances, } = useTokenData(); const { connected: connectedWallet, @@ -127,17 +80,52 @@ const TokenSwapContainer: React.FC = () => { switchNetwork, } = useWallet(); - const { estimateSwapRoute } = useSwap({ + const { swap, estimateSwapRoute, estimatedRoutes, tokenAmountLimit } = useSwap({ tokenA, tokenB, direction: type, - slippage: 1, + slippage, }); const themeKey = useAtomValue(ThemeState.themeKey); const swapNow = useCallback(() => { - setOpenedConfirmModal(true); - }, []); + if (!tokenA || !tokenB) { + return; + } + setSubmitted(true); + const swapAmount = type === "EXACT_IN" ? tokenAAmount : tokenBAmount; + swap(estimatedRoutes, swapAmount).then(result => { + if (result !== false) { + setNotice(null, {timeout: 50000, type: "pending", closeable: true, id: Math.random() * 19999}); + setTimeout(() => { + if (!!result) { + setNotice(null, {timeout: 50000, type: "success" as TNoticeType, closeable: true, id: Math.random() * 19999}); + } else { + setNotice(null, {timeout: 50000, type: "error" as TNoticeType, closeable: true, id: Math.random() * 19999}); + } + }, 1000); + } + setSwapResult({ + success: !!result, + hash: (result as unknown as SwapResponse)?.tx_hash || "", + }); + }).catch(() => { + setSwapResult({ + success: false, + hash: "", + }); + }); + }, [ + setSwapResult, + setSubmitted, + setNotice, + swap, + tokenAAmount, + tokenBAmount, + tokenA, + tokenB, + type, + ]); const connectWallet = useCallback(() => { if (!connectedWallet) { @@ -281,30 +269,6 @@ const TokenSwapContainer: React.FC = () => { isLoading, ]); - const dataTokenInfo: DataTokenInfo = useMemo(() => { - return { - tokenA, - tokenAAmount, - tokenABalance, - tokenAUSDStr: numberToUSD(tokenAUSD), - tokenB, - tokenBAmount, - tokenBBalance, - tokenBUSDStr: numberToUSD(tokenBUSD), - direction: type, - }; - }, [ - type, - tokenA, - tokenB, - tokenAAmount, - tokenB, - tokenABalance, - tokenBBalance, - tokenAUSD, - tokenBUSD, - ]); - const changeTokenA = useCallback( (token: TokenModel) => { let changedSwapDirection = type; @@ -337,12 +301,17 @@ const TokenSwapContainer: React.FC = () => { prev.tokenA?.symbol === token.symbol ? prev.tokenB : prev.tokenA, type: changedSwapDirection, })); + router.push(`/tokens/${token.symbol}?tokenB=${token.path}&direction=EXACT_IN`); }, [tokenA, type, tokenBAmount, tokenAAmount] ); const changeTokenAAmount = useCallback( - (value: string) => { + (value: string, none?: boolean) => { + if (none) { + setIsLoading(false); + return; + } if (!matchInputNumber(value)) { return; } @@ -361,7 +330,11 @@ const TokenSwapContainer: React.FC = () => { ); const changeTokenBAmount = useCallback( - (value: string) => { + (value: string, none?: boolean) => { + if (none) { + setIsLoading(false); + return; + } if (!matchInputNumber(value)) { return; } @@ -386,7 +359,7 @@ const TokenSwapContainer: React.FC = () => { const swapButtonText = useMemo(() => { if (!connectedWallet) { - return "Connect Wallet"; + return "Wallet Login"; } if (isSwitchNetwork) { return "Switch to Gnoland"; @@ -439,7 +412,7 @@ const TokenSwapContainer: React.FC = () => { return true; } if (isSwitchNetwork) { - return false; + return true; } if (!tokenA || !tokenB) { return false; @@ -497,31 +470,74 @@ const TokenSwapContainer: React.FC = () => { swapRateUSD, priceImpact: 0.1, guaranteedAmount: { - amount: BigNumber(tokenBAmount).toNumber(), + amount: tokenAmountLimit, currency: type === "EXACT_IN" ? tokenB.symbol : tokenA.symbol, }, gasFee: gasFeeAmount, gasFeeUSD, }; - }, [gasFeeAmount, type, swapRate, tokenA, tokenB, tokenBAmount]); + }, [gasFeeAmount, type, swapRate, tokenA, tokenB, tokenAmountLimit]); - const changeSlippage = useCallback((value: string) => { - setSlippage(BigNumber(value || 0).toNumber()); - }, [setSlippage]); + const closeModal = useCallback(() => { + setSubmitted(false); + setSwapResult(null); + setOpenedConfirmModal(false); + updateBalances(); + }, [updateBalances, swapResult]); + const swapTokenInfo: SwapTokenInfo = useMemo(() => { + return { + tokenA, + tokenAAmount, + tokenABalance, + tokenAUSD, + tokenAUSDStr: numberToUSD(tokenAUSD), + tokenB, + tokenBAmount, + tokenBBalance, + tokenBUSD, + tokenBUSDStr: numberToUSD(tokenBUSD), + direction: type, + slippage + }; + }, [slippage, type, tokenA, tokenAAmount, tokenABalance, tokenAUSD, tokenB, tokenBAmount, tokenBBalance, tokenBUSD]); + useEffect(() => { + if (tokens.length === 0 || Object.keys(router.query).length === 0) { + return; + } + if (!initialized) { + setInitialized(true); + const query = router.query; + const currentTokenA = tokens.find(token => token.path === query.tokenA) || null; + const currentTokenB = tokens.find(token => token.path === query.tokenB) || null; + const direction: SwapDirectionType = query.direction === "EXACT_OUT" ? "EXACT_OUT" : "EXACT_IN"; + setSwapValue({ + tokenA: currentTokenA, + tokenB: currentTokenB, + type: direction, + }); + setQuery({ + tokenA: currentTokenA?.path || null, + tokenB: currentTokenB?.path || null, + direction, + }); + return; + } + }, [initialized, router, tokenA?.path, tokenB?.path, tokens]); + return ( <> setOpenedConfirmModal(true)} switchSwapDirection={switchSwapDirection} copied={copied} handleCopied={handleCopied} themeKey={themeKey} handleSetting={handleSetting} isSwitchNetwork={isSwitchNetwork} - dataTokenInfo={dataTokenInfo} + dataTokenInfo={swapTokenInfo} changeTokenA={changeTokenA} changeTokenB={changeTokenB} changeTokenAAmount={changeTokenAAmount} @@ -532,14 +548,14 @@ const TokenSwapContainer: React.FC = () => { swapSummaryInfo={swapSummaryInfo} swapRouteInfos={swapRouteInfos} /> - {openedConfirmModal && ( + {openedConfirmModal && swapSummaryInfo && ( setOpenedConfirmModal(false)} + close={closeModal} /> )} {openedSetting && ( diff --git a/packages/web/src/containers/trending-crypto-card-list-container/TrendingCryptoCardListContainer.tsx b/packages/web/src/containers/trending-crypto-card-list-container/TrendingCryptoCardListContainer.tsx index cf6859dee..1165eb64b 100644 --- a/packages/web/src/containers/trending-crypto-card-list-container/TrendingCryptoCardListContainer.tsx +++ b/packages/web/src/containers/trending-crypto-card-list-container/TrendingCryptoCardListContainer.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState, useEffect } from "react"; import TrendingCryptoCardList from "@components/token/trending-crypto-card-list/TrendingCryptoCardList"; import { MATH_NEGATIVE_TYPE } from "@constants/option.constant"; @@ -36,7 +36,16 @@ export const trendingCryptoListInit = [ ]; const TrendingCryptoCardListContainer: React.FC = () => { - return ; + const [loading, setLoading] = useState(true); + + useEffect(() => { + const timeout = setTimeout(() => { + setLoading(false); + }, 2000); + return () => clearTimeout(timeout); + }, []); + + return ; }; export default TrendingCryptoCardListContainer; diff --git a/packages/web/src/containers/tvl-chart-container/TvlChartContainer.tsx b/packages/web/src/containers/tvl-chart-container/TvlChartContainer.tsx index f7c0e643b..766a9b9a0 100644 --- a/packages/web/src/containers/tvl-chart-container/TvlChartContainer.tsx +++ b/packages/web/src/containers/tvl-chart-container/TvlChartContainer.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState } from "react"; +import React, { useCallback, useState, useEffect } from "react"; import { useQuery } from "@tanstack/react-query"; import TvlChart from "@components/dashboard/tvl-chart/TvlChart"; import { CHART_TYPE } from "@constants/option.constant"; @@ -18,6 +18,10 @@ export interface TvlChartInfo { }[]; } +const months = [ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +]; + function createXAxisDummyDatas(currentTab: CHART_TYPE) { const now = Date.now(); switch (currentTab) { @@ -25,39 +29,66 @@ function createXAxisDummyDatas(currentTab: CHART_TYPE) { return Array.from({ length: 8 }, (_, index) => { const date = new Date(now); date.setDate(date.getDate() - 1 * index); - const monthStr = `${date.getMonth() + 1}`.padStart(2, "0"); - const dateStr = `${date.getDate()}`.padStart(2, "0"); - return `${monthStr}-${dateStr}`; + const monthStr = months[date.getMonth()]; + const dayStr = `${date.getDate()}`.padStart(2, "0"); + const yearStr = date.getFullYear(); + return `${monthStr} ${dayStr}, ${yearStr}`; }).reverse(); case "1M": return Array.from({ length: 8 }, (_, index) => { const date = new Date(now); date.setMonth(date.getMonth() - 1 * index); + const monthStr = months[date.getMonth()]; + const dayStr = `${date.getDate()}`.padStart(2, "0"); const yearStr = date.getFullYear(); - const monthStr = `${date.getMonth() + 1}`.padStart(2, "0"); - return `${yearStr}-${monthStr}`; + + return `${monthStr} ${dayStr}, ${yearStr}`; }).reverse(); case "1Y": - case "YTD": - default: return Array.from({ length: 8 }, (_, index) => { const date = new Date(now); date.setFullYear(date.getFullYear() - 1 * index); + const monthStr = months[date.getMonth()]; + const dayStr = `${date.getDate()}`.padStart(2, "0"); + const yearStr = date.getFullYear(); + + return `${monthStr} ${dayStr}, ${yearStr}`; + }).reverse(); + case "ALL": + default: + return Array.from({ length: 10 }, (_, index) => { + const date = new Date(now); + date.setMonth(date.getMonth() - 1 * index); + const monthStr = months[date.getMonth()]; + const dayStr = `${date.getDate()}`.padStart(2, "0"); const yearStr = date.getFullYear(); - return `${yearStr}`; + + return `${monthStr} ${dayStr}, ${yearStr}`; }).reverse(); } } -function createDummyAmountDatas() { - const length = 24; +function createDummyAmountDatas(tvlChartType: CHART_TYPE) { + let length = 8; + if (CHART_TYPE["7D"] === tvlChartType) { + length = 8; + } + if (CHART_TYPE["1M"] === tvlChartType) { + length = 31; + } + if (CHART_TYPE["1Y"] === tvlChartType) { + length = 91; + } + if (CHART_TYPE["ALL"] === tvlChartType) { + length = 91; + } return Array.from({ length }, (_, index) => { const date = new Date(); date.setHours(date.getHours() - index); return { amount: { - value: `${Math.round(Math.random() * 500) + 1000}`, + value: `${Math.round(Math.random() * 5000000) + 100000000}`, denom: "USD" }, time: date.toString() @@ -76,6 +107,15 @@ async function fetchTvlPriceInfo(): Promise { } const TvlChartContainer: React.FC = () => { + const [loading, setLoading] = useState(true); + + useEffect(() => { + const timeout = setTimeout(() => { + setLoading(false); + }, 2000); + return () => clearTimeout(timeout); + }, []); + const [tvlChartType, setTvlChartType] = useState( CHART_TYPE["7D"], ); @@ -100,7 +140,7 @@ const TvlChartContainer: React.FC = () => { const getChartInfo = useCallback(() => { const xAxisLabels = getXAxisLabels(tvlChartType); - const datas = createDummyAmountDatas(); + const datas = createDummyAmountDatas(tvlChartType); const chartInfo: TvlChartInfo = { xAxisLabels, @@ -116,6 +156,7 @@ const TvlChartContainer: React.FC = () => { changeTvlChartType={changeTvlChartType} tvlPriceInfo={tvlPriceInfo} tvlChartInfo={getChartInfo()} + loading={loading} /> ); }; diff --git a/packages/web/src/containers/volume-chart-container/VolumeChartContainer.tsx b/packages/web/src/containers/volume-chart-container/VolumeChartContainer.tsx index 9b7e67bad..2417bae87 100644 --- a/packages/web/src/containers/volume-chart-container/VolumeChartContainer.tsx +++ b/packages/web/src/containers/volume-chart-container/VolumeChartContainer.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState } from "react"; +import React, { useCallback, useState, useEffect } from "react"; import { useQuery } from "@tanstack/react-query"; import VolumeChart from "@components/dashboard/volume-chart/VolumeChart"; import { CHART_TYPE } from "@constants/option.constant"; @@ -11,13 +11,16 @@ export interface VolumePriceInfo { export interface VolumeChartInfo { datas: string[]; xAxisLabels: string[]; + times: string[]; } const initialVolumePriceInfo: VolumePriceInfo = { amount: "$994,120,000", fee: "$12,231", }; - +const months = [ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +]; function createXAxisDummyDatas(currentTab: CHART_TYPE) { const now = Date.now(); switch (currentTab) { @@ -25,33 +28,67 @@ function createXAxisDummyDatas(currentTab: CHART_TYPE) { return Array.from({ length: 8 }, (_, index) => { const date = new Date(now); date.setDate(date.getDate() - 1 * index); - const monthStr = `${date.getMonth() + 1}`.padStart(2, "0"); - const dateStr = `${date.getDate()}`.padStart(2, "0"); - return `${monthStr}-${dateStr}`; + const monthStr = months[date.getMonth()]; + const dayStr = `${date.getDate()}`.padStart(2, "0"); + const yearStr = date.getFullYear(); + return `${monthStr} ${dayStr}, ${yearStr}`; }).reverse(); case "1M": return Array.from({ length: 8 }, (_, index) => { const date = new Date(now); date.setMonth(date.getMonth() - 1 * index); + const monthStr = months[date.getMonth()]; + const dayStr = `${date.getDate()}`.padStart(2, "0"); const yearStr = date.getFullYear(); - const monthStr = `${date.getMonth() + 1}`.padStart(2, "0"); - return `${yearStr}-${monthStr}`; + + return `${monthStr} ${dayStr}, ${yearStr}`; }).reverse(); case "1Y": - case "YTD": - default: return Array.from({ length: 8 }, (_, index) => { const date = new Date(now); date.setFullYear(date.getFullYear() - 1 * index); + const monthStr = months[date.getMonth()]; + const dayStr = `${date.getDate()}`.padStart(2, "0"); const yearStr = date.getFullYear(); - return `${yearStr}`; + + return `${monthStr} ${dayStr}, ${yearStr}`; + }).reverse(); + case "ALL": + default: + return Array.from({ length: 10 }, (_, index) => { + const date = new Date(now); + date.setMonth(date.getMonth() - 1 * index); + const monthStr = months[date.getMonth()]; + const dayStr = `${date.getDate()}`.padStart(2, "0"); + const yearStr = date.getFullYear(); + + return `${monthStr} ${dayStr}, ${yearStr}`; }).reverse(); } } -function createDummyAmountDatas() { - const length = 24; - return Array.from({ length }, () => `${Math.round(Math.random() * 50) + 1}`); +function createDummyAmountDatas(volumeChartType: CHART_TYPE) { + let length = 8; + if (CHART_TYPE["7D"] === volumeChartType) { + length = 8; + } + if (CHART_TYPE["1M"] === volumeChartType) { + length = 31; + } + if (CHART_TYPE["1Y"] === volumeChartType) { + length = 91; + } + if (CHART_TYPE["ALL"] === volumeChartType) { + length = 91; + } + return Array.from({ length }, (_, index) => { + const date = new Date(); + date.setHours(date.getHours() - index); + return { + amount: `${Math.round(Math.random() * 10000000) + 10000000}`, + time: date.toString() + }; + }); } async function fetchVolumePriceInfo(): Promise { @@ -61,6 +98,15 @@ async function fetchVolumePriceInfo(): Promise { } const VolumeChartContainer: React.FC = () => { + const [loading, setLoading] = useState(true); + + useEffect(() => { + const timeout = setTimeout(() => { + setLoading(false); + }, 2000); + return () => clearTimeout(timeout); + }, []); + const [volumeChartType, setVolumeChartType] = useState( CHART_TYPE["7D"], ); @@ -80,11 +126,12 @@ const VolumeChartContainer: React.FC = () => { const getChartInfo = useCallback(() => { const xAxisLabels = createXAxisDummyDatas(volumeChartType); - const datas = createDummyAmountDatas(); + const datas = createDummyAmountDatas(volumeChartType); const chartInfo: VolumeChartInfo = { xAxisLabels, - datas: datas, + datas: datas.map(item => item.amount), + times: datas.map(item => item.time), }; return chartInfo; @@ -96,6 +143,7 @@ const VolumeChartContainer: React.FC = () => { changeVolumeChartType={changeVolumeChartType} volumePriceInfo={volumePriceInfo} volumeChartInfo={getChartInfo()} + loading={loading} /> ); }; diff --git a/packages/web/src/context/NoticeContext.ts b/packages/web/src/context/NoticeContext.ts index 3526193b3..38a7ef096 100644 --- a/packages/web/src/context/NoticeContext.ts +++ b/packages/web/src/context/NoticeContext.ts @@ -1,16 +1,17 @@ import { createContext } from "react"; -export type TNoticeType = "success" | "error"; +export type TNoticeType = "success" | "error" | "pending"; export interface INoticeOptions { - type?: TNoticeType; - timeout?: number; + type: TNoticeType; + id: number; + timeout: number; closeable?: boolean; onClose?: () => void; } export interface INoticeContext { - setNotice: (content: any, options?: INoticeOptions) => void; + setNotice: (content: any, options: INoticeOptions) => void; onCloseNotice: () => void; } diff --git a/packages/web/src/hooks/common/use-clear-modal.tsx b/packages/web/src/hooks/common/use-clear-modal.tsx index e2aefff6a..528d62c23 100644 --- a/packages/web/src/hooks/common/use-clear-modal.tsx +++ b/packages/web/src/hooks/common/use-clear-modal.tsx @@ -1,14 +1,16 @@ import { useAtom } from "jotai"; -import { CommonState } from "@states/index"; +import { CommonState, WalletState } from "@states/index"; import { useCallback } from "react"; export const useClearModal = () => { const [, setOpenedModal] = useAtom(CommonState.openedModal); const [, setModalContent] = useAtom(CommonState.modalContent); + const [, setWalletAccount] = useAtom(WalletState.loadingConnect); const clear = useCallback(() => { setOpenedModal(false); setModalContent(null); + setWalletAccount("initial"); }, [setOpenedModal, setModalContent]); return clear; diff --git a/packages/web/src/hooks/common/use-esc-close-modal.tsx b/packages/web/src/hooks/common/use-esc-close-modal.tsx index ddce78017..d61ccc7c1 100644 --- a/packages/web/src/hooks/common/use-esc-close-modal.tsx +++ b/packages/web/src/hooks/common/use-esc-close-modal.tsx @@ -11,9 +11,9 @@ function useEscCloseModal(callback: () => void) { window.addEventListener("keydown", handleEsc); return () => { - window.addEventListener("keydown", handleEsc); + window.removeEventListener("keydown", handleEsc); }; - }, []); + }, [callback]); } export default useEscCloseModal; diff --git a/packages/web/src/hooks/common/use-floating-tooltip.tsx b/packages/web/src/hooks/common/use-floating-tooltip.tsx index feba103a0..d0fc3f63d 100644 --- a/packages/web/src/hooks/common/use-floating-tooltip.tsx +++ b/packages/web/src/hooks/common/use-floating-tooltip.tsx @@ -42,16 +42,25 @@ export function useFloatingTooltip({ ? offset : position.includes("left") ? offset * -1 + : position.includes("top-start") + ? offset + : position.includes("top-end") + ? - offset : 0; - const verticalOffset = placement.includes("bottom") - ? offset + const verticalOffset = placement.includes("bottom") || placement.includes("right") || placement.includes("left") + ? offset + 32 : position.includes("top") ? offset * -1 : 0; - + const handleMouseMove = useCallback( - ({ clientX, clientY }: MouseEvent | React.MouseEvent) => { + ( event: MouseEvent | React.MouseEvent | React.TouchEvent) => { + const isTouch = event.type.startsWith("touch"); + const touch = isTouch ? (event as React.TouchEvent).touches[0] : null; + const clientX = (isTouch ? touch?.clientX : (event as React.MouseEvent).clientX) || 0; + const clientY = (isTouch ? touch?.clientY : (event as React.MouseEvent).clientY) || 0; + refs.setPositionReference({ getBoundingClientRect() { return { diff --git a/packages/web/src/hooks/common/use-slippage.ts b/packages/web/src/hooks/common/use-slippage.ts index 551df559a..8faa4369b 100644 --- a/packages/web/src/hooks/common/use-slippage.ts +++ b/packages/web/src/hooks/common/use-slippage.ts @@ -1,21 +1,18 @@ import { useAtom } from "jotai"; import { CommonState } from "@states/index"; -import { useCallback } from "react"; +import { useCallback, useEffect } from "react"; import { DEFAULT_SLIPPAGE } from "@constants/option.constant"; -import { isNumber } from "@utils/number-utils"; export const useSlippage = () => { const [slippage, setSlippage] = useAtom(CommonState.slippage || 0); - const changeSlippage = useCallback( - (slippage: number) => { - if (!isNumber(slippage) || Number.isNaN(slippage)) { - setSlippage(0.01); - return; - } + useEffect(() => { + setSlippage(DEFAULT_SLIPPAGE); + }, []); - const changedSlippage = Math.min(100, Math.max(0.01, slippage)); - setSlippage(changedSlippage); + const changeSlippage = useCallback( + (slippage: string) => { + setSlippage(slippage); }, [setSlippage], ); diff --git a/packages/web/src/hooks/pool/use-pool.tsx b/packages/web/src/hooks/pool/use-pool.tsx index ea4965792..e42c4ebb0 100644 --- a/packages/web/src/hooks/pool/use-pool.tsx +++ b/packages/web/src/hooks/pool/use-pool.tsx @@ -48,7 +48,7 @@ export const usePool = ({ swapFeeTier: SwapFeeTierType; startPrice: string; priceRange: AddLiquidityPriceRage; - slippage: number; + slippage: string; }) => { if (!tokenA || !tokenB || !account) { return null; diff --git a/packages/web/src/hooks/swap/use-swap.tsx b/packages/web/src/hooks/swap/use-swap.tsx index 6def72a6b..c6f1583ef 100644 --- a/packages/web/src/hooks/swap/use-swap.tsx +++ b/packages/web/src/hooks/swap/use-swap.tsx @@ -11,7 +11,7 @@ interface UseSwapProps { tokenA: TokenModel | null; tokenB: TokenModel | null; direction: SwapDirectionType; - slippage: number; + slippage: string; } export const useSwap = ({ @@ -28,8 +28,8 @@ export const useSwap = ({ const selectedTokenPair = tokenA !== null && tokenB !== null; const tokenAmountLimit = useMemo(() => { - if (estimatedAmount && !Number.isNaN(slippage)) { - const slippageAmountNumber = BigNumber(estimatedAmount).multipliedBy(slippage * 0.01); + if (estimatedAmount && !Number.isNaN(Number(slippage))) { + const slippageAmountNumber = BigNumber(estimatedAmount).multipliedBy(Number(slippage) * 0.01); const tokenAmountLimit = direction === "EXACT_IN" ? BigNumber(estimatedAmount).minus(slippageAmountNumber).toNumber() : BigNumber(estimatedAmount).plus(slippageAmountNumber).toNumber(); diff --git a/packages/web/src/hooks/token/use-earn-add-liquidity-confirm-modal.tsx b/packages/web/src/hooks/token/use-earn-add-liquidity-confirm-modal.tsx index a957f2d44..6cfa634bf 100644 --- a/packages/web/src/hooks/token/use-earn-add-liquidity-confirm-modal.tsx +++ b/packages/web/src/hooks/token/use-earn-add-liquidity-confirm-modal.tsx @@ -8,6 +8,7 @@ import { useAtom } from "jotai"; import { useCallback, useMemo } from "react"; import { TokenAmountInputModel } from "./use-token-amount-input"; import { getCurrentPriceByRaw } from "@utils/swap-utils"; +import { useNotice } from "@hooks/common/use-notice"; export interface EarnAddLiquidityConfirmModalProps { tokenA: TokenModel | null; @@ -16,7 +17,7 @@ export interface EarnAddLiquidityConfirmModalProps { tokenBAmountInput: TokenAmountInputModel; currentPrice: string; priceRange: AddLiquidityPriceRage | null; - slippage: number; + slippage: string; swapFeeTier: SwapFeeTierType | null; createPool: ( params: { @@ -25,7 +26,7 @@ export interface EarnAddLiquidityConfirmModalProps { swapFeeTier: SwapFeeTierType; startPrice: string; priceRange: AddLiquidityPriceRage; - slippage: number; + slippage: string; } ) => Promise; } @@ -47,6 +48,7 @@ export const useEarnAddLiquidityConfirmModal = ({ const [, setOpenedModal] = useAtom(CommonState.openedModal); const [, setModalContent] = useAtom(CommonState.modalContent); const navigator = useNavigate(); + const { setNotice } = useNotice(); const amountInfo = useMemo(() => { if (!tokenA || !tokenB || !swapFeeTier) { @@ -114,6 +116,7 @@ export const useEarnAddLiquidityConfirmModal = ({ if (!tokenA || !tokenB || !priceRange || !swapFeeTier) { return; } + setNotice(null, {timeout: 50000, type: "pending", closeable: true, id: Math.random() * 19999}); createPool({ tokenAAmount: tokenAAmountInput.amount, tokenBAmount: tokenBAmountInput.amount, diff --git a/packages/web/src/hooks/wallet/use-connect-status-wallet-modal.tsx b/packages/web/src/hooks/wallet/use-connect-status-wallet-modal.tsx new file mode 100644 index 000000000..848d68a22 --- /dev/null +++ b/packages/web/src/hooks/wallet/use-connect-status-wallet-modal.tsx @@ -0,0 +1,22 @@ +import ConnectWalletStatusModalContainer from "@containers/connect-wallet-status-modal-container/ConnectWalletStatusModalContainer"; +import { CommonState } from "@states/index"; +import { useAtom } from "jotai"; +import { useCallback } from "react"; + +export interface Props { + openModal: () => void; +} + +export const useConnectWalletStatusModal = (): Props => { + const [, setOpenedModal] = useAtom(CommonState.openedModal); + const [, setModalContent] = useAtom(CommonState.modalContent); + + const openModal = useCallback(() => { + setOpenedModal(true); + setModalContent(); + }, [setModalContent, setOpenedModal]); + + return { + openModal, + }; +}; diff --git a/packages/web/src/hooks/wallet/use-wallet.ts b/packages/web/src/hooks/wallet/use-wallet.ts index 9f0b62829..d8c73c4f4 100644 --- a/packages/web/src/hooks/wallet/use-wallet.ts +++ b/packages/web/src/hooks/wallet/use-wallet.ts @@ -1,3 +1,4 @@ +// import { network } from './../../states/common'; import { WalletClient } from "@common/clients/wallet-client"; import { AdenaClient } from "@common/clients/wallet-client/adena"; import { useGnoswapContext } from "@hooks/common/use-gnoswap-context"; @@ -13,7 +14,7 @@ export const useWallet = () => { const [walletClient, setWalletClient] = useAtom(WalletState.client); const [walletAccount, setWalletAccount] = useAtom(WalletState.account); const [, setNetwork] = useAtom(CommonState.network); - + const [loadingConnect, setLoadingConnect] = useAtom(WalletState.loadingConnect); const connected = useMemo(() => { return walletAccount !== null && walletAccount.address.length > 0; }, [walletAccount]); @@ -51,23 +52,29 @@ export const useWallet = () => { const switchNetwork = useCallback( async () => { + setLoadingConnect("loading"); const res = await accountRepository.switchNetwork(CHAIN_ID); if (res.code === 0) { const account = await accountRepository.getAccount(); setWalletAccount(account); accountRepository.setConnectedWallet(true); } + setLoadingConnect("done"); }, [accountRepository, setWalletAccount] ); const connectAdenaClient = useCallback(() => { + const connectedBySession = accountRepository.isConnectedWalletBySession(); + if (!connectedBySession) { + setLoadingConnect("loading"); + } const adena = AdenaClient.createAdenaClient(); if (adena !== null) { adena.initAdena(); } setWalletClient(adena); - }, [setWalletClient]); + }, [setWalletClient, setLoadingConnect, accountRepository]); const connectAccount = useCallback(async () => { const established = await accountRepository @@ -80,18 +87,21 @@ export const useWallet = () => { if (established.code === 0 || established.code === 4001) { const account = await accountRepository.getAccount(); - const network = NetworkData.find( - network => network.chainId === account.chainId, - ); + const network = account.chainId === CHAIN_ID; + // const network = NetworkData.find( + // network => network.chainId === account.chainId, + // ); if (!network) { switchNetwork(); } setWalletAccount(account); accountRepository.setConnectedWallet(true); + setLoadingConnect("done"); } else { accountRepository.setConnectedWallet(false); + setLoadingConnect("error"); } - }, [accountRepository, setWalletAccount]); + }, [accountRepository, setWalletAccount, setLoadingConnect]); const disconnectWallet = useCallback(() => { setWalletAccount(null); @@ -111,10 +121,10 @@ export const useWallet = () => { const isSwitchNetwork = useMemo(() => { if (!walletAccount) return true; - const network = NetworkData.find( - network => network.chainId === walletAccount.chainId, - ); - + // const network = NetworkData.find( + // network => network.chainId === walletAccount.chainId, + // ); + const network = walletAccount.chainId === CHAIN_ID; return network ? false : true; }, [walletAccount]); @@ -129,5 +139,6 @@ export const useWallet = () => { disconnectWallet, switchNetwork, isSwitchNetwork: isSwitchNetwork, + loadingConnect, }; }; diff --git a/packages/web/src/layouts/governance-layout/GovernanceLayout.tsx b/packages/web/src/layouts/governance-layout/GovernanceLayout.tsx index cb3f79286..e083786a0 100644 --- a/packages/web/src/layouts/governance-layout/GovernanceLayout.tsx +++ b/packages/web/src/layouts/governance-layout/GovernanceLayout.tsx @@ -36,7 +36,7 @@ const GovernanceLayout: React.FC = ({
    {summary} - Stake GNOS/GNOT Positions to earn xGNOS + Stake GNS/GNOT Positions to earn xGNOS Click here diff --git a/packages/web/src/models/account/account-history-model.ts b/packages/web/src/models/account/account-history-model.ts index 95c679270..9c22bfa92 100644 --- a/packages/web/src/models/account/account-history-model.ts +++ b/packages/web/src/models/account/account-history-model.ts @@ -12,4 +12,5 @@ export interface TransactionModel { status: StatusOptions; createdAt: string; content?: string; + isRead?: boolean; } diff --git a/packages/web/src/models/pool/mapper/pool-mapper.ts b/packages/web/src/models/pool/mapper/pool-mapper.ts index 4e70d31c5..fb42cb3ea 100644 --- a/packages/web/src/models/pool/mapper/pool-mapper.ts +++ b/packages/web/src/models/pool/mapper/pool-mapper.ts @@ -37,7 +37,7 @@ export class PoolMapper { address: "g1sqaft388ruvsseu97r04w4rr4szxkh4nn6xpax", path: "gno.land/r/gnos", decimals: 4, - symbol: "GNOS", + symbol: "GNS", logoURI: "/gnos.svg", priceId: "gno.land/r/gnos", }, @@ -102,7 +102,7 @@ export class PoolMapper { address: "g1sqaft388ruvsseu97r04w4rr4szxkh4nn6xpax", path: "gno.land/r/gnos", decimals: 4, - symbol: "GNOS", + symbol: "GNS", logoURI: "/gnos.svg", priceId: "gno.land/r/gnos", }, diff --git a/packages/web/src/models/swap/info/swap-confirm-model.ts b/packages/web/src/models/swap/info/swap-confirm-model.ts index a4b84d90e..77232578c 100644 --- a/packages/web/src/models/swap/info/swap-confirm-model.ts +++ b/packages/web/src/models/swap/info/swap-confirm-model.ts @@ -9,7 +9,7 @@ export interface SwapConfirmModel { tokenAAmount: AmountModel; tokenBAmount: AmountModel; type: SwapDirectionType; - slippage: number; + slippage: string; gasFee: BigNumber; } diff --git a/packages/web/src/models/swap/swap-token-info.ts b/packages/web/src/models/swap/swap-token-info.ts index 55618a53b..f68fbc840 100644 --- a/packages/web/src/models/swap/swap-token-info.ts +++ b/packages/web/src/models/swap/swap-token-info.ts @@ -24,5 +24,5 @@ export interface SwapTokenInfo { direction: SwapDirectionType; - slippage: number; + slippage: string; } diff --git a/packages/web/src/providers/gnoswap-theme-provider/GnoswapThemeProvider.tsx b/packages/web/src/providers/gnoswap-theme-provider/GnoswapThemeProvider.tsx index deed065f5..bb2e93483 100644 --- a/packages/web/src/providers/gnoswap-theme-provider/GnoswapThemeProvider.tsx +++ b/packages/web/src/providers/gnoswap-theme-provider/GnoswapThemeProvider.tsx @@ -1,18 +1,30 @@ import { Global, ThemeProvider } from "@emotion/react"; -import { useEffect, useMemo } from "react"; +import { useEffect, useMemo, useState } from "react"; import { getTheme } from "@utils/theme-utils"; import globalStyle from "@styles/globalStyle"; import { useAtom } from "jotai"; import { ThemeState } from "@states/index"; import { useWindowSize } from "@hooks/common/use-window-size"; import { DeviceSize } from "@styles/media"; +import { GNOSWAP_THEME_KEY } from "@states/theme"; +import { ThemeKeys } from "@styles/ThemeTypes"; + +const getInitialThemeValue = () => { + const storedTheme = localStorage.getItem(GNOSWAP_THEME_KEY); + return storedTheme !== null ? storedTheme.slice(1, -1) as ThemeKeys : "dark"; +}; const GnoswapThemeProvider: React.FC = ({ children, }) => { const [themeKey] = useAtom(ThemeState.themeKey); + const [currentTheme, setCurrentTheme] = useState(null); const { breakpoint, handleBreakpoint } = useWindowSize(); + useEffect(() => { + setCurrentTheme(themeKey); + }, [themeKey]); + useEffect(() => { if (typeof window !== "undefined") { handleWindowSize(); @@ -27,9 +39,17 @@ const GnoswapThemeProvider: React.FC = ({ handleBreakpoint(window?.innerWidth || DeviceSize.WEB); } + useEffect(() => { + const initialTheme = getInitialThemeValue(); + + setCurrentTheme(initialTheme); + }, []); + const theme = useMemo(() => { - return getTheme(themeKey); - }, [themeKey]); + return getTheme(currentTheme || "dark"); + }, [currentTheme]); + + if (!currentTheme) return null; return ( diff --git a/packages/web/src/repositories/pool/pool-repository-impl.ts b/packages/web/src/repositories/pool/pool-repository-impl.ts index f0cc953d5..4024d03ea 100644 --- a/packages/web/src/repositories/pool/pool-repository-impl.ts +++ b/packages/web/src/repositories/pool/pool-repository-impl.ts @@ -213,7 +213,7 @@ export class PoolRepositoryImpl implements PoolRepository { maxTick: number, tokenAAmount: string, tokenBAmount: string, - slippage: number, + slippage: string, caller: string, ) { const tokenAPath = tokenA.path; diff --git a/packages/web/src/repositories/pool/request/create-pool-request.ts b/packages/web/src/repositories/pool/request/create-pool-request.ts index 99e8a33cd..0edc90eab 100644 --- a/packages/web/src/repositories/pool/request/create-pool-request.ts +++ b/packages/web/src/repositories/pool/request/create-pool-request.ts @@ -10,6 +10,6 @@ export interface CreatePoolRequest { startPrice: string; minTick: number; maxTick: number; - slippage: number; + slippage: string; caller: string; } diff --git a/packages/web/src/repositories/swap/swap-repository-impl.ts b/packages/web/src/repositories/swap/swap-repository-impl.ts index e365d8653..7efb2005d 100644 --- a/packages/web/src/repositories/swap/swap-repository-impl.ts +++ b/packages/web/src/repositories/swap/swap-repository-impl.ts @@ -149,7 +149,7 @@ export class SwapRepositoryImpl implements SwapRepository { return 0; }; - public setSlippage = (slippage: number): boolean => { + public setSlippage = (slippage: string): boolean => { this.localStorageClient.set("slippage", `${slippage}`); return true; }; diff --git a/packages/web/src/repositories/swap/swap-repository-mock.ts b/packages/web/src/repositories/swap/swap-repository-mock.ts index daa944996..ddb6056cc 100644 --- a/packages/web/src/repositories/swap/swap-repository-mock.ts +++ b/packages/web/src/repositories/swap/swap-repository-mock.ts @@ -52,7 +52,7 @@ export class SwapRepositoryMock implements SwapRepository { return 0; }; - public setSlippage = (slippage: number): boolean => { + public setSlippage = (slippage: string): boolean => { this.localStorageClient.set("slippage", `${slippage}`); return true; }; diff --git a/packages/web/src/repositories/swap/swap-repository.ts b/packages/web/src/repositories/swap/swap-repository.ts index a93236060..91c962b19 100644 --- a/packages/web/src/repositories/swap/swap-repository.ts +++ b/packages/web/src/repositories/swap/swap-repository.ts @@ -18,7 +18,7 @@ export interface SwapRepository { getSlippage: () => number; - setSlippage: (slippage: number) => boolean; + setSlippage: (slippage: string) => boolean; swap: (request: SwapRequest) => Promise; } diff --git a/packages/web/src/repositories/token/mock/assets.json b/packages/web/src/repositories/token/mock/assets.json new file mode 100644 index 000000000..6be067a17 --- /dev/null +++ b/packages/web/src/repositories/token/mock/assets.json @@ -0,0 +1,56 @@ +[ + { + "name": "Tong", + "pkg_path": "gno.land/r/demo/tong", + "symbol": "TONG", + "decimals": 4, + "description": "Tong is a realm of GRC20", + "website_url": "https://test3.gno.land/r/demo/tong", + "image": "https://raw.githubusercontent.com/onbloc/gno-token-resource/e780d05dbf4919894951de7825239730b2b86f58/grc20/images/gno_land_r_demo_tong.svg" + }, + { + "name": "Foo", + "pkg_path": "gno.land/r/foo", + "symbol": "FOO", + "decimals": 4, + "description": "Foo is a GRC20 token solely with the testing purpose.", + "website_url": "https://twitter.com/gnoswaplabs", + "image": "https://raw.githubusercontent.com/onbloc/gno-token-resource/e780d05dbf4919894951de7825239730b2b86f58/grc20/images/gno_land_r_foo.svg" + }, + { + "name": "Bar", + "pkg_path": "gno.land/r/bar", + "symbol": "BAR", + "decimals": 4, + "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nisi orci, ultrices sit amet mi eget, efficitur elementum tellus. Integer augue purus, rutrum eu pretium sit amet, varius in quam. In auctor gravida pretium. Maecenas est ante, pulvinar id accumsan eget, aliquam ac turpis. Donec vulputate nunc tellus. In sit amet mattis nisl. Mauris libero quam, hendrerit dapibus scelerisque eu, fringilla sit amet sapien. Proin ut semper risus. Vivamus fringilla eget lectus ut faucibus. Proin vitae mollis massa. Sed bibendum tortor eget aliquam commodo. Etiam et eleifend augue. Donec molestie placerat elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nisi orci, ultrices sit amet mi eget, efficitur elementum tellus. Integer augue purus, rutrum eu pretium sit amet, varius in quam. In auctor gravida pretium. Maecenas est ante, pulvinar id accumsan eget, aliquam ac turpis. Donec vulputate nunc tellus. In sit amet mattis nisl. Mauris libero quam, hendrerit dapibus scelerisque.", + "website_url": "https://twitter.com/gnoswaplabs", + "image": "https://raw.githubusercontent.com/onbloc/gno-token-resource/e780d05dbf4919894951de7825239730b2b86f58/grc20/images/gno_land_r_bar.svg" + }, + { + "name": "Baz", + "pkg_path": "gno.land/r/baz", + "symbol": "BAZ", + "decimals": 4, + "description": "Baz is a GRC20 token solely with the testing purpose.", + "website_url": "https://twitter.com/gnoswaplabs", + "image": "https://raw.githubusercontent.com/onbloc/gno-token-resource/e780d05dbf4919894951de7825239730b2b86f58/grc20/images/gno_land_r_baz.svg" + }, + { + "name": "Qux", + "pkg_path": "gno.land/r/qux", + "symbol": "QUX", + "decimals": 4, + "description": "Qux is a GRC20 token solely with the testing purpose.", + "website_url": "https://twitter.com/gnoswaplabs", + "image": "https://raw.githubusercontent.com/onbloc/gno-token-resource/e780d05dbf4919894951de7825239730b2b86f58/grc20/images/gno_land_r_qux.svg" + }, + { + "name": "Gnoswap", + "pkg_path": "gno.land/r/gns", + "symbol": "GNS", + "decimals": 4, + "description": "GNS is a GRC20 token solely with the testing purpose.", + "website_url": "https://twitter.com/gnoswaplabs", + "image": "https://raw.githubusercontent.com/onbloc/gno-token-resource/e780d05dbf4919894951de7825239730b2b86f58/grc20/images/gno_land_r_gns.svg" + } + ] diff --git a/packages/web/src/states/common.ts b/packages/web/src/states/common.ts index 7694f26d3..26db941f2 100644 --- a/packages/web/src/states/common.ts +++ b/packages/web/src/states/common.ts @@ -1,7 +1,6 @@ import { NetworkModel } from "@models/common/network-model"; import { DEVICE_TYPE } from "@styles/media"; import { atom } from "jotai"; -import { atomWithStorage } from "jotai/utils"; import { DEFAULT_SLIPPAGE } from "@constants/option.constant"; interface HeaderToggleProps { @@ -24,4 +23,4 @@ export const currentWidth = atom(0); export const network = atom(null); -export const slippage = atomWithStorage("slippage", DEFAULT_SLIPPAGE); +export const slippage = atom(DEFAULT_SLIPPAGE); diff --git a/packages/web/src/states/swap.ts b/packages/web/src/states/swap.ts index 5d098644e..720d5a49e 100644 --- a/packages/web/src/states/swap.ts +++ b/packages/web/src/states/swap.ts @@ -1,8 +1,10 @@ import { atom } from "jotai"; import { TokenSwapModel } from "@models/token/token-swap-model"; -export const swap = atom({ +export const swap = atom({ tokenA: null, tokenB: null, type: "EXACT_IN", + tokenBAmount: "", + tokenAAmount: "", }); diff --git a/packages/web/src/states/theme.ts b/packages/web/src/states/theme.ts index 004385270..66acad52e 100644 --- a/packages/web/src/states/theme.ts +++ b/packages/web/src/states/theme.ts @@ -1,6 +1,6 @@ import { ThemeKeys } from "@styles/ThemeTypes"; import { atomWithStorage } from "jotai/utils"; -const GNOSWAP_THEME_KEY = "app_theme"; +export const GNOSWAP_THEME_KEY = "app_theme"; export const themeKey = atomWithStorage(GNOSWAP_THEME_KEY, "dark"); diff --git a/packages/web/src/states/token.ts b/packages/web/src/states/token.ts index ce713bfff..45d109613 100644 --- a/packages/web/src/states/token.ts +++ b/packages/web/src/states/token.ts @@ -10,28 +10,8 @@ export const tokenPrices = atom>({}); export const balances = atom>({}); export const swap = atom({ - tokenA: { - chainId: "dev", - createdAt: "2023-10-10T08:48:46+09:00", - name: "Gnoswap", - address: "g1sqaft388ruvsseu97r04w4rr4szxkh4nn6xpax", - path: "gno.land/r/gnos", - decimals: 4, - symbol: "GNOS", - logoURI: "https://s2.coinmarketcap.com/static/img/coins/64x64/5994.png", - priceId: "gno.land/r/gnos", - }, - tokenB: { - chainId: "dev", - createdAt: "2023-10-10T08:48:46+09:00", - name: "Gnoswap", - address: "g1sqaft388ruvsseu97r04w4rr4szxkh4nn6xpax", - path: "gno.land/r/gnos", - decimals: 4, - symbol: "GNOS", - logoURI: "https://s2.coinmarketcap.com/static/img/coins/64x64/5994.png", - priceId: "gno.land/r/gnos", - }, + tokenA: null, + tokenB: null, type: "EXACT_IN", }); diff --git a/packages/web/src/states/wallet.ts b/packages/web/src/states/wallet.ts index 5a03dd2dd..0b5e28361 100644 --- a/packages/web/src/states/wallet.ts +++ b/packages/web/src/states/wallet.ts @@ -5,3 +5,5 @@ import { atom } from "jotai"; export const client = atom(null); export const account = atom(null); + +export const loadingConnect = atom("initial"); \ No newline at end of file diff --git a/packages/web/src/styles/ThemeTypes.ts b/packages/web/src/styles/ThemeTypes.ts index 820fda5a4..74e09466e 100644 --- a/packages/web/src/styles/ThemeTypes.ts +++ b/packages/web/src/styles/ThemeTypes.ts @@ -24,6 +24,9 @@ export interface ThemeColorType { background21: string; background22: string; background23: string; + background24: string; + background25: string; + bgLoading: string; backgroundOpacity: string; backgroundOpacity2: string; backgroundOpacity3: string; @@ -34,6 +37,7 @@ export interface ThemeColorType { backgroundGradient3: string; backgroundGradient4: string; backgroundGradient5: string; + backgroundGradient6: string; border01: string; border02: string; border03: string; @@ -107,6 +111,7 @@ export interface ThemeColorType { greenDark: string; shadow: string; shadow01: string; + shadow02: string; } export type ThemeColorKeyTypes = keyof ThemeColorType; diff --git a/packages/web/src/utils/notification-utils.ts b/packages/web/src/utils/notification-utils.ts index 4ac878763..c08550cb2 100644 --- a/packages/web/src/utils/notification-utils.ts +++ b/packages/web/src/utils/notification-utils.ts @@ -17,6 +17,7 @@ export const notificationDummyList: Array = [ new Date().setDate(new Date().getDate() - 40), ).toString(), content: "Unwrapped 2.01821423 GNOT", + isRead: false, }, { txType: 1, @@ -32,6 +33,7 @@ export const notificationDummyList: Array = [ new Date().setDate(new Date().getDate() - 31), ).toString(), content: "Unwrapped 2.01821423 GNOT", + isRead: false, }, { txType: 2, @@ -47,6 +49,7 @@ export const notificationDummyList: Array = [ new Date().setDate(new Date().getDate() - 30), ).toString(), content: "Unstaked 2.01821423 GNOT", + isRead: false, }, { txType: 1, @@ -61,7 +64,8 @@ export const notificationDummyList: Array = [ createdAt: new Date( new Date().setDate(new Date().getDate() - 29), ).toString(), - content: "Added 2.01821423 GNOT to GNOT/GNOS Pool", + content: "Added 2.01821423 GNOT to GNOT/GNS Pool", + isRead: false, }, { txType: 4, @@ -76,7 +80,8 @@ export const notificationDummyList: Array = [ createdAt: new Date( new Date().setDate(new Date().getDate() - 8), ).toString(), - content: "Removed 2.01821423 GNOT and 1.0352 GNOS", + content: "Removed 2.01821423 GNOT and 1.0352 GNS", + isRead: false, }, { txType: 5, @@ -91,7 +96,8 @@ export const notificationDummyList: Array = [ createdAt: new Date( new Date().setDate(new Date().getDate() - 7), ).toString(), - content: "Created GNOT/GNOS Pool", + content: "Created GNOT/GNS Pool", + isRead: false, }, { txType: 1, @@ -106,7 +112,8 @@ export const notificationDummyList: Array = [ createdAt: new Date( new Date().setDate(new Date().getDate() - 6), ).toString(), - content: "Added 2.01821423 GNOT to GNOT/GNOS Pool", + content: "Added 2.01821423 GNOT to GNOT/GNS Pool", + isRead: false, }, { txType: 7, @@ -120,6 +127,7 @@ export const notificationDummyList: Array = [ status: "SUCCESS", createdAt: new Date().toString(), content: "Approved GNOT", + isRead: false, }, ]; diff --git a/packages/web/src/utils/number-utils.ts b/packages/web/src/utils/number-utils.ts index 218cd1594..885308e70 100644 --- a/packages/web/src/utils/number-utils.ts +++ b/packages/web/src/utils/number-utils.ts @@ -144,6 +144,10 @@ export function numberToUSD(value: number) { return Number.isNaN(value) ? "-" : `$${BigNumber(value).toFormat()}`; } +export function numberToUSDV2(value: number) { + return Number.isNaN(value) ? "-" : `$${value}`; +} + export function matchInputNumber(value: string) { const regexpOfNum = /^(\d*)[\.]?(\d*)?$/g; const regexpOfStartWithZeroes = /^(?!00)/;