diff --git a/.gitignore b/.gitignore index 72ae1fb8..add0b8bc 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ /.pnp .pnp.js .eslintcache +/.vscode # testing /coverage @@ -18,9 +19,11 @@ .env.development.local .env.test.local .env.production.local +.env* npm-debug.log* yarn-debug.log* yarn-error.log* -.idea/ \ No newline at end of file +.idea/ +.vscode/ diff --git a/package.json b/package.json index 1418e095..d1c10f68 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "octofi-app-aquafarm", - "version": "4.2.1", + "version": "4.3.0", "private": true, "homepage": "https://app.octo.fi", "dependencies": { @@ -40,6 +40,8 @@ "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", "@transak/transak-sdk": "^1.0.28", + "@types/aos": "^3.0.4", + "@types/dompurify": "^2.2.2", "@types/jest": "^25.2.1", "@types/lodash.flatmap": "^4.5.6", "@types/luxon": "^1.24.4", @@ -47,12 +49,16 @@ "@types/node": "^13.13.5", "@types/qs": "^6.9.2", "@types/react": "^16.9.34", + "@types/react-bootstrap-table-next": "^4.0.14", + "@types/react-csv": "^1.1.2", "@types/react-dom": "^16.9.7", "@types/react-redux": "^7.1.8", "@types/react-router-dom": "^5.0.0", + "@types/react-slick": "^0.23.5", "@types/react-virtualized-auto-sizer": "^1.0.0", "@types/react-window": "^1.8.2", "@types/rebass": "^4.0.5", + "@types/remarkable": "^2.0.2", "@types/styled-components": "^5.1.0", "@types/testing-library__cypress": "^5.0.5", "@types/wcag-contrast": "^3.0.0", @@ -242,5 +248,9 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "jest": { + "resetMocks": false, + "resetModules": false } } diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 116514ae..7e38c28b 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -25,7 +25,7 @@ "menu": { "Account": "Account", "menu": "Menu", - "buy": "Buy / Swap", + "buy": "Buy", "crypto": "Crypto with fiat", "giftCards": "Gift Cards & more", "swap": "Swaptopus", @@ -36,17 +36,21 @@ "spot": "Spot Trading", "marketMaker": "Market Making", "margin": "Margin Trading", + "exchange": "Exchange", "invest": "Invest", - "pools": "Liquidity Pools", - "tokenSets": "Tokensets", + "pools": "Pools", + "tokenSets": "TokenSets", + "tokens": "Top Tokens", "loans": "Loans", - "nft": "NFT", + "nft": "NFT Marketplace", "tools": "Tools", + "more": "More", "governance": "Governance", "rankings": "Rankings", "explore": "Explore", "dashboard": "Dashboard", "wallet": "Wallet", + "profile": "Profile", "history": "History", "disconnect": "Disconnect from OctoFi", "connect": "Connect", @@ -60,7 +64,7 @@ "connect": "Connect to wallet", "notConnected": "Wallet Not Connected", "connected": "Wallet Connected", - "connectedTo": "You connected to", + "connectedTo": "You are connected to", "viewOrChange": "View or change account", "title": "Wallet" }, @@ -123,6 +127,7 @@ "buyButton": "Buy Crypto Asset" }, "exchange": { + "title": "Exchange", "selectCrypto": "Select your Crypto", "selectFiat": "Select your Currency", "selectPair": "Select a pair", @@ -147,8 +152,8 @@ "pending": "Pending...", "failed": "Transaction failed", "orderSubmitted": "Your order submitted successfully!", - "fromLabel": "You Pay", - "toLabel": "You Receive", + "fromLabel": "Pay", + "toLabel": "Receive", "exchangeSearch": "Our tentacles are searching in {{ loaded }} of {{ all }} exchanges", "orderId": "Order ID", "address": "ADDRESS", @@ -216,6 +221,7 @@ "tokensets": { "portfolios": "Portfolios", "rebalancingSets": "Rebalancing Sets", + "otherSets": "Other", "oneDay": "1 Day", "week": "1 Week", "month": "1 Month", @@ -266,10 +272,12 @@ "more": "More", "totalAssets": "Total Assets", "totalDebts": "Total Debts", + "totalDeposits": "Total Deposits", "netWorth": "Net Worth", "platforms": "Platforms", "accountOverview": "Account Overview", - "dashboard": " ", + "dashboard": "Dashboard", + "profile": "Profile", "blockNumber": "Block Number", "transactionHash": "Transaction Hash", "download": "Download {{ file }}", diff --git a/src/App.js b/src/App.js index 248a73d4..a089ca8e 100644 --- a/src/App.js +++ b/src/App.js @@ -14,6 +14,8 @@ import TransactionUpdater from "./state/transactions/updater"; import UserUpdater from "./state/user/updater"; import Routes from "./Routes"; import "./global.scss"; +import "slick-carousel/slick/slick.css"; +import "slick-carousel/slick/slick-theme.css"; import Web3ReactManager from "./components/Web3ReactManager"; import TransactionHandler from "./components/TransactionHandler"; import { useIsDarkMode } from "./state/user/hooks"; @@ -54,7 +56,7 @@ function App() { /> diff --git a/src/App.test.js b/src/App.test.js index f9f7b247..cefe6b3a 100644 --- a/src/App.test.js +++ b/src/App.test.js @@ -1,8 +1,40 @@ -import { render, screen } from "@testing-library/react"; +import { render } from "./testing/customRender"; import App from "./App"; -test("renders learn react link", () => { - render(); - const linkElement = screen.getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); +jest.mock("./theme", () => ({ + FixedGlobalStyle: () => "FixedGlobalStyle", + ThemedGlobalStyle: () => "ThemedGlobalStyle", +})); + +jest.mock("./components/RouteChanger/routeChanger", () => () =>
RouteChanger
); +jest.mock("./components/WalletModal", () => () =>
WalletModal
); +jest.mock("./state/lists/updater", () => () =>
ListsUpdater
); +jest.mock("./state/application/updater", () => () =>
ApplicationUpdater
); +jest.mock("./state/multicall/updater", () => () =>
MultiCallUpdater
); +jest.mock("./state/transactions/updater", () => () =>
TransactionUpdater
); +jest.mock("./state/user/updater", () => () =>
UserUpdater
); +jest.mock("./Routes", () => () =>
Routes
); +jest.mock("./components/Web3ReactManager", () => ({ children }) =>
Web3ReactManager {children}
); +jest.mock("./components/TransactionHandler", () => () =>
TransactionHandler
); + +let state = { + initialState: { + user: { + userDarkMode: false, + }, + }, +}; + +test("smoke test light mode", () => { + render(, state); +}); + +test("smoke test dark mode", () => { + state.initialState.user.userDarkMode = true; + render(, state); +}); + +test("requires user state", () => { + state.initialState.user = null; + expect(() => render(, state)).toThrow(); }); diff --git a/src/Routes.js b/src/Routes.js index 1db032da..18c96c27 100644 --- a/src/Routes.js +++ b/src/Routes.js @@ -2,28 +2,27 @@ import { Switch, Route, Redirect } from "react-router-dom"; import { useEffect, lazy, Suspense } from "react"; import { useDispatch, useSelector } from "react-redux"; -import { useActiveWeb3React } from "./hooks"; +// import { useActiveWeb3React } from "./hooks"; import SplashScreen from "./components/SplashScreen"; import { fetchCurrencies } from "./state/currency/actions"; -import HomePage from "./pages/Home"; import { useDarkModeManager } from "./state/user/hooks"; -import Page from "./components/Page"; -import WrongNetwork from "./components/WrongNetwork"; +// import Page from "./components/Page"; +// import WrongNetwork from "./components/WrongNetwork"; +import HomePage from "./pages/Home"; const Dashboard = lazy(() => import("./pages/Dashboard")); const Platform = lazy(() => import("./pages/Platform")); const Pools = lazy(() => import("./pages/Pools")); const Swap = lazy(() => import("./pages/Swap")); -const InstantSwap = lazy(() => import("./pages/InstantSwap")); +const Exchange = lazy(() => import("./pages/Exchange")); const Explore = lazy(() => import("./pages/Explore")); -const ExploreTypeList = lazy(() => import("./pages/ExploreTypeList")); -const CoinDetails = lazy(() => import("./pages/CoinDetails")); +const MarketsExplore = lazy(() => import("./pages/MarketsExplore")); +const CoinDetailsPage = lazy(() => import("./pages/CoinDetailsPage")); const Governance = lazy(() => import("./pages/Governance")); const CreateProposal = lazy(() => import("./pages/CreateProposal")); const Proposals = lazy(() => import("./pages/Proposals")); const Vote = lazy(() => import("./pages/Vote")); const History = lazy(() => import("./pages/History")); -const Wallet = lazy(() => import("./pages/Wallet")); const FiatOff = lazy(() => import("./pages/FiatOff")); const FiatOn = lazy(() => import("./pages/FiatOn")); const NFT = lazy(() => import("./pages/NFT")); @@ -31,13 +30,12 @@ const Borrow = lazy(() => import("./pages/Borrow")); const TokenSets = lazy(() => import("./pages/TokenSets")); const Launchpad = lazy(() => import("./pages/Launchpad")); const LaunchpadItem = lazy(() => import("./pages/LaunchpadItem")); -const NewLaunchpad = lazy(() => import("./pages/NewLaunchpad"));; +const NewLaunchpad = lazy(() => import("./pages/NewLaunchpad")); const CrossRouteHandler = lazy(() => import("./CrossRouteHandler")); - const Routes = (props) => { const [darkMode] = useDarkModeManager(); - const { account, chainId } = useActiveWeb3React(); + // const { account, chainId } = useActiveWeb3React(); const selectedCurrency = useSelector((state) => state.currency.selected); const dispatch = useDispatch(); @@ -58,47 +56,38 @@ const Routes = (props) => { <> }> - - - {chainId && chainId !== 1 ? ( + {/* {chainId && chainId !== 1 ? ( - ) : ( - - - - - - - - - - - - - - - - - - - - - - {account && ( - <> - - - - - )} - - - - )} + ) : ()} */} + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/images/account/assets.svg b/src/assets/images/account/assets.svg new file mode 100644 index 00000000..a50a9168 --- /dev/null +++ b/src/assets/images/account/assets.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/account/copy.svg b/src/assets/images/account/copy.svg index 850a3198..ccc06316 100644 --- a/src/assets/images/account/copy.svg +++ b/src/assets/images/account/copy.svg @@ -1,3 +1,3 @@ - - - + + + \ No newline at end of file diff --git a/src/assets/images/account/debts.svg b/src/assets/images/account/debts.svg index 5565083f..3e3f4e13 100644 --- a/src/assets/images/account/debts.svg +++ b/src/assets/images/account/debts.svg @@ -1,17 +1,10 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/account/deposits.svg b/src/assets/images/account/deposits.svg index d39a14cc..01dec05e 100644 --- a/src/assets/images/account/deposits.svg +++ b/src/assets/images/account/deposits.svg @@ -1,52 +1,52 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/account/external-link.svg b/src/assets/images/account/external-link.svg index 3efbdbca..3ae1d276 100644 --- a/src/assets/images/account/external-link.svg +++ b/src/assets/images/account/external-link.svg @@ -1,3 +1,3 @@ - - - + + + \ No newline at end of file diff --git a/src/assets/images/account/networth.svg b/src/assets/images/account/networth.svg new file mode 100644 index 00000000..6d9a6434 --- /dev/null +++ b/src/assets/images/account/networth.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/account/wallet.svg b/src/assets/images/account/wallet.svg index 82bb60f8..341b123a 100644 --- a/src/assets/images/account/wallet.svg +++ b/src/assets/images/account/wallet.svg @@ -1,4 +1,4 @@ - - - - + + + + \ No newline at end of file diff --git a/src/assets/images/assets/assets.svg b/src/assets/images/assets/assets.svg deleted file mode 100644 index d95ea677..00000000 --- a/src/assets/images/assets/assets.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/assets/images/assets/debts.svg b/src/assets/images/assets/debts.svg deleted file mode 100644 index 74c10fdc..00000000 --- a/src/assets/images/assets/debts.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/src/assets/images/assets/networth.svg b/src/assets/images/assets/networth.svg deleted file mode 100644 index ad65d7c7..00000000 --- a/src/assets/images/assets/networth.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/src/assets/images/bell.svg b/src/assets/images/bell.svg deleted file mode 100644 index 96db094b..00000000 --- a/src/assets/images/bell.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/assets/images/global/arrow-down.svg b/src/assets/images/global/arrow-down.svg deleted file mode 100644 index 2fa8f4c1..00000000 --- a/src/assets/images/global/arrow-down.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/components/AccountCard/index.tsx b/src/components/AccountCard/index.tsx index f79d4f9a..904bae3e 100644 --- a/src/components/AccountCard/index.tsx +++ b/src/components/AccountCard/index.tsx @@ -1,172 +1,94 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -import React from "react"; -import { isMobile } from "react-device-detect"; +import { PropsWithChildren, useContext } from "react"; +import { useTranslation } from "react-i18next"; +import Skeleton from "react-loading-skeleton"; +import SVG from "react-inlinesvg"; +import { Button } from "react-bootstrap"; +import { ThemeContext } from "styled-components"; -import CurrencyText from "../CurrencyText"; -import styled from "styled-components"; -import WalletIcon from "../../assets/images/account/wallet.svg"; -import DepositsIcon from "../../assets/images/account/deposits.svg"; +import AssetIcon from "../../assets/images/account/assets.svg"; import DebtIcon from "../../assets/images/account/debts.svg"; -import WalletTable from "../AssetTable/wallet"; -import AssetTable from "../AssetTable"; -import SVG from "react-inlinesvg"; -import { useTranslation } from "react-i18next"; +import DepositsIcon from "../../assets/images/account/deposits.svg"; +import NetWorthIcon from "../../assets/images/account/networth.svg"; +import WalletIcon from "../../assets/images/account/wallet.svg"; +import ArrowRightIcon from "../../assets/images/global/arrow-right.svg"; +import CurrencyText from "../CurrencyText"; +import * as Styled from "./styleds"; const icons: any = { - wallet: WalletIcon, + assets: AssetIcon, debts: DebtIcon, deposits: DepositsIcon, + netWorth: NetWorthIcon, + wallet: WalletIcon, }; -const Wrapper = styled.div` - padding-bottom: 20px; - height: 100%; - display: block; -`; - -const Card = styled.div` - background-color: ${({ theme }) => theme.modalBG}; - color: ${({ theme }) => theme.text1}; - display: flex; - flex-direction: column; - border-radius: 20px; - cursor: pointer; - height: 100%; -`; - -const TableContainer = styled.div` - padding: 0 0.75rem; - - @media (min-width: 768px) { - padding: 0; - } -`; - -const CardIcon = styled.div` - width: 56px; - height: 56px; - border-radius: 56px; - color: ${({ theme }) => theme.primary}; - background-color: ${({ theme }) => theme.primaryLight}; - overflow: hidden; - display: flex; - align-items: center; - justify-content: center; - - @media (min-width: 768px) { - width: 74px; - height: 74px; - border-radius: 74px; - } -`; - -const CardBody = styled.div` - display: flex; - align-items: center; - padding: 1.875rem 1.25rem; - border-bottom: 1px solid ${({ theme }) => theme.text3}; - @media (min-width: 768px) { - padding: 1.875rem; - } -`; - -const CardBottomBody = styled.div` - display: flex; - flex-direction: column; - padding: 1.25rem 0.5rem 0.5rem; - height: 100%; -`; - -const CardContent = styled.div` - display: flex; - flex-direction: column; - flex-wrap: wrap; - justify-content: center; - padding-left: 26px; - - @media (min-width: 768px) { - padding-left: 30px; - } -`; - -const Title = styled.span` - font-weight: 500; - font-size: 1rem; - color: ${({ theme }) => theme.text1}; - display: block; - margin-bottom: 0.25rem; -`; - -const Value = styled.span` - font-weight: 700; - font-size: 1.25rem; - color: ${({ theme }) => theme.text1}; - display: block; - margin: 0; - - @media (min-width: 768px) { - font-size: 1.75rem; - } -`; +export type AccountCardProps = { + color?: any; + type?: string; + title: string; + value: string; + assets?: any; + onShowMore?: any; + loading?: boolean; + show?: boolean; + className?: string | undefined; +}; function AccountCard({ - className, - value, - title, - clickHandler, + color = "primary", type = "wallet", - balances, -}: { - className: string | undefined; - value: string | null; - title: string; - clickHandler?: any; - type?: string; - balances: any; -}) { + title, + value, + assets, + onShowMore, + loading = false, + show, + className = "", + children, +}: PropsWithChildren) { const { t } = useTranslation(); + const theme = useContext(ThemeContext); + // @ts-ignore + const themeColor = theme[color]; + const showCardBody = children && assets?.balances?.length > 0; + const showCardAction = assets?.balances?.length > 5; + + if (!show) { + return null; + } return ( - - - {/* begin::Body */} - - - - - - - {title} - - {value} - - - - + + + + + + + + {title} + + {loading ? : {value}} + + + + + {showCardBody && ( +
- - {type === "wallet" ? ( - - ) : ( - - )} - -
- -
+
{children}
+ + {showCardAction && ( +
+ +
+ )}
-
- {/* end::Body */} -
-
+ + )} + ); } diff --git a/src/components/AccountCard/styleds.tsx b/src/components/AccountCard/styleds.tsx new file mode 100644 index 00000000..f1637f35 --- /dev/null +++ b/src/components/AccountCard/styleds.tsx @@ -0,0 +1,59 @@ +import styled from "styled-components"; +import SVG from "react-inlinesvg"; + +export const Card = styled.div` + background-color: ${({ theme }) => theme.modalBG}; + color: ${({ theme }) => theme.text1}; + display: flex; + flex-direction: column; + border-radius: 18px; + margin-bottom: 1rem; +`; + +export const CardHeader = styled.div` + display: flex; + align-items: center; + padding: 1.5rem; +`; + +export const CardIcon = styled.div<{ color: string }>` + width: 60px; + height: 60px; + border-radius: 50%; + background-color: ${({ color }) => color}; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; +`; + +export const CardImg = styled(SVG)` + width: 50%; + height: auto; +`; + +export const CardBody = styled.div` + display: flex; + flex-direction: column; + padding: 20px 0.5rem; + height: 100%; + border-top: 1px solid ${({ theme }) => theme.borderColor}; +`; + +export const CardHeaderContent = styled.div` + margin-left: 20px; +`; + +export const Title = styled.p` + font-weight: 500; + font-size: 1rem; + color: ${({ theme }) => theme.text1}; + margin-bottom: 0; +`; + +export const Value = styled.p` + font-weight: 600; + font-size: 1.5rem; + color: ${({ theme }) => theme.text1}; + margin: 0; +`; diff --git a/src/components/AddLiquidityModal/index.tsx b/src/components/AddLiquidityModal/index.tsx index a4abbd84..d45d505b 100644 --- a/src/components/AddLiquidityModal/index.tsx +++ b/src/components/AddLiquidityModal/index.tsx @@ -1,6 +1,5 @@ -import React, { useCallback, useEffect, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import { useSelector, useDispatch } from "react-redux"; -import SVG from "react-inlinesvg"; import { Button, Row, Col } from "react-bootstrap"; import CurrencyInputPanel from "../CurrencyInputPanel"; import { useActiveWeb3React } from "../../hooks"; @@ -40,7 +39,7 @@ import curvePipeABI from "../../constants/abis/curve.json"; import balancerPipeABI from "../../constants/abis/balancer.json"; import yVaultPipeABI from "../../constants/abis/yVault.json"; import { clearSelectedPool } from "../../state/pools/actions"; -import { AccountState, AccountStateContent, AccountStateDesc, AccountStateTitle, PriceTopbar } from "./uniswap"; +import { PriceTopbar } from "./uniswap"; import { useWalletModalToggle } from "../../state/application/hooks"; import { LightCard } from "../StyledCards"; import { useTranslation } from "react-i18next"; @@ -112,7 +111,7 @@ export const ResponsivePlatformTitle = styled(PlatformTitle)` margin-left: 10px; `; -export default function AddLiquidityModal ({ history }: RouteComponentProps) { +export default function AddLiquidityModal({ history }: RouteComponentProps) { const { account, chainId, library } = useActiveWeb3React(); const dispatch = useDispatch(); const { t } = useTranslation(); @@ -295,7 +294,7 @@ export default function AddLiquidityModal ({ history }: RouteComponentProps) { {currencies[Field.CURRENCY_A]?.symbol} Input - + @@ -342,20 +341,6 @@ export default function AddLiquidityModal ({ history }: RouteComponentProps) { {!account ? ( - - - - - - {t("wallet.notConnected")} - - - - ) : !showConfirm ? ( - - - - - {t("wallet.connected")} - - {t("wallet.connectedTo")} {account} - - - - - + - - + + {t("pools.selectedPool")} @@ -409,8 +379,8 @@ export default function AddLiquidityModal ({ history }: RouteComponentProps) { - - + + {t("pools.selectGasSetting")} @@ -420,7 +390,6 @@ export default function AddLiquidityModal ({ history }: RouteComponentProps) { className={ "d-flex flex-column flex-xl-row align-items-stretch align-items-xl-center justify-content-center" } - style={{ paddingTop: 30 }} > {!account ? ( - - + + ) : attemptingTxn ? ( ) : txHash ? ( diff --git a/src/components/AddressInputPanel/index.js b/src/components/AddressInputPanel/index.js deleted file mode 100644 index e667fe64..00000000 --- a/src/components/AddressInputPanel/index.js +++ /dev/null @@ -1,138 +0,0 @@ -import React, { useCallback } from "react"; -import styled from "styled-components"; -import useENS from "../../hooks/useENS"; -import { useActiveWeb3React } from "../../hooks"; -import { ExternalLink } from "../../theme"; -import { AutoColumn } from "../Column"; -import { RowBetween } from "../Row"; -import { getEtherscanLink } from "../../utils"; - -const InputPanel = styled.div` - ${({ theme }) => theme.flexColumnNoWrap} - position: relative; - z-index: 1; - width: 100%; - margin-bottom: ${({ withoutMargin }) => (withoutMargin ? ".5rem" : "2.75rem")}; - - @media (max-width: 767px) { - margin-bottom: ${({ withoutMargin }) => (withoutMargin ? ".5rem" : "1.5rem")}; - } -`; - -const ContainerRow = styled.div` - display: flex; - justify-content: center; - align-items: center; - border-radius: 1.25rem; - border: 1px solid ${({ error, theme }) => (error ? theme.red1 : theme.bg2)}; - transition: border-color 300ms ${({ error }) => (error ? "step-end" : "step-start")}, - color 500ms ${({ error }) => (error ? "step-end" : "step-start")}; - background-color: ${({ theme }) => theme.bg1}; - padding: 1rem; - height: 56px; -`; - -const InputContainer = styled.div` - flex: 1; -`; - -const Input = styled.input` - font-size: 1rem; - outline: none; - border: none; - flex: 1 1 auto; - background-color: ${({ theme }) => theme.bg1}; - transition: color 300ms ${({ error }) => (error ? "step-end" : "step-start")}; - color: ${({ error, theme }) => (error ? theme.red1 : theme.text1)}; - overflow: hidden; - text-overflow: ellipsis; - font-weight: 500; - width: 100%; - padding: 0px; - -webkit-appearance: textfield; - - ::-webkit-search-decoration { - -webkit-appearance: none; - } - - ::-webkit-outer-spin-button, - ::-webkit-inner-spin-button { - -webkit-appearance: none; - } - - ::placeholder { - color: ${({ theme }) => theme.text3}; - font-weight: 400; - } -`; - -const Label = styled.span` - color: ${({ theme }) => theme.text1}; - font-weight: 400; - font-size: 0.875rem; - padding: 0; - - @media (min-width: 768px) { - padding: 0 1.5rem; - } -`; - -const PATTERN = /^[13][a-km-zA-HJ-NP-Z1-9]{25,80}$|^(bc1)[0-9A-Za-z]{25,80}$|^(0x[a-fA-F0-9]{40})$|^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$/; - -export default function AddressInputPanel({ - id, - value, - onChange, - withoutMargin = false, - label = "Recipient", - placeholder = "Wallet Address or ENS name", -}) { - const { chainId } = useActiveWeb3React(); - const { address, name } = useENS(value); - - const handleInput = useCallback( - (event) => { - const input = event.target.value; - const withoutSpaces = input.replace(/\s+/g, ""); - onChange(withoutSpaces); - }, - [onChange] - ); - - const error = Boolean(value?.length > 0 && !PATTERN.test(value)); - - return ( - - - - - - {address && chainId && ( - - (View on Etherscan) - - )} - - - - - - - - ); -} diff --git a/src/components/AddressInputPanel/index.tsx b/src/components/AddressInputPanel/index.tsx new file mode 100644 index 00000000..ed1ad6c9 --- /dev/null +++ b/src/components/AddressInputPanel/index.tsx @@ -0,0 +1,81 @@ +import { useCallback } from "react"; +import { ExternalLink as ExternalIcon } from "react-feather"; +import { ADDRESS_PATTERN } from "../../constants"; +import useENS from "../../hooks/useENS"; +import { useActiveWeb3React } from "../../hooks"; +import { ExternalLink } from "../../theme"; +import { getEtherscanLink } from "../../utils"; +import { AutoColumn } from "../Column"; +import * as Styled from "./styleds"; + +export type AddressInputPanelProps = { + id?: any; + value?: any; + onChange?: (T: string) => void; + label?: string; + placeholder?: string; +}; + +const AddressInputPanel = ({ + id, + value, + onChange, + label = "Recipient", + placeholder = "Wallet Address or ENS name", +}: AddressInputPanelProps) => { + const { chainId } = useActiveWeb3React(); + const { address, name } = useENS(value); + + const handleInput = useCallback( + (event) => { + const input = event.target.value; + const withoutSpaces = input.replace(/\s+/g, ""); + if (onChange) { + onChange(withoutSpaces); + } + }, + [onChange] + ); + + const error = Boolean(value?.length > 0 && !ADDRESS_PATTERN.test(value)); + + return ( + + + + + {label} + + + {address && chainId && ( +
+ + + View on Etherscan + +
+ )} +
+
+
+ ); +}; + +export default AddressInputPanel; diff --git a/src/components/AddressInputPanel/styleds.tsx b/src/components/AddressInputPanel/styleds.tsx new file mode 100644 index 00000000..293b0705 --- /dev/null +++ b/src/components/AddressInputPanel/styleds.tsx @@ -0,0 +1,66 @@ +import styled from "styled-components"; + +export const InputPanel = styled.div` + ${({ theme }) => theme.flexColumnNoWrap} + position: relative; + z-index: 1; + width: 100%; +`; + +export const InputContainer = styled.div` + flex: 1; +`; + +export const ContainerRow = styled.div<{ error?: boolean }>` + background-color: ${({ theme }) => theme.bg1}; + border: 1px solid ${({ error, theme }) => (error ? theme.red1 : theme.bg1)}; + border-radius: 18px; + display: flex; + justify-content: center; + align-items: center; + transition: border-color 300ms ${({ error }) => (error ? "step-end" : "step-start")}, + color 500ms ${({ error }) => (error ? "step-end" : "step-start")}; + overflow: hidden; +`; + +export const Label = styled.label` + color: ${({ theme }) => theme.text1}; + display: block; + font-weight: 400; + font-size: 0.875rem; + margin: 0; + padding: 0.875rem; + min-width: 160px; + width: 160px; +`; + +export const Input = styled.input<{ error?: boolean }>` + font-size: 1rem; + outline: none; + border: none; + flex: 1 1 auto; + background-color: ${({ theme }) => theme.bg1}; + transition: color 300ms ${({ error }) => (error ? "step-end" : "step-start")}; + color: ${({ error, theme }) => (error ? theme.red1 : theme.text1)}; + overflow: hidden; + text-overflow: ellipsis; + height: 56px; + font-weight: 500; + width: 100%; + padding: 0 10px; + -webkit-appearance: textfield; + + ::-webkit-search-decoration { + -webkit-appearance: none; + } + + ::-webkit-outer-spin-button, + ::-webkit-inner-spin-button { + -webkit-appearance: none; + } + + ::placeholder { + color: ${({ theme }) => theme.text3}; + font-weight: 400; + } +`; diff --git a/src/components/AssetModal/wallet.js b/src/components/AssetModal/WalletModal.js similarity index 89% rename from src/components/AssetModal/wallet.js rename to src/components/AssetModal/WalletModal.js index 541c3cfe..7e6efc0f 100644 --- a/src/components/AssetModal/wallet.js +++ b/src/components/AssetModal/WalletModal.js @@ -3,7 +3,7 @@ import { Modal } from "../Modal/bootstrap"; import { useSelector } from "react-redux"; import styled from "styled-components"; -import WalletTable from "../AssetTable/wallet"; +import WalletTable from "../AssetTable/WalletTable"; import SVG from "react-inlinesvg"; const CustomTitle = styled.h4` @@ -53,7 +53,7 @@ const WalletModal = (props) => { if (token.metadata.symbol === "ETH") { props.history.push("/coins/ethereum"); } else { - props.history.push(`/coins/contract/${token.metadata.address}`); + props.history.push(`/coins/${token.metadata.address}`); } }; let data = overview.wallet.balances || []; @@ -70,7 +70,7 @@ const WalletModal = (props) => { - + ); diff --git a/src/components/AssetTable/WalletTable.tsx b/src/components/AssetTable/WalletTable.tsx new file mode 100644 index 00000000..0541f27a --- /dev/null +++ b/src/components/AssetTable/WalletTable.tsx @@ -0,0 +1,79 @@ +import CurrencyLogo from "../CurrencyLogo"; +import CurrencyText from "../CurrencyText"; +import Loading from "../Loading"; +import * as Styled from "./styleds"; + +export type WalletTableProps = { + balances: any; + size?: string; + loading?: boolean; + onClickToken: (T: any) => void; + show?: boolean; +}; + +const WalletTable = ({ balances, size = "md", loading, onClickToken, show = true }: WalletTableProps) => { + if (loading) { + return ; + } + + if (!show) { + return null; + } + + if (balances.length === 0) { + return null; + } + + return ( +
+ + + + + + + + + + {/* @ts-ignore */} + {balances.map((row, index) => { + return ( + onClickToken(row)} + > + + + + + ); + })} + +
AssetsBalanceValue
+
+ + + + + {row.metadata.symbol} + +
+
+ + {row.balance ? row.balance.toSignificant(6) : 0} + + + + {row.balanceUSD} + +
+
+ ); +}; + +export default WalletTable; diff --git a/src/components/AssetTable/index.js b/src/components/AssetTable/index.js deleted file mode 100644 index 4e3a80bc..00000000 --- a/src/components/AssetTable/index.js +++ /dev/null @@ -1,152 +0,0 @@ -import React from "react"; -import {ETHER, Token} from "@uniswap/sdk"; - -import { useActiveWeb3React } from "../../hooks"; -import CurrencyText from "../CurrencyText"; -import CurrencyLogo from "../CurrencyLogo"; -import styled from "styled-components"; - -const LogoContainer = styled.div` - max-width: 32px; - max-height: 32px; - height: 32px; - width: 32px; - - @media (max-width: 1199px) { - max-width: 24px; - max-height: 24px; - height: 24px; - width: 24px; - } -`; - -const CustomText = styled.span` - color: ${({ theme }) => theme.text1}; - font-weight: 500; - font-size: ${({ size }) => - size === "sm" ? "0.75rem" : size === "md" ? ".875rem" : size === "lg" ? "1rem" : ".875rem"}; - - @media (min-width: 1200px) { - font-weight: 700; - font-size: ${({ size }) => - size === "sm" ? "0.75rem" : size === "md" ? "1rem" : size === "lg" ? "1.25rem" : "1rem"}; - } -`; -const Title = styled.span` - color: ${({ theme }) => theme.text1}; - font-weight: 500; - font-size: ${({ size }) => - size === "sm" ? "0.75rem" : size === "md" ? ".875rem" : size === "lg" ? "1rem" : ".875rem"}; - margin-left: 0.875rem; - - @media (min-width: 1200px) { - font-weight: 700; - font-size: ${({ size }) => - size === "sm" ? "0.75rem" : size === "md" ? "1rem" : size === "lg" ? "1.25rem" : "1rem"}; - margin-left: 1.25rem; - } -`; - -const AssetTable = (props) => { - const { chainId } = useActiveWeb3React(); - const { balances } = props; - - let data = balances ? balances : []; - - return ( -
- - - - - - - - - - {data.map((row) => { - if (row.underlying.length > 0) { - return row.underlying.map((uRow) => { - const currency = uRow.metadata.address.toLowerCase() === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' ? ETHER : new Token( - chainId, - uRow.metadata.address, - uRow.metadata.decimals, - uRow.metadata.symbol, - uRow.metadata.name - ); - return ( - - - - - - ); - }); - } else { - const currency = row.base.metadata.address.toLowerCase() === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' ? ETHER : new Token( - chainId, - row.base.metadata.address, - row.base.metadata.decimals, - row.base.metadata.symbol, - row.base.metadata.name - ); - return ( - - - - - - ); - } - })} - -
AssetsBalanceValue
-
- - - - - {uRow.metadata.symbol} - -
-
- - {(uRow.balance / 10 ** uRow.metadata.decimals).toFixed(6)} - - - - {uRow.balanceUSD} - -
-
- - - - - {row.base.metadata.symbol} - -
-
- - {(row.base.balance / 10 ** row.base.metadata.decimals).toFixed(6)} - - - - {row.base.balanceUSD} - -
-
- ); -}; - -export default AssetTable; diff --git a/src/components/AssetTable/index.tsx b/src/components/AssetTable/index.tsx new file mode 100644 index 00000000..429fc2bd --- /dev/null +++ b/src/components/AssetTable/index.tsx @@ -0,0 +1,124 @@ +import { ETHER, Token } from "@uniswap/sdk"; + +import { useActiveWeb3React } from "../../hooks"; +import CurrencyText from "../CurrencyText"; +import CurrencyLogo from "../CurrencyLogo"; +import Loading from "../Loading"; +import * as Styled from "./styleds"; + +export type AssetTableProps = { + balances: Array; + size?: string; + loading?: boolean; +}; + +const AssetTable = ({ balances, size = "md", loading }: AssetTableProps) => { + const { chainId } = useActiveWeb3React(); + + if (loading) { + return ; + } + + if (balances.length === 0) { + return null; + } + + return ( +
+ + + + + + + + + + {balances.map((row) => { + if (row.underlying.length > 0) { + // @ts-ignore + return row.underlying.map((uRow, index) => { + const currency = + uRow.metadata.address.toLowerCase() === "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + ? ETHER + : new Token( + // @ts-ignore + chainId, + uRow.metadata.address, + uRow.metadata.decimals, + uRow.metadata.symbol, + uRow.metadata.name + ); + return ( + + + + + + ); + }); + } else { + const currency = + row.base.metadata.address.toLowerCase() === "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + ? ETHER + : new Token( + // @ts-ignore + chainId, + row.base.metadata.address, + row.base.metadata.decimals, + row.base.metadata.symbol, + row.base.metadata.name + ); + return ( + + + + + + ); + } + })} + +
AssetsBalanceValue
+
+ + + + + {uRow.metadata.symbol} + +
+
+ + {(uRow.balance / 10 ** uRow.metadata.decimals).toFixed(6)} + + + + {uRow.balanceUSD} + +
+
+ + + + + {row.base.metadata.symbol} + +
+
+ + {(row.base.balance / 10 ** row.base.metadata.decimals).toFixed(6)} + + + + {row.base.balanceUSD} + +
+
+ ); +}; + +export default AssetTable; diff --git a/src/components/AssetTable/styleds.tsx b/src/components/AssetTable/styleds.tsx new file mode 100644 index 00000000..edfe0855 --- /dev/null +++ b/src/components/AssetTable/styleds.tsx @@ -0,0 +1,43 @@ +import styled from "styled-components"; + +export const LogoContainer = styled.div` + max-width: 32px; + max-height: 32px; + height: 32px; + width: 32px; + + @media (max-width: 1199px) { + max-width: 24px; + max-height: 24px; + height: 24px; + width: 24px; + } +`; + +export const CustomText = styled.span<{ size: string }>` + color: ${({ theme }) => theme.text1}; + font-weight: 500; + font-size: ${({ size }) => + size === "sm" ? "0.75rem" : size === "md" ? ".875rem" : size === "lg" ? "1rem" : ".875rem"}; + + @media (min-width: 1200px) { + font-weight: 700; + font-size: ${({ size }) => + size === "sm" ? "0.75rem" : size === "md" ? "1rem" : size === "lg" ? "1.25rem" : "1rem"}; + } +`; + +export const Title = styled.span<{ size: string }>` + color: ${({ theme }) => theme.text1}; + font-weight: 500; + font-size: ${({ size }) => + size === "sm" ? "0.75rem" : size === "md" ? ".875rem" : size === "lg" ? "1rem" : ".875rem"}; + margin-left: 0.875rem; + + @media (min-width: 1200px) { + font-weight: 700; + font-size: ${({ size }) => + size === "sm" ? "0.75rem" : size === "md" ? "1rem" : size === "lg" ? "1.25rem" : "1rem"}; + margin-left: 1.25rem; + } +`; diff --git a/src/components/AssetTable/wallet.js b/src/components/AssetTable/wallet.js deleted file mode 100644 index e53ee71a..00000000 --- a/src/components/AssetTable/wallet.js +++ /dev/null @@ -1,108 +0,0 @@ -import React from "react"; - -import CurrencyText from "../CurrencyText"; -import CurrencyLogo from "../CurrencyLogo"; -import styled from "styled-components"; - -const LogoContainer = styled.div` - max-width: 32px; - max-height: 32px; - height: 32px; - width: 32px; - - @media (max-width: 1199px) { - max-width: 24px; - max-height: 24px; - height: 24px; - width: 24px; - } -`; - -const CustomText = styled.span` - color: ${({ theme }) => theme.text1}; - font-weight: 500; - font-size: ${({ size }) => - size === "sm" ? "0.75rem" : size === "md" ? ".875rem" : size === "lg" ? "1rem" : ".875rem"}; - - @media (min-width: 1200px) { - font-weight: 700; - font-size: ${({ size }) => - size === "sm" ? "0.75rem" : size === "md" ? "1rem" : size === "lg" ? "1.25rem" : "1rem"}; - } -`; - -const Title = styled.span` - color: ${({ theme }) => theme.text1}; - font-weight: 500; - font-size: ${({ size }) => - size === "sm" ? "0.75rem" : size === "md" ? ".875rem" : size === "lg" ? "1rem" : ".875rem"}; - margin-left: 0.875rem; - - @media (min-width: 1200px) { - font-weight: 700; - font-size: ${({ size }) => - size === "sm" ? "0.75rem" : size === "md" ? "1rem" : size === "lg" ? "1.25rem" : "1rem"}; - margin-left: 1.25rem; - } -`; - -const WalletTable = (props) => { - const { balances } = props; - let data = balances || []; - - return ( -
- - - - - - - - - - {data.map((row) => { - return ( - false} - > - - - - - ); - })} - -
AssetsBalanceValue
-
- - - - - {row.metadata.symbol} - -
-
- - {row.balance ? row.balance.toSignificant(6) : 0} - - - - {row.balanceUSD} - -
-
- ); -}; - -export default WalletTable; diff --git a/src/components/BaseTokenModal/CurrencyList.js b/src/components/BaseTokenModal/CurrencyList.js index a3d78bab..ab673fc4 100644 --- a/src/components/BaseTokenModal/CurrencyList.js +++ b/src/components/BaseTokenModal/CurrencyList.js @@ -15,7 +15,7 @@ export const MenuItem = styled(RowBetween)` cursor: ${({ disabled }) => !disabled && "pointer"}; pointer-events: ${({ disabled }) => disabled && "none"}; :hover { - background-color: ${({ theme, disabled }) => !disabled && theme.bg2}; + background-color: ${({ theme, disabled }) => !disabled && theme.bg1}; } opacity: ${({ disabled, selected }) => (disabled || selected ? 0.5 : 1)}; `; @@ -39,7 +39,7 @@ function CurrencyRow({ currency, onSelect, isSelected, style }) { onClick={() => (isSelected ? null : onSelect())} disabled={isSelected} > - + {currency.symbol === "WETH" ? "Ethereum" : currency.name} diff --git a/src/components/BaseTokenModal/index.js b/src/components/BaseTokenModal/index.js index 5f7222d0..52e2799c 100644 --- a/src/components/BaseTokenModal/index.js +++ b/src/components/BaseTokenModal/index.js @@ -48,7 +48,7 @@ export default function CurrencySelectModal({ isOpen, onDismiss, onCurrencySelec theme.bg3}; + background-color: ${({ theme }) => theme.bg1}; color: ${({ theme }) => theme.text1}; border-radius: 18px; box-shadow: none; @@ -181,7 +181,7 @@ export default function BaseTokenSelector({ margin={true} /> ) : currency ? ( - + ) : null} {pair ? ( diff --git a/src/components/BookOption/index.js b/src/components/BookOption/index.js index 2201c1df..c800049e 100644 --- a/src/components/BookOption/index.js +++ b/src/components/BookOption/index.js @@ -10,7 +10,7 @@ const Wrapper = styled.div` min-width: 30px; min-height: 30px; padding: 10px 6px; - background-color: ${({ theme }) => theme.bg3}; + background-color: ${({ theme }) => theme.bg1}; border-radius: 30px; opacity: ${({ active }) => (active ? "1" : "0.5")}; cursor: pointer; diff --git a/src/components/BorrowInputPanel/index.js b/src/components/BorrowInputPanel/index.js index bfba01ef..568debc2 100644 --- a/src/components/BorrowInputPanel/index.js +++ b/src/components/BorrowInputPanel/index.js @@ -89,7 +89,7 @@ const CurrencySelect = styled.button` height: 56px; font-size: 0.875rem; font-weight: 500; - background-color: ${({ theme }) => theme.bg3}; + background-color: ${({ theme }) => theme.bg1}; color: ${({ theme }) => theme.text1}; border-bottom-left-radius: 18px; border-top-left-radius: 18px; @@ -200,7 +200,7 @@ export default class BorrowInputPanel extends React.Component { - {token && } + {token && } {(selected && selected?.length > 20 ? ( diff --git a/src/components/BrdigeCurrencySelectModal/CurrencyList.js b/src/components/BridgeCurrencySelectModal/CurrencyList.js similarity index 99% rename from src/components/BrdigeCurrencySelectModal/CurrencyList.js rename to src/components/BridgeCurrencySelectModal/CurrencyList.js index e93768cc..01ef3835 100644 --- a/src/components/BrdigeCurrencySelectModal/CurrencyList.js +++ b/src/components/BridgeCurrencySelectModal/CurrencyList.js @@ -39,7 +39,7 @@ const MenuItem = styled(RowBetween)` cursor: ${({ disabled }) => !disabled && "pointer"}; pointer-events: ${({ disabled }) => disabled && "none"}; :hover { - background-color: ${({ theme, disabled }) => !disabled && theme.bg2}; + background-color: ${({ theme, disabled }) => !disabled && theme.bg1}; } opacity: ${({ disabled, selected }) => (disabled || selected ? 0.5 : 1)}; `; diff --git a/src/components/BrdigeCurrencySelectModal/index.js b/src/components/BridgeCurrencySelectModal/index.js similarity index 99% rename from src/components/BrdigeCurrencySelectModal/index.js rename to src/components/BridgeCurrencySelectModal/index.js index c73d6405..445953a8 100644 --- a/src/components/BrdigeCurrencySelectModal/index.js +++ b/src/components/BridgeCurrencySelectModal/index.js @@ -128,7 +128,7 @@ function CurrencySelectModal({ theme.primary}; - color: ${({ theme }) => theme.bg2}; + color: ${({ theme }) => theme.bg1}; } :focus { @@ -224,7 +221,7 @@ const NumericalInput = styled.input` font-family: inherit; outline: none; flex: 1 1 auto; - background-color: ${({ theme }) => theme.bg3}; + background-color: ${({ theme }) => theme.bg1}; text-align: ${({ align }) => align && align}; white-space: nowrap; overflow: hidden; @@ -317,10 +314,6 @@ export default function BridgeInputPanel({ let allTokens = useAllTokenDetails() - const userTokenBalance = useAddressBalance(account, selectedTokenAddress) - - const [valueRange, setValueRange] = useState('') - const handleDismissSearch = useCallback(() => { setModalIsOpen(false); }, [setModalIsOpen]); diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx index b1a2ec62..eaeb88a3 100644 --- a/src/components/Button/index.tsx +++ b/src/components/Button/index.tsx @@ -53,7 +53,7 @@ export const ButtonPrimary = styled(Base)` background-color: ${({ theme }) => darken(0.1, theme.primary)}; } &:disabled { - background-color: ${({ theme, altDisabledStyle }) => (altDisabledStyle ? theme.primary : theme.bg2)}; + background-color: ${({ theme, altDisabledStyle }) => (altDisabledStyle ? theme.primary : theme.bg1)}; color: ${({ theme, altDisabledStyle }) => (altDisabledStyle ? theme.text2 : theme.text1)}; cursor: auto; box-shadow: none; @@ -92,20 +92,20 @@ export const ButtonLight = styled(Base)` `; export const ButtonGray = styled(Base)` - background-color: ${({ theme }) => theme.bg2}; + background-color: ${({ theme }) => theme.bg1}; color: ${({ theme }) => theme.text2}; font-size: 16px; font-weight: 500; &:focus { - box-shadow: 0 0 0 1pt ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg2)}; - background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg2)}; + box-shadow: 0 0 0 1pt ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg1)}; + background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg1)}; } &:hover { - background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg2)}; + background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg1)}; } &:active { - box-shadow: 0 0 0 1pt ${({ theme, disabled }) => !disabled && darken(0.1, theme.bg2)}; - background-color: ${({ theme, disabled }) => !disabled && darken(0.1, theme.bg2)}; + box-shadow: 0 0 0 1pt ${({ theme, disabled }) => !disabled && darken(0.1, theme.bg1)}; + background-color: ${({ theme, disabled }) => !disabled && darken(0.1, theme.bg1)}; } `; @@ -160,7 +160,7 @@ export const ButtonPink = styled(Base)` `; export const ButtonOutlined = styled(Base)` - border: 1px solid ${({ theme }) => theme.bg2}; + border: 1px solid ${({ theme }) => theme.bg1}; background-color: transparent; color: ${({ theme }) => theme.text1}; diff --git a/src/components/Chart/index.js b/src/components/Chart/index.js index ae75f332..6a070ef3 100644 --- a/src/components/Chart/index.js +++ b/src/components/Chart/index.js @@ -1,156 +1,138 @@ -import { Component } from "react"; +import React from "react"; import ReactApexChart from "react-apexcharts"; import moment from "moment"; import { isMobile } from "react-device-detect"; import "./style.scss"; -class Chart extends Component { - getOptions = () => { - return { - chart: { - id: "area-datetime", - height: 400, - toolbar: { - show: false, - }, - zoom: { - enabled: false, - }, - sparkline: { - enabled: false, - }, - animations: { - enabled: false, - }, - type: "area", +const Chart = (props) => { + // const [options, setOptions] = useState(); + const options = { + chart: { + id: "area-datetime", + height: 300, + toolbar: { + show: false, }, - markers: { - size: 0, - style: "hollow", + zoom: { + enabled: false, }, - dataLabels: { + sparkline: { enabled: false, }, - stroke: { - width: 4, - curve: "smooth", + animations: { + enabled: false, }, - colors: [this.props.color], - xaxis: { - categories: this.props.categories, - tickAmount: 6, - labels: { - show: true, - formatter: (value, timestamp) => { - const m = moment(value); - return m.format( - this.props.selected === "day" - ? "DD MMM, HH:mm" - : this.props.selected === "week" || this.props.selected === "month" - ? "DD MMM" - : this.props.selected === "six_month" || this.props.selected === "year" - ? "YYYY-MM-DD" - : "DD MMM" - ); - }, - style: { - cssClass: "chart__label", - }, - rotate: !isMobile ? 0 : -89, - }, - - axisBorder: { - show: false, - }, - - axisTicks: { - show: false, - }, - crosshairs: { - show: false, + type: "area", + }, + markers: { + size: 0, + style: "hollow", + }, + dataLabels: { + enabled: false, + }, + stroke: { + width: 4, + curve: "smooth", + }, + colors: [props.color], + xaxis: { + categories: props.categories, + tickAmount: 6, + labels: { + show: true, + formatter: (value, timestamp) => { + const m = moment(value); + return m.format( + props.selected === "day" + ? "DD MMM, HH:mm" + : props.selected === "week" || props.selected === "month" + ? "DD MMM" + : props.selected === "six_month" || props.selected === "year" + ? "YYYY-MM-DD" + : "DD MMM" + ); }, - tooltip: { - enabled: false, + style: { + cssClass: "chart__label", }, + rotate: !isMobile ? 0 : -89, }, - yaxis: { - labels: { - show: true, - style: { - cssClass: "chart__label", - fontSize: "14px", - fontWeight: "500", - }, - formatter: (val) => { - return Number(val).toFixed(6); - }, - }, - - axisBorder: { - show: false, - }, + axisBorder: { + show: false, + }, + axisTicks: { + show: false, + }, + crosshairs: { + show: false, }, - plotOptions: {}, tooltip: { - theme: "dark", - y: { - formatter: (value) => { - return value + this.props?.token?.symbol; - }, + enabled: false, + }, + }, + yaxis: { + labels: { + show: false, + style: { + cssClass: "chart__label", + fontSize: "14px", + fontWeight: "500", }, - custom: ({ series, seriesIndex, dataPointIndex, w }) => { - const category = w?.globals?.lastXAxis?.categories[dataPointIndex]; - return ` -
-
- Performance - ${series[seriesIndex][dataPointIndex]?.toFixed( - 4 - )} ${this.props?.token?.symbol} -
-
- Date - ${category} -
-
- `; + formatter: (val) => { + return Number(val).toFixed(6); }, }, - grid: { - borderColor: this.props.theme.modalBG, - clipMarkers: false, - yaxis: { - lines: { - show: false, - }, + + axisBorder: { + show: false, + }, + }, + plotOptions: {}, + tooltip: { + theme: "dark", + y: { + formatter: (value) => { + return value + props?.token?.symbol; }, }, - fill: { - gradient: { - enabled: true, - opacityFrom: 0.55, - opacityTo: 0, + custom: ({ series, seriesIndex, dataPointIndex, w }) => { + // const category = w?.globals?.lastXAxis?.categories[dataPointIndex]; + const category = props.categories[dataPointIndex]; + return ` +
+
+ Balance + ${series[seriesIndex][dataPointIndex]?.toFixed(4)} ${props?.token?.symbol} +
+
+ Date + ${category} +
+
+ `; + }, + }, + grid: { + borderColor: props.theme.modalBG, + clipMarkers: false, + yaxis: { + lines: { + show: false, }, }, - padding: { - top: 0, - bottom: 0, - right: 0, - left: 0, + }, + fill: { + gradient: { + enabled: true, + opacityFrom: 0.55, + opacityTo: 0, }, - } - } - render() { - return ( - - ); - } -} + }, + }; + + return ; +}; export default Chart; diff --git a/src/components/Chart/style.scss b/src/components/Chart/style.scss index 91a57631..c77afa15 100644 --- a/src/components/Chart/style.scss +++ b/src/components/Chart/style.scss @@ -1,31 +1,29 @@ @import "../../styles/abstracts/variables"; -.chart { - &__label { +.chart__label { + color: $light-title; + fill: $light-title; + + .dark-mode & { + color: $title; + fill: $title; + } +} + +.chart__tooltip { + &-title { color: $light-title; - fill: $light-title; + font-size: 0.75rem; .dark-mode & { color: $title; - fill: $title; } } - &__tooltip { - &-title { - color: $light-title; - font-size: 0.75rem; - - .dark-mode & { - color: $title; - } - } - - &-value { - color: $secondary; - font-weight: 700; - font-size: 0.75rem; - } + &-value { + color: $secondary; + font-weight: 700; + font-size: 0.75rem; } } @@ -40,4 +38,4 @@ background-color: rgba($background, 0.9); color: $title; } -} \ No newline at end of file +} diff --git a/src/components/ChartCard/index.js b/src/components/ChartCard/index.js index 55afa98e..0f7fb266 100644 --- a/src/components/ChartCard/index.js +++ b/src/components/ChartCard/index.js @@ -1,191 +1,106 @@ -import React, { Component } from "react"; +import React, { useEffect, useContext, useState } from "react"; import moment from "moment"; import axios from "axios"; import Web3 from "web3"; import { connect } from "react-redux"; -import styled, { ThemeContext } from "styled-components"; +import { ThemeContext } from "styled-components"; +import BigNumber from "bignumber.js"; -import Loading from "../Loading"; import EthDater from "../../lib/ethDater"; -import Chart from "../Chart"; -import withWeb3Account from "../hoc/withWeb3Account"; -import TokenSelector from "../TokenSelector"; +import { promisify } from "../../utils/promisify"; import { ERC20_ABI } from "../../constants/abis/erc20"; -import BigNumber from "bignumber.js"; -import { USDC } from "../../constants"; - -const LoadingCol = styled.div` - background-color: ${({ theme }) => theme.modalBG}; - border-radius: 20px; - min-height: 400px; - margin-top: 10px; -`; - -const CustomTitle = styled.span` - font-weight: 700; - font-size: 1.25rem; - color: ${({ theme }) => theme.text1}; - margin-bottom: 0.75rem; - - @media (min-width: 991px) { - margin-right: 1rem; - margin-bottom: 0; - } -`; - -const Card = styled.div` - background-color: ${({ theme }) => theme.modalBG}; - color: ${({ theme }) => theme.text1}; - display: flex; - border-radius: 20px; - justify-content: center; - flex-direction: column; - padding: 0 0 8px; - margin-top: 20px; - - @media (min-width: 768px) { - padding: 30px 30px 38px; - margin-top: 0; - } -`; - -const ToolbarButton = styled.button` - font-size: 0.875rem; - padding: 0 20px; - color: ${({ theme }) => theme.text1}; - opacity: ${({ selected }) => (selected ? 0.5 : 1)}; - - &:first-child { - padding-left: 0; - } - - &:last-child { - padding-right: 0; - } - - &:focus, - &:hover, - &:active { - text-decoration: none; - outline: none; - box-shadow: none; - } -`; - -const CardHeader = styled.div` - display: flex; - align-items: center; - justify-content: space-between; - background-color: transparent; - - padding: 20px; - - @media (min-width: 768px) { - padding: 0; - } -`; - -class ChartCard extends Component { - static contextType = ThemeContext; - - constructor(props) { - super(props); - this.web3Provider = new Web3(new Web3.providers.WebsocketProvider(process.env.REACT_APP_WSS_URL)); - this.dater = new EthDater(this.web3Provider); - - this.state = { - token: USDC, - series: [ - { - name: "Performance", - data: [], - }, - ], - categories: [], - selected: "month", - options: [ - { - title: "Day", - id: "day", - }, - { - title: "Week", - id: "week", - }, - { - title: "Month", - id: "month", - }, - { - title: "6 Month", - id: "six_month", - }, - { - title: "Year", - id: "year", - }, - ], - loading: false, - }; - } - - async componentDidMount() { - this.changeOption("month"); - } +import { WBTC } from "../../constants"; +import withWeb3Account from "../hoc/withWeb3Account"; +import Loading from "../Loading"; +import Chart from "../Chart"; +import { LoadingDiv, CustomTitle, Wrapper, ToolbarButton, CardHeader } from "./styleds"; + +const ChartCard = (props) => { + const web3Provider = new Web3(new Web3.providers.WebsocketProvider(process.env.REACT_APP_WSS_URL)); + const dater = new EthDater(web3Provider); + const theme = useContext(ThemeContext); + const selectedToken = props.token || WBTC; + const [connected, setConnected] = useState(false); + const [loading, setLoading] = useState(false); + const [selectedRange, setSelectedRange] = useState("month"); + const [categories, setCategories] = useState([]); + const [chartColor, setChartColor] = useState(theme.primary); + const [series, setSeries] = useState([ + { + name: "Balance", + data: [], + }, + ]); + const options = [ + { + title: "1D", + id: "day", + }, + { + title: "1W", + id: "week", + }, + { + title: "1M", + id: "month", + }, + { + title: "6M", + id: "six_month", + }, + { + title: "1Y", + id: "year", + }, + ]; + + useEffect(() => { + changeOption(selectedRange, selectedToken); + // return () => { + // cleanup + // } + }, [selectedRange, selectedToken, props.account]); + + const changeOption = async (id, token = WBTC) => { + setLoading(true); - changeOption = async (id, token = USDC) => { - this.setState({ - selected: id, - loading: true, - }); try { - let date = this.getStartDate(id); - let blockNumber = await this.dater.getDate(date, false); - let latestBlockNumber = await this.getBlockNumber(); - let balances = await this.getBalanceInRange( - this.props.account, - blockNumber.block, - latestBlockNumber, - token - ); - let transformedBalances = balances.map((balance) => { - return balance.balance?.toFixed(5); - }); - let categories = balances.map((balance) => { - return moment(balance.time).format("YYYY MMM D, HH:mm:ss"); - }); - this.setState({ - loading: false, - series: [ + if (props.account) { + setConnected(true); + let date = getStartDate(id); + let blockNumber = await dater.getDate(date, false); + let latestBlockNumber = await getBlockNumber(); + let balances = await getBalanceInRange(props.account, blockNumber.block, latestBlockNumber, token); + let transformedBalances = balances.map((balance) => { + return balance.balance?.toFixed(5); + }); + let categories = balances.map((balance) => { + return moment(balance.time).format("YYYY MMM D, HH:mm:ss"); + }); + setLoading(false); + setSeries([ { - name: "Performance", + name: "Balance", data: transformedBalances, }, - ], - categories, - }); + ]); + setCategories(categories); + } else { + setLoading(false); + setConnected(false); + } } catch (e) { console.log(e); } }; - promisify = (inner) => - new Promise((resolve, reject) => - inner((err, res) => { - if (err) { - reject(err); - } else { - resolve(res); - } - }) - ); - - async getFirstBlock(address) { + const getFirstBlock = async (address) => { try { let response = await axios.get( "https://api.etherscan.io/api?module=account&action=txlist&address=" + address + - "&startblock=0&page=1&offset=10&sort=asc&apikey=KM1S3UP6BGICACR5X2IE5E3ZIADV33DKA1" + "&startblock=0&page=1&offset=10&sort=asc&apikey=" + + process.env.REACT_APP_ETHERSCAN_API_KEY ); let data = response.data; @@ -197,15 +112,21 @@ class ChartCard extends Component { } catch (error) { console.error(error); } - } + }; - getBlockNumber = () => { + const unpack = (rows, index) => { + return rows.map(function (row) { + return row[index]; + }); + }; + + const getBlockNumber = () => { return new Promise((resolve, reject) => { - resolve(this.props.web3.library.blockNumber > 0 ? this.props.web3.library.blockNumber : 999999999); + resolve(props.web3.library.blockNumber > 0 ? props.web3.library.blockNumber : 999999999); }); }; - getBalanceInRange = async (address, startBlock, endBlock, token) => { + const getBalanceInRange = async (address, startBlock, endBlock, token) => { const web3 = new Web3(Web3.givenProvider || new Web3.providers.HttpProvider(process.env.REACT_APP_NETWORK_URL)); // Calculate the step size given the range of blocks and the number of points we want let step = Math.floor((endBlock - startBlock) / 200); @@ -223,21 +144,19 @@ class ChartCard extends Component { // Loop over the blocks, using the step value for (let i = startBlock; i < endBlock; i = i + step) { // If we already have data about that block, skip it - if (!this.state.series[0].data.find((x) => (x.hasOwnProperty("block") ? x.block === i : false))) { + if (!series[0].data.find((x) => (x.hasOwnProperty("block") ? x.block === i : false))) { let balancePromise; // Create a promise to query the ETH balance for that block if (token?.symbol === "ETH" || !token?.symbol) { - balancePromise = this.promisify((cb) => this.web3Provider.eth.getBalance(address, i, cb)); + balancePromise = promisify((cb) => web3Provider.eth.getBalance(address, i, cb)); } else { tokenContract.defaultBlock = i; - balancePromise = this.promisify((cb) => - tokenContract.methods.balanceOf(address).call({}, i, cb) - ); + balancePromise = promisify((cb) => tokenContract.methods.balanceOf(address).call({}, i, cb)); } // Create a promise to get the timestamp for that block - let timePromise = this.promisify((cb) => this.web3Provider.eth.getBlock(i, cb)); + let timePromise = promisify((cb) => web3Provider.eth.getBlock(i, cb)); // Push data to a linear array of promises to run in parallel. promises.push(i, balancePromise, timePromise); } @@ -251,7 +170,7 @@ class ChartCard extends Component { for (let i = 0; i < results.length; i = i + 3) { let balance = token?.symbol === "ETH" || !token?.symbol - ? this.web3Provider.utils.fromWei(results[i + 1], "ether") + ? web3Provider.utils.fromWei(results[i + 1], "ether") : new BigNumber(results[i + 1]).dividedBy(10 ** token?.decimals).toFixed(2); balances.push({ block: results[i], @@ -266,13 +185,7 @@ class ChartCard extends Component { } }; - unpack(rows, index) { - return rows.map(function (row) { - return row[index]; - }); - } - - getStartDate = (id) => { + const getStartDate = (id) => { let date = moment(); switch (id) { case "year": { @@ -302,74 +215,50 @@ class ChartCard extends Component { return date; }; - changeBaseToken = (token) => { - this.setState({ - token, - }); - - this.changeOption(this.state.selected, token); - }; - - render() { - const theme = this.context; - - return this.state.loading && false ? ( - - - - ) : ( - - {/* begin::Header */} - -
- Portfolio Performance - -
-
- {this.state.options.map((option, index) => { - return ( - - {option.title} - - ); - })} -
-
- {/* end::Header */} - - {/* begin::Content */} -
- {this.state.loading ? ( - - - - ) : ( - - )} + return ( + + +
+ Portfolio Performance
- {/* end::Content */} - - ); - } -} +
+ {options.map((option, index) => { + return ( + setSelectedRange(option.id)} + className={"btn btn-sm btn-link btn-inline"} + selected={selectedRange === option.id} + key={index} + > + {option.title} + + ); + })} +
+
+ +
+ {loading ? ( + + + + ) : connected ? ( + + ) : ( +
Not Connected
+ )} +
+
+ ); +}; const mapStateToProps = (state) => { return { diff --git a/src/components/ChartCard/styleds.tsx b/src/components/ChartCard/styleds.tsx new file mode 100644 index 00000000..d55350fe --- /dev/null +++ b/src/components/ChartCard/styleds.tsx @@ -0,0 +1,58 @@ +import styled from "styled-components"; + +export const LoadingDiv = styled.div` + background-color: ${({ theme }) => theme.modalBG}; + border-radius: 20px; + min-height: 400px; + margin-top: 10px; +`; + +export const CustomTitle = styled.span` + font-weight: 700; + font-size: 1.25rem; + color: ${({ theme }) => theme.text1}; + margin-bottom: 0.75rem; + + @media (min-width: 991px) { + margin-right: 1rem; + margin-bottom: 0; + } +`; + +export const Wrapper = styled.div` + background-color: ${({ theme }) => theme.modalBG}; + color: ${({ theme }) => theme.text1}; + border-radius: 18px; + display: flex; + flex-direction: column; + justify-content: center; + padding: 0 1.25rem; + border: 1px solid ${({ theme }) => theme.borderColor}; +`; + +export const ToolbarButton = styled.button<{ selected: boolean }>` + font-size: 0.875rem; + padding: 0.75rem; + color: ${({ theme, selected }) => (selected ? theme.success : theme.text1)}; + + &:focus, + &:hover, + &:active { + text-decoration: none; + outline: none; + box-shadow: none; + } +`; + +export const CardHeader = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + background-color: transparent; + + padding: 20px; + + @media (min-width: 768px) { + padding: 0; + } +`; diff --git a/src/components/CheckBalance/index.js b/src/components/CheckBalance/index.tsx similarity index 97% rename from src/components/CheckBalance/index.js rename to src/components/CheckBalance/index.tsx index 0968d79f..1fe903cd 100644 --- a/src/components/CheckBalance/index.js +++ b/src/components/CheckBalance/index.tsx @@ -38,7 +38,7 @@ const CheckBalance = (props) => { return ( - + diff --git a/src/components/CircleBar/index.js b/src/components/CircleBar/index.js deleted file mode 100644 index cb374d93..00000000 --- a/src/components/CircleBar/index.js +++ /dev/null @@ -1,26 +0,0 @@ -import styled from "styled-components"; - -const SVG = styled.svg` - display: flex; - width: ${({ width }) => `${width}px`}; - height: ${({ height }) => `${height}px`}; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -` -const Path = styled.path` - stroke-dasharray: 292.273, 292.273; - stroke-dashoffset: ${({ percent }) => percent ? Math.floor(292.273 - (292.273 * percent / 100)) : 292.273}; -` - -const CircleBar = props => { - return ( - - - - - ) -} - -export default CircleBar; \ No newline at end of file diff --git a/src/components/CircleBar/index.tsx b/src/components/CircleBar/index.tsx new file mode 100644 index 00000000..0e9a00d6 --- /dev/null +++ b/src/components/CircleBar/index.tsx @@ -0,0 +1,51 @@ +import styled from "styled-components"; +import useTheme from "../../hooks/useTheme"; + +const SVG = styled.svg<{ width: number | string; height: number | string }>` + display: flex; + width: ${({ width }) => `${width}px`}; + height: ${({ height }) => `${height}px`}; +`; + +const Path = styled.path<{ percent?: number }>` + stroke-dasharray: 292.273, 292.273; + stroke-dashoffset: ${({ percent }) => (percent ? Math.floor(292.273 - (292.273 * percent) / 100) : 292.273)}; +`; + +export type CircleBarProps = { + width?: number | string; + height?: number | string; + stroke?: string; + fill?: string; + percent?: number; +}; + +const CircleBar = ({ + width = 24, + height = 24, + stroke = "rgba(255, 255, 255, 0.15)", + fill, + percent = 0, +}: CircleBarProps) => { + const theme = useTheme(); + + return ( + + + + + ); +}; + +export default CircleBar; diff --git a/src/components/CircleLoading/index.js b/src/components/CircleLoading/index.js deleted file mode 100644 index b17e59e0..00000000 --- a/src/components/CircleLoading/index.js +++ /dev/null @@ -1,34 +0,0 @@ -import styled, { keyframes } from "styled-components"; - -const fill = keyframes` - from { - stroke-dashoffset: 0; - } - - to { - stroke-dashoffset: 292.273; - } -` - -const SVG = styled.svg` - display: flex; - width: ${({ width }) => `${width}px`}; - height: ${({ height }) => `${height}px`}; -` -const Path = styled.path` - stroke-dasharray: 292.273, 292.273; - animation: ${fill} 15s linear infinite forwards; -` - -const CircleLoading = props => { - return ( - - - {!props.loading && !props.priceLoading && ( - - )} - - ) -} - -export default CircleLoading; \ No newline at end of file diff --git a/src/components/CircleLoading/index.tsx b/src/components/CircleLoading/index.tsx new file mode 100644 index 00000000..9c317775 --- /dev/null +++ b/src/components/CircleLoading/index.tsx @@ -0,0 +1,64 @@ +import styled, { keyframes } from "styled-components"; +import useTheme from "../../hooks/useTheme"; + +const fill = keyframes` + from { + stroke-dashoffset: 0; + } + + to { + stroke-dashoffset: 292.273; + } +`; + +const SVG = styled.svg` + display: flex; + width: ${({ width }) => `${width}px`}; + height: ${({ height }) => `${height}px`}; +`; + +const Path = styled.path` + stroke-dasharray: 292.273, 292.273; + animation: ${fill} 15s linear infinite forwards; +`; + +export type CircleLoadingProps = { + width?: number | string; + height?: number | string; + loading?: boolean; + priceLoading?: boolean; + stroke?: string; + fill?: string; +}; + +const CircleLoading = ({ + width = 24, + height = 24, + loading = false, + priceLoading = false, + stroke = "#eee", + fill, +}: CircleLoadingProps) => { + const theme = useTheme(); + + return ( + + + {!loading && !priceLoading && ( + + )} + + ); +}; + +export default CircleLoading; diff --git a/src/components/CoinDetails/index.tsx b/src/components/CoinDetails/index.tsx new file mode 100644 index 00000000..a6e5cd68 --- /dev/null +++ b/src/components/CoinDetails/index.tsx @@ -0,0 +1,569 @@ +import { useContext, useEffect, useMemo, useState } from "react"; +import { Row, Col } from "react-bootstrap"; +import { useSelector, useDispatch } from "react-redux"; +import dompurify from "dompurify"; +import { ThemeContext } from "styled-components"; + +import { useActiveWeb3React } from "../../hooks"; +import { useTokenContract } from "../../hooks/useContract"; +import { useIsDarkMode } from "../../state/user/hooks"; +import { fetchSelectedCoin, fetchHistoricalData } from "../../state/market/actions"; +import Card from "../Card"; +import Loading from "../Loading"; +import CurrencyText from "../CurrencyText"; +import HistoricalChart from "../HistoricalChart"; +import ArrowUp from "../Icons/Coin/ArrowUp"; +import ArrowDown from "../Icons/Coin/ArrowDown"; +import GradientButton from "../UI/Button"; +import * as Styled from "./styleds"; + +export type CoinDetailsProps = { + id?: string; +}; + +const CoinDetails = ({ id }: CoinDetailsProps) => { + const { account } = useActiveWeb3React(); + const theme = useContext(ThemeContext); + const darkMode = useIsDarkMode(); + const dispatch = useDispatch(); + // @ts-ignore + const marketData = useSelector((state) => state.market); + const [walletBalance, setWalletBalance] = useState(0); + const selected = marketData.selected.data || false; + const tokenContract = useTokenContract(selected.contract_address); + + useEffect(() => { + if (tokenContract) { + tokenContract.decimals().then((decimals: any) => { + tokenContract.balanceOf(account).then((response: any) => { + const balance = response.toString(); + setWalletBalance(balance / 10 ** decimals); + }); + }); + } + }, [tokenContract, account]); + + useEffect(() => { + dispatch(fetchSelectedCoin(id)); + + dispatch(fetchHistoricalData(id)); + }, [dispatch, id]); + + const coinAbout = useMemo(() => { + if (selected) { + return dompurify?.sanitize(selected?.description?.en); + } + + return ""; + }, [selected]); + + if (marketData?.selected?.loading) { + return ( + + + +
+ +
+
+ +
+ ); + } + + return ( + <> + {selected && walletBalance > 0 && ( + +
+ Your "{selected.name}" Balance + + {selected.symbol.toUpperCase()} {walletBalance.toFixed(6)} ( + {walletBalance * selected.market_data.current_price.usd}) + +
+
+ )} + + + {selected && selected.contract_address && ( +
+ + Do you wanna Exchange {selected.name}? + +
+ + + Buy + + + + + Sell + + +
+
+ )} + + +
+ + + + + + + + + + + + + + + + + + +
+ {selected && selected.market_data.price_change_percentage_24h >= 0 ? ( + // TODO: fix fills + // @ts-ignore + + ) : ( + // @ts-ignore + + )} + = 0 + ? "text-success" + : "text-danger" + } + > + {selected && Number(selected.market_data.price_change_percentage_24h).toFixed(4)}% + +
+ Daily Changes Percentage +
+
+ + + + +
+ {selected && selected.market_data.price_change_percentage_7d >= 0 ? ( + + ) : ( + + )} + = 0 + ? "text-success" + : "text-danger" + } + > + {selected && Number(selected.market_data.price_change_percentage_7d).toFixed(4)}% + +
+ Weekly Changes Percentage +
+
+ + + + +
+ {selected && selected.market_data.price_change_percentage_30d >= 0 ? ( + + ) : ( + + )} + = 0 + ? "text-success" + : "text-danger" + } + > + {selected && Number(selected.market_data.price_change_percentage_30d).toFixed(4)}% + +
+ Monthly Changes Percentage +
+
+ + + + +
+ {selected && selected.market_data.price_change_percentage_1y >= 0 ? ( + + ) : ( + + )} + = 0 + ? "text-success" + : "text-danger" + } + > + {selected && Number(selected.market_data.price_change_percentage_1y).toFixed(4)}% + +
+ Yearly Changes Percentage +
+
+ +
+ + + +
+ + Market Cap + ${selected && selected.market_data.market_cap.usd} + + + All time High + + ${selected && selected.market_data.ath.usd} + + + + All Time Low + + ${selected && selected.market_data.atl.usd} + + +
+
+
+ + + + + + + + + + + + + + Currency Name + + + {selected && selected.name} + + + + + + + Symbol + + + + {selected && selected.symbol.toUpperCase()} + + + + + + + + Website + + + + {selected && selected.links.homepage[0]} + + + + + + + + Whitepaper + + + + {selected && selected.ico_data ? selected.ico_data.links.whitepaper : "-"} + + + + + + + + Block Explorer + + + + {selected && (selected.links.blockchain_site[0] || "-")} + + + + + + + + Github + + + + {selected && (selected.links.repos_url.github[0] || "-")} + + + + + + + + Twitter + + + + {selected && + (selected.links.twitter_screen_name + ? `https://twitter.com/${selected.links.twitter_screen_name}` + : "-")} + + + + + + + + Facebook + + + + {selected && + (selected.links.facebook_username + ? `https://facebook.com/${selected.links.facebook_username}` + : "-")} + + + + + + + + Reddit + + + + {selected && (selected.links.subreddit_url || "-")} + + + + + + + + Telegram + + + + {selected && + (selected.links.telegram_channel_identifier + ? `https://t.me/${selected.links.telegram_channel_identifier}` + : "-")} + + + + + + + + Bitcoin Talk + + + + {selected && + (selected.links.bitcointalk_thread_identifier + ? `https://bitcointalk.org/index.php?topic=${selected.links.bitcointalk_thread_identifier}` + : "-")} + + + + + + + + + ); +}; + +export default CoinDetails; diff --git a/src/components/CoinDetails/styleds.tsx b/src/components/CoinDetails/styleds.tsx new file mode 100644 index 00000000..438de1cb --- /dev/null +++ b/src/components/CoinDetails/styleds.tsx @@ -0,0 +1,218 @@ +import styled from "styled-components"; +import { Col } from "react-bootstrap"; +import { Link } from "react-router-dom"; +import Card, { ResponsiveCard } from "../Card"; + +export const ChartResponsiveCard = styled(ResponsiveCard)` + @media (max-width: 991px) { + margin-bottom: -1px !important; + border: none; + + .card-body { + padding-top: 15px; + padding-bottom: 15px; + } + } +`; + +export const ResponsiveCol = styled.div` + @media (max-width: 991px) { + margin: -1px -30px 0; + padding: 15px 30px; + background-color: ${({ theme }) => theme.modalBG}; + } +`; + +export const BalanceCard = styled(Card)` + .card-body { + padding: 20px 30px; + } + + @media (max-width: 991px) { + margin-bottom: 40px; + + .card-body { + padding: 20px 15px; + } + } +`; + +export const StyledCard = styled(Card)` + @media (max-width: 991px) { + border: 1px solid ${({ theme }) => theme.text4}; + margin-bottom: 0 !important; + + .card-header, + .card-body { + padding: 20px 15px; + } + + .card-header { + min-height: 57px; + } + + h4 { + font-size: 0.875rem; + } + } + + @media (min-width: 992px) { + .card-header { + padding: 15px 30px; + position: relative; + border: none; + + &::before { + content: ""; + position: absolute; + left: 30px; + right: 30px; + bottom: 0; + border-bottom: 1px solid ${({ theme }) => theme.text3}; + } + } + + h4 { + font-size: 1rem; + } + } +`; + +export const ChangesCard = styled(StyledCard)` + @media (max-width: 991px) { + .card-body { + padding-top: 34px; + padding-bottom: 34px; + } + } +`; + +export const About = styled.div` + font-weight: 400; + font-size: 1rem; + line-height: 19px; + + @media (max-width: 991px) { + font-size: 0.875rem; + line-height: 17px; + text-align: justify; + } +`; + +export const DetailsCol = styled(Col)` + &:not(:last-child) { + margin-bottom: 20px; + } +`; + +export const DetailsInnerCol = styled(Col)` + width: initial !important; + flex: 1; + max-width: 100%; +`; + +export const DetailsDesc = styled.span` + font-weight: 400; + font-size: 1rem; + + @media (max-width: 991px) { + font-size: 0.75rem; + } +`; + +export const DetailsValue = styled.span` + font-weight: 700; + font-size: 1rem; + text-align: right; + width: 100%; + + @media (max-width: 991px) { + font-size: 0.875rem; + } +`; + +export const DetailsLink = styled.a<{ withUnderline?: boolean }>` + color: ${({ theme }) => theme.primary}; + text-decoration: ${({ withUnderline }) => (withUnderline ? "underline" : "none")}; + font-weight: 700; + font-size: 1rem; + text-align: right; + width: 100%; + + @media (max-width: 991px) { + font-size: 0.875rem; + font-weight: 400; + max-width: 180px; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + } +`; + +export const StatsDesc = styled.span<{ last?: boolean }>` + color: ${({ theme }) => theme.text3}; + font-size: 0.875rem; + display: block; + margin-bottom: 1.25rem; + + @media (max-width: 991px) { + font-size: 0.75rem; + color: ${({ theme }) => theme.text1}; + margin-bottom: ${({ last }) => (!last ? "0.875rem" : "0")}; + } +`; + +export const StatsValue = styled.span<{ last?: boolean }>` + color: ${({ theme }) => theme.text1}; + font-size: 1rem; + font-weight: 700; + + @media (max-width: 991px) { + font-size: 0.875rem; + margin-bottom: ${({ last }) => (!last ? "0.875rem" : "0")}; + } +`; + +export const ChangesTitle = styled.span` + font-weight: 700; + font-size: 1.25rem; + margin-left: 1rem; +`; + +export const ChangesSubtitle = styled.span` + font-weight: 400; + font-size: 0.875rem; + display: block; + margin-top: 18px; +`; + +export const BalanceText = styled.span` + font-weight: 400; + font-size: 0.875rem; + margin-bottom: 20px; + + @media (min-width: 991px) { + margin-bottom: 0; + font-size: 1rem; + } +`; + +export const BalanceValue = styled.span` + font-weight: 700; + font-size: 1rem; +`; + +export const BuyHelper = styled.span` + font-weight: 400; + font-size: 1rem; + + @media (max-width: 991px) { + margin-bottom: 30px; + } +`; + +export const BuyLink = styled(Link)` + flex: 1; + display: flex; + flex-direction: column; +`; diff --git a/src/components/Collapse/index.js b/src/components/Collapse/index.js index 3acb87b7..532d9405 100644 --- a/src/components/Collapse/index.js +++ b/src/components/Collapse/index.js @@ -1,280 +1,13 @@ import React, { useContext, useRef, useState, useCallback, useMemo } from "react"; +import { useTranslation } from "react-i18next"; +import { ThemeContext } from "styled-components"; import { ETHER, Token } from "@uniswap/sdk"; import moment from "moment"; +import { ChevronRight, Download, Upload, FileText, Repeat } from "react-feather"; -import CurrencyLogo from "../CurrencyLogo"; -import FileIcon from "../Icons/History/File"; -import ArrowRightIcon from "../Icons/ArrowRight"; -import ExchangeIcon from "../Icons/History/Exchange"; -import ArrowDownIcon from "../Icons/History/ArrowDown"; -import ArrowUpIcon from "../Icons/History/ArrowUp"; -import styled, { ThemeContext } from "styled-components"; import { useActiveWeb3React } from "../../hooks"; -import { useTranslation } from "react-i18next"; - -const Wrapper = styled.div` - border: 1px solid ${({ theme }) => theme.text4}; - background-color: ${({ theme }) => theme.bg3}; - border-radius: 18px; - margin-bottom: 15px; - overflow: hidden; - transition: all 0.6s ease; - will-change: transform, height, border-color, box-shadow; - - @media (min-width: 768px) { - margin-bottom: 1.25rem; - } -`; - -const Header = styled.div` - padding: 20px; - cursor: pointer; - transition: 0.3s ease background-color; - position: relative; - background-color: ${({ theme }) => theme.modalBG}; - - @media (min-width: 992px) { - padding: 15px 40px 15px 20px; - } -`; - -const HeaderSection = styled.div` - flex: 1; - - padding-bottom: ${({ type }) => (type === "application" ? "0" : "30px")}; - - @media (min-width: 992px) { - min-width: ${({ type }) => (type === "title" ? "300px" : type === "tokens" ? "400px" : "initial")}; - padding: 0; - } -`; - -const HeaderShowMore = styled.div` - width: 40px; - height: 40px; - background-color: ${({ theme }) => theme.bg2}; - display: flex; - align-items: center; - justify-content: center; - transform: ${({ show }) => (show ? "rotate(90deg)" : "rotate(0deg)")}; - border-radius: 40px; - transition: 0.4s ease all; - will-change: background-color, transform; - margin-left: 35px; - - @media (max-width: 991px) { - position: absolute; - top: 13px; - right: 15px; - transform: ${({ show }) => (show ? "rotate(-90deg)" : "rotate(90deg)")}; - } - - &:hover { - background-color: ${({ theme }) => theme.bg1}; - } -`; - -const Body = styled.div` - padding: 0; - position: relative; -`; - -const BodyInside = styled.div` - padding: 20px 20px 30px; - min-width: 320px; - align-self: stretch; -`; - -const Footer = styled.div` - padding: 20px 40px 20px 20px; - align-self: stretch; -`; - -const BodyToken = styled.div` - display: flex; - align-items: center; - - &:not(:last-child) { - margin-bottom: 30px; - } -`; - -const TypeIcon = styled.div` - width: 50px; - height: 50px; - border-radius: 50px; - display: flex; - align-items: center; - justify-content: center; - background-color: ${({ theme }) => theme.primaryLight}; -`; - -const HeaderTitle = styled.span` - font-weight: 400; - font-size: 0.875rem; - margin-bottom: 0.75rem; - - @media (min-width: 992px) { - font-size: 1rem; - margin-bottom: 0.5rem; - } -`; - -const Date = styled.span` - font-weight: 400; - font-size: 0.875rem; - - @media (min-width: 992px) { - font-size: 1rem; - } -`; - -const LogoContainer = styled.div` - margin-right: 10px; - - img, - div { - width: 30px; - height: 30px; - - @media (min-width: 768px) { - width: 34px; - height: 34px; - } - } -`; - -const Value = styled.span` - font-weight: 500; - color: ${({ color, theme }) => (color ? theme[color] : theme.text1)}; - font-size: 0.875rem; - - @media (min-width: 992px) { - font-size: 1rem; - } -`; - -const Desc = styled.span` - font-weight: 500; - color: ${({ theme }) => theme.text3}; - font-size: 0.875rem; - - @media (min-width: 992px) { - font-size: 1rem; - } -`; - -const Application = styled.span` - border-radius: 18px !important; - height: 48px !important; - padding: 0.5rem 1rem !important; -`; - -const Arrow = styled.div` - padding: 0 26px; - - @media (max-width: 991px) { - display: flex; - align-items: center; - justify-content: center; - padding: 14px 0; - - svg { - transform: rotate(90deg); - } - } -`; - -const Separator = styled.div` - height: 0; - width: 100%; - border-bottom: 1px solid ${({ theme }) => theme.text4}; -`; - -const VerticalSeparator = styled.div` - width: 0; - margin: 0 ${({ margin }) => `${margin}rem`}; - align-self: stretch; - border-right: 1px solid ${({ theme }) => theme.text4}; - position: relative; - - @media (max-width: 991px) { - width: 100%; - height: 0; - border-right: none; - border-bottom: 1px solid ${({ theme }) => theme.text4}; - margin: ${({ margin }) => `${margin}rem`} 0; - } -`; - -const PrimaryCircle = styled.div` - width: 32px; - height: 32px; - border: 1px solid ${({ theme }) => theme.primary}; - background-color: ${({ theme }) => theme.bg1}; - border-radius: 32px; - display: flex; - align-items: center; - justify-content: center; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -`; - -const Details = styled.div` - display: flex; - justify-content: center; - flex-direction: column; - - &:not(:last-child) { - margin-bottom: 30px; - - @media (min-width: 992px) { - margin-bottom: ${({ vertical }) => (vertical ? "30px" : 0)}; - } - } -`; - -const DescTitle = styled.span` - font-weight: 400; - font-size: 1rem; - margin-bottom: 0.375rem; - color: ${({ theme }) => theme.text1}; -`; - -const DescValue = styled.span` - font-weight: 700; - font-size: 1rem; - color: ${({ theme }) => theme.text1}; -`; -const DescAnchor = styled.a` - font-weight: 700; - font-size: 1rem; - text-decoration: underline; - color: ${({ theme }) => theme.primary}; -`; - -const TradeSide = styled.div` - min-width: 172px; -`; - -const ReceivedCoins = styled.div` - padding: 20px 50px; - - @media (max-width: 991px) { - padding: 30px 20px; - } -`; - -const CollapseView = styled.div` - overflow: hidden; - max-height: ${({ height }) => `${height}px` || 0}; - display: flex; - flex-direction: column; - - transition: 0.4s ease all; -`; +import CurrencyLogo from "../CurrencyLogo"; +import * as Styled from "./styleds"; const hashText = (hash) => { return hash.slice(0, 6) + "..." + hash.slice(-4); @@ -323,28 +56,28 @@ const Collapse = (props) => { }, [props.txn, account]); return ( - -
+ - - + + {tokens.ref.value === "0" && props.txn.length === 1 ? ( - + ) : tokens.from.length === 1 && tokens.to.length === 1 ? ( - + ) : (tokens.from.length > 1 && tokens.to.length === 1) || (tokens.from.length === 1 && tokens.to.length > 1) ? ( - + ) : ( - + )} - +
- + {tokens.ref.value === "0" && props.txn.length === 1 ? "Contracts / Approval" : tokens.from.length === 1 && tokens.to.length === 1 @@ -355,17 +88,20 @@ const Collapse = (props) => { : tokens.from.length > 1 || tokens.to.length > 1 ? "Swap" : "Receive"} - - {moment(tokens.ref.timeStamp, "X").format("hh:MM A")} + + + {moment(tokens.ref.timeStamp, "X").format("hh:MM A")} +
-
- + + {tokens.from.length > 0 && ( - - {tokens.from.map((token) => { + + {tokens.from.map((token, index) => { if (token.contractAddress && token.tokenDecimal) { const currency = new Token( chainId, @@ -375,24 +111,24 @@ const Collapse = (props) => { token.tokenName ); return ( - + - + ); } else { return ( - + - + ); } })}
- + {tokens.from.length !== 1 ? ( - - {tokens.from.length} {t("tokens.assets")} - + + {tokens.from.length} {t("tokens.assets")} + ) : ( `-${ tokens.from[0].tokenDecimal @@ -403,27 +139,27 @@ const Collapse = (props) => { : (Number(tokens.from[0].value) / 10 ** 18).toFixed(4) } ${tokens.from[0].tokenSymbol || "ETH"}` )} - + {tokens.from.length === 1 && ( - + {tokens.from[0].contractAddress ? hashText(tokens.from[0].contractAddress) : "Ethereum"} - + )}
-
+ )} {tokens.from.length > 0 && tokens.to.length > 0 && ( - - - + + + )} {tokens.to.length > 0 && ( - - {tokens.to.map((token) => { + + {tokens.to.map((token, index) => { if (token.contractAddress && token.tokenDecimal) { const currency = new Token( chainId, @@ -434,24 +170,24 @@ const Collapse = (props) => { ); return ( - + - + ); } else { return ( - + - + ); } })}
- + {tokens.to.length !== 1 ? ( - - {tokens.to.length} {t("tokens.assets")} - + + {tokens.to.length} {t("tokens.assets")} + ) : ( `+${ tokens.to[0].tokenDecimal @@ -462,44 +198,44 @@ const Collapse = (props) => { : (Number(tokens.to[0].value) / 10 ** 18).toFixed(4) } ${tokens.to[0].tokenSymbol || "ETH"}` )} - + {tokens.to.length === 1 && ( - + {tokens.to[0].contractAddress ? hashText(tokens.to[0].contractAddress) : "Ethereum"} - + )}
-
+ )} -
- +
- {tokens.from.length > 0 ? "Application" : "From"} - + {tokens.from.length > 0 ? "Application" : "From"} + {tokens.from.length > 0 ? hashText(tokens.from[0].to || tokens.from[0].contractAddress) : tokens.to.length > 0 ? hashText(tokens.to[0].from || tokens.to[0].contractAddress) : ""} - +
-
- - - -
- + + + + + +
{tokens.from.length > 0 && tokens.to.length > 0 ? ( <> - - - + + + {tokens.from.map((token, index) => { const currency = token.contractAddress && token.tokenDecimal @@ -512,36 +248,36 @@ const Collapse = (props) => { ) : ETHER; return ( - - + +
- {`-${ + {`-${ token.contractAddress ? ( Number(token.value) / 10 ** Number(token.tokenDecimal || 18) ).toFixed(4) : (Number(token.value) / 10 ** 18).toFixed(4) - } ${token.tokenSymbol || "ETH"}`} - + } ${token.tokenSymbol || "ETH"}`} + {token.contractAddress ? hashText(token.contractAddress) : "Ethereum"} - +
-
+ ); })} -
- - - - - - + + + + + + + {tokens.to.map((token, index) => { const currency = token.contractAddress && token.tokenDecimal @@ -554,85 +290,82 @@ const Collapse = (props) => { ) : ETHER; return ( - - + +
- {`+${ + {`+${ token.contractAddress ? ( Number(token.value) / 10 ** Number(token.tokenDecimal || 18) ).toFixed(4) : (Number(token.value) / 10 ** 18).toFixed(4) - } ${token.tokenSymbol || "ETH"}`} - + } ${token.tokenSymbol || "ETH"}`} + {token.contractAddress ? hashText(token.contractAddress) : "Ethereum"} - +
-
+ ); })} -
- - -
- {t("pools.fee")} - + + + + + {t("pools.fee")} + {((tokens.ref.gasUsed * tokens.ref.gasPrice) / 10 ** 18).toFixed(6)} ETH - -
-
- {t("blockNumber")} - {tokens.ref.blockNumber} -
-
- {t("transactionHash")} - + + + {t("blockNumber")} + {tokens.ref.blockNumber} + + + {t("transactionHash")} + {tokens.ref.hash.slice(0, 10)}...{tokens.ref.hash.slice(-8)} ↗ - -
-
- + + + + ) : ( - <> - -
-
- {t("pools.fee")} - - {((tokens.ref.gasUsed * tokens.ref.gasPrice) / 10 ** 18).toFixed(6)} ETH - -
-
- {t("blockNumber")} - {tokens.ref.blockNumber} -
-
- {t("blockNumber")} - - {tokens.ref.hash.slice(0, 10)}...{tokens.ref.hash.slice(-8)} ↗ - -
-
- + + + {t("pools.fee")} + + {((tokens.ref.gasUsed * tokens.ref.gasPrice) / 10 ** 18).toFixed(6)} ETH + + + + {t("blockNumber")} + {tokens.ref.blockNumber} + + + {t("blockNumber")} + + {tokens.ref.hash.slice(0, 10)}...{tokens.ref.hash.slice(-8)} ↗ + + + )}
-
-
+ + ); }; diff --git a/src/components/Collapse/styleds.tsx b/src/components/Collapse/styleds.tsx new file mode 100644 index 00000000..485fa18f --- /dev/null +++ b/src/components/Collapse/styleds.tsx @@ -0,0 +1,249 @@ +import styled from "styled-components"; + +export const Wrapper = styled.div` + border-bottom: 1px solid ${({ theme }) => theme.borderColor2}; + background-color: ${({ theme }) => theme.bg1}; + overflow: hidden; + transition: all 0.6s ease; + will-change: transform, height, border-color, box-shadow; + + &:first-child { + border-top: 1px solid ${({ theme }) => theme.borderColor2}; + } +`; + +export const Header = styled.div` + background-color: ${({ theme }) => theme.modalBG}; + cursor: pointer; + padding: 1.25rem 1rem; + position: relative; + transition: 0.3s ease background-color; +`; + +export const HeaderSection = styled.div<{ type: string }>` + flex: 1; + padding-bottom: ${({ type }) => (type === "application" ? "0" : "30px")}; + + @media (min-width: 992px) { + min-width: ${({ type }) => (type === "title" ? "300px" : type === "tokens" ? "400px" : "initial")}; + padding: 0; + } +`; + +export const HeaderShowMore = styled.div<{ show: boolean }>` + width: 40px; + height: 40px; + background-color: ${({ theme }) => theme.bg1}; + display: flex; + align-items: center; + justify-content: center; + transform: ${({ show }) => (show ? "rotate(90deg)" : "rotate(0deg)")}; + border-radius: 40px; + transition: 0.4s ease all; + will-change: background-color, transform; + margin-left: 35px; + + @media (max-width: 991px) { + position: absolute; + top: 13px; + right: 15px; + transform: ${({ show }) => (show ? "rotate(-90deg)" : "rotate(90deg)")}; + } + + &:hover { + background-color: ${({ theme }) => theme.bg1}; + } +`; + +export const Body = styled.div` + padding: 0; + position: relative; +`; + +export const BodyInside = styled.div` + padding: 20px 20px 30px; + min-width: 320px; + align-self: stretch; +`; + +export const Footer = styled.div` + padding: 20px 40px 20px 20px; + align-self: stretch; + border-top: 1px solid ${({ theme }) => theme.borderColor}; +`; + +export const BodyToken = styled.div` + display: flex; + align-items: center; + + &:not(:last-child) { + margin-bottom: 30px; + } +`; + +export const TypeIcon = styled.div` + background-color: ${({ theme }) => theme.primaryLight}; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + height: 2.5rem; + width: 2.5rem; +`; + +export const HeaderTitle = styled.span` + font-weight: 400; + font-size: 0.875rem; +`; + +export const Date = styled.span` + font-weight: 400; + font-size: 0.875rem; + + @media (min-width: 992px) { + font-size: 1rem; + } +`; + +export const LogoContainer = styled.div` + margin-right: 10px; + + img, + div { + width: 30px; + height: 30px; + + @media (min-width: 768px) { + width: 34px; + height: 34px; + } + } +`; + +export const Value = styled.span<{ color: any }>` + font-weight: 500; + color: ${({ color, theme }) => (color ? theme[color] : theme.text1)}; + font-size: 0.875rem; + + @media (min-width: 992px) { + font-size: 1rem; + } +`; + +export const Desc = styled.span` + font-weight: 500; + color: ${({ theme }) => theme.text3}; + font-size: 0.875rem; +`; + +export const Application = styled.div` + color: ${({ theme }) => theme.text2}; + font-size: 1rem; +`; + +export const Arrow = styled.div` + padding: 0 26px; + + @media (max-width: 991px) { + display: flex; + align-items: center; + justify-content: center; + padding: 14px 0; + + svg { + transform: rotate(90deg); + } + } +`; + +export const Separator = styled.div` + height: 0; + width: 100%; + border-bottom: 1px solid ${({ theme }) => theme.text4}; +`; + +export const VerticalSeparator = styled.div<{ margin: number }>` + width: 0; + margin: 0 ${({ margin }) => `${margin}rem`}; + align-self: stretch; + border-right: 1px solid ${({ theme }) => theme.text4}; + position: relative; + + @media (max-width: 991px) { + width: 100%; + height: 0; + border-right: none; + border-bottom: 1px solid ${({ theme }) => theme.text4}; + margin: ${({ margin }) => `${margin}rem`} 0; + } +`; + +export const PrimaryCircle = styled.div` + width: 32px; + height: 32px; + border: 1px solid ${({ theme }) => theme.primary}; + background-color: ${({ theme }) => theme.bg1}; + border-radius: 32px; + display: flex; + align-items: center; + justify-content: center; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +`; + +export const Details = styled.div<{ vertical: boolean }>` + display: flex; + justify-content: center; + flex-direction: column; + + &:not(:last-child) { + margin-bottom: 30px; + + @media (min-width: 992px) { + margin-bottom: ${({ vertical }) => (vertical ? "30px" : 0)}; + } + } +`; + +export const DescTitle = styled.span` + font-weight: 400; + font-size: 1rem; + margin-bottom: 0.375rem; + color: ${({ theme }) => theme.text1}; +`; + +export const DescValue = styled.span` + font-weight: 700; + font-size: 1rem; + color: ${({ theme }) => theme.text1}; +`; + +export const DescAnchor = styled.a` + font-weight: 700; + font-size: 1rem; + text-decoration: underline; + color: ${({ theme }) => theme.primary}; +`; + +export const TradeSide = styled.div` + min-width: 172px; +`; + +export const ReceivedCoins = styled.div` + padding: 20px 50px; + + @media (max-width: 991px) { + padding: 30px 20px; + } +`; + +export const CollapseView = styled.div<{ height: number }>` + overflow: hidden; + max-height: ${({ height }) => `${height}px` || 0}; + display: flex; + flex-direction: column; + + transition: 0.4s ease all; +`; diff --git a/src/components/ConfirmAddModalBottom/index.tsx b/src/components/ConfirmAddModalBottom/index.tsx index 28b164e5..03db57df 100644 --- a/src/components/ConfirmAddModalBottom/index.tsx +++ b/src/components/ConfirmAddModalBottom/index.tsx @@ -1,5 +1,4 @@ import { Currency, CurrencyAmount, Fraction, Percent } from "@uniswap/sdk"; -import React from "react"; import Button from "../UI/Button"; import { RowBetween, RowFixed } from "../Row"; import CurrencyLogo from "../../components/CurrencyLogo"; @@ -43,7 +42,7 @@ export function ConfirmAddModalBottom({ {parsedAmounts[Field.CURRENCY_A]?.toSignificant(6)} @@ -54,7 +53,7 @@ export function ConfirmAddModalBottom({ {parsedAmounts[Field.CURRENCY_B]?.toSignificant(6)} diff --git a/src/components/CryptoInput/index.js b/src/components/CryptoInput/index.js index 339c761a..a48aef1c 100644 --- a/src/components/CryptoInput/index.js +++ b/src/components/CryptoInput/index.js @@ -1,4 +1,4 @@ -import React, {useCallback, useMemo, useState} from "react"; +import React, { useCallback, useMemo, useState } from "react"; import styled from "styled-components"; import { isMobile } from "react-device-detect"; @@ -87,7 +87,7 @@ const CurrencySelect = styled.button` height: 56px; font-size: 1rem; font-weight: 700; - background-color: ${({ theme }) => theme.bg3}; + background-color: ${({ theme }) => theme.bg1}; color: ${({ theme }) => theme.text1}; border-bottom-left-radius: ${({ reverse }) => (reverse ? "0" : `1.125rem`)}; border-top-left-radius: ${({ reverse }) => (reverse ? "0" : `1.125rem`)}; @@ -128,11 +128,11 @@ export default function CryptoInput({ }, [setModalOpen]); const iconHTML = useMemo(() => { - if('icon' in selected) { + if ("icon" in selected) { return dompurify.sanitize(selected.icon); } - return '' - }, [selected]) + return ""; + }, [selected]); return ( @@ -179,17 +179,15 @@ export default function CryptoInput({ )} {!hideInput && ( - <> - { - onUserInput(val, type); - }} - reverse={reverse && !isMobile} - /> - + { + onUserInput(val, type); + }} + reverse={reverse && !isMobile} + /> )} {reverse && !isMobile && ( { + // @ts-ignore + const selectedCurrency = useSelector((state) => state.currency.selected); + const { i18n } = useTranslation(); + const dispatch = useDispatch(); + + const selectDestinationCurrency = (id: any) => { + dispatch(actions.fetchCurrencies(id)); + i18n.changeLanguage(rates[id].lng); + }; + + useEffect(() => { + dispatch(actions.fetchCurrencies(selectedCurrency)); + }, [selectedCurrency, dispatch]); + + return ( + + + + + {selectedCurrency} + + + + + {Object.keys(rates).map((currency) => ( + + + + {currency} + + + ))} + + + + ); +}; + +export default CurrencyDropdown; diff --git a/src/components/HeaderDropdown/styles.ts b/src/components/CurrencyDropdown/styleds.tsx similarity index 53% rename from src/components/HeaderDropdown/styles.ts rename to src/components/CurrencyDropdown/styleds.tsx index b13b0fe2..e41a5976 100644 --- a/src/components/HeaderDropdown/styles.ts +++ b/src/components/CurrencyDropdown/styleds.tsx @@ -1,11 +1,10 @@ import styled from "styled-components"; -import { Button as BootstrapButton } from "react-bootstrap"; -import { Link } from "react-router-dom"; +import { Button } from "react-bootstrap"; import Img from "../UI/Img"; -export const Wrapper = styled.div` +export const CurrencyWrapper = styled.div` + padding: 0; position: relative; - padding: 14px 0; &:hover .header-dropdown { opacity: 1; @@ -14,10 +13,6 @@ export const Wrapper = styled.div` } `; -export const CurrencyWrapper = styled(Wrapper)` - padding: 0; -`; - export const Item = styled.div` display: flex; align-items: center; @@ -30,9 +25,9 @@ export const DropDown = styled.div` background-color: ${({ theme }) => theme.modalBG}; border-radius: 0.75rem; box-shadow: -1px 11px 43px rgba(0, 0, 0, 0.12); - padding: 20px; width: auto; - min-width: 260px; + min-width: 250px; + padding: 10px; opacity: 0; visibility: hidden; @@ -43,7 +38,7 @@ export const DropDown = styled.div` z-index: 99999; `; -export const DropDownItem = styled(Link)` +export const DropDownItem = styled(Button)` display: flex; align-items: center; position: relative; @@ -52,14 +47,11 @@ export const DropDownItem = styled(Link)` white-space: nowrap; color: ${({ theme }) => theme.text1}; - &:not(:last-child) { - margin-bottom: 20px; - } - &:hover, &:focus, - &:active { - color: ${({ theme }) => theme.text2}; + &:active , + &.active { + color: ${({ theme }) => theme.primary}; box-shadow: none; outline: none; text-decoration: none; @@ -67,39 +59,24 @@ export const DropDownItem = styled(Link)` `; export const Title = styled.span` - font-weight: 500; - font-size: 0.75rem; - color: ${({ theme }) => theme.text3}; - margin-bottom: 8px; - display: block; -`; - -export const Title2 = styled.span` font-weight: 500; font-size: 1rem; - color: ${({ theme }) => theme.text2}; `; -export const IconButton = styled(BootstrapButton)` - color: ${({ theme }) => theme.primary}; - margin-right: 20px; -`; +export const IconButton = styled(Button)` + color: ${({ theme }) => theme.text1}; -export const SeeAllButton = styled(Link)` - text-decoration: none; - padding-top: 12px; - font-weight: 500; - font-size: 0.75rem; - color: ${({ theme }) => theme.primary}; - display: block; - text-align: center; + &:hover, + &:focus { + color: ${({ theme }) => theme.text1}; + text-decoration: none; + } `; export const CurrencyLogo = styled(Img)` - width: 24px; + border-radius: 50%; height: 24px; - margin-right: 20px; - border-radius: 24px; - border: 1px solid #a890fe; min-width: 24px; + width: 24px; + margin-right: 0.5rem; `; diff --git a/src/components/CurrencyInputPanel/index.tsx b/src/components/CurrencyInputPanel/index.tsx index 1b889c51..151a526a 100644 --- a/src/components/CurrencyInputPanel/index.tsx +++ b/src/components/CurrencyInputPanel/index.tsx @@ -1,167 +1,17 @@ -import { Currency, Pair } from "@uniswap/sdk"; import React, { useState, useCallback } from "react"; -import styled from "styled-components"; +import { useTranslation } from "react-i18next"; +import { Button } from "react-bootstrap"; +import { Currency, Pair } from "@uniswap/sdk"; import CurrencySearchModal from "../SearchModal/CurrencySearchModal"; import CurrencyLogo from "../CurrencyLogo"; import DoubleCurrencyLogo from "../DoubleLogo"; import { RowBetween } from "../Row"; import { Input as NumericalInput } from "../NumericalInput"; -import { ReactComponent as DropDown } from "../../assets/images/dropdown.svg"; import { useActiveWeb3React } from "../../hooks"; import { useCurrencyBalance } from "../../state/wallet/hooks"; -import { useTranslation } from "react-i18next"; - -const InputRow = styled.div<{ selected: boolean }>` - ${({ theme }) => theme.flexRowNoWrap}; - align-items: center; - - padding: 0.75rem 0; -`; - -const Label = styled.span` - color: ${({ theme }) => theme.text1}; - font-weight: 400; - font-size: 0.875rem; - padding: 0; - - @media (min-width: 768px) { - padding: 0 1.5rem; - } -`; - -const CurrencySelect = styled.button<{ selected: boolean }>` - align-items: center; - height: 56px; - font-size: 0.875rem; - font-weight: 500; - background-color: ${({ theme }) => theme.bg3}; - color: ${({ theme }) => theme.text1}; - border-bottom-left-radius: 18px; - border-top-left-radius: 18px; - box-shadow: none; - outline: none; - cursor: pointer; - user-select: none; - border: none; - padding: 0.875rem 0.625rem; - min-width: 116px; - width: 116px; - - @media (min-width: 768px) { - min-width: 178px; - width: 178px; - padding: 0.625rem 1.5rem; - font-size: 1rem; - font-weight: 700; - } - - :focus, - :hover { - background-color: ${({ theme }) => theme.bg4}; - outline: none; - } -`; - -const LabelRow = styled.div` - ${({ theme }) => theme.flexRowNoWrap}; - align-items: center; - color: ${({ theme }) => theme.text1}; - font-size: 0.75rem; - line-height: 1rem; - padding: 0; -`; - -const InputContainer = styled.div` - position: relative; - display: flex; - align-items: center; - flex-wrap: nowrap; - flex: 1; -`; - -const Aligner = styled.span` - display: flex; - align-items: center; - justify-content: space-between; -`; - -const StyledDropDown = styled(DropDown)<{ selected: boolean }>` - margin: 0 0.25rem 0 0.5rem; - height: 35%; - - path { - stroke: ${({ theme }) => theme.text1}; - stroke-width: 1.5px; - } -`; - -const InputPanel = styled.div<{ hideInput?: boolean; withoutMargin?: boolean }>` - ${({ theme }) => theme.flexColumnNoWrap}; - position: relative; - z-index: 1; - margin-bottom: ${({ withoutMargin }) => (withoutMargin ? "0.5rem" : "1rem")}; - - @media (min-width: 768px) { - margin-bottom: ${({ withoutMargin }) => (withoutMargin ? "0.5rem" : "1.75rem")}; - } -`; - -const StyledTokenName = styled.span<{ active?: boolean }>` - margin-right: auto; - padding-left: ${({ active }) => (active ? "0.625rem" : "0")}; - font-size: 1rem; - - @media (min-width: 768px) { - padding-left: ${({ active }) => (active ? "0.75rem" : "0")}; - } -`; - -const StyledBalanceMax = styled.button` - background-color: ${({ theme }) => theme.primaryLight}; - border: none; - border-radius: 10px; - font-size: 1rem; - padding: 0.25rem 0.625rem; - height: 32px; - max-height: 32px; - font-weight: 500; - cursor: pointer; - color: ${({ theme }) => theme.primary}; - position: absolute; - bottom: calc(100% + 10px); - right: 0; - transition: all ease 0.3s; - - @media (min-width: 768px) { - height: 40px; - max-height: 40px; - padding: 0.5rem; - top: 8px; - right: 8px; - } - - :hover { - background-color: ${({ theme }) => theme.primary}; - color: ${({ theme }) => theme.bg2}; - } - - :focus { - outline: none; - } -`; - -const Balance = styled.span<{ showBalance?: boolean }>` - color: ${({ theme }) => theme.text1}; - font-weight: 500; - font-size: 0.875rem; - padding: 0; - - @media (max-width: 767px) { - margin-right: ${({ showBalance }) => (showBalance ? "70px" : "0")}; - margin-top: ${({ showBalance }) => (showBalance ? "-10px" : "0")}; - } -`; +import * as Styled from "./styles"; +import { ChevronDown } from "react-feather"; interface CurrencyInputPanelProps { value: string; @@ -174,7 +24,6 @@ interface CurrencyInputPanelProps { disableCurrencySelect?: boolean; hideBalance?: boolean; pair?: Pair | null; - hideInput?: boolean; otherCurrency?: Currency | null; id: string; showCommonBases?: boolean; @@ -193,7 +42,6 @@ export default function CurrencyInputPanel({ disableCurrencySelect = false, hideBalance = false, pair = null, // used for double token logo - hideInput = false, otherCurrency, id, showCommonBases, @@ -208,37 +56,18 @@ export default function CurrencyInputPanel({ setModalOpen(false); }, [setModalOpen]); + const onOpenCurrencySelect = () => { + if (!disableCurrencySelect) { + setModalOpen(true); + } + }; + return ( - -
- {!hideInput && ( - - - - {account && ( - - {!!currency && selectedCurrencyBalance - ? t("balance", { balanceInput: selectedCurrencyBalance?.toSignificant(6) }) - : " -"} - - )} - - - )} - - { - if (!disableCurrencySelect) { - setModalOpen(true); - } - }} - > - + + +
+ + {pair ? ( ) : currency ? ( - + ) : null} + {pair ? ( - - {pair?.token0.symbol}:{pair?.token1.symbol} - + + {label} + + + {pair?.token0.symbol}:{pair?.token1.symbol} + + + ) : currency ? ( + + {label} + + + {currency.symbol && currency.symbol.length > 16 + ? currency.symbol.slice(0, 4) + + "..." + + currency.symbol.slice(currency.symbol.length - 5, currency.symbol.length) + : currency?.symbol} + + ) : ( - - {(currency && currency.symbol && currency.symbol.length > 20 - ? currency.symbol.slice(0, 4) + - "..." + - currency.symbol.slice(currency.symbol.length - 5, currency.symbol.length) - : currency?.symbol) || t("selectToken")} - - )} - {!disableCurrencySelect && } - - - {!hideInput && ( - - { - onUserInput(val); - }} - /> - {account && currency && showMaxButton && label !== "To" && ( - {t("max")} + {t("selectToken")} )} - + + {!disableCurrencySelect && } + + +
+ + {account && currency && showMaxButton && label !== "To" && ( + + )} + + { + onUserInput(val); + }} + /> + + {account && ( + + {!!currency && selectedCurrencyBalance + ? t("balance", { balanceInput: selectedCurrencyBalance?.toSignificant(6) }) + : " -"} + )} -
-
+ + {!disableCurrencySelect && onCurrencySelect && ( )} -
+ ); } diff --git a/src/components/CurrencyInputPanel/styles.ts b/src/components/CurrencyInputPanel/styles.ts new file mode 100644 index 00000000..ea3fdaf9 --- /dev/null +++ b/src/components/CurrencyInputPanel/styles.ts @@ -0,0 +1,101 @@ +import styled from 'styled-components'; +import { ReactComponent as DropDown } from "../../assets/images/dropdown.svg"; + +export const InputPanel = styled.div<{ hideInput?: boolean; withoutMargin?: boolean }>` + ${({ theme }) => theme.flexColumnNoWrap}; + position: relative; + z-index: 1; + // background: ${({ theme }) => theme.bg5}; + // border-radius: 18px; + // padding: 0.5rem; +`; + +export const InputRow = styled.div<{ selected: boolean }>` + ${({ theme }) => theme.flexRowNoWrap}; + align-items: center; +`; + +export const CurrencySelect = styled.button<{ selected?: boolean }>` + background-color: ${({ theme }) => theme.bg1}; + color: ${({ theme }) => theme.text1}; + border-bottom-left-radius: 18px; + border-top-left-radius: 18px; + border: 1px solid ${({ theme }) => theme.bg1}; + box-shadow: none; + cursor: pointer; + outline: none; + user-select: none; + height: 56px; + min-width: 160px; + width: 160px; + padding: 0 0.5rem; + align-items: center; + text-align: left; + line-height: 1; + + &:focus, + &:hover { + background-color: ${({ theme }) => theme.primaryLight}; + outline: none; + border-color: ${({ theme }) => theme.primary}; + } +`; + +export const InputContainer = styled.div` + position: relative; + display: flex; + align-items: center; + flex-wrap: nowrap; + flex: 1; + background: ${({ theme }) => theme.bg1}; + border-top-right-radius: 12px; + border-bottom-right-radius: 12px; + padding: 0 0.5rem; +`; + +export const Aligner = styled.div` + display: flex; + align-items: center; + gap: 12px; +`; + +export const TextWrap = styled.div` + margin-right: auto; + position: relative; +`; + +export const Label = styled.div` + color: ${({ theme }) => theme.text1}; + line-height: 1; + font-weight: 400; + font-size: 0.625rem; + margin-bottom: 4px; +`; + +export const TokenName = styled.span` + font-size: 0.875rem; + font-weight: 500; + margin-right: auto; +`; + +export const Balance = styled.span<{ showBalance?: boolean }>` + font-weight: 500; + font-size: 0.75rem; +`; + + +// export const LabelRow = styled.div` +// color: ${({ theme }) => theme.text1}; +// font-size: 0.65rem; +// line-height: 1rem; +// `; + +// export const CustomDropDown = styled(DropDown) <{ selected: boolean }>` +// margin: 0 0.25rem 0 0.5rem; +// height: 35%; + +// path { +// stroke: ${({ theme }) => theme.text1}; +// stroke-width: 1.5px; +// } +// `; \ No newline at end of file diff --git a/src/components/CurrencyLogo/index.tsx b/src/components/CurrencyLogo/index.tsx index e4134a59..51e49488 100644 --- a/src/components/CurrencyLogo/index.tsx +++ b/src/components/CurrencyLogo/index.tsx @@ -1,5 +1,5 @@ -import { Currency, ETHER, Token } from "@uniswap/sdk"; import React, { useMemo } from "react"; +import { Currency, ETHER, Token } from "@uniswap/sdk"; import styled from "styled-components"; import EthereumLogo from "../../assets/images/ethereum-logo.png"; @@ -10,31 +10,27 @@ import Logo from "../TokenLogo"; const getTokenLogoURL = (address: string) => `https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/${address}/logo.png`; -const StyledEthereumLogo = styled.img<{ size: string | undefined }>` - width: ${({ size }) => size || "100%"}; - height: ${({ size }) => size || "100%"}; - border-radius: ${({ size }) => size || "100%"}; - border: 2px solid ${({ theme }) => theme.text1}; +const StyledEthereumLogo = styled.img<{ size: number | string }>` + border-radius: ${({ size }) => `${size}px` || "100%"}; color: ${({ theme }) => theme.text1}; + width: ${({ size }) => `${size}px` || "100%"}; + height: ${({ size }) => `${size}px` || "100%"}; `; -const StyledLogo = styled(Logo)<{ size: string | undefined }>` - width: ${({ size }) => size || "100%"}; - height: ${({ size }) => size || "100%"}; - border-radius: ${({ size }) => size || "100%"}; - border: 2px solid ${({ theme }) => theme.text1}; +const StyledTokenLogo = styled(Logo)<{ size: number | string }>` + border-radius: ${({ size }) => `${size}px` || "100%"}; color: ${({ theme }) => theme.text1}; + width: ${({ size }) => `${size}px` || "100%"}; + height: ${({ size }) => `${size}px` || "100%"}; `; -export default function CurrencyLogo({ - currency, - size, - style, -}: { +export type CurrencyLogoProps = { currency?: Currency; - size?: string; + size?: number | string; style?: React.CSSProperties; -}) { +}; + +export default function CurrencyLogo({ currency, size = 32, style }: CurrencyLogoProps) { const uriLocations = useHttpLocations(currency instanceof WrappedTokenInfo ? currency.logoURI : undefined); const srcs: string[] = useMemo(() => { @@ -54,5 +50,5 @@ export default function CurrencyLogo({ return ; } - return ; + return ; } diff --git a/src/components/CurrencySelectModal/CurrencyList.js b/src/components/CurrencySelectModal/CurrencyList.js index fda77d91..d419f9bc 100644 --- a/src/components/CurrencySelectModal/CurrencyList.js +++ b/src/components/CurrencySelectModal/CurrencyList.js @@ -16,7 +16,7 @@ export const MenuItem = styled(RowBetween)` cursor: ${({ disabled }) => !disabled && "pointer"}; pointer-events: ${({ disabled }) => disabled && "none"}; :hover { - background-color: ${({ theme, disabled }) => !disabled && theme.bg2}; + background-color: ${({ theme, disabled }) => !disabled && theme.bg1}; } opacity: ${({ disabled, selected }) => (disabled || selected ? 0.5 : 1)}; `; diff --git a/src/components/CurrencySelectModal/index.js b/src/components/CurrencySelectModal/index.js index 58b94385..eadcaa16 100644 --- a/src/components/CurrencySelectModal/index.js +++ b/src/components/CurrencySelectModal/index.js @@ -55,7 +55,7 @@ export default function CurrencySelectModal({ { + const { account } = useActiveWeb3React(); + const theme = useTheme(); + const { t } = useTranslation(); + const api = new TransakApi(process.env.REACT_APP_TRANSAK_ENVIRONMENT); + let typeTimeout: any = undefined; + const [loading, setLoading] = useState(true); + const [priceLoading, setPriceLoading] = useState(false); + const [cryptoCurrencies, setCryptoCurrencies] = useState([]); + const [fiatCurrencies, setFiatCurrencies] = useState([]); + const [selectedCrypto, setSelectedCrypto] = useState({}); + const [selectedCryptoValue, setSelectedCryptoValue] = useState(""); + const [selectedFiat, setSelectedFiat] = useState({}); + const [selectedFiatValue, setSelectedFiatValue] = useState(""); + const [conversionRate, setConversionRate] = useState(0); + + useEffect(() => { + const getCurrencies = async () => { + try { + await api.get("crypto")?.then((result) => { + setCryptoCurrencies(result.data.response); + setSelectedCrypto( + result.data.response.find( + (item: any) => item.symbol === process.env.REACT_APP_TRANSAK_CRYPTO_SYMBOL + ) || null + ); + }); + + await api.get("fiat")?.then((result) => { + setFiatCurrencies(result.data.response); + setSelectedFiat( + result.data.response.find( + (item: any) => item.symbol === process.env.REACT_APP_TRANSAK_FIAT_SYMBOL + ) || null + ); + }); + + setLoading(false); + } catch (e) { + toast.error("Something went wrong. Please try again later."); + setLoading(false); + } + }; + + getCurrencies(); + }, []); + + const fetchPrices = async (value: any, type: string) => { + try { + setPriceLoading(true); + debugger; + const prices = await api.get("price", { + type: "BUY", + fiat: selectedFiat?.symbol, + crypto: selectedCrypto?.symbol, + amount: Number(value) || 300, + amountType: type, + network: type === "crypto" ? value.network.name : selectedCrypto?.network?.name, + }); + + let fiatValue = 0; + let cryptoValue = 0; + if (type === "fiat") { + fiatValue = value; + cryptoValue = prices?.data?.response?.cryptoAmount; + } else { + cryptoValue = value; + fiatValue = prices?.data?.response?.fiatAmount; + } + + setSelectedFiatValue(fiatValue); + setSelectedCryptoValue(cryptoValue); + + setPriceLoading(false); + } catch (e) { + if (e.hasOwnProperty("response")) { + toast.error(e?.response?.data?.error?.message); + } + + if (type === "fiat") { + setSelectedFiatValue(value); + } else { + setSelectedCryptoValue(value); + } + + setPriceLoading(false); + } + }; + + const onUserInputHandler = async (value: any, type: string) => { + if (selectedFiat && selectedCrypto) { + clearTimeout(typeTimeout); + typeTimeout = setTimeout(() => { + fetchPrices(value, type); + }, TYPING_INTERVAL); + + if (type === "fiat") { + setSelectedFiatValue(value); + } else { + setSelectedCryptoValue(value); + } + console.log(selectedFiat); + } else { + if (type === "fiat") { + setSelectedFiatValue(value); + } else { + setSelectedCryptoValue(value); + } + setPriceLoading(false); + } + }; + + const onSelect = async (value: any, type: string) => { + setPriceLoading(true); + + let cryptoValue = ""; + if ((selectedCrypto !== null || type === "crypto") && (selectedFiat !== null || type === "fiat")) { + try { + const prices = await api.get("price", { + type: "BUY", + fiat: type === "fiat" ? value.symbol : selectedFiat?.symbol, + crypto: type === "crypto" ? value.symbol : selectedCrypto?.symbol, + amount: selectedFiatValue || 300, + amountType: "fiat", + network: type === "crypto" ? value.network.name : selectedCrypto?.network?.name, + }); + + if (selectedFiatValue) { + cryptoValue = prices?.data?.response?.cryptoAmount; + } + + if (type === "crypto") { + setSelectedCrypto(value); + setSelectedCryptoValue(cryptoValue); + setPriceLoading(false); + setConversionRate(prices?.data?.response?.conversionPrice); + } else { + setSelectedFiat(value); + setSelectedCryptoValue(cryptoValue); + setPriceLoading(false); + setConversionRate(prices?.data?.response?.conversionPrice); + } + } catch (e) { + if (e.hasOwnProperty("response")) { + toast.error(e?.response?.data?.error?.message); + } + + if (type === "fiat") { + setSelectedFiat(value); + } else { + setSelectedCrypto(value); + } + + setPriceLoading(false); + setConversionRate(0); + } + } + }; + + const buyHandler = () => { + if (!account) { + // TODO: fix + // props.toggleWalletModal(); + } else { + let transak = new TransakSDK({ + apiKey: process.env.REACT_APP_TRANSAK_API_KEY, + environment: process.env.REACT_APP_TRANSAK_ENVIRONMENT, + walletAddress: account, + themeColor: theme.primary, + hostURL: window.location.origin, + widgetHeight: "650px", + widgetWidth: "500px", + fiatAmount: Number(selectedFiatValue), + fiatCurrency: selectedFiat?.symbol, + defaultCryptoCurrency: selectedCrypto?.symbol, + }); + + transak.init(); + + transak.on(transak.EVENTS.TRANSAK_ORDER_SUCCESSFUL, (orderData) => { + toast.success("Your Purchase was completed Successfully! You can see your assets in dashboard."); + transak.close(); + }); + transak.on(transak.EVENTS.ORDER_COMPLETED, (orderData) => { + toast.success("Your Purchase was completed Successfully. You can see your assets in dashboard."); + transak.close(); + }); + transak.on(transak.EVENTS.TRANSAK_ORDER_CANCELLED, (orderData) => { + toast.error("You Canceled The Purchase. Maybe the next time!"); + transak.close(); + }); + transak.on(transak.EVENTS.TRANSAK_ORDER_FAILED, (orderData) => { + toast.error("Your order failed unfortunately. Please try again later."); + transak.close(); + }); + transak.on(transak.EVENTS.ORDER_FAILED, (orderData) => { + toast.error("Your order process failed unfortunately, Please try again later"); + transak.close(); + }); + } + }; + + const checkLimits = () => { + if (selectedFiat?.symbol === "INR") { + return ( + !selectedCrypto || + !selectedCryptoValue || + !selectedFiatValue || + !selectedFiat || + selectedFiatValue < 750 + ); + } else { + return ( + !selectedCrypto || !selectedCryptoValue || !selectedFiatValue || !selectedFiat || selectedFiatValue < 10 + ); + } + }; + + if (loading) { + return ( +
+ +
+ ); + } + + return ( + +
+ +
+ +
+ + + + {selectedFiat && selectedCrypto + ? `1 ${selectedCrypto?.symbol} ≈ ${(conversionRate > 0 + ? 1 / conversionRate + : 1 / selectedCrypto?.priceUSD + ).toFixed(4)} ${selectedFiat?.symbol}` + : ""} + +
+ +
+ + {priceLoading ? ( + + ) : !account ? ( + t("wallet.connect") + ) : !selectedCrypto ? ( + t("exchange.selectCrypto") + ) : !selectedFiat ? ( + t("exchange.selectFiat") + ) : !selectedFiatValue || !selectedCryptoValue ? ( + t("exchange.enterAmount") + ) : checkLimits() ? ( + t("exchange.lowValue") + ) : ( + t("fiatOn.buyButton") + )} + +
+
+ ); +}; + +export default FiatOnramp; diff --git a/src/components/FiatOnramp/styleds.tsx b/src/components/FiatOnramp/styleds.tsx new file mode 100644 index 00000000..39330b3f --- /dev/null +++ b/src/components/FiatOnramp/styleds.tsx @@ -0,0 +1,18 @@ +import styled from "styled-components"; +import { Button } from "react-bootstrap"; + +export const PriceText = styled.div` + color: ${({ theme }) => theme.text1}; + font-size: 0.875rem; + padding: 0 0.5rem; + text-align: right; +`; + +export const BuyButton = styled(Button)` + width: 100%; + + @media (min-width: 768px) { + width: auto; + min-width: 250px; + } +`; diff --git a/src/components/Footer/index.js b/src/components/Footer/index.js deleted file mode 100644 index 8a628d2d..00000000 --- a/src/components/Footer/index.js +++ /dev/null @@ -1,251 +0,0 @@ -import { Link } from "react-router-dom"; -import { Trans, useTranslation } from "react-i18next"; -import styled from "styled-components"; -import Logo from "../Logo"; -import "./styles.scss"; -import Socials from "../Socials"; -import { routes } from "../../constants/headerRoutes"; -import DesktopImage from "../../assets/images/footer/footer-desktop.png"; -import MobileImage from "../../assets/images/footer/footer-mobile.png"; -import FooterBG from "../../assets/images/footer_1.svg"; -import FooterSecondBG from "../../assets/images/footer_2.svg"; - -const Container = styled.footer` - background-color: ${({ theme }) => theme.modalBG}; - border: 1px solid ${({ theme }) => theme.text4}; - border-bottom-width: 0; - border-radius: 18px; - padding: 74px 70px 115px; - margin: 113px -86px -1px; - position: relative; - - @media (max-width: 991px) { - margin: 82px 0 0; - - padding: 21px 24px 40px; - } - - &::before { - content: ""; - width: 270px; - height: 290px; - position: absolute; - right: 32px; - top: -102px; - background-image: url(${({ desktop }) => desktop}); - background-repeat: no-repeat; - background-size: contain; - - @media (max-width: 991px) { - background-image: url(${({ mobile }) => mobile}); - width: 149px; - height: 158px; - right: 0; - top: -72px; - } - } -`; - -const Content = styled.div` - z-index: 10; - position: relative; -`; - -const FooterAbout = styled.div` - width: 275px; -`; - -const BG = styled.div` - position: absolute; - z-index: 1; - width: 100%; - height: 100%; - border-radius: 18px; - top: 0; - left: 0; - overflow: hidden; - - &::before { - content: ""; - position: absolute; - top: -46px; - left: 0; - background-image: url(${({ imageOne }) => imageOne}); - background-size: 876px 331px; - background-repeat: no-repeat; - width: 876px; - height: 331px; - - @media (max-width: 991px) { - bottom: -9px; - left: -130px; - right: auto; - background-size: 705px 267px; - width: 705px; - height: 267px; - } - } - - &::after { - content: ""; - position: absolute; - bottom: -9px; - right: 42px; - background-image: url(${({ imageTwo }) => imageTwo}); - background-size: 810px 282px; - background-repeat: no-repeat; - width: 810px; - height: 282px; - z-index: 1; - - @media (max-width: 991px) { - bottom: -197px; - left: -369px; - right: auto; - background-size: 827px 288px; - width: 827px; - height: 288px; - } - } -`; - -const MobileSocials = styled.div` - padding-top: 60px; -`; - -const LogoContainer = styled.div` - margin-bottom: 67px; - - @media (max-width: 991px) { - margin-bottom: 0; - } -`; - -const CopyRight = styled.div` - font-size: 0.875rem; - - &.link { - color: inherit; - text-decoration: none; - - &:hover { - text-decoration: none; - color: ${({ theme }) => theme.text2}; - } - } -`; - -const List = styled.div` - padding-top: 96px; - - @media (max-width: 991px) { - padding-top: 47px; - flex-wrap: wrap; - } -`; - -const ListContainer = styled.div` - flex: 1; -`; - -const Links = styled.ul` - list-style: none; - margin: 0; - padding: 0 45px 0 0; - - @media (max-width: 991px) { - padding-right: 16px; - flex-basis: 50%; - } -`; - -const StyledLink = styled(Link)` - text-decoration: none; - font-weight: 400; - display: block; - color: ${({ theme }) => theme.text1}; - margin-bottom: 22px; - white-space: nowrap; - font-size: 0.875rem; - - &.footer__link--primary { - color: ${({ theme }) => theme.primary}; - margin-bottom: 24px; - font-weight: 500; - } - - &:hover { - text-decoration: none; - outline: none; - color: ${({ theme }) => theme.text2}; - } -`; - -const Footer = (props) => { - const { t } = useTranslation(); - - return ( - - - - - - - -
- - - - Decentralized Finance Tentacles {" "} - - - - -
-
- - - {Object.keys(routes).map((key) => { - const r = routes[key]; - return ( - -
  • - - {t(`menu.${r.title}`)} - -
  • - {r.hasOwnProperty("routes") && - Object.values(r.routes).map((item, index) => { - return ( -
  • - - {t(`menu.${item.title}`)} - -
  • - ); - })} -
    - ); - })} -
    -
    - - - - - - Decentralized Finance Tentacles {" "} - - - - - -
    -
    - ); -}; - -export default Footer; diff --git a/src/components/Footer/index.tsx b/src/components/Footer/index.tsx new file mode 100644 index 00000000..595a288b --- /dev/null +++ b/src/components/Footer/index.tsx @@ -0,0 +1,18 @@ +import { Trans } from "react-i18next"; +import Socials from "../Socials"; +import * as Styled from "./styleds"; + +const Footer = () => { + return ( + + + + + Decentralized Finance Tentacles + + + + ); +}; + +export default Footer; diff --git a/src/components/Footer/styleds.tsx b/src/components/Footer/styleds.tsx new file mode 100644 index 00000000..f95b1e4a --- /dev/null +++ b/src/components/Footer/styleds.tsx @@ -0,0 +1,11 @@ +import styled from "styled-components"; + +export const Footer = styled.footer` + border-top: 1px solid ${({ theme }) => theme.borderColor}; + padding: 2rem 1rem; + margin-top: 5rem; +`; + +export const CopyRight = styled.div` + font-size: 0.875rem; +`; diff --git a/src/components/Footer/styles.scss b/src/components/Footer/styles.scss deleted file mode 100644 index 70a5c1f8..00000000 --- a/src/components/Footer/styles.scss +++ /dev/null @@ -1,32 +0,0 @@ -@import "../../styles/abstracts/variables"; - -.footer { - &__content { - z-index: 10; - position: relative; - } - - &__about { - } - - &__bg { - } - - &__mobile-socials { - } - - &__logo { - } - - &__copyright { - } - - &__list { - } - - &__links { - } - - &__link { - } -} diff --git a/src/components/Header/index.js b/src/components/Header/index.js deleted file mode 100644 index cc047a4d..00000000 --- a/src/components/Header/index.js +++ /dev/null @@ -1,254 +0,0 @@ -import { Navbar, Button, Nav } from "react-bootstrap"; -import { NavLink } from "react-router-dom"; -import styled from "styled-components"; -import SVG from "react-inlinesvg"; - -import Logo from "../Logo"; -import "./styles.scss"; -import { useWalletModalToggle } from "../../state/application/hooks"; -import { useActiveWeb3React } from "../../hooks"; -import { routes } from "../../constants/headerRoutes"; -import HeaderDropdown from "../HeaderDropdown"; -import SettingsDropdown from "../SettingsDropdown"; -import SideDrawer from "../SideDrawer"; -import { useEffect, useState } from "react"; -import { emitter } from "../../lib/helper"; -import { shortenAddress } from "../../utils"; -import { useTranslation } from "react-i18next"; - -const Container = styled.div` - transition: all ease 0.4s; - position: fixed; - top: 0; - left: 0; - right: ${({ right }) => (right ? `${right}px` : "0")}; - background-color: ${({ theme, scrolled }) => (scrolled ? theme.modalBG : "transparent")}; - box-shadow: ${({ scrolled }) => (scrolled ? "-1px 11px 43px rgba(0, 0, 0, 0.12)" : "0 0 0 rgba(0, 0, 0, 0)")}; - z-index: 800; - - & .navbar, - & .header__inner { - min-height: ${({ scrolled }) => (scrolled ? "80px" : "112px")}; - transition: 0.3s ease all; - } - - @media (max-width: 1199px) { - z-index: 1090; - - body.modal-open & { - background-color: ${({ theme }) => theme.modalBG}; - box-shadow: 0 0 0 rgba(0, 0, 0, 0); - } - } -`; - -const WalletLink = styled.div` - color: ${({ theme }) => theme.text}; - background-color: ${({ theme }) => theme.bg5}; - border-radius: 12px; - padding: 8px 16px; - font-size: 1rem; - font-weight: 500; - cursor: pointer; -`; - -const StyledNavbarBrand = styled(Navbar.Brand)` - transition: 0.4s all ease; - - @media (max-width: 1199px) { - body.modal-open & { - transform: ${({ hasCallback }) => (hasCallback ? "translateX(52px)" : "translateX(0)")}; - } - } -`; - -const BackButton = styled.button` - border: none; - color: ${({ theme }) => theme.primary}; - background-color: ${({ theme }) => theme.primaryLight}; - border-radius: 300px; - width: 32px; - height: 32px; - position: absolute; - top: calc(50% - 16px); - left: 0; - display: flex; - align-items: center; - justify-content: center; - transition: all ease 0.4s; - - @media (max-width: 1199px) { - transform: translateX(-50px) scale(0.9); - opacity: 0; - visibility: hidden; - - body.modal-open & { - transform: ${({ hasCallback }) => - hasCallback ? "translateX(0) scale(1)" : "translateX(-50px) scale(0.9)"}; - opacity: ${({ hasCallback }) => (hasCallback ? "1" : "0")}; - visibility: ${({ hasCallback }) => (hasCallback ? "visible" : "hidden")}; - } - } - - &:hover { - background-color: ${({ theme }) => theme.primary}; - color: ${({ theme }) => theme.bg2}; - } - - &:hover, - &:active, - &:focus { - outline: none; - box-shadow: none; - text-decoration: none; - } -`; - -const MenuIcon = styled.div` - color: ${({ theme }) => theme.text1}; -`; - -const Header = (props) => { - const { account } = useActiveWeb3React(); - const toggleConnectModal = useWalletModalToggle(); - const [sidedrawer, setSidedrawer] = useState(false); - const [scrolled, setScrolled] = useState(false); - const [scrollbarWidth, setScrollbarWidth] = useState(0); - const { t } = useTranslation(); - const [callback, setCallback] = useState({ - action: undefined, - }); - - const handleUserScroll = (e) => { - const scroll = e.target.scrollTop; - - if (scroll > 50) { - setScrolled(true); - } else { - setScrolled(false); - } - }; - - const _getScrollbarWidth = () => { - const scrollDiv = document.createElement("div"); - scrollDiv.className = "modal-scrollbar-measure"; - document.body.appendChild(scrollDiv); - const scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth; - document.body.removeChild(scrollDiv); - return scrollbarWidth; - }; - - const handleResize = () => { - const PaddingWidth = _getScrollbarWidth(); - setScrollbarWidth(PaddingWidth); - }; - - const dismissHandler = () => { - setSidedrawer(false); - }; - - const setModalCallback = (e) => { - setCallback({ - action: e.action, - }); - }; - - const removeModalCallback = () => { - setCallback({ - action: undefined, - }); - }; - - useEffect(() => { - emitter.on("open-modal", setModalCallback); - emitter.on("close-modal", removeModalCallback); - - return () => { - emitter.off("open-modal", setModalCallback); - emitter.off("close-modal", removeModalCallback); - }; - }, []); - - useEffect(() => { - document.body.addEventListener("scroll", handleUserScroll); - - return () => { - document.body.removeEventListener("scroll", handleUserScroll); - }; - }, [handleUserScroll]); - - useEffect(handleResize, []); - - useEffect(() => { - window.addEventListener("resize", handleResize); - - return () => { - window.removeEventListener("resize", handleResize); - }; - }, []); - - return ( - <> - - -
    - -
    - - - -
    -
    - - - - - setSidedrawer(true)} - /> - -
    - - -
    - {!account ? ( - - ) : ( -
    - - {account && shortenAddress(account)} - -
    - )} - -
    -
    -
    -
    -
    - - ); -}; - -export default Header; diff --git a/src/components/Header/index.tsx b/src/components/Header/index.tsx new file mode 100644 index 00000000..aaf87dcf --- /dev/null +++ b/src/components/Header/index.tsx @@ -0,0 +1,158 @@ +import { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import SVG from "react-inlinesvg"; +import { Navbar, Button, Nav } from "react-bootstrap"; + +import { routes } from "../../constants/headerRoutes"; +import { useActiveWeb3React } from "../../hooks"; +import { emitter } from "../../lib/helper"; +import { useWalletModalToggle } from "../../state/application/hooks"; +import { shortenAddress } from "../../utils"; +import HeaderDropdown from "../HeaderDropdown"; +import Logo from "../Logo"; +import SettingsDropdown from "../SettingsDropdown"; +import SideDrawer from "../SideDrawer"; +import * as Styled from "./styleds"; + +const Header = () => { + const { account } = useActiveWeb3React(); + const toggleConnectModal = useWalletModalToggle(); + const [sidedrawer, setSidedrawer] = useState(false); + const [scrolled, setScrolled] = useState(false); + const [scrollbarWidth, setScrollbarWidth] = useState(0); + const { t } = useTranslation(); + const [callback, setCallback] = useState({ + action: undefined, + }); + + const _getScrollbarWidth = () => { + const scrollDiv = document.createElement("div"); + scrollDiv.className = "modal-scrollbar-measure"; + document.body.appendChild(scrollDiv); + const scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth; + document.body.removeChild(scrollDiv); + return scrollbarWidth; + }; + + const dismissHandler = () => { + setSidedrawer(false); + }; + + useEffect(() => { + // @ts-ignore + const setModalCallback = (e) => { + setCallback({ + action: e.action, + }); + }; + + const removeModalCallback = () => { + setCallback({ + action: undefined, + }); + }; + + emitter.on("open-modal", setModalCallback); + emitter.on("close-modal", removeModalCallback); + + return () => { + emitter.off("open-modal", setModalCallback); + emitter.off("close-modal", removeModalCallback); + }; + }, []); + + useEffect(() => { + // @ts-ignore + const handleUserScroll = (e) => { + const scroll = e.target.scrollTop; + + if (scroll > 50) { + setScrolled(true); + } else { + setScrolled(false); + } + }; + + document.body.addEventListener("scroll", handleUserScroll); + + return () => { + document.body.removeEventListener("scroll", handleUserScroll); + }; + }, []); + + useEffect(() => { + const handleResize = () => { + const PaddingWidth = _getScrollbarWidth(); + setScrollbarWidth(PaddingWidth); + }; + + window.addEventListener("resize", handleResize); + handleResize(); + + return () => { + window.removeEventListener("resize", handleResize); + }; + }, []); + + return ( + <> + + {/* @ts-ignore */} + +
    + +
    + + + +
    + + + + + + setSidedrawer(true)} + /> + + + + +
    + {!account ? ( + + ) : ( +
    + + {account && shortenAddress(account)} + +
    + )} + +
    +
    +
    +
    +
    + + ); +}; + +export default Header; diff --git a/src/components/Header/styleds.tsx b/src/components/Header/styleds.tsx new file mode 100644 index 00000000..5d217341 --- /dev/null +++ b/src/components/Header/styleds.tsx @@ -0,0 +1,140 @@ +import styled from "styled-components"; +import { NavLink } from 'react-router-dom'; +import { Navbar } from "react-bootstrap"; + +export const Container = styled.div<{ right: number; scrolled: boolean }>` + transition: all ease 0.4s; + position: fixed; + top: 0; + left: 0; + right: ${({ right }) => (right ? `${right}px` : "0")}; + background-color: ${({ theme, scrolled }) => (scrolled ? theme.modalBG : "transparent")}; + box-shadow: ${({ scrolled }) => (scrolled ? "-1px 11px 43px rgba(0, 0, 0, 0.12)" : "0 0 0 rgba(0, 0, 0, 0)")}; + z-index: 800; + + @media (max-width: 1199px) { + z-index: 1090; + + body.modal-open & { + background-color: ${({ theme }) => theme.modalBG}; + box-shadow: 0 0 0 rgba(0, 0, 0, 0); + } + } +`; + +export const WalletLink = styled.div` + color: ${({ theme }) => theme.text1}; + background-color: ${({ theme }) => theme.bg5}; + border-radius: 12px; + padding: 0.5rem 1rem; + font-size: 1rem; + font-weight: 500; + cursor: pointer; +`; + +export const NavbarBrand = styled(Navbar.Brand)<{ hasCallback: boolean }>` + transition: 0.4s all ease; + + @media (max-width: 1199px) { + body.modal-open & { + transform: ${({ hasCallback }) => (hasCallback ? "translateX(52px)" : "translateX(0)")}; + } + } +`; + +export const BackButton = styled.button<{ hasCallback: boolean }>` + border: none; + color: ${({ theme }) => theme.primary}; + background-color: ${({ theme }) => theme.primaryLight}; + border-radius: 300px; + width: 32px; + height: 32px; + position: absolute; + top: calc(50% - 16px); + left: 0; + display: flex; + align-items: center; + justify-content: center; + transition: all ease 0.4s; + + @media (max-width: 1199px) { + transform: translateX(-50px) scale(0.9); + opacity: 0; + visibility: hidden; + + body.modal-open & { + transform: ${({ hasCallback }) => + hasCallback ? "translateX(0) scale(1)" : "translateX(-50px) scale(0.9)"}; + opacity: ${({ hasCallback }) => (hasCallback ? "1" : "0")}; + visibility: ${({ hasCallback }) => (hasCallback ? "visible" : "hidden")}; + } + } + + &:hover { + background-color: ${({ theme }) => theme.primary}; + color: ${({ theme }) => theme.bg2}; + } + + &:hover, + &:active, + &:focus { + outline: none; + box-shadow: none; + text-decoration: none; + } +`; + +export const MenuIcon = styled.div` + color: ${({ theme }) => theme.text1}; +`; + +export const HeadNavbar = styled(Navbar)<{ scrolled?: boolean }>` + min-height: ${({ scrolled }) => (scrolled ? "80px" : "96px")}; + background-color: transparent; + padding-top: 0; + padding-bottom: 0; +`; + +export const HeaderInner = styled.div<{ scrolled?: boolean }>` + min-height: ${({ scrolled }) => (scrolled ? "80px" : "96px")}; + transition: 0.3s ease all; + display: flex; + align-items: center; + justify-content: space-between; + + @media (max-width: 1200px) { + width: 100%; + } +`; + +export const HeaderItem = styled(NavLink)` + color: ${({ theme }) => theme.text1} !important; + text-decoration: none !important; + font-weight: 500; + font-size: 1rem; + padding: 1rem 1.625rem; + position: relative; + + &:hover { + color: ${({ theme }) => theme.text2} !important; + } + + &::before { + content: ""; + position: absolute; + bottom: 0.625rem; + left: 1.25rem; + right: 1.25rem; + height: 1px; + background-color: ${({ theme }) => theme.primary} !important; + transform: scaleX(0); + transform-origin: left center; + transition: 0.4s ease all; + will-change: transform; + } + + &.active::before { + transform: scaleX(1); + transform-origin: right center; + } +`; diff --git a/src/components/Header/styles.scss b/src/components/Header/styles.scss deleted file mode 100644 index 58b98e4d..00000000 --- a/src/components/Header/styles.scss +++ /dev/null @@ -1,75 +0,0 @@ -@import "../../styles/abstracts/variables"; - -.header { - background-color: transparent; - min-height: 112px; - - &.navbar { - padding-top: 0; - padding-bottom: 0; - } - - .navbar-brand { - margin-right: 4px; - } -} - -.header__inner { - min-height: 112px; - display: flex; - align-items: center; - justify-content: space-between; - - @media (max-width: 1200px) { - width: 100%; - } -} - -.header__logo { - display: flex; - align-items: center; - text-decoration: none !important; - position: relative; - - &-img { - width: 35px; - height: 25px; - margin-right: 6px; - } -} - -.header__nav { - align-items: center; -} - -.header__item { - text-decoration: none !important; - color: $title; - font-weight: 500; - font-size: 1rem; - padding: 1rem 1.625rem; - position: relative; - - &:hover { - color: $overline; - } - - &::before { - content: ""; - position: absolute; - bottom: 0.625rem; - left: 1.625rem; - right: 1.625rem; - height: 1px; - background-color: $primary; - transform: scaleX(0); - transform-origin: left center; - transition: 0.4s ease all; - will-change: transform; - } - - &--active::before { - transform: scaleX(1); - transform-origin: right center; - } -} diff --git a/src/components/HeaderDropdown/CurrencyDropdown.js b/src/components/HeaderDropdown/CurrencyDropdown.js deleted file mode 100644 index f287cdde..00000000 --- a/src/components/HeaderDropdown/CurrencyDropdown.js +++ /dev/null @@ -1,52 +0,0 @@ -import { useEffect } from "react"; -import { Row, Col } from "react-bootstrap"; -import { withRouter } from "react-router-dom"; -import { useDispatch, useSelector } from "react-redux"; -import { useTranslation } from "react-i18next"; -import SVG from "react-inlinesvg"; -import { rates } from "../../constants"; -import * as actions from "../../state/currency/actions"; -import FlagIcon from "../../assets/images/flag.svg"; -import { CurrencyWrapper, Item, IconButton, DropDown, DropDownItem, CurrencyLogo, Title2 } from "./styles"; - -const HeaderDropdown = ({ items, title, ...props }) => { - const selectedCurrency = useSelector((state) => state.currency.selected); - const { i18n } = useTranslation(); - const dispatch = useDispatch(); - - const selectDestinationCurrency = (id) => { - dispatch(actions.fetchCurrencies(id)); - i18n.changeLanguage(rates[id].lng); - }; - - useEffect(() => { - dispatch(actions.fetchCurrencies(selectedCurrency)); - }, [selectedCurrency, dispatch]); - - return ( - - - - - - - - - {Object.keys(rates).map((currency) => ( - - - - {currency} - - - ))} - - - - ); -}; - -export default withRouter(HeaderDropdown); diff --git a/src/components/HeaderDropdown/NotificationDropDown.js b/src/components/HeaderDropdown/NotificationDropDown.js deleted file mode 100644 index 542a6c38..00000000 --- a/src/components/HeaderDropdown/NotificationDropDown.js +++ /dev/null @@ -1,30 +0,0 @@ -import SVG from "react-inlinesvg"; -import { withRouter } from "react-router-dom"; -import { useTranslation } from "react-i18next"; -import BellIcon from "../../assets/images/bell.svg"; -import PendingTransactionsList from "../TransactionsList/PendingTransactionsList"; -import ConfirmedTransactionsList from "../TransactionsList/ConfirmedTransactionsList"; -import { Wrapper, Item, DropDown, IconButton, Title, SeeAllButton } from "./styles"; - -const NotificationDropdown = () => { - const { t } = useTranslation(); - - return ( - - - - - - - - {t("pendingTransactions")} - - {t("confirmedTransactions")} - - {t("seeAllTransactions")} - - - ); -}; - -export default withRouter(NotificationDropdown); diff --git a/src/components/HeaderDropdown/UserDropdown.js b/src/components/HeaderDropdown/UserDropdown.js deleted file mode 100644 index 003502a1..00000000 --- a/src/components/HeaderDropdown/UserDropdown.js +++ /dev/null @@ -1,122 +0,0 @@ -import { useContext } from "react"; -import styled, { ThemeContext } from "styled-components"; -import { Link, withRouter } from "react-router-dom"; -import { useTranslation } from "react-i18next"; -import SVG from "react-inlinesvg"; - -import ArrowDown from "../../assets/images/global/arrow-down.svg"; -import { useActiveWeb3React } from "../../hooks"; -import UserIcon from "../Icons/User"; -import { Wrapper, Item, DropDown } from "./styles"; - -const Button = styled.button` - margin-right: 0.75rem; -`; - -const DropDownItem = styled(Link)` - display: flex; - align-items: center; - position: relative; - padding-left: 20px; - outline: none; - text-decoration: none; - white-space: nowrap; - color: ${({ theme }) => theme.text1}; - - &:not(:last-child) { - margin-bottom: 20px; - } - - &:hover, - &:focus, - &:active { - color: ${({ theme }) => theme.text2}; - box-shadow: none; - outline: none; - text-decoration: none; - } - - &::before { - content: ""; - position: absolute; - left: 0; - top: 6px; - width: 10px; - height: 10px; - border-radius: 10px; - background-color: ${({ theme }) => theme.success}; - } -`; - -const DropDownButton = styled.button` - display: flex; - align-items: center; - position: relative; - padding-left: 20px; - outline: none; - text-decoration: none; - white-space: nowrap; - border: none; - background-color: transparent; - color: ${({ theme }) => theme.text1}; - - &:not(:last-child) { - margin-bottom: 20px; - } - - &:hover, - &:focus, - &:active { - color: ${({ theme }) => theme.text2}; - box-shadow: none; - outline: none; - text-decoration: none; - } - - &::before { - content: ""; - position: absolute; - left: 0; - top: 6px; - width: 10px; - height: 10px; - border-radius: 10px; - background-color: ${({ theme }) => theme.success}; - } -`; - -const HeaderDropdown = ({ items, title, ...props }) => { - const { account, deactivate } = useActiveWeb3React(); - const theme = useContext(ThemeContext); - const { t } = useTranslation(); - - const logoutHandler = () => { - if (account) { - deactivate(); - props.history.push("/"); - } - }; - - return ( - - - - - - - {Object.values(items).map((item, index) => { - return ( - - {t(`menu.${item.title}`)} - - ); - })} - {t("menu.disconnect")} - - - ); -}; - -export default withRouter(HeaderDropdown); diff --git a/src/components/HeaderDropdown/index.tsx b/src/components/HeaderDropdown/index.tsx new file mode 100644 index 00000000..035fa6b0 --- /dev/null +++ b/src/components/HeaderDropdown/index.tsx @@ -0,0 +1,34 @@ +import { useTranslation } from "react-i18next"; +import { ChevronDown } from "react-feather"; +import useTheme from "../../hooks/useTheme"; +import * as Styled from "./styleds"; + +export type HeaderDropdownProps = { + items: Array; + title: string; +}; + +const HeaderDropdown = ({ items, title }: HeaderDropdownProps) => { + const theme = useTheme(); + const { t } = useTranslation(); + + return ( + + + {t(`menu.${title}`)} + + + + {Object.values(items).map((item, index) => { + return ( + + {t(`menu.${item.title}`)} + + ); + })} + + + ); +}; + +export default HeaderDropdown; diff --git a/src/components/HeaderDropdown/index.js b/src/components/HeaderDropdown/styleds.tsx similarity index 65% rename from src/components/HeaderDropdown/index.js rename to src/components/HeaderDropdown/styleds.tsx index c2e0fa28..84e4bb65 100644 --- a/src/components/HeaderDropdown/index.js +++ b/src/components/HeaderDropdown/styleds.tsx @@ -1,12 +1,7 @@ -import styled, { ThemeContext } from "styled-components"; -import { useContext } from "react"; -import SVG from "react-inlinesvg"; +import styled from "styled-components"; import { Link } from "react-router-dom"; -import ArrowDown from "../../assets/images/global/arrow-down.svg"; -import { useTranslation } from "react-i18next"; - -const Wrapper = styled.div` +export const Wrapper = styled.div` position: relative; &:hover .header-dropdown { @@ -16,7 +11,7 @@ const Wrapper = styled.div` } `; -const Item = styled.div` +export const Item = styled.div` text-decoration: none !important; color: ${({ theme }) => theme.text1}; font-weight: 500; @@ -48,14 +43,14 @@ const Item = styled.div` } `; -const ItemInner = styled.span` +export const ItemInner = styled.span<{ active?: boolean }>` display: flex; padding-right: 0.75rem; color: ${({ theme, active }) => (active ? theme.primary : theme.text1)}; white-space: nowrap; `; -const DropDown = styled.div` +export const DropDown = styled.div` position: absolute; top: 100%; left: 1.625rem; @@ -73,7 +68,7 @@ const DropDown = styled.div` z-index: 99999; `; -const DropDownItem = styled(Link)` +export const DropDownItem = styled(Link)<{ state: any }>` display: flex; align-items: center; position: relative; @@ -107,27 +102,3 @@ const DropDownItem = styled(Link)` background-color: ${({ theme, state }) => (state ? theme[state] : theme.primary)}; } `; - -const HeaderDropdown = ({ items, title, ...props }) => { - const theme = useContext(ThemeContext); - const { t } = useTranslation(); - return ( - - - {t(`menu.${title}`)} - - - - {Object.values(items).map((item, index) => { - return ( - - {t(`menu.${item.title}`)} - - ); - })} - - - ); -}; - -export default HeaderDropdown; diff --git a/src/components/HistorySectionList/index.js b/src/components/HistorySectionList/index.js deleted file mode 100644 index 73b11e59..00000000 --- a/src/components/HistorySectionList/index.js +++ /dev/null @@ -1,70 +0,0 @@ -import React from "react"; -import styled from "styled-components"; - -const Section = styled.div` - display: flex; - flex-direction: column; - - @media (min-width: 768px) { - margin-bottom: 0.125rem; - } -`; - -const SectionHeader = styled.div` - display: flex; - align-items: center; - justify-content: space-between; - margin-bottom: 1.25rem; -`; - -const SectionHeaderInner = styled.div` - display: flex; - flex-direction: column; -`; - -const SectionTitle = styled.h4` - font-weight: 700; - font-size: 1.25rem; - margin: 0; - color: ${({ theme }) => theme.text1}; - - @media (max-width: 991px) { - font-size: 1rem; - } -`; - -const SectionSubTitle = styled.span` - font-weight: 400; - font-size: 1rem; - color: ${({ theme }) => theme.text1}; -`; - -const SectionBody = styled.div` - margin-bottom: 5px; - display: flex; - flex-direction: ${({ direction }) => direction}; - - @media (min-width: 768px) { - margin-bottom: 0.5rem; - } -`; - -const SectionList = (props) => { - return props.sections.map((section, index) => { - return ( -
    - - - {section.title} - {section.description && {section.description}} - - {section.headerAction || null} - - - {section.content} -
    - ); - }); -}; - -export default SectionList; diff --git a/src/components/HistorySectionList/index.tsx b/src/components/HistorySectionList/index.tsx new file mode 100644 index 00000000..8696a33a --- /dev/null +++ b/src/components/HistorySectionList/index.tsx @@ -0,0 +1,38 @@ +import * as Styled from "./styleds"; + +export type HistorySection = { + title: string; + content: JSX.Element[]; + description?: string; + headerAction?: any; +}; + +export type HistorySectionListProps = { + sections: Array; +}; + +const HistorySectionList = ({ sections }: HistorySectionListProps) => { + return ( + <> + {sections.map((section, index) => { + return ( +
    + +
    + {section.title} + {section.description && ( + {section.description} + )} +
    + {section.headerAction || null} +
    + +
    {section.content}
    +
    + ); + })} + + ); +}; + +export default HistorySectionList; diff --git a/src/components/HistorySectionList/styleds.tsx b/src/components/HistorySectionList/styleds.tsx new file mode 100644 index 00000000..af3b949e --- /dev/null +++ b/src/components/HistorySectionList/styleds.tsx @@ -0,0 +1,26 @@ +import styled from "styled-components"; + +export const SectionHeader = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + padding: 2rem 30px; +`; + +export const SectionTitle = styled.h4` + color: ${({ theme }) => theme.text1}; + font-weight: 700; + font-size: 1rem; + margin: 0; + + @media (min-width: 768px) { + font-size: 1.25rem; + } +`; + +export const SectionSubTitle = styled.p` + color: ${({ theme }) => theme.text1}; + font-weight: 400; + font-size: 1rem; + margin: 0; +`; diff --git a/src/components/Icons/ArrowDown.js b/src/components/Icons/ArrowDown.js index 973459f6..dd2b43c3 100644 --- a/src/components/Icons/ArrowDown.js +++ b/src/components/Icons/ArrowDown.js @@ -6,9 +6,9 @@ const ArrowDown = ({ size, fill, color }) => { { d="M6.70710678,18.7071068 C6.31658249,19.0976311 5.68341751,19.0976311 5.29289322,18.7071068 C4.90236893,18.3165825 4.90236893,17.6834175 5.29289322,17.2928932 L11.2928932,11.2928932 C11.6714722,10.9143143 12.2810586,10.9010687 12.6757246,11.2628459 L18.6757246,16.7628459 C19.0828436,17.1360383 19.1103465,17.7686056 18.7371541,18.1757246 C18.3639617,18.5828436 17.7313944,18.6103465 17.3242754,18.2371541 L12.0300757,13.3841378 L6.70710678,18.7071068 Z" id="Path-94" fill={fill || color} - fill-rule="nonzero" + fillRule="nonzero" transform="translate(12.000003, 14.999999) scale(1, -1) translate(-12.000003, -14.999999) " > diff --git a/src/components/Icons/ArrowRight.js b/src/components/Icons/ArrowRight.js index 05006db2..5f504ef3 100644 --- a/src/components/Icons/ArrowRight.js +++ b/src/components/Icons/ArrowRight.js @@ -6,16 +6,16 @@ const ArrowRightIcon = ({ size, fill, color }) => { diff --git a/src/components/Icons/ArrowRightLong.js b/src/components/Icons/ArrowRightLong.js index 00f0720e..a83dcc67 100644 --- a/src/components/Icons/ArrowRightLong.js +++ b/src/components/Icons/ArrowRightLong.js @@ -6,9 +6,9 @@ const ArrowRightLongIcon = ({ size, fill, color }) => { { d="M9.70710318,15.7071045 C9.31657888,16.0976288 8.68341391,16.0976288 8.29288961,15.7071045 C7.90236532,15.3165802 7.90236532,14.6834152 8.29288961,14.2928909 L14.2928896,8.29289093 C14.6714686,7.914312 15.281055,7.90106637 15.675721,8.26284357 L21.675721,13.7628436 C22.08284,14.136036 22.1103429,14.7686034 21.7371505,15.1757223 C21.3639581,15.5828413 20.7313908,15.6103443 20.3242718,15.2371519 L15.0300721,10.3841355 L9.70710318,15.7071045 Z" id="Path-94" fill={fill || color} - fill-rule="nonzero" + fillRule="nonzero" transform="translate(14.999999, 11.999997) scale(1, -1) rotate(90.000000) translate(-14.999999, -11.999997) " > diff --git a/src/components/Icons/Exchange.js b/src/components/Icons/Exchange.js index 84ae4f33..7e5d9e31 100644 --- a/src/components/Icons/Exchange.js +++ b/src/components/Icons/Exchange.js @@ -26,7 +26,7 @@ const ExchangeIcon = ({ size, fill, color }) => { d="M9.79289322,3.79289322 C10.1834175,3.40236893 10.8165825,3.40236893 11.2071068,3.79289322 C11.5976311,4.18341751 11.5976311,4.81658249 11.2071068,5.20710678 L8.20710678,8.20710678 C7.81658249,8.59763107 7.18341751,8.59763107 6.79289322,8.20710678 L3.79289322,5.20710678 C3.40236893,4.81658249 3.40236893,4.18341751 3.79289322,3.79289322 C4.18341751,3.40236893 4.81658249,3.40236893 5.20710678,3.79289322 L7.5,6.08578644 L9.79289322,3.79289322 Z" id="Path-104" fill={fill || color} - fill-rule="nonzero" + fillRule="nonzero" transform="translate(7.500000, 6.000000) rotate(-270.000000) translate(-7.500000, -6.000000) " > { d="M18.7928932,15.7928932 C19.1834175,15.4023689 19.8165825,15.4023689 20.2071068,15.7928932 C20.5976311,16.1834175 20.5976311,16.8165825 20.2071068,17.2071068 L17.2071068,20.2071068 C16.8165825,20.5976311 16.1834175,20.5976311 15.7928932,20.2071068 L12.7928932,17.2071068 C12.4023689,16.8165825 12.4023689,16.1834175 12.7928932,15.7928932 C13.1834175,15.4023689 13.8165825,15.4023689 14.2071068,15.7928932 L16.5,18.0857864 L18.7928932,15.7928932 Z" id="Path-104-Copy" fill={fill || color} - fill-rule="nonzero" + fillRule="nonzero" transform="translate(16.500000, 18.000000) scale(1, -1) rotate(270.000000) translate(-16.500000, -18.000000) " > diff --git a/src/components/InnerCard/index.js b/src/components/InnerCard/index.js index ed5fecd4..0adfbc87 100644 --- a/src/components/InnerCard/index.js +++ b/src/components/InnerCard/index.js @@ -7,11 +7,12 @@ import CurrencyText from "../CurrencyText"; import ArrowRightIcon from "../Icons/ArrowRight"; const Container = styled.div` - padding-right: 20px; + padding-right: 1.5rem; height: 100%; `; + const Wrapper = styled.div` - border: 1px solid ${({ theme }) => theme.bg2}; + border: 1px solid ${({ theme }) => theme.bg1}; background-color: ${({ theme }) => theme.modalBG}; box-shadow: 0 0 5px transparent; padding: 1.25rem; @@ -29,7 +30,7 @@ const Wrapper = styled.div` } &:hover { - background-color: ${({ theme }) => theme.bg2}; + background-color: ${({ theme }) => theme.bg1}; border-color: ${({ theme }) => theme.primary}; box-shadow: ${({ theme }) => `0 5px 15px ${theme.primary}20`}; } @@ -136,7 +137,7 @@ const PriceDiff = styled.span` const DetailsButton = styled.button` border-radius: 12px; - background-color: ${({ theme }) => theme.bg2}; + background-color: ${({ theme }) => theme.bg1}; padding: 6px 18px; max-height: 40px; min-height: 40px; @@ -155,7 +156,7 @@ const DetailsButton = styled.button` transition: 0.4s ease all; &:hover { - color: ${({ theme }) => theme.bg2}; + color: ${({ theme }) => theme.bg1}; background-color: ${({ theme }) => theme.primary}; } diff --git a/src/components/InstantSwap/RateList/index.tsx b/src/components/InstantSwap/RateList/index.tsx new file mode 100644 index 00000000..f16ae842 --- /dev/null +++ b/src/components/InstantSwap/RateList/index.tsx @@ -0,0 +1,29 @@ +import RateOption from "../RateOption"; + +export type RateListProps = { + items: Array; + loading?: boolean; + onSelectRate: (T: any) => void; + rate?: any; + pair?: any; +}; + +const RateList = ({ items, loading, onSelectRate, rate, pair }: RateListProps) => { + if (loading) { + return null; + } + + if (items.length === 0) { + return null; + } + + return ( +
    + {items.map((item, index) => ( + + ))} +
    + ); +}; + +export default RateList; diff --git a/src/components/InstantSwap/RateOption/index.tsx b/src/components/InstantSwap/RateOption/index.tsx new file mode 100644 index 00000000..8c44024d --- /dev/null +++ b/src/components/InstantSwap/RateOption/index.tsx @@ -0,0 +1,54 @@ +import { useTranslation } from "react-i18next"; +import { isMobile } from "react-device-detect"; +import { toAbsoluteUrl } from "../../../lib/helper"; +import { DEXesImages, DEXesName } from "../../../constants"; +import * as Styled from "./styleds"; + +export type RateOptionProps = { + item: any; + onSelectRate: (T: any) => void; + rate?: any; + pair: any; + index?: any; +}; + +const RateOption = ({ item, onSelectRate, rate, pair, index }: RateOptionProps) => { + const { t } = useTranslation(); + // @ts-ignore + const dexImage = DEXesImages[item.platform]; + // @ts-ignore + const dexName = DEXesName[item.platform]; + + return ( + onSelectRate(item)} + className="d-flex align-items-center justify-content-between" + > +
    + + {dexName} +
    + +
    + + {item.rate?.toFixed(6)} {pair.destination?.token?.symbol}/{pair.deposit?.token?.symbol} + + {index === 0 ? ( + {t("best")} + ) : ( + + )} +
    +
    + ); +}; + +export default RateOption; diff --git a/src/components/InstantSwap/RateOption/styleds.tsx b/src/components/InstantSwap/RateOption/styleds.tsx new file mode 100644 index 00000000..a1db3021 --- /dev/null +++ b/src/components/InstantSwap/RateOption/styleds.tsx @@ -0,0 +1,38 @@ +import styled from "styled-components"; +import { Text } from "rebass"; + +export const PlatformCard = styled.div<{ selected: boolean }>` + background-color: ${({ selected, theme }) => (selected ? theme.bg1 : theme.primaryLight)}; + border: 1px solid ${({ theme, selected }) => (selected ? theme.primary : theme.primaryLight)}; + border-radius: 0.75rem; + cursor: pointer; + margin-bottom: 0.75rem; + padding: 0.5rem 0.75rem; + transition: all ease 0.3s; + + &:hover { + background-color: ${({ theme }) => theme.bg1}; + border-color: ${({ theme }) => theme.primary}; + } + + @media (min-width: 768px) { + padding: 0.65rem 1rem; + } +`; + +export const Logo = styled.img<{ size: number }>` + border-radius: 50%; + width: ${({ size }) => (size ? `${size}px` : "1.5rem")}; + height: ${({ size }) => (size ? `${size}px` : "1.5rem")}; + margin-right: 1rem; +`; + +export const RateText = styled(Text)` + color: ${({ theme }) => theme.text1}; + font-size: 0.75rem; + + @media (min-width: 768px) { + font-size: 0.85rem; + font-weight: 500; + } +`; diff --git a/src/components/InstantSwap/RefreshRatesButton/index.tsx b/src/components/InstantSwap/RefreshRatesButton/index.tsx new file mode 100644 index 00000000..40fedd54 --- /dev/null +++ b/src/components/InstantSwap/RefreshRatesButton/index.tsx @@ -0,0 +1,24 @@ +import { useTranslation } from "react-i18next"; +import useTheme from "../../../hooks/useTheme"; +import CircleLoading from "../../CircleLoading"; +import * as Styled from "./styleds"; + +export type RefreshRatesButtonProps = { + loading?: boolean; + priceLoading?: boolean; + onRefresh: () => void; +}; + +const RefreshRatesButton = ({ loading, priceLoading, onRefresh }: RefreshRatesButtonProps) => { + const { t } = useTranslation(); + const theme = useTheme(); + + return ( + + {t("instantSwap.refreshPrice")} + + + ); +}; + +export default RefreshRatesButton; diff --git a/src/components/InstantSwap/RefreshRatesButton/styleds.tsx b/src/components/InstantSwap/RefreshRatesButton/styleds.tsx new file mode 100644 index 00000000..7e2b7b9d --- /dev/null +++ b/src/components/InstantSwap/RefreshRatesButton/styleds.tsx @@ -0,0 +1,24 @@ +import styled from "styled-components"; + +export const LoadingContainer = styled.div` + padding: 0.5rem; + background-color: ${({ theme }) => theme.bg1}; + border: 1px solid ${({ theme }) => theme.borderColor}; + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: border-color 0.3s ease; + + &:hover { + border-color: ${({ theme }) => theme.primary}; + } +`; + +export const LoadingText = styled.span` + font-size: 0.75rem; + font-weight: 500; + color: ${({ theme }) => theme.text3}; + margin-right: 0.75rem; +`; diff --git a/src/components/InstantSwap/SwapInputPanel/index.tsx b/src/components/InstantSwap/SwapInputPanel/index.tsx new file mode 100644 index 00000000..8d05d596 --- /dev/null +++ b/src/components/InstantSwap/SwapInputPanel/index.tsx @@ -0,0 +1,148 @@ +import { useCallback, useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { ChevronDown } from "react-feather"; +import { ETHER, Token } from "@uniswap/sdk"; +import { useActiveWeb3React } from "../../../hooks"; +import { useCurrencyBalance } from "../../../state/wallet/hooks"; +import { Input as NumericalInput } from "../../NumericalInput"; +import SwapSelectModal from "../../SwapSelectModal"; +import * as Styled from "./styleds"; +import { Button } from "react-bootstrap"; + +export type SwapInputPanelProps = { + value?: any; + label?: string; + disabled?: boolean; + selected: any; + id: string; + currencies: Array; + type: string; + disableCurrencySelect: boolean; + onSelect: () => void; + onUserInput?: (X: any, Y: any, Z: any) => void; + onChangeBalance: (T: any) => void; + showMaxButton?: boolean; +}; + +const SwapInputPanel = ({ + value, + onUserInput, + label = "Input", + onSelect, + disabled = false, + selected, + id, + currencies, + type, + disableCurrencySelect = false, + onChangeBalance = (balance) => balance, + showMaxButton = false, +}: SwapInputPanelProps) => { + let currency = undefined; + const { t } = useTranslation(); + if (selected) { + currency = new Token(selected.chainId, selected.address, selected.decimals, selected.symbol, selected.name); + } + const [modalOpen, setModalOpen] = useState(false); + const { account } = useActiveWeb3React(); + const selectedCurrencyBalance = useCurrencyBalance( + account ?? undefined, + selected && selected.symbol === "ETH" ? ETHER : currency + ); + + useEffect(() => { + onChangeBalance(selectedCurrencyBalance); + }, [selected]); + + const handleDismissSearch = useCallback(() => { + setModalOpen(false); + }, [setModalOpen]); + + const onOpenCurrencySelect = () => { + if (!disableCurrencySelect) { + setModalOpen(true); + } + }; + + const onUseMax = () => { + alert("Use Max"); + }; + + return ( + + +
    + + + {selected ? ( + <> + + + + {label} + + + {selected?.symbol && selected?.symbol.length > 16 + ? selected?.symbol.slice(0, 4) + + "..." + + selected?.symbol.slice( + selected?.symbol.length - 5, + selected?.symbol.length + ) + : selected?.symbol} + + + + ) : ( + {t("selectToken")} + )} + + {!disableCurrencySelect && } + + +
    + + {onUserInput && ( + + {account && currency && showMaxButton && label !== "To" && ( + + )} + + { + onUserInput(val, type, selectedCurrencyBalance); + }} + /> + + {account && ( + + {!!currency && selectedCurrencyBalance + ? t("balance", { + balanceInput: selectedCurrencyBalance?.toSignificant(6), + }) + : " -"} + + )} + + )} +
    + + {!disableCurrencySelect && onSelect && ( + + )} +
    + ); +}; + +export default SwapInputPanel; diff --git a/src/components/InstantSwap/SwapInputPanel/styleds.tsx b/src/components/InstantSwap/SwapInputPanel/styleds.tsx new file mode 100644 index 00000000..aa44e995 --- /dev/null +++ b/src/components/InstantSwap/SwapInputPanel/styleds.tsx @@ -0,0 +1,91 @@ +import styled from "styled-components"; + +export const InputPanel = styled.div` + position: relative; + z-index: 1; + // border-radius: 18px; + // background: ${({ theme }) => theme.bg5}; + // padding: 0.75rem; +`; + +export const InputRow = styled.div<{ selected?: boolean }>` + ${({ theme }) => theme.flexRowNoWrap}; + align-items: center; +`; + +export const CurrencySelect = styled.button<{ selected?: boolean }>` + background-color: ${({ theme }) => theme.bg1}; + color: ${({ theme }) => theme.text1}; + border-bottom-left-radius: 18px; + border-top-left-radius: 18px; + border: 1px solid ${({ theme }) => theme.bg1}; + box-shadow: none; + cursor: pointer; + outline: none; + user-select: none; + height: 56px; + min-width: 160px; + width: 160px; + padding: 0 0.5rem; + align-items: center; + text-align: left; + line-height: 1; + + &:focus, + &:hover { + background-color: ${({ theme }) => theme.primaryLight}; + outline: none; + border-color: ${({ theme }) => theme.primary}; + } +`; + +export const InputContainer = styled.div` + position: relative; + display: flex; + align-items: center; + flex-wrap: nowrap; + flex: 1; + background: ${({ theme }) => theme.bg1}; + border-top-right-radius: 12px; + border-bottom-right-radius: 12px; + padding: 0 0.5rem; +`; + +export const Aligner = styled.div` + display: flex; + align-items: center; +`; + +export const Logo = styled.img` + border-radius: 50%; + margin-right: 0.5rem; + height: 24px; + width: 24px; + text-indent: 100%; + white-space: nowrap; + overflow: hidden; +`; + +export const TextWrap = styled.div` + margin-right: auto; + position: relative; +`; + +export const Label = styled.div` + color: ${({ theme }) => theme.text1}; + line-height: 1; + font-weight: 400; + font-size: 0.625rem; + margin-bottom: 4px; +`; + +export const TokenName = styled.span` + font-size: 0.875rem; + font-weight: 500; + margin-right: auto; +`; + +export const Balance = styled.span<{ showBalance?: boolean }>` + font-weight: 500; + font-size: 0.75rem; +`; diff --git a/src/pages/InstantSwap/index.js b/src/components/InstantSwap/index.js similarity index 78% rename from src/pages/InstantSwap/index.js rename to src/components/InstantSwap/index.js index f810ebf1..ea45064c 100644 --- a/src/pages/InstantSwap/index.js +++ b/src/components/InstantSwap/index.js @@ -1,159 +1,46 @@ import React from "react"; -import { Row, Col, Button, ProgressBar } from "react-bootstrap"; import { connect } from "react-redux"; -import styled, { ThemeContext } from "styled-components"; -import _ from "lodash"; -import { Text } from "rebass"; import { isMobile } from "react-device-detect"; import QRCode from "react-qr-code"; +import _ from "lodash"; +import { ThemeContext } from "styled-components"; +import { Row, Col, ProgressBar, Button } from "react-bootstrap"; +import { toast } from "react-hot-toast"; +import { withTranslation } from "react-i18next"; +import { ArrowDown, Download, Plus, Minus } from "react-feather"; +import { ChainId } from "@uniswap/sdk"; +import moment from "moment"; +import Web3 from "web3"; +import BigNumber from "bignumber.js"; +import { assert } from "@0x/assert"; +import { AbiDecoder, intervalUtils } from "@0x/utils"; +import { ResponsiveCard } from "../Card"; -import { Modal } from "../../components/Modal/bootstrap"; -import { ResponsiveCard } from "../../components/Card"; -import withWeb3Account from "../../components/hoc/withWeb3Account"; import { BTC, CHANGE_NOW_FLOW, - DEXesImages, - DEXesName, PARASWAP_REFERRER_ACCOUNT, SIMPLE_SWAP_FIXED, supportedDEXes, ZERO, } from "../../constants"; -import InstantSwapApi from "../../http/instantSwap"; -import SwapInputPanel from "../../components/SwapInputPanel"; -import { walletTokens as tokens } from "../../constants/spot-config/mainnet/config.json"; -import { toAbsoluteUrl } from "../../lib/helper"; import ERC20_ABI from "../../constants/abis/erc20.json"; -import { getContract } from "../../utils"; -import Page from "../../components/Page"; -import Web3 from "web3"; -import { RowBetween } from "../../components/Row"; -import { CloseIcon, LinkStyledButton } from "../../theme"; -import { toast } from "react-hot-toast"; -import BigNumber from "bignumber.js"; -import { assert } from "@0x/assert"; -import { AbiDecoder, intervalUtils } from "@0x/utils"; -import { withTranslation } from "react-i18next"; -import { ArrowWrapper } from "../../components/swap/styleds"; -import { ArrowDown } from "react-feather"; -import AddressInputPanel from "../../components/AddressInputPanel"; +import { walletTokens as tokens } from "../../constants/spot-config/mainnet/config.json"; +import InstantSwapApi from "../../http/instantSwap"; import { addTransaction } from "../../state/transactions/actions"; -import { ChainId } from "@uniswap/sdk"; -import moment from "moment"; -import CircleLoading from "../../components/CircleLoading"; - -const HEX_REGEX = /^0x[0-9A-F]*$/i; - -const SwapButton = styled(Button)` - height: 56px; - min-width: 250px; - align-self: center; - - @media (max-width: 767px) { - width: 100%; - } -`; - -const StyledRow = styled(Row)` - margin-top: 20px; -`; - -const CustomCard = styled(ResponsiveCard)` - margin-top: 20px; - - & .card-body { - @media (min-width: 768px) { - padding: 42px 30px 32px; - } - } -`; - -const RateText = styled(Text)` - color: ${({ theme }) => theme.text1}; - - @media (max-width: 767px) { - font-size: 12px; - } -`; - -const Logo = styled.img` - width: ${({ size }) => (size ? `${size}px` : "24px")}; - height: ${({ size }) => (size ? `${size}px` : "24px")}; - border-radius: ${({ size }) => (size ? `${size}px` : "24px")}; - background-color: ${({ theme }) => theme.text1}; - border: 2px solid ${({ theme }) => theme.text1}; - margin-right: 15px; -`; - -const ProgressContainer = styled.div` - background: ${({ theme }) => theme.primaryLight}; - border-radius: 18px; - padding: 26px 20px; - margin-top: 10px; - - .progress { - height: 5px; - background-color: ${({ theme }) => theme.primaryLight}; - border-radius: 15px; - - &-bar { - border-radius: 15px; - } - } -`; - -const PlatformCard = styled.div` - padding: 12px 20px; - border-radius: 18px; - background-color: ${({ selected, theme }) => (selected ? theme.bg2 : theme.primaryLight)}; - margin-bottom: 10px; - min-height: 56px; - border: 1px solid ${({ theme, selected }) => (selected ? theme.primary : "transparent")}; - transition: all ease 0.4s; - cursor: pointer; - - &:hover { - background-color: ${({ theme }) => theme.bg2}; - border-color: ${({ theme }) => theme.primary}; - } +import { CloseIcon } from "../../theme"; +import { getContract } from "../../utils"; - @media (max-width: 767px) { - padding: 8px 8px 8px 10px; - } -`; - -const SwitchCol = styled(Col)` - margin-bottom: 1.25rem; -`; - -const LoadingContainer = styled.div` - padding: 10px; - background-color: ${({ theme }) => theme.bg1}; - border: 1px solid ${({ theme }) => theme.borderColor}; - border-radius: 12px; - height: 40px; - width: 140px; - max-width: 140px; - min-height: 40px; - display: flex; - align-items: center; - justify-content: center; - margin-bottom: 1.25rem; - cursor: pointer; - transition: border-color 0.3s ease; - - &:hover { - border-color: ${({ theme }) => theme.primary}; - } -`; +import AddressInputPanel from "../AddressInputPanel"; +import withWeb3Account from "../hoc/withWeb3Account"; +import { Modal } from "../Modal/bootstrap"; +import { RowBetween } from "../Row"; +import RateList from "./RateList"; +import SwapInputPanel from "./SwapInputPanel"; +import RefreshRatesButton from "./RefreshRatesButton"; +import * as Styled from "./styleds"; -const LoadingText = styled.span` - font-size: 0.75rem; - font-weight: 500; - color: ${({ theme }) => theme.text3}; - margin-right: 0.75rem; -`; +const HEX_REGEX = /^0x[0-9A-F]*$/i; const PATTERN = { btc: /^[13][a-km-zA-HJ-NP-Z1-9]{25,80}$|^(bc1)[0-9A-Za-z]{25,80}$/, @@ -1356,7 +1243,6 @@ class InstantSwap extends React.Component { const { t } = this.props; const { pair, rate, recipient } = this.state; - let result; if (pair.destination.token.symbol.toUpperCase() === "BTC") { if (recipient === null) { @@ -1378,35 +1264,35 @@ class InstantSwap extends React.Component { switch (rate.source) { case "1inch": { - result = await this.oneInchBuyHandler(pair, rate); + await this.oneInchBuyHandler(pair, rate); break; } case "paraswap": { - result = await this.paraSwapBuyHandler(pair, rate); + await this.paraSwapBuyHandler(pair, rate); break; } case "dexag": { - result = await this.dexagBuyHandler(pair, rate); + await this.dexagBuyHandler(pair, rate); break; } case "simpleSwap": { - result = await this.simpleSwapBuyHandler(pair, rate); + await this.simpleSwapBuyHandler(pair, rate); break; } case "stealthex": { - result = await this.stealthexBuyHandler(pair, rate); + await this.stealthexBuyHandler(pair, rate); break; } case "changeNow": { - result = await this.changeNowBuyHandler(pair, rate); + await this.changeNowBuyHandler(pair, rate); break; } case "sideShift": { - result = await this.sideShiftBuyHandler(pair, rate); + await this.sideShiftBuyHandler(pair, rate); break; } case "godex": { - result = await this.godexBuyHandler(pair, rate); + await this.godexBuyHandler(pair, rate); break; } default: { @@ -1480,7 +1366,7 @@ class InstantSwap extends React.Component { updatePriceIntervally = (deposit, destination) => { if (this.priceInterval) { clearInterval(this.priceInterval); - this.priceInterval = null; + // this.priceInterval = null; } this.priceInterval = setInterval(() => { this.getNewPrice(deposit, destination); @@ -1507,258 +1393,176 @@ class InstantSwap extends React.Component { priceLoading, loadingState, buyState, - recipient, + // recipient, } = this.state; const { t } = this.props; const theme = this.context; return ( - - - - - - {rates.length > 0 && ( - - - {t("instantSwap.refreshPrice")} - + + + + + + + + + + + + {/* {recipient === null ? ( +
    + +
    + ) : ( + <> +
    + + + +
    + +
    + +
    + +
    + +
    + + )} */} + + {!!rate && ( +
    + {/* + Exchange Rate + */} + + {pair.deposit.token && pair.destination.token + ? `1 ${ + pair.deposit.token.symbol || pair.deposit.token.code + } ≈ ${rate.rate.toFixed(4)} ${ + pair.destination.token.symbol || pair.destination.token.code + }` + : null} + + + {rates.length > 0 && ( +
    + - - - )} - - - - - - - - - - {recipient === null && ( - this.onChangeRecipient("")} - > - + {t("addSend")} ({t("optional")}) - +
    )} - - - - +
    + )} - {recipient !== null && ( - <> - - - - - this.onChangeRecipient(null)} - > - - {t("removeSend")} - - - - - - - )} - {!!rate && ( - + {loading ? ( +
    - - Exchange Rate - - - {pair.deposit.token && pair.destination.token - ? `1 ${ - pair.deposit.token.symbol || pair.deposit.token.code - } = ${rate.rate.toFixed(4)} ${ - pair.destination.token.symbol || pair.destination.token.code - }` - : null} - - - )} - - - {loading ? ( -
    - - {t("instantSwap.exchangeSearch", { - loaded: loadingState.loaded, - all: loadingState.all, - })} - - - - -
    - ) : ( - 0 && - rate - ) || - rate.platform === "godex") - } - variant={ + + {t("instantSwap.exchangeSearch", { + loaded: loadingState.loaded, + all: loadingState.all, + })} + + + + +
    + ) : ( + 0 && rate - ? "primary" - : "outline-primary" - } - > - {!this.props.web3.account - ? t("wallet.connect") - : !pair.deposit.token || !pair.destination.token - ? t("exchange.selectPair") - : !hasEnoughBalance - ? t("insufficientBalance") - : !pair.deposit.value || !pair.destination.value - ? t("exchange.enterAmount") - : !rate && !loading - ? t("errors.unavailablePair") - : rate.platform === "godex" - ? t("errors.unavailablePair") - : Number(pair.deposit.value) > 0 - ? this.getBuyStates()[buyState] - : t("exchange.enterAmount")} - - )} - - {rates.length > 0 && !loading && ( - - {rates.map((item, index) => { - return ( - -
    - - - {DEXesName[item.platform]} - -
    - -
    - - {item?.rate?.toFixed(6)} {pair?.destination?.token?.symbol}/ - {pair?.deposit?.token?.symbol} - - {index === 0 ? ( - - {t("best")} - - ) : ( - - )} -
    -
    - ); - })} - + ) || + rate.platform === "godex") + } + > + {!this.props.web3.account + ? t("wallet.connect") + : !pair.deposit.token || !pair.destination.token + ? t("exchange.selectPair") + : !hasEnoughBalance + ? t("insufficientBalance") + : !pair.deposit.value || !pair.destination.value + ? t("exchange.enterAmount") + : !rate && !loading + ? t("errors.unavailablePair") + : rate.platform === "godex" + ? t("errors.unavailablePair") + : Number(pair.deposit.value) > 0 + ? this.getBuyStates()[buyState] + : t("exchange.enterAmount")} +
    )} -
    -
    +
    + + + + + - +
    + - + ); } } diff --git a/src/components/InstantSwap/styleds.tsx b/src/components/InstantSwap/styleds.tsx new file mode 100644 index 00000000..ddc9fd17 --- /dev/null +++ b/src/components/InstantSwap/styleds.tsx @@ -0,0 +1,60 @@ +import styled from "styled-components"; +import { Button } from "react-bootstrap"; +import { Text } from "rebass"; + +export const SwapButton = styled(Button)` + height: 56px; + min-width: 250px; + align-self: center; + + @media (max-width: 767px) { + width: 100%; + } +`; + +export const RateText = styled(Text)` + color: ${({ theme }) => theme.text1}; + + @media (max-width: 767px) { + font-size: 12px; + } +`; + +export const ProgressContainer = styled.div` + background: ${({ theme }) => theme.primaryLight}; + border-radius: 18px; + padding: 26px 20px; + margin-top: 10px; + + .progress { + height: 5px; + background-color: ${({ theme }) => theme.primaryLight}; + border-radius: 15px; + } + + .progress-bar { + border-radius: 15px; + } +`; + +export const SwitchCol = styled.div<{ clickable?: boolean }>` + background: ${({ theme }) => theme.bg1}; + border: 4px solid ${({ theme }) => theme.modalBG}; + border-radius: 50%; + margin-left: 1.75rem; + margin-top: -8px; + margin-bottom: -8px; + display: flex; + align-items: center; + justify-content: center; + position: relative; + height: 44px; + width: 44px; + text-align: center; + z-index: 2; + + &:hover { + background: ${({ theme, clickable }) => (clickable ? theme.primary : theme.bg1)}; + cursor: ${({ clickable }) => (clickable ? "pointer" : "auto")}; + } +`; diff --git a/src/components/LaunchpadCard/index.js b/src/components/LaunchpadCard/index.js index c1825477..e9fee6fc 100644 --- a/src/components/LaunchpadCard/index.js +++ b/src/components/LaunchpadCard/index.js @@ -223,7 +223,7 @@ const LaunchpadCard = ({ address, presale }) => { }, [presale, baseToken]); return ( - +
    diff --git a/src/components/Logo/index.js b/src/components/Logo/index.js deleted file mode 100644 index 85bc7f1e..00000000 --- a/src/components/Logo/index.js +++ /dev/null @@ -1,24 +0,0 @@ -import React, { useContext } from "react"; -import { Link } from "react-router-dom"; -import { ThemeContext } from "styled-components"; -import LogoImage from "../../assets/images/logo.svg"; -import { TYPE } from "../../theme"; - -const Logo = (props) => { - const theme = useContext(ThemeContext); - return ( - - {process.env.REACT_APP_BRAND} - - {process.env.REACT_APP_BRAND} - - - ); -}; - -export default Logo; diff --git a/src/components/Logo/index.tsx b/src/components/Logo/index.tsx new file mode 100644 index 00000000..c159de54 --- /dev/null +++ b/src/components/Logo/index.tsx @@ -0,0 +1,42 @@ +import { useContext } from "react"; +import { Link } from "react-router-dom"; +import styled, { ThemeContext } from "styled-components"; +import LogoImage from "../../assets/images/logo.svg"; +import { TYPE } from "../../theme"; + +export const StyledLink = styled(Link)` + display: flex; + align-items: center; + text-decoration: none !important; + position: relative; + margin-right: 0; +`; + +export const StyledImg = styled.img` + width: 35px; + height: 25px; + margin-right: 6px; +`; + +export type LogoProps = { + hideOnMobile?: boolean; +}; + +const Logo = ({ hideOnMobile }: LogoProps) => { + const theme = useContext(ThemeContext); + return ( + + + + {process.env.REACT_APP_BRAND} + + + ); +}; + +export default Logo; diff --git a/src/components/MarketMakerInputPanel/index.js b/src/components/MarketMakerInputPanel/index.js index af5b8626..6f087487 100644 --- a/src/components/MarketMakerInputPanel/index.js +++ b/src/components/MarketMakerInputPanel/index.js @@ -103,7 +103,7 @@ const CurrencySelect = styled.button` height: 56px; font-size: 0.875rem; font-weight: 500; - background-color: ${({ theme }) => theme.bg3}; + background-color: ${({ theme }) => theme.bg1}; color: ${({ theme }) => theme.text1}; border-bottom-left-radius: 18px; border-top-left-radius: 18px; diff --git a/src/pages/ExploreTypeList/MarketTokens.js b/src/components/MarketTokens/index.js similarity index 74% rename from src/pages/ExploreTypeList/MarketTokens.js rename to src/components/MarketTokens/index.js index 999966c8..509673e4 100644 --- a/src/pages/ExploreTypeList/MarketTokens.js +++ b/src/components/MarketTokens/index.js @@ -1,171 +1,24 @@ -import { Tab, Row, Col, Nav } from "react-bootstrap"; -import styled from "styled-components"; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { withRouter } from "react-router-dom"; import { useDispatch, useSelector } from "react-redux"; +import { useTranslation } from "react-i18next"; +import { Tab, Row, Col } from "react-bootstrap"; import BootstrapTable from "react-bootstrap-table-next"; -import { withRouter } from "react-router-dom"; +import SVG from "react-inlinesvg"; -import { - InputGroupFormControl as FormControl, - InputGroupPrepend, - InputGroup, - InputGroupText, -} from "../../components/Form"; import SearchIcon from "../../assets/images/search.svg"; -import Loading from "../../components/Loading"; -import CurrencyText from "../../components/CurrencyText"; -import { fetchAllCoins, fetchCoinMarketPrices, fetchMarketCoins } from "../../state/market/actions"; -import CurrencyLogo from "../../components/CurrencyLogo"; -import ArrowDown from "../../components/Icons/ArrowDown"; -import ResponsiveTable from "../../components/ResponsiveTable"; import { sortedData } from "../../lib/helper"; import MarketApi from "../../http/market"; -import SVG from "react-inlinesvg"; -import { useTranslation } from "react-i18next"; -import {useIsDarkMode} from "../../state/user/hooks"; - -const LogoContainer = styled.div` - width: 32px; - height: 32px; - border-radius: 32px; - - @media (max-width: 991px) { - width: 24px; - height: 24px; - border-radius: 24px; - } -`; - -const Logo = styled.img` - width: 32px; - height: 32px; - border-radius: 32px; - background-color: ${({ theme }) => theme.text1}; - border: 2px solid ${({ theme }) => theme.text1}; - - @media (max-width: 991px) { - width: 24px; - height: 24px; - border-radius: 24px; - } -`; - -const CustomNav = styled(Nav)` - margin-left: -30px !important; - margin-right: -30px !important; - overflow: auto; - - @media (min-width: 768px) { - margin-left: -10px !important; - margin-right: -10px !important; - } -`; - -const CustomNavItem = styled(Nav.Item)` - flex-grow: initial !important; - - padding: 0 10px 10px; - - @media (max-width: 767px) { - padding: 0 5px 10px; - } - - &:first-child { - @media (max-width: 767px) { - padding-left: 30px; - } - } - &:last-child { - @media (max-width: 767px) { - padding-right: 30px; - } - } -`; -const CustomNavLink = styled(Nav.Link)` - border-radius: 18px !important; - color: ${({ theme }) => theme.primary}; - background-color: ${({ theme }) => theme.primaryLight}; - white-space: nowrap; - padding: 14px 24px; - min-height: 56px; - font-weight: 500; - display: flex; - align-items: center; - justify-content: center; - - @media (max-width: 767px) { - padding: 6px 15px; - font-size: 1rem; - min-height: 32px; - border-radius: 12px !important; - } - - &:hover { - color: ${({ theme }) => theme.primary}; - } - - &.active { - color: ${({ theme }) => theme.text1}; - background-color: ${({ theme }) => theme.primary}; - } -`; - -const HeaderCol = styled(Col)` - margin: -10px 0 20px; - - @media (min-width: 768px) { - margin-bottom: 25px; - } -`; - -const CustomInputGroup = styled(InputGroup)` - margin-bottom: 30px; -`; - -const MarketLink = styled.a` - color: ${({ theme }) => theme.text1}; - @media (max-width: 991px) { - flex-basis: 100px; - } -`; - -const CustomTitle = styled.h4` - color: ${({ theme }) => theme.text1}; - font-size: 1rem; - - @media (max-width: 991px) { - font-size: 0.875rem; - } -`; - -const SymbolText = styled.span` - font-weight: 500; - font-size: 0.75rem; - color: ${({ theme }) => theme.text1}; - - @media (max-width: 991px) { - font-size: 0.875rem; - font-weight: 400; - } -`; - -const CellText = styled.span` - font-weight: 500; - font-size: 0.875rem; - color: ${({ theme }) => theme.text1}; - - &.font-size-base { - font-size: 1rem; - } - - @media (max-width: 991px) { - font-weight: 700; - - &.label { - font-weight: 500; - } - } -`; +import { fetchAllCoins, fetchCoinMarketPrices, fetchMarketCoins } from "../../state/market/actions"; +import { useIsDarkMode } from "../../state/user/hooks"; +import CurrencyLogo from "../CurrencyLogo"; +import CurrencyText from "../CurrencyText"; +import ArrowDown from "../Icons/ArrowDown"; +import { InputGroupFormControl as FormControl, InputGroupPrepend, InputGroupText } from "../Form"; +import Loading from "../Loading"; +import ResponsiveTable from "../ResponsiveTable"; +import * as Styled from "./styleds"; +import "./style.scss"; const api = new MarketApi(); @@ -305,18 +158,22 @@ const MarketTokens = (props) => { return (
    {row.image ? ( - + ) : ( - + - + )}
    - {row.name} - {row.symbol.toUpperCase()} + {row.name} + {row.symbol.toUpperCase()}
    {hasCoinFetch && ( - )} @@ -329,9 +186,9 @@ const MarketTokens = (props) => { dataField: "current_price", text: t("table.price"), formatter: (cellContent, row) => ( - + {row.current_price} - + ), sort: true, }, @@ -401,9 +258,9 @@ const MarketTokens = (props) => { dataField: "market_cap", text: t("table.marketCap"), formatter: (cellContent, row) => ( - + {row.market_cap || "-"} - + ), sort: true, }, @@ -424,7 +281,7 @@ const MarketTokens = (props) => { setExpanded(expanded.concat(row.id)); } } else { - props.history.push(`/tools/market/${row.id}`); + props.history.push(`/market/${row.id}`); } }, }; @@ -446,14 +303,14 @@ const MarketTokens = (props) => { className="d-flex flex-row justify-content-lg-center justify-content-start flex-grow-1 mb-3 mb-lg-0 flex-lg-column" key={market} > - {market} ↗ - + = row.current_price @@ -469,7 +326,7 @@ const MarketTokens = (props) => { }) ) : (
    - {t("errors.default")} + {t("errors.default")}
    )}
    @@ -561,30 +418,30 @@ const MarketTokens = (props) => { return ( - - - - {t("featuredCoins")} - - - {t("allCoins")} - - - - + + + {t("featuredCoins")} + + + {t("allCoins")} + + + + - - + + diff --git a/src/components/MarketTokens/style.scss b/src/components/MarketTokens/style.scss new file mode 100644 index 00000000..ed4e5548 --- /dev/null +++ b/src/components/MarketTokens/style.scss @@ -0,0 +1,67 @@ +.explore__table { + border-collapse: separate; + border-spacing: 0 0; + margin-bottom: 0 !important; + + thead th { + background-color: rgba(#202020, 0.1); + color: #202020; + font-size: 0.875rem; + font-weight: 500; + text-overflow: ellipsis; + white-space: nowrap; + padding: 1.25rem 0.75rem; + min-height: 56px; + + .dark-mode & { + background-color: rgba(white, 0.1); + color: white; + } + + &:focus { + outline: none; + } + + &:first-child { + border-top-left-radius: 18px; + border-bottom-left-radius: 18px; + } + + &:last-child { + border-top-right-radius: 18px; + border-bottom-right-radius: 18px; + } + } + + th, + td { + vertical-align: middle !important; + + &:first-child { + padding: 1.25rem 1.375rem; + } + + &:last-child { + padding: 1.25rem 0.5rem; + } + } + + td { + cursor: pointer; + color: #202020; + + .dark-mode & { + color: white; + } + } + + tr:not(:last-child) { + td { + border-bottom: 1px solid rgba(#202020, 0.5) !important; + + .dark-mode & { + border-color: rgba(white, 0.5) !important; + } + } + } +} diff --git a/src/components/MarketTokens/styleds.tsx b/src/components/MarketTokens/styleds.tsx new file mode 100644 index 00000000..4c690041 --- /dev/null +++ b/src/components/MarketTokens/styleds.tsx @@ -0,0 +1,147 @@ +import styled from "styled-components"; +import { Col, Nav } from "react-bootstrap"; +import { InputGroup } from "../../components/Form"; + +export const Logo = styled.img` + width: 32px; + height: 32px; + border-radius: 32px; + background-color: ${({ theme }) => theme.text1}; + border: 2px solid ${({ theme }) => theme.text1}; + + @media (max-width: 991px) { + width: 24px; + height: 24px; + border-radius: 24px; + } +`; + +export const LogoContainer = styled.div` + width: 32px; + height: 32px; + border-radius: 32px; + + @media (max-width: 991px) { + width: 24px; + height: 24px; + border-radius: 24px; + } +`; + +export const CellText = styled.span` + font-weight: 500; + font-size: 0.875rem; + color: ${({ theme }) => theme.text1}; + + &.font-size-base { + font-size: 1rem; + } + + @media (max-width: 991px) { + font-weight: 700; + + &.label { + font-weight: 500; + } + } +`; + +export const SymbolText = styled.span` + font-weight: 500; + font-size: 1rem; + color: ${({ theme }) => theme.text1}; + + @media (max-width: 991px) { + font-size: 0.875rem; + font-weight: 400; + } +`; + +export const CustomTitle = styled.h4` + color: ${({ theme }) => theme.text1}; + font-size: 1.25rem; + + @media (max-width: 991px) { + font-size: 0.875rem; + } +`; + +export const CustomNav = styled(Nav)` + margin-left: -30px !important; + margin-right: -30px !important; + overflow: auto; + + @media (min-width: 768px) { + margin-left: -10px !important; + margin-right: -10px !important; + } +`; + +export const CustomNavItem = styled(Nav.Item)` + flex-grow: initial !important; + + padding: 0 10px 10px; + + @media (max-width: 767px) { + padding: 0 5px 10px; + } + + &:first-child { + @media (max-width: 767px) { + padding-left: 30px; + } + } + &:last-child { + @media (max-width: 767px) { + padding-right: 30px; + } + } +`; + +export const CustomNavLink = styled(Nav.Link)` + border-radius: 18px !important; + color: ${({ theme }) => theme.primary}; + background-color: ${({ theme }) => theme.primaryLight}; + white-space: nowrap; + padding: 14px 24px; + min-height: 56px; + font-weight: 500; + display: flex; + align-items: center; + justify-content: center; + + @media (max-width: 767px) { + padding: 6px 15px; + font-size: 1rem; + min-height: 32px; + border-radius: 12px !important; + } + + &:hover { + color: ${({ theme }) => theme.primary}; + } + + &.active { + color: ${({ theme }) => theme.text1}; + background-color: ${({ theme }) => theme.primary}; + } +`; + +export const HeaderCol = styled(Col)` + margin: -10px 0 20px; + + @media (min-width: 768px) { + margin-bottom: 25px; + } +`; + +export const CustomInputGroup = styled(InputGroup)` + margin-bottom: 30px; +`; + +export const MarketLink = styled.a` + color: ${({ theme }) => theme.text1}; + @media (max-width: 991px) { + flex-basis: 100px; + } +`; diff --git a/src/components/NFTCard/index.js b/src/components/NFTCard/index.tsx similarity index 51% rename from src/components/NFTCard/index.js rename to src/components/NFTCard/index.tsx index 120a0f05..50a8fce3 100644 --- a/src/components/NFTCard/index.js +++ b/src/components/NFTCard/index.tsx @@ -1,162 +1,41 @@ -import styled from "styled-components"; +import { useState } from "react"; +import toast from "react-hot-toast"; +import { useTranslation } from "react-i18next"; import SVG from "react-inlinesvg"; +import moment from "moment"; import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; -import Img from "../UI/Img"; +import { NFT_REFERRER_ACCOUNT } from "../../constants"; import { useActiveWeb3React } from "../../hooks"; -import moment from "moment"; +import useTheme from "../../hooks/useTheme"; import { toUnitAmount } from "../../lib/helper"; -import { useState } from "react"; -import Loading from "../Loading"; -import toast from "react-hot-toast"; import { useWalletModalToggle } from "../../state/application/hooks"; -import { NFT_REFERRER_ACCOUNT } from "../../constants"; -import { useIsDarkMode } from "../../state/user/hooks"; -import { useTranslation } from "react-i18next"; - -const Wrapper = styled.div` - text-decoration: none; - display: flex; - flex-direction: column; - background-color: ${({ theme }) => theme.bg2}; - border-radius: 12px; - padding: 10px; - height: calc(100% - 20px); - margin-bottom: 20px; - border: 1px solid ${({ theme }) => theme.text4}; - - @media (max-width: 991px) { - padding: 20px; - } - - &:hover, - &:focus, - &:active { - outline: none; - text-decoration: none; - box-shadow: none; - } - - > div { - display: flex; - flex-direction: column; - flex: 1; - } -`; - -const ImageContainer = styled.div` - width: 100%; - padding-top: 100%; - position: relative; - border-radius: 12px; - background-color: ${({ theme }) => theme.bg1}; - margin-bottom: 15px; - - @media (max-width: 991px) { - margin-bottom: 20px; - } -`; - -const Image = styled(Img)` - width: 100%; - height: 100%; - position: absolute; - top: 0; - left: 0; - border-radius: 12px; -`; - -const Content = styled.div` - padding: 0 5px; - display: flex; - flex-direction: column; - - @media (max-width: 991px) { - padding: 0; - } -`; - -const CollectionText = styled.span` - display: block; - color: ${({ theme }) => theme.text1}; - font-size: 0.875rem; - font-weight: 400; - margin-bottom: 5px; - - @media (max-width: 991px) { - margin-bottom: 11px; - } -`; - -const Name = styled.span` - color: ${({ theme }) => theme.text1}; - font-size: 1rem; - font-weight: 700; - line-height: 19px; - margin-bottom: 1rem; - - @media (max-width: 991px) { - font-size: 1.125rem; - line-height: 22px; - } -`; - -const DetailsList = styled.ul` - margin: 0 0 20px; - padding: 0; - display: flex; - flex-direction: column; - list-style: none; - - @media (max-width: 991px) { - flex-direction: row; - align-items: center; - justify-content: space-between; - margin: 0 0 40px; - } -`; - -const DetailsItem = styled.li` - display: flex; - align-items: center; - - &:not(:last-child) { - margin-bottom: 15px; - - @media (max-width: 991px) { - margin-bottom: 0; - } - } -`; - -const DetailsText = styled.span` - display: block; - color: ${({ theme }) => theme.text1}; - font-size: 0.875rem; - font-weight: ${({ bold }) => (bold ? 700 : 400)}; - padding-left: 11px; - - @media (max-width: 991px) { - padding-left: 6px; - } -`; +import Loading from "../Loading"; +import * as Styled from "./styleds"; + +export type Order = { + listingTime?: any; + asset?: any; + assetBundle?: any; + currentPrice?: any; + paymentTokenContract?: any; +}; -const StyledButton = styled.button` - height: 40px; - border-radius: 12px; - font-size: 0.875rem; -`; +export type NFTCardProps = { + seaport?: any; + order?: Order; + loading?: boolean; +}; -const NFTCard = (props) => { +const NFTCard = ({ loading, order, seaport }: NFTCardProps) => { const { account } = useActiveWeb3React(); const { t } = useTranslation(); + const theme = useTheme(); const toggleWalletModal = useWalletModalToggle(); - const [creatingOrder, refresh] = useState(false); - const { loading, order, seaport } = props; - const darkMode = useIsDarkMode(); - + const [creatingOrder, setCreatingOrder] = useState(false); const { listingTime, asset, assetBundle, currentPrice, paymentTokenContract } = order || {}; const price = !order ? "0" : toUnitAmount(currentPrice, paymentTokenContract); + // @ts-ignore const priceLabel = parseFloat(price).toLocaleString(undefined, { minimumSignificantDigits: 1 }); const owner = !order ? undefined : asset ? asset.owner : assetBundle.assets[0].owner; @@ -165,24 +44,25 @@ const NFTCard = (props) => { const timeLabel = moment(ts).local().fromNow(); const isOwner = !order ? false : account && account.toLowerCase() === owner.address.toLowerCase(); - const buyHandler = async (e) => { + const buyHandler = async (e: any) => { e.preventDefault(); if (account) { if (seaport) { try { - refresh(true); + const message = t("instantSwap.orderSubmitted"); + setCreatingOrder(true); await seaport.fulfillOrder({ order, accountAddress: account, referrerAddress: NFT_REFERRER_ACCOUNT, }); - toast.success(t("instantSwap.orderSubmitted")); + toast.success(message); } catch (error) { if (error?.hasOwnProperty("code") && error?.code !== 4001) { toast.error(error?.message || t("errors.default")); } } finally { - refresh(false); + setCreatingOrder(false); } } } else { @@ -191,12 +71,9 @@ const NFTCard = (props) => { }; return ( - - - + + + {loading ? ( { style={{ position: "absolute", top: 0, left: 0, borderRadius: 18 }} /> ) : ( - {props.name} + )} - - - + + + {loading ? ( ) : asset ? ( @@ -216,52 +93,52 @@ const NFTCard = (props) => { ) : ( assetBundle.assets[0].collection.name )} - - + + {loading ? : asset ? asset.name : assetBundle.name} - - - + + + {loading ? (
    - + - +
    ) : ( <> - + {priceLabel} {paymentTokenContract.symbol} - + )} -
    - + + {loading ? (
    - + - +
    ) : ( <> - {timeLabel} + {timeLabel} )} -
    -
    -
    + + + {loading ? ( ) : ( - { ) : ( "Buy Asset" )} - + )}
    -
    + ); }; diff --git a/src/components/NFTCard/styleds.tsx b/src/components/NFTCard/styleds.tsx new file mode 100644 index 00000000..68e5f156 --- /dev/null +++ b/src/components/NFTCard/styleds.tsx @@ -0,0 +1,137 @@ +import styled from "styled-components"; +import Img from "../UI/Img"; + +export const Wrapper = styled.div` + min-width: 240px; + max-width: 320px; + text-decoration: none; + display: flex; + flex-direction: column; + background-color: ${({ theme }) => theme.modalBG}; + border-radius: 12px; + padding: 10px; + height: calc(100% - 20px); + margin-bottom: 20px; + border: 1px solid ${({ theme }) => theme.text4}; + + @media (max-width: 991px) { + padding: 20px; + } + + &:hover, + &:focus, + &:active { + outline: none; + text-decoration: none; + box-shadow: none; + } + + > div { + display: flex; + flex-direction: column; + flex: 1; + } +`; + +export const ImageContainer = styled.div` + width: 100%; + padding-top: 100%; + position: relative; + border-radius: 12px; + background-color: ${({ theme }) => theme.bg1}; + margin-bottom: 15px; + + @media (max-width: 991px) { + margin-bottom: 20px; + } +`; + +export const Image = styled(Img)` + width: 100%; + height: 100%; + position: absolute; + top: 0; + left: 0; + border-radius: 12px; +`; + +export const Content = styled.div` + padding: 0 5px; + display: flex; + flex-direction: column; + + @media (max-width: 991px) { + padding: 0; + } +`; + +export const CollectionText = styled.span` + display: block; + color: ${({ theme }) => theme.text1}; + font-size: 0.875rem; + font-weight: 400; + margin-bottom: 5px; + + @media (max-width: 991px) { + margin-bottom: 11px; + } +`; + +export const Name = styled.span` + color: ${({ theme }) => theme.text1}; + font-size: 1rem; + font-weight: 700; + line-height: 19px; + margin-bottom: 1rem; + + @media (max-width: 991px) { + font-size: 1.125rem; + line-height: 22px; + } +`; + +export const DetailsList = styled.ul` + margin: 0 0 20px; + padding: 0; + display: flex; + flex-direction: column; + list-style: none; + + @media (max-width: 991px) { + flex-direction: row; + align-items: center; + justify-content: space-between; + margin: 0 0 40px; + } +`; + +export const DetailsItem = styled.li` + display: flex; + align-items: center; + + &:not(:last-child) { + margin-bottom: 15px; + + @media (max-width: 991px) { + margin-bottom: 0; + } + } +`; + +export const DetailsText = styled.span<{ bold?: boolean }>` + display: block; + color: ${({ theme }) => theme.text1}; + font-size: 0.875rem; + font-weight: ${({ bold }) => (bold ? 700 : 400)}; + padding-left: 11px; + + @media (max-width: 991px) { + padding-left: 6px; + } +`; + +export const StyledButton = styled.button` + height: 40px; + border-radius: 12px; + font-size: 0.875rem; +`; diff --git a/src/components/NumericalInput/index.tsx b/src/components/NumericalInput/index.tsx index 5ea4c8b5..4fe90612 100644 --- a/src/components/NumericalInput/index.tsx +++ b/src/components/NumericalInput/index.tsx @@ -18,7 +18,7 @@ const StyledInput = styled.input<{ outline: none; border: none; flex: 1 1 auto; - background-color: ${({ theme }) => theme.bg3}; + background-color: ${({ theme }) => theme.bg1}; font-size: 1rem; text-align: ${({ align }) => align && align}; white-space: nowrap; @@ -30,13 +30,10 @@ const StyledInput = styled.input<{ border-bottom-left-radius: ${({ reverse }) => (reverse ? "1.125rem" : `0`)}; border-top-left-radius: ${({ reverse }) => (reverse ? "1.125rem" : `0`)}; padding-right: 0.625rem; - padding-left: ${({ noBorder, reverse }) => (reverse ? "1.375rem" : noBorder ? "0.625rem" : `1.5rem`)}; + padding-left: 0.625rem; + // padding-left: ${({ noBorder, reverse }) => (reverse ? "1.375rem" : noBorder ? "0.625rem" : `1.5rem`)}; -webkit-appearance: textfield; - border-left: ${({ theme, noBorder }) => (noBorder ? "none" : `3px solid ${theme.modalBG}`)}; - - @media (min-width: 768px) { - padding-right: 4.5625rem; - } + // border-left: ${({ theme, noBorder }) => (noBorder ? "none" : `3px solid ${theme.modalBG}`)}; ::-webkit-search-decoration { -webkit-appearance: none; diff --git a/src/components/OfframpList/index.tsx b/src/components/OfframpList/index.tsx new file mode 100644 index 00000000..2f4e17f5 --- /dev/null +++ b/src/components/OfframpList/index.tsx @@ -0,0 +1,47 @@ +import { Row, Col } from "react-bootstrap"; +import SVG from "react-inlinesvg"; +import { FiatOffItem } from "../../typings"; +import * as Styled from "./styleds"; + +const OfframpList = ({ items }: { items: Array }) => { + return ( + + {items.map((item, index) => { + return ( + + + + + + + + {item.traits.map((trait, idx) => { + return ( + + + + + {trait.title} + + ); + })} + + + Go to {item.title} + + + + + ); + })} + + ); +}; + +export default OfframpList; diff --git a/src/components/OfframpList/styleds.tsx b/src/components/OfframpList/styleds.tsx new file mode 100644 index 00000000..fc4d50d9 --- /dev/null +++ b/src/components/OfframpList/styleds.tsx @@ -0,0 +1,73 @@ +import styled from "styled-components"; +import { Button } from "react-bootstrap"; +import Img from "../UI/Img"; + +export const InnerCard = styled.div` + display: flex; + flex-direction: column; + border-radius: 12px; + background-color: ${({ theme }) => theme.modalBG}; + padding: 10px; + align-items: stretch; + border: 1px solid ${({ theme }) => theme.borderColor}; + margin-bottom: 20px; +`; + +export const ItemImageContainer = styled.div` + position: relative; + width: 100%; + padding-top: 100%; + border-radius: 12px; + overflow: hidden; + background-color: ${({ theme }) => theme.modalBG}; +`; + +export const ItemImage = styled(Img)` + width: 100%; + height: 100%; + position: absolute; + top: 0; + left: 0; + right: 0; + object-fit: cover; +`; + +export const ItemContent = styled.div` + display: flex; + align-items: stretch; + flex-direction: column; +`; + +export const ItemList = styled.div` + flex: 1; + padding: 18px 4px; +`; + +export const ItemTrait = styled.div` + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 8px; +`; + +export const CustomButton = styled(Button)` + border-radius: 10px; + height: 48px; + min-height: 48px; +`; + +export const ListIcon = styled.span` + display: flex; + align-items: center; + justify-content: center; + width: 35px; + height: 32px; + color: ${({ theme }) => theme.primary}; +`; + +export const ListTitle = styled.span` + font-weight: 500; + color: ${({ theme }) => theme.text1}; + font-size: 0.875rem; + line-height: 1.125rem; +`; diff --git a/src/components/OverviewCard/index.tsx b/src/components/OverviewCard/index.tsx index e9f873af..f7a6347b 100644 --- a/src/components/OverviewCard/index.tsx +++ b/src/components/OverviewCard/index.tsx @@ -1,90 +1,5 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -import React from "react"; - import CurrencyText from "../CurrencyText"; -import styled from "styled-components"; - -const Card = styled.div` - background-color: ${({ theme }) => theme.modalBG}; - color: ${({ theme }) => theme.text1}; - display: flex; - align-items: center; - border-radius: 20px; - padding: 1.875rem; - margin-bottom: 20px; - cursor: pointer; - - @media (min-width: 768px) { - padding: 2rem; - } -`; - -const CardIcon = styled.div` - width: 56px; - height: 56px; - border-radius: 56px; - flex-basis: 56px; - border: 2px solid ${({ theme }) => theme.text1}; - overflow: hidden; - display: flex; - align-items: center; - justify-content: center; - - @media (min-width: 768px) { - width: 66px; - height: 66px; - border-radius: 66px; - flex-basis: 66px; - } -`; - -const CardImage = styled.img` - width: 100%; - height: 100%; - object-fit: cover; -`; - -const CardBody = styled.div` - display: flex; - align-items: center; - padding: 0; -`; - -const CardContent = styled.div` - display: flex; - flex-direction: column; - flex-wrap: wrap; - justify-content: center; - padding-left: 20px; - - @media (min-width: 768px) { - padding-left: 30px; - } -`; - -const Title = styled.span` - font-weight: 500; - font-size: 0.875rem; - color: ${({ theme }) => theme.text1}; - display: block; - margin-bottom: 0.25rem; - - @media (min-width: 768px) { - font-size: 1rem; - } -`; - -const Value = styled.span` - font-weight: 700; - font-size: 1.25rem; - color: ${({ theme }) => theme.text1}; - display: block; - margin: 0; - - @media (min-width: 768px) { - font-size: 1.75rem; - } -`; +import * as Styled from "./styleds"; function OverviewCard({ className, @@ -106,24 +21,20 @@ function OverviewCard({ description?: string; }) { return ( - <> - - {/* begin::Body */} - - - - - - - {title} - - {value} - - - - {/* end::Body */} - - + + + + + + + + {title} + + {value} + + + + ); } diff --git a/src/components/OverviewCard/styleds.tsx b/src/components/OverviewCard/styleds.tsx new file mode 100644 index 00000000..0b50fbca --- /dev/null +++ b/src/components/OverviewCard/styleds.tsx @@ -0,0 +1,83 @@ +import styled from "styled-components"; + +export const Card = styled.div` + background-color: ${({ theme }) => theme.modalBG}; + color: ${({ theme }) => theme.text1}; + display: flex; + align-items: center; + border-radius: 20px; + padding: 1.875rem; + margin-bottom: 20px; + cursor: pointer; + + @media (min-width: 768px) { + padding: 2rem; + } +`; + +export const CardIcon = styled.div` + width: 56px; + height: 56px; + border-radius: 56px; + flex-basis: 56px; + border: 2px solid ${({ theme }) => theme.text1}; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; + + @media (min-width: 768px) { + width: 66px; + height: 66px; + border-radius: 66px; + flex-basis: 66px; + } +`; + +export const CardImage = styled.img` + width: 100%; + height: 100%; + object-fit: cover; +`; + +export const CardBody = styled.div` + display: flex; + align-items: center; + padding: 0; +`; + +export const CardContent = styled.div` + display: flex; + flex-direction: column; + flex-wrap: wrap; + justify-content: center; + padding-left: 20px; + + @media (min-width: 768px) { + padding-left: 30px; + } +`; + +export const Title = styled.span` + font-weight: 500; + font-size: 0.875rem; + color: ${({ theme }) => theme.text1}; + display: block; + margin-bottom: 0.25rem; + + @media (min-width: 768px) { + font-size: 1rem; + } +`; + +export const Value = styled.span` + font-weight: 700; + font-size: 1.25rem; + color: ${({ theme }) => theme.text1}; + display: block; + margin: 0; + + @media (min-width: 768px) { + font-size: 1.75rem; + } +`; diff --git a/src/components/Page/index.js b/src/components/Page/index.js deleted file mode 100644 index 0473ae08..00000000 --- a/src/components/Page/index.js +++ /dev/null @@ -1,83 +0,0 @@ -import { Row, Col } from "react-bootstrap"; -import styled from "styled-components"; - -import Header from "../Header"; -import Footer from "../Footer"; -import {useEffect} from "react"; -import { withRouter } from "react-router"; -import { useActiveWeb3React } from "../../hooks"; -import WrongNetwork from "../WrongNetwork"; - -const PageContainer = styled.div` - background-color: ${({ hasBg, theme }) => (hasBg ? theme.modalBG : "transparent")}; - padding-top: 112px; - - @media (min-width: 768px) { - background-color: transparent; - } -`; - -const PageContent = styled.div` - @media (min-width: 768px) { - margin-left: ${({ size }) => (size === "lg" ? "-60px" : size === "sm" ? "60px" : "0")}; - margin-right: ${({ size }) => (size === "lg" ? "-60px" : size === "sm" ? "60px" : "0")}; - } - @media (min-width: 1400px) { - margin-left: ${({ size }) => - size === "xl" ? "-110px" : size === "lg" ? "-60px" : size === "sm" ? "60px" : "0"}; - margin-right: ${({ size }) => - size === "xl" ? "-110px" : size === "lg" ? "-60px" : size === "sm" ? "60px" : "0"}; - } -`; - -const Title = styled.h1` - font-size: 1.25rem; - font-weight: 700; - color: ${({ theme }) => theme.text1}; - margin-top: 26px; - margin-bottom: ${({ morePadding }) => (morePadding ? "50px" : "20px")} !important; - - @media (min-width: 768px) { - font-size: 2.5rem; - margin-top: 86px; - margin-bottom: 50px !important; - } -`; - -const Page = (props) => { - const { chainId } = useActiveWeb3React(); - - const notNetworkSensitive = props?.notNetworkSensitive || false - - useEffect(() => { - document.body.scrollTo(0, 0); - }, []); - - return ( - <> -
    - - - {props.title && ( - - - {props.title} - - - )} - {notNetworkSensitive - ? props.children - : (!chainId || (chainId === 1)) - ? props.children - : ( - - ) - } - -
    - - - ); -}; - -export default withRouter(Page); diff --git a/src/components/Page/index.tsx b/src/components/Page/index.tsx new file mode 100644 index 00000000..9f7c7f3c --- /dev/null +++ b/src/components/Page/index.tsx @@ -0,0 +1,42 @@ +import { PropsWithChildren, useEffect } from "react"; +import { useActiveWeb3React } from "../../hooks"; +import Header from "../Header"; +import Footer from "../Footer"; +import WrongNetwork from "../WrongNetwork"; +import { PageContainer, Title } from "./styleds"; + +export type PageProps = { + title?: string; + fluid?: boolean; + networkSensitive?: boolean; + hasBg?: boolean; +}; + +const Page = ({ + title, + fluid = false, + networkSensitive = false, + hasBg = false, + children, +}: PropsWithChildren) => { + const { chainId } = useActiveWeb3React(); + + useEffect(() => { + document.body.scrollTo(0, 0); + }, []); + + return ( +
    +
    + +
    + {title && {title}} + {networkSensitive ? !chainId || chainId === 1 ? children : : children} +
    +
    +
    +
    + ); +}; + +export default Page; diff --git a/src/components/Page/styleds.tsx b/src/components/Page/styleds.tsx new file mode 100644 index 00000000..2bf4eed5 --- /dev/null +++ b/src/components/Page/styleds.tsx @@ -0,0 +1,21 @@ +import styled from "styled-components"; + +export const PageContainer = styled.div<{ hasBg: boolean }>` + background-color: ${({ hasBg, theme }) => (hasBg ? theme.modalBG : "transparent")}; + padding-top: 136px; + + @media (min-width: 768px) { + background-color: transparent; + } +`; + +export const Title = styled.h1` + font-size: 1.25rem; + font-weight: 700; + color: ${({ theme }) => theme.text1}; + margin-top: 0; + + @media (min-width: 768px) { + font-size: 2.5rem; + } +`; diff --git a/src/components/Platforms/index.tsx b/src/components/Platforms/index.tsx new file mode 100644 index 00000000..b5623d7b --- /dev/null +++ b/src/components/Platforms/index.tsx @@ -0,0 +1,50 @@ +import { Row, Col } from "react-bootstrap"; +import SVG from "react-inlinesvg"; +import { useTranslation } from "react-i18next"; + +import LayoutBlockIcon from "../../assets/images/global/layout-block.svg"; +import Loading from "../Loading"; +import OverviewCard from "../OverviewCard"; +import * as Styled from "./styleds"; + +export type PlatformsProps = { + balance: Array; + onSelectPlatform: (T: string) => void; + loading?: boolean; +}; + +export default function Platforms({ balance, onSelectPlatform, loading }: PlatformsProps) { + const { t } = useTranslation(); + + if (loading) { + return ; + } + + if (balance.length === 0) { + return ( + + +
    {t("errors.noPlatform")}
    + {t("errors.noPlatformDesc")} +
    + ); + } + + return ( + + {balance.map((b, index: number) => { + return ( + + onSelectPlatform(b.metadata.name)} + className={"mb-3"} + title={b.metadata.name} + value={b.total.toFixed(4)} + image={b.metadata.logo.href} + /> + + ); + })} + + ); +} diff --git a/src/components/Platforms/styleds.tsx b/src/components/Platforms/styleds.tsx new file mode 100644 index 00000000..592fdbff --- /dev/null +++ b/src/components/Platforms/styleds.tsx @@ -0,0 +1,12 @@ +import styled from "styled-components"; + +export const Wrap = styled.div` + background-color: ${({ theme }) => theme.modalBG}; + color: ${({ theme }) => theme.text1}; + display: flex; + align-items: center; + border-radius: 18px; + justify-content: center; + flex-direction: column; + padding: 2.5rem; +`; diff --git a/src/components/PoolInput/index.tsx b/src/components/PoolInput/index.tsx index 6fc1a249..d3eff9f3 100644 --- a/src/components/PoolInput/index.tsx +++ b/src/components/PoolInput/index.tsx @@ -23,7 +23,7 @@ const CurrencySelect = styled.button<{ selected: boolean }>` height: 56px; font-size: 0.875rem; font-weight: 500; - background-color: ${({ theme }) => theme.bg3}; + background-color: ${({ theme }) => theme.bg1}; color: ${({ theme }) => theme.text1}; border-bottom-left-radius: 18px; border-top-left-radius: 18px; @@ -133,7 +133,7 @@ const StyledBalanceMax = styled.button` :hover { background-color: ${({ theme }) => theme.primary}; - color: ${({ theme }) => theme.bg2}; + color: ${({ theme }) => theme.bg1}; } :focus { diff --git a/src/components/ProgressSteps/index.tsx b/src/components/ProgressSteps/index.tsx index 069f457e..7e79f14e 100644 --- a/src/components/ProgressSteps/index.tsx +++ b/src/components/ProgressSteps/index.tsx @@ -14,7 +14,7 @@ const Circle = styled.div<{ confirmed?: boolean; disabled?: boolean }>` min-width: 30px; min-height: 30px; background-color: ${({ theme, confirmed, disabled }) => - disabled ? theme.bg2 : confirmed ? theme.green1 : theme.primary}; + disabled ? theme.bg1 : confirmed ? theme.green1 : theme.primary}; border-radius: 0.5rem; color: ${({ theme }) => theme.text1}; display: flex; @@ -36,9 +36,9 @@ const Connector = styled.div<{ prevConfirmed?: boolean; disabled?: boolean }>` background: linear-gradient( 90deg, ${({ theme, prevConfirmed, disabled }) => - disabled ? theme.bg2 : transparentize(0.5, prevConfirmed ? theme.green1 : theme.primary)} + disabled ? theme.bg1 : transparentize(0.5, prevConfirmed ? theme.green1 : theme.primary)} 0%, - ${({ theme, prevConfirmed, disabled }) => (disabled ? theme.bg2 : prevConfirmed ? theme.primary : theme.bg2)} + ${({ theme, prevConfirmed, disabled }) => (disabled ? theme.bg1 : prevConfirmed ? theme.primary : theme.bg1)} 80% ); opacity: 0.6; diff --git a/src/components/RemoveLiquidityModal/index.tsx b/src/components/RemoveLiquidityModal/index.tsx index 1593f58f..25b45387 100644 --- a/src/components/RemoveLiquidityModal/index.tsx +++ b/src/components/RemoveLiquidityModal/index.tsx @@ -1,6 +1,5 @@ import React, { useCallback, useEffect, useState, useMemo } from "react"; import { useSelector, useDispatch } from "react-redux"; -import SVG from "react-inlinesvg"; import { ETHER } from "@uniswap/sdk"; import { Button, Row, Col } from "react-bootstrap"; import { useActiveWeb3React } from "../../hooks"; @@ -43,13 +42,7 @@ import PoolInput from "../PoolInput"; import { usePoolBalance } from "../../state/pools/hooks"; import ERC20_ABI from "../../constants/abis/erc20.json"; import { BigNumber } from "@ethersproject/bignumber"; -import { - AccountState, - AccountStateContent, - AccountStateDesc, - AccountStateTitle, - PriceTopbar, -} from "../AddLiquidityModal/uniswap"; +import { PriceTopbar } from "../AddLiquidityModal/uniswap"; import { useWalletModalToggle } from "../../state/application/hooks"; import { LightCard } from "../StyledCards"; import { PlatformTitle } from "../AddLiquidityModal"; @@ -379,7 +372,7 @@ export default function RemoveLiquidityModal({ history }: RouteComponentProps) { Ethereum Output - + + + + {Object.keys(routes).map((key, index) => { + // @ts-ignore + const r = routes[key]; + if (r.hasOwnProperty("path")) { + return ( + + {t(`menu.${r.title}`)} + + ); + } else { + return ; + } + })} + + + + ); +}; + +export default SideDrawer; diff --git a/src/components/SideDrawer/styleds.tsx b/src/components/SideDrawer/styleds.tsx new file mode 100644 index 00000000..bb7e393a --- /dev/null +++ b/src/components/SideDrawer/styleds.tsx @@ -0,0 +1,60 @@ +import styled from "styled-components"; +import { Link } from "react-router-dom"; + +export const Backdrop = styled.div<{ open: boolean }>` + position: fixed; + right: 0; + left: 0; + top: 0; + bottom: 0; + background-color: rgba(11, 15, 50, 0.8); + opacity: ${({ open }) => (open ? "1" : "0")}; + visibility: ${({ open }) => (open ? "visible" : "hidden")}; + z-index: 100001; + transition: 0.5s ease all; +`; + +export const Wrapper = styled.div<{ open: boolean }>` + position: fixed; + right: 0; + top: 0; + bottom: 0; + overflow: auto; + width: 260px; + background-color: ${({ theme }) => theme.modalBG}; + transition: 0.5s ease all; + transform: ${({ open }) => (open ? "translateX(0)" : "translateX(100%)")}; + z-index: 100001; +`; + +export const Content = styled.div` + display: flex; + flex-direction: column; +`; + +export const Header = styled.div` + padding: 1.5rem 1rem; + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid ${({ theme }) => theme.borderColor2}; +`; + +export const LinkItem = styled(Link)` + padding: 1.5rem 1.25rem; + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid ${({ theme }) => theme.borderColor}; + color: ${({ theme }) => theme.text1}; + font-size: 0.875rem; + font-weight: 400; + + &:hover, + &:focus, + &:active { + outline: none; + text-decoration: none; + color: ${({ theme }) => theme.text2}; + } +`; diff --git a/src/components/SnapshotSpaceCard/index.tsx b/src/components/SnapshotSpaceCard/index.tsx new file mode 100644 index 00000000..d3ca890b --- /dev/null +++ b/src/components/SnapshotSpaceCard/index.tsx @@ -0,0 +1,55 @@ +import { isMobile } from "react-device-detect"; +import SVG from "react-inlinesvg"; +import Skeleton from "react-loading-skeleton"; +import PinStartIcon from "../../assets/images/governance/pin-start.svg"; +import { useLogo } from "../../state/governance/hooks"; +import { SnapshotSpaceProps } from "../../typings"; +import * as Styled from "./styleds"; + +export type SnapshotSpaceCardProps = { + space: SnapshotSpaceProps; + id: string; + pinned?: boolean; + symbolIndex: string; + loading?: boolean; +}; + +const SnapshotSpaceCard = ({ space, id, pinned = false, symbolIndex, loading = false }: SnapshotSpaceCardProps) => { + const LogoURL = useLogo(id, symbolIndex); + + if (loading) { + return ( + + + + + + + + + + + ); + } + + return ( + + {pinned && ( + + + + )} + + + {space.name} + {space.symbol} + + ); +}; + +export default SnapshotSpaceCard; diff --git a/src/components/SpaceCard/index.js b/src/components/SnapshotSpaceCard/styleds.tsx similarity index 57% rename from src/components/SpaceCard/index.js rename to src/components/SnapshotSpaceCard/styleds.tsx index f5b2e6a2..f3394557 100644 --- a/src/components/SpaceCard/index.js +++ b/src/components/SnapshotSpaceCard/styleds.tsx @@ -1,14 +1,8 @@ -import React from "react"; import styled from "styled-components"; -import { isMobile } from "react-device-detect"; import { Link } from "react-router-dom"; -import SVG from "react-inlinesvg"; -import Skeleton from "react-loading-skeleton"; import Img from "../UI/Img"; -import { useLogo } from "../../state/governance/hooks"; - -const Wrapper = styled(Link)` +export const Wrapper = styled(Link)<{ loading: boolean }>` background-color: ${({ theme }) => theme.modalBG}; border: 1px solid transparent; padding: 50px; @@ -28,7 +22,7 @@ const Wrapper = styled(Link)` } &:hover { - background-color: ${({ theme, loading }) => !loading && theme.bg2}; + background-color: ${({ theme, loading }) => !loading && theme.bg1}; text-decoration: none; border-color: ${({ theme }) => theme.primary}; } @@ -39,7 +33,7 @@ const Wrapper = styled(Link)` } `; -const Logo = styled(Img)` +export const Logo = styled(Img)` width: 100px; height: 100px; border-radius: 100px; @@ -53,7 +47,7 @@ const Logo = styled(Img)` } `; -const Title = styled.h3` +export const Title = styled.h3` font-size: 1.25rem; font-weight: 700; color: ${({ theme }) => theme.text1}; @@ -66,7 +60,7 @@ const Title = styled.h3` margin-bottom: 0.875rem; } `; -const CurrencyName = styled.span` +export const CurrencyName = styled.span` font-size: 1rem; font-weight: 500; color: ${({ theme }) => theme.text1}; @@ -78,7 +72,7 @@ const CurrencyName = styled.span` } `; -const StarWrapper = styled.div` +export const StarWrapper = styled.div` position: absolute; right: 20px; top: 20px; @@ -101,32 +95,3 @@ const StarWrapper = styled.div` } } `; - -const SpaceCard = (props) => { - const LogoURL = useLogo(props.id, props.symbolIndex); - return ( - - {!props.loading && props.pinned && ( - - - - )} - {props.loading ? ( - - ) : ( - - )} - {props.loading ? <Skeleton height={20} width={isMobile ? 80 : 120} /> : props.name} - - {props.loading ? : props.symbol} - - - ); -}; - -export default SpaceCard; diff --git a/src/components/SnapshotSpaceList/index.tsx b/src/components/SnapshotSpaceList/index.tsx new file mode 100644 index 00000000..ebb7000c --- /dev/null +++ b/src/components/SnapshotSpaceList/index.tsx @@ -0,0 +1,72 @@ +import { Row, Col } from "react-bootstrap"; +import { SnapshotSpaceProps } from "../../typings"; +import SnapshotSpaceCard from "../SnapshotSpaceCard"; + +export type SnapshotSpaceListProps = { + items: Array; + loading: boolean | any; +}; + +export type SnapshotSpaceItemProps = { + space: SnapshotSpaceProps; + key: string; + pinned: boolean; +}; + +const SnapshotSpaceList = ({ loading, items }: SnapshotSpaceListProps) => { + // TODO: implement InfiniteScroll + const renderedItems = items.slice(0, 100); + + if (loading) { + return ( + + {[...Array(12)].map((value, i) => { + return ( + + + + ); + })} + + ); + } + + if (items.length === 0) { + return

    No results

    ; + } + + return ( + + {renderedItems.map((item, i) => { + return ( + + + + ); + })} + {items.length > 100 && ( + +

    Use the Search for more results

    + + )} +
    + ); +}; + +export default SnapshotSpaceList; diff --git a/src/components/Socials/index.js b/src/components/Socials/index.js deleted file mode 100644 index 5dee8a92..00000000 --- a/src/components/Socials/index.js +++ /dev/null @@ -1,60 +0,0 @@ -import SVG from "react-inlinesvg"; -import styled from "styled-components"; - -import Github from '../../assets/images/socials/github.svg'; -import Youtube from '../../assets/images/socials/youtube.svg'; -import Telegram from '../../assets/images/socials/telegram.svg'; -import Twitter from '../../assets/images/socials/twitter.svg'; -import "./styles.scss"; - -const socials = [ - { - name: 'Twitter', - image: Twitter, - url: "https://twitter.com/octofinance", - }, - { - name: 'Telegram', - image: Telegram, - url: "https://t.me/OctoFi", - }, - { - name: 'Youtube', - image: Youtube, - url: "https://www.youtube.com/channel/UCQ8TelmjLpFKQAsZCXIs5Tw", - }, - { - name: 'Github', - image: Github, - url: "https://github.com/octofi", - }, -]; - -const StyledLink = styled.a` - color: ${({ theme }) => theme.text1}; -`; - -const Socials = (props) => { - return ( -
    -
      - {socials.map((social, i) => { - return ( -
    • - - - -
    • - ); - })} -
    -
    - ); -}; - -export default Socials; diff --git a/src/components/Socials/index.tsx b/src/components/Socials/index.tsx new file mode 100644 index 00000000..824726dc --- /dev/null +++ b/src/components/Socials/index.tsx @@ -0,0 +1,29 @@ +import SVG from "react-inlinesvg"; + +import { SocialLink } from "../../typings"; +import { socials } from "../../data/socials"; +import * as Styled from "./styleds"; + +const Socials = () => { + if (!socials || socials.length === 0) { + return null; + } + + return ( +
    + + {socials.map((social: SocialLink, i) => { + return ( + + + + + + ); + })} + +
    + ); +}; + +export default Socials; diff --git a/src/components/Socials/styleds.tsx b/src/components/Socials/styleds.tsx new file mode 100644 index 00000000..312b8d81 --- /dev/null +++ b/src/components/Socials/styleds.tsx @@ -0,0 +1,22 @@ +import styled from "styled-components"; + +export const SocialList = styled.ul` + list-style: none; + padding: 0; + display: flex; + align-items: center; + width: 100%; + margin: 0; +`; + +export const SocialItem = styled.li` + padding-bottom: 10px; + + &:not(:last-child) { + margin-right: 2.25rem; + } +`; + +export const SocialLink = styled.a` + color: ${({ theme }) => theme.text1}; +`; diff --git a/src/components/Socials/styles.scss b/src/components/Socials/styles.scss deleted file mode 100644 index ceff2c90..00000000 --- a/src/components/Socials/styles.scss +++ /dev/null @@ -1,20 +0,0 @@ -.socials { - list-style: none; - padding: 0; - display: flex; - align-items: center; - width: 100%; - margin: 0 0 44px; - - @media (max-width: 991px) { - margin-bottom: 32px; - } - - &__item { - padding-bottom: 10px; - - &:not(:last-child) { - margin-right: 2.25rem; - } - } -} diff --git a/src/components/SpotInputPanel/index.js b/src/components/SpotInputPanel/index.js index 16c499a6..7dedba85 100644 --- a/src/components/SpotInputPanel/index.js +++ b/src/components/SpotInputPanel/index.js @@ -90,7 +90,7 @@ const CurrencySelect = styled.button` height: 48px; font-size: 0.875rem; font-weight: 500; - background-color: ${({ theme }) => theme.bg3}; + background-color: ${({ theme }) => theme.bg1}; color: ${({ theme }) => theme.text1}; border-bottom-left-radius: 18px; border-top-left-radius: 18px; diff --git a/src/components/StyledCards/index.tsx b/src/components/StyledCards/index.tsx index bd132802..18c7b50f 100644 --- a/src/components/StyledCards/index.tsx +++ b/src/components/StyledCards/index.tsx @@ -24,11 +24,11 @@ export const LightGreyCard = styled(StyledCard)` `; export const GreyCard = styled(StyledCard)` - background-color: ${({ theme }) => theme.bg2}; + background-color: ${({ theme }) => theme.bg1}; `; export const OutlineCard = styled(StyledCard)` - border: 1px solid ${({ theme }) => theme.bg2}; + border: 1px solid ${({ theme }) => theme.bg1}; `; export const YellowCard = styled(StyledCard)` diff --git a/src/components/SwapHeader/index.js b/src/components/SwapHeader/index.js new file mode 100644 index 00000000..601fc271 --- /dev/null +++ b/src/components/SwapHeader/index.js @@ -0,0 +1,54 @@ +import { useContext, useRef } from "react"; +import { useTranslation } from "react-i18next"; +import { ThemeContext } from "styled-components"; +import { Text } from "rebass"; + +import { useModalOpen, useToggleUniswapSettingsMenu } from "../../state/application/hooks"; +import { ApplicationModal } from "../../state/application/actions"; +import { useUserSlippageTolerance, useUserTransactionTTL } from "../../state/user/hooks"; +import { useOnClickOutside } from "../../hooks/useOnClickOutside"; +import { AutoColumn } from "../Column"; +import TransactionSettings from "../TransactionSettings"; +import { Header, CardTitle, StyledMenuIcon, StyledMenu, MenuFlyout, StyledMenuButton } from "./styles"; + +const SwapHeader = () => { + const node = useRef(); + const { t } = useTranslation(); + const open = useModalOpen(ApplicationModal.UNISWAPSETTINGS); + const toggle = useToggleUniswapSettingsMenu(); + + const theme = useContext(ThemeContext); + const [userSlippageTolerance, setUserslippageTolerance] = useUserSlippageTolerance(); + + const [ttl, setTtl] = useUserTransactionTTL(); + + useOnClickOutside(node, open ? toggle : undefined); + + return ( +
    + {t("convert")} + + + + + {open && ( + + + + Transaction Settings + + + + + )} + +
    + ); +}; + +export default SwapHeader; diff --git a/src/components/SwapHeader/styles.ts b/src/components/SwapHeader/styles.ts new file mode 100644 index 00000000..24d9bbd5 --- /dev/null +++ b/src/components/SwapHeader/styles.ts @@ -0,0 +1,79 @@ +import styled from "styled-components"; +import { Settings } from "react-feather"; + +export const Header = styled.div` + margin-bottom: 16px; +`; + +export const CardTitle = styled.h3` + font-weight: 700; + font-size: 1.25rem; + margin-bottom: 0; +`; + +export const StyledMenuIcon = styled(Settings)` + height: 20px; + width: 20px; + + > * { + stroke: ${({ theme }) => theme.text2}; + } + + :hover { + opacity: 0.7; + } +`; + +export const StyledMenu = styled.div` + margin-left: 0.5rem; + display: flex; + justify-content: center; + align-items: center; + position: relative; + border: none; + text-align: left; +`; + +export const MenuFlyout = styled.span` + min-width: 20.125rem; + background-color: ${({ theme }) => theme.bg1}; + box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), + 0px 24px 32px rgba(0, 0, 0, 0.01); + border: 1px solid ${({ theme }) => theme.text4}; + border-radius: 12px; + display: flex; + flex-direction: column; + font-size: 1rem; + position: absolute; + top: 3rem; + right: 0rem; + z-index: 100; + + ${({ theme }) => theme.mediaWidth.upToMedium` + min-width: 18.125rem; + `}; +`; + +export const StyledMenuButton = styled.button` + position: relative; + width: 100%; + height: 100%; + border: none; + background-color: transparent; + margin: 0; + padding: 0; + height: 35px; + + padding: 0.15rem 0.5rem; + border-radius: 0.5rem; + + :hover, + :focus { + cursor: pointer; + outline: none; + } + + svg { + margin-top: 2px; + } +`; diff --git a/src/components/SwapInputPanel/index.js b/src/components/SwapInputPanel/index.js deleted file mode 100644 index b4fc85e1..00000000 --- a/src/components/SwapInputPanel/index.js +++ /dev/null @@ -1,243 +0,0 @@ -import React, { useCallback, useEffect, useState } from "react"; -import styled from "styled-components"; -import { RowBetween } from "../Row"; -import { Input as NumericalInput } from "../NumericalInput"; -import { ReactComponent as DropDown } from "../../assets/images/dropdown.svg"; -import SwapSelectModal from "../SwapSelectModal"; -import { ETHER, Token } from "@uniswap/sdk"; -import { useActiveWeb3React } from "../../hooks"; -import { useCurrencyBalance } from "../../state/wallet/hooks"; -import { useTranslation } from "react-i18next"; - -const InputRow = styled.div` - ${({ theme }) => theme.flexRowNoWrap}; - align-items: center; - - padding: 1rem 0 1.25rem; - - @media (min-width: 768px) { - padding-top: 0.75rem; - } -`; - -const Label = styled.span` - color: ${({ theme }) => theme.text1}; - font-weight: 400; - font-size: 0.875rem; - padding: 0; - - @media (min-width: 768px) { - padding: 0 1.5rem; - } -`; - -const Balance = styled.span` - color: ${({ theme }) => theme.text1}; - font-weight: 400; - font-size: 0.75rem; - padding: 0; - margin-bottom: -6px; - - @media (min-width: 768px) { - font-size: 0.875rem; - font-weight: 500; - margin-bottom: 0; - } -`; - -const InputContainer = styled.div` - position: relative; - display: flex; - align-items: center; - flex-wrap: nowrap; - flex: 1; -`; - -const Logo = styled.img` - width: ${({ size }) => size || "100%"}; - height: ${({ size }) => size || "100%"}; - border-radius: ${({ size }) => size || "100%"}; - border: 2px solid ${({ theme }) => theme.text1}; - color: ${({ theme }) => theme.text1}; - margin-right: ${({ margin }) => (margin ? "8px" : 0)}; -`; - -const LabelRow = styled.div` - ${({ theme }) => theme.flexRowNoWrap}; - align-items: center; - color: ${({ theme }) => theme.text1}; - font-size: 0.75rem; - line-height: 1rem; - padding: 0; -`; - -const StyledDropDown = styled(DropDown)` - margin: 0 0.25rem 0 0.5rem; - height: 35%; - - path { - stroke: ${({ selected, theme }) => theme.text1}; - stroke-width: 1.5px; - } -`; - -const Aligner = styled.span` - display: flex; - align-items: center; - justify-content: space-between; -`; - -const InputPanel = styled.div` - ${({ theme }) => theme.flexColumnNoWrap}; - position: relative; - border-radius: 0.42rem; -`; - -const StyledTokenName = styled.span` - margin-right: auto; - padding-left: ${({ active }) => (active ? "0.625rem" : "0")}; - font-size: 1rem; - - @media (min-width: 768px) { - padding-left: ${({ active }) => (active ? "0.75rem" : "0")}; - } -`; - -const CurrencySelect = styled.button` - align-items: center; - height: 56px; - font-size: 0.875rem; - font-weight: 500; - background-color: ${({ theme }) => theme.bg3}; - color: ${({ theme }) => theme.text1}; - border-bottom-left-radius: 18px; - border-top-left-radius: 18px; - box-shadow: none; - outline: none; - cursor: pointer; - user-select: none; - border: none; - padding: 0.875rem 0.625rem; - min-width: 116px; - width: 116px; - - @media (min-width: 768px) { - min-width: 178px; - width: 178px; - padding: 0.625rem 1.5rem; - font-size: 1rem; - font-weight: 700; - } - - :focus, - :hover { - background-color: ${({ theme }) => theme.bg4}; - outline: none; - } -`; - -export default function SwapInputPanel({ - value, - onUserInput, - label = "Input", - onSelect, - disable = false, - selected, - hideInput = false, - id, - currencies, - type, - disableCurrencySelect = false, - onChangeBalance = (balance) => balance, -}) { - let currency = undefined; - const { t } = useTranslation(); - if (selected) { - currency = new Token(selected.chainId, selected.address, selected.decimals, selected.symbol, selected.name); - } - const [modalOpen, setModalOpen] = useState(false); - const { account } = useActiveWeb3React(); - const selectedCurrencyBalance = useCurrencyBalance( - account ?? undefined, - selected && selected.symbol === "ETH" ? ETHER : currency - ); - - useEffect(() => { - onChangeBalance(selectedCurrencyBalance); - }, [selected]); - - const handleDismissSearch = useCallback(() => { - setModalOpen(false); - }, [setModalOpen]); - - return ( - -
    - {!hideInput && ( - - - - - {account && ( - - {!!currency && selectedCurrencyBalance - ? t("balance", { balanceInput: selectedCurrencyBalance?.toSignificant(6) }) - : " -"} - - )} - - - )} - - { - if (!disableCurrencySelect) { - setModalOpen(true); - } - }} - > - - {selected ? : null} - - {(selected && selected?.symbol && selected?.symbol.length > 20 - ? selected?.symbol.slice(0, 4) + - "..." + - selected?.symbol.slice(selected?.symbol.length - 5, selected?.symbol.length) - : selected?.symbol) || t("selectToken")} - - {!disableCurrencySelect && } - - - - {!hideInput && ( - - { - onUserInput(val, type, selectedCurrencyBalance); - }} - /> - - )} - -
    - - {!disableCurrencySelect && onSelect && ( - - )} -
    - ); -} diff --git a/src/components/SwapSelectModal/CurrencyList.js b/src/components/SwapSelectModal/CurrencyList.js index fbac0d17..d54e1842 100644 --- a/src/components/SwapSelectModal/CurrencyList.js +++ b/src/components/SwapSelectModal/CurrencyList.js @@ -4,6 +4,7 @@ import { Text } from "rebass"; import styled from "styled-components"; import Column from "../Column"; import { RowBetween } from "../Row"; +import Img from "../UI/Img"; export const MenuItem = styled(RowBetween)` padding: 4px 20px; @@ -14,24 +15,23 @@ export const MenuItem = styled(RowBetween)` cursor: ${({ disabled }) => !disabled && "pointer"}; pointer-events: ${({ disabled }) => disabled && "none"}; :hover { - background-color: ${({ theme, disabled }) => !disabled && theme.bg2}; + background-color: ${({ theme, disabled }) => !disabled && theme.bg1}; } opacity: ${({ disabled, selected }) => (disabled || selected ? 0.5 : 1)}; `; -const Logo = styled.img` +const Logo = styled(Img)` + border-radius: 50%; + margin-right: ${({ margin }) => (margin ? "8px" : 0)}; width: ${({ size }) => (size ? `${size}px` : "24px")}; height: ${({ size }) => (size ? `${size}px` : "24px")}; - border-radius: ${({ size }) => (size ? `${size}px` : "24px")}; - border: 2px solid ${({ theme }) => theme.text1}; - background-color: ${({ theme }) => theme.text1}; - margin-right: ${({ margin }) => (margin ? "8px" : 0)}; @media (max-width: 1199px) { width: 24px; height: 24px; } `; + const StyledBalanceText = styled(Text)` white-space: nowrap; overflow: hidden; @@ -51,10 +51,6 @@ const StyledText = styled(Text)` } `; -function Symbol({ symbol }) { - return {symbol}; -} - function CurrencyRow({ currency, onSelect, isSelected, style }) { return ( (isSelected ? null : onSelect())} disabled={isSelected} > - + {currency.symbol} @@ -71,7 +67,7 @@ function CurrencyRow({ currency, onSelect, isSelected, style }) { - + {currency.name} ); diff --git a/src/components/SwapSelectModal/index.js b/src/components/SwapSelectModal/index.js index da6331a7..90975c71 100644 --- a/src/components/SwapSelectModal/index.js +++ b/src/components/SwapSelectModal/index.js @@ -1,24 +1,19 @@ -import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from "react"; -import Modal from "../Modal"; -import Column from "../Column"; +import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; import AutoSizer from "react-virtualized-auto-sizer"; -import CurrencyList from "./CurrencyList"; -import styled, { ThemeContext } from "styled-components"; +import SVG from "react-inlinesvg"; import SearchIcon from "../../assets/images/search.svg"; import { InputGroupFormControl as FormControl, InputGroup, InputGroupPrepend, InputGroupText } from "../Form"; -import SVG from "react-inlinesvg"; - -const StyledColumn = styled(Column)` - padding: 0; -`; +import Modal from "../Modal"; +import Column from "../Column"; +import CurrencyList from "./CurrencyList"; +import styled from "styled-components"; const HeaderContainer = styled.div` padding: 30px 30px 20px; - border-bottom: 1px solid rgba(255, 255, 255, 0.2); + border-bottom: 1px solid ${({ theme }) => theme.borderColor2}; `; export default function SwapSelectModal({ isOpen, onDismiss, onCurrencySelect, selectedCurrency, currencies, type }) { - const theme = useContext(ThemeContext); const [searchQuery, setSearchQuery] = useState(""); const fixedList = useRef(); const inputRef = useRef(); @@ -60,9 +55,9 @@ export default function SwapSelectModal({ isOpen, onDismiss, onCurrencySelect, s return ( - + - + @@ -91,7 +86,7 @@ export default function SwapSelectModal({ isOpen, onDismiss, onCurrencySelect, s )}
    - + ); } diff --git a/src/components/Toggle/index.tsx b/src/components/Toggle/index.tsx index e243e6d5..418d0c0c 100644 --- a/src/components/Toggle/index.tsx +++ b/src/components/Toggle/index.tsx @@ -29,7 +29,7 @@ const ToggleElement = styled.span<{ isActive?: boolean; isOnSwitch?: boolean }>` const StyledToggle = styled.button<{ isActive?: boolean; activeElement?: boolean }>` border-radius: 12px; border: none; - background: ${({ theme }) => theme.bg2}; + background: ${({ theme }) => theme.bg1}; display: flex; width: fit-content; cursor: pointer; diff --git a/src/components/TokenInputPanel/index.js b/src/components/TokenInputPanel/index.js index 0c083c16..51e33a6d 100644 --- a/src/components/TokenInputPanel/index.js +++ b/src/components/TokenInputPanel/index.js @@ -20,7 +20,7 @@ const ContainerRow = styled.div` justify-content: center; align-items: center; border-radius: 1.25rem; - border: 1px solid ${({ error, theme }) => (error ? theme.red1 : theme.bg2)}; + border: 1px solid ${({ error, theme }) => (error ? theme.red1 : theme.bg1)}; transition: border-color 300ms ${({ error }) => (error ? "step-end" : "step-start")}, color 500ms ${({ error }) => (error ? "step-end" : "step-start")}; background-color: ${({ theme }) => theme.bg1}; diff --git a/src/components/TokenLogo/index.tsx b/src/components/TokenLogo/index.tsx index 7dfa8762..b66de52b 100644 --- a/src/components/TokenLogo/index.tsx +++ b/src/components/TokenLogo/index.tsx @@ -1,8 +1,8 @@ -import React, { useState } from "react"; +import { useState } from "react"; import SVG from "react-inlinesvg"; import { ImageProps } from "rebass"; import styled from "styled-components"; -import QuestionMark from '../../assets/images/question-mark.svg'; +import QuestionMark from "../../assets/images/question-mark.svg"; const BAD_SRCS: { [tokenAddress: string]: true } = {}; diff --git a/src/components/TokenSelector/index.js b/src/components/TokenSelector/index.js index 2caf2f22..48ba3177 100644 --- a/src/components/TokenSelector/index.js +++ b/src/components/TokenSelector/index.js @@ -33,7 +33,7 @@ const CurrencySelect = styled.button` height: 56px; font-size: 0.875rem; font-weight: 500; - background-color: ${({ theme }) => theme.bg3}; + background-color: ${({ theme }) => theme.bg1}; color: ${({ theme }) => theme.text1}; border-radius: 18px; box-shadow: none; @@ -174,7 +174,7 @@ export default function TokenSelector({ margin={true} /> ) : currency ? ( - + ) : null} {pair ? ( diff --git a/src/components/TokenSetsTable/index.tsx b/src/components/TokenSetsTable/index.tsx new file mode 100644 index 00000000..f52bb1de --- /dev/null +++ b/src/components/TokenSetsTable/index.tsx @@ -0,0 +1,160 @@ +import { useEffect, useState } from "react"; +import { useSelector, useDispatch } from "react-redux"; +import BootstrapTable from "react-bootstrap-table-next"; + +import { fetchTokens } from "../../state/explore/actions"; +import CurrencyLogo from "../CurrencyLogo"; +import CurrencyText from "../CurrencyText"; +import Loading from "../Loading"; +import ResponsiveTable from "../ResponsiveTable"; +import * as Styled from "./styleds"; + +const TokenSetsExploreTable = () => { + const dispatch = useDispatch(); + const [data, setData] = useState>([]); + const [loading, setLoading] = useState(true); + // @ts-ignore + const exploreSets = useSelector((state) => state.explore); + + useEffect(() => { + if (exploreSets.tokenSets.data.length === 0) { + dispatch(fetchTokens()); + } + + setData(exploreSets.tokenSets.data); + setLoading(false); + }, [exploreSets, dispatch]); + + const columns = [ + { + dataField: "id", + text: "ID", + // @ts-ignore + formatter: (cellContent, row, rowIndex) => ( + {rowIndex + 1} + ), + }, + + { + dataField: "name", + text: "NAME", + // @ts-ignore + formatter: (cellContent, row, rowIndex) => ( +
    + {row.image ? ( + + ) : ( + + + + )} +
    + + {row.name} + +
    +
    + ), + style: { + maxWidth: 250, + }, + notCentered: true, + }, + { + dataField: "price_usd", + text: "CURRENT PRICE", + // @ts-ignore + formatter: (cellContent, row) => ( + + {row.price_usd} + + ), + }, + { + dataField: "components", + text: "ASSETS", + // @ts-ignore + formatter: (cellContent, row) => ( +
    + {/* @ts-ignore */} + {row.components.map((c, index) => { + return ( + + {c.symbol} + + ); + })} +
    + ), + }, + { + dataField: "natural_units", + text: "NATURAL UNITS", + // @ts-ignore + formatter: (cellContent, row) => ( + {row.natural_unit} + ), + }, + { + dataField: "unit_shares", + text: "UNIT SHARES", + // @ts-ignore + formatter: (cellContent, row) => ( + {row.unit_shares} + ), + }, + { + dataField: "market_cap", + text: "MARKET CAP", + // @ts-ignore + formatter: (cellContent, row) => ( + + {row.market_cap} + + ), + }, + ]; + + if (loading) { + return ( +
    + +
    + ); + } + + if (data.length === 0) { + return

    No data to display

    ; + } + + return ( + <> + + + + + + + ); +}; + +export default TokenSetsExploreTable; diff --git a/src/components/TokenSetsTable/styleds.tsx b/src/components/TokenSetsTable/styleds.tsx new file mode 100644 index 00000000..ac637300 --- /dev/null +++ b/src/components/TokenSetsTable/styleds.tsx @@ -0,0 +1,232 @@ +import styled from "styled-components"; +import { Col, Nav } from "react-bootstrap"; +import { InputGroup } from "../Form"; + +export const CardTitle = styled.h2` + font-size: 1rem; + font-weight: 500; + margin-top: 0; + margin-bottom: 20px; + + @media (min-width: 768px) { + font-size: 1.25rem; + } +`; + +export const Logo = styled.img` + width: 32px; + height: 32px; + border-radius: 32px; + background-color: ${({ theme }) => theme.text1}; + border: 2px solid ${({ theme }) => theme.text1}; + + @media (max-width: 991px) { + width: 24px; + height: 24px; + border-radius: 24px; + } +`; + +export const LogoContainer = styled.div` + width: 32px; + height: 32px; + border-radius: 32px; + + @media (max-width: 991px) { + width: 24px; + height: 24px; + border-radius: 24px; + } +`; + +export const CellText = styled.span` + font-weight: 500; + font-size: 0.875rem; + color: ${({ theme }) => theme.text1}; + + &.font-size-base { + font-size: 1rem; + } + + @media (max-width: 991px) { + font-weight: 700; + + &.label { + font-weight: 500; + } + } +`; + +export const SymbolText = styled.span` + font-weight: 500; + font-size: 1rem; + color: ${({ theme }) => theme.text1}; + + @media (max-width: 991px) { + font-size: 0.875rem; + font-weight: 400; + } +`; + +export const CustomTitle = styled.h4` + color: ${({ theme }) => theme.text1}; + font-size: 1.25rem; + + @media (max-width: 991px) { + font-size: 0.875rem; + } +`; + +export const TokenSetCustomTitle = styled(CustomTitle)` + font-size: 1rem; +`; + +export const CustomNav = styled(Nav)` + margin-left: -30px !important; + margin-right: -30px !important; + overflow: auto; + + @media (min-width: 768px) { + margin-left: -10px !important; + margin-right: -10px !important; + } +`; + +export const CustomNavItem = styled(Nav.Item)` + flex-grow: initial !important; + + padding: 0 10px 10px; + + @media (max-width: 767px) { + padding: 0 5px 10px; + } + + &:first-child { + @media (max-width: 767px) { + padding-left: 30px; + } + } + &:last-child { + @media (max-width: 767px) { + padding-right: 30px; + } + } +`; + +export const CustomNavLink = styled(Nav.Link)` + border-radius: 18px !important; + color: ${({ theme }) => theme.primary}; + background-color: ${({ theme }) => theme.primaryLight}; + white-space: nowrap; + padding: 14px 24px; + min-height: 56px; + font-weight: 500; + display: flex; + align-items: center; + justify-content: center; + + @media (max-width: 767px) { + padding: 6px 15px; + font-size: 1rem; + min-height: 32px; + border-radius: 12px !important; + } + + &:hover { + color: ${({ theme }) => theme.primary}; + } + + &.active { + color: ${({ theme }) => theme.text1}; + background-color: ${({ theme }) => theme.primary}; + } +`; + +export const HeaderCol = styled(Col)` + margin: -10px 0 20px; + + @media (min-width: 768px) { + margin-bottom: 25px; + } +`; + +export const CustomInputGroup = styled(InputGroup)` + margin-bottom: 30px; +`; + +export const MarketLink = styled.a` + color: ${({ theme }) => theme.text1}; + @media (max-width: 991px) { + flex-basis: 100px; + } +`; + +export const ExploreTable = styled.div` + .table { + border-collapse: separate; + border-spacing: 0 0; + margin-bottom: 0 !important; + + thead th { + background-color: rgba(#202020, 0.1); + color: #202020; + font-size: 0.875rem; + font-weight: 500; + text-overflow: ellipsis; + white-space: nowrap; + padding: 1.25rem 0.75rem; + min-height: 56px; + + .dark-mode & { + background-color: rgba(white, 0.1); + color: white; + } + + &:focus { + outline: none; + } + + &:first-child { + border-top-left-radius: 18px; + border-bottom-left-radius: 18px; + } + + &:last-child { + border-top-right-radius: 18px; + border-bottom-right-radius: 18px; + } + } + + th, + td { + vertical-align: middle !important; + + &:first-child { + padding: 1.25rem 1.375rem; + } + + &:last-child { + padding: 1.25rem 0.5rem; + } + } + + td { + cursor: pointer; + color: #202020; + + .dark-mode & { + color: white; + } + } + + tr:not(:last-child) { + td { + border-bottom: 1px solid rgba(#202020, 0.5) !important; + + .dark-mode & { + border-color: rgba(white, 0.5) !important; + } + } + } + } +`; diff --git a/src/pages/TokenSets/TokenSetTab.js b/src/components/TokenSetsTabs/TokenSetTab.js similarity index 70% rename from src/pages/TokenSets/TokenSetTab.js rename to src/components/TokenSetsTabs/TokenSetTab.js index 0a92bbc2..2ff4b290 100644 --- a/src/pages/TokenSets/TokenSetTab.js +++ b/src/components/TokenSetsTabs/TokenSetTab.js @@ -1,80 +1,17 @@ -import { Row, Col } from "react-bootstrap"; -import styled from "styled-components"; - -import ResponsiveTable from "../../components/ResponsiveTable"; -import TokenSetsApi from "../../http/tokenSet"; import React, { useState, useEffect } from "react"; -import Img from "../../components/UI/Img"; -import CurrencyText from "../../components/CurrencyText"; -import ArrowUp from "../../components/Icons/ArrowUp"; -import ArrowDown from "../../components/Icons/ArrowDown"; -import BootstrapTable from "react-bootstrap-table-next"; -import "./styles.scss"; -import Loading from "../../components/Loading"; import { useTranslation } from "react-i18next"; +import BootstrapTable from "react-bootstrap-table-next"; -const api = new TokenSetsApi(); - -const Title = styled.span` - font-size: 1.125rem; - font-weight: bold; - margin-left: 20px; - line-height: 1.35; - display: block; - - @media (max-width: 991px) { - margin-left: 0; - margin-right: 12px; - font-size: 0.875rem; - } -`; - -const LogoContainer = styled.div` - max-width: 55px; - max-height: 55px; - min-width: 55px; - min-height: 55px; - height: 55px; - width: 55px; - border-radius: 12px; - background-color: ${({ theme }) => theme.text1}; - display: flex; - align-items: center; - justify-content: center; - - & img { - width: 40px; - height: 40px; - } - - @media (max-width: 991px) { - max-width: 40px; - max-height: 40px; - min-width: 40px; - min-height: 40px; - height: 40px; - width: 40px; - - & img { - width: 24px; - height: 24px; - } - } -`; - -const CellText = styled.span` - font-size: 0.875rem; - font-weight: 500; - line-height: 1; - - @media (max-width: 991px) { - font-size: 0.75rem; - } -`; +import ResponsiveTable from "../ResponsiveTable"; +import TokenSetsApi from "../../http/tokenSet"; +import Img from "../UI/Img"; +import CurrencyText from "../CurrencyText"; +import ArrowUp from "../Icons/ArrowUp"; +import ArrowDown from "../Icons/ArrowDown"; +import Loading from "../Loading"; +import * as Styled from "./styleds"; -const CellBoldText = styled(CellText)` - font-weight: 700; -`; +const api = new TokenSetsApi(); const TokenSetTab = (props) => { const [sets, setSets] = useState([]); @@ -92,14 +29,14 @@ const TokenSetTab = (props) => { const res = await api.fetchPortfolios(); data = res.data.portfolios; let ids = data.map((row) => row.address); - const historicalRes = await api.getTokenSetsHitorical(ids); + const historicalRes = await api.getTokenSetsHistorical(ids); setSetsHistorical(historicalRes); } else { const res = await api.fetchAllSets(); data = res.data.rebalancing_sets; let ids = data.map((row) => row.address); - const historicalRes = await api.getTokenSetsHitorical(ids); + const historicalRes = await api.getTokenSetsHistorical(ids); setSetsHistorical(historicalRes); } @@ -125,10 +62,10 @@ const TokenSetTab = (props) => { formatter: (cellContent, row) => { return (
    - + {row.name} - - {row.name} + + {row.name}
    ); }, @@ -138,9 +75,9 @@ const TokenSetTab = (props) => { text: t("table.marketCap"), formatter: (cellContent, row) => { return ( - + {row.market_cap} - + ); }, }, @@ -149,9 +86,9 @@ const TokenSetTab = (props) => { text: t("table.price"), formatter: (cellContent, row) => { return ( - + {row.price_usd} - + ); }, }, @@ -162,7 +99,7 @@ const TokenSetTab = (props) => { const h = historical[index] || {}; return ( - = 0 ? "label-light-success" @@ -179,7 +116,7 @@ const TokenSetTab = (props) => { {(h.market_data?.price_change_percentage_24h && h?.market_data?.price_change_percentage_24h.toFixed(2) + "%") || "-"} - + ); }, formatExtraData: { @@ -193,7 +130,7 @@ const TokenSetTab = (props) => { const h = historical[index] || {}; return ( - = 0 ? "label-light-success" @@ -210,7 +147,7 @@ const TokenSetTab = (props) => { {(h.market_data?.price_change_percentage_7d && h?.market_data?.price_change_percentage_7d.toFixed(2) + "%") || "-"} - + ); }, formatExtraData: { @@ -224,7 +161,7 @@ const TokenSetTab = (props) => { const h = historical[index] || {}; return ( - = 0 ? "label-light-success" @@ -241,7 +178,7 @@ const TokenSetTab = (props) => { {(h.market_data?.price_change_percentage_30d && h?.market_data?.price_change_percentage_30d.toFixed(2) + "%") || "-"} - + ); }, formatExtraData: { @@ -255,7 +192,7 @@ const TokenSetTab = (props) => { const h = historical[index] || {}; return ( - = 0 ? "label-light-success" @@ -272,7 +209,7 @@ const TokenSetTab = (props) => { {(h.market_data?.price_change_percentage_200d && h?.market_data?.price_change_percentage_200d.toFixed(2) + "%") || "-"} - + ); }, formatExtraData: { @@ -286,7 +223,7 @@ const TokenSetTab = (props) => { const h = historical[index] || {}; return ( - = 0 ? "label-light-success" @@ -303,7 +240,7 @@ const TokenSetTab = (props) => { {(h.market_data?.price_change_percentage_1y && h?.market_data?.price_change_percentage_1y.toFixed(2) + "%") || "-"} - + ); }, formatExtraData: { @@ -321,31 +258,37 @@ const TokenSetTab = (props) => { }, }; + if (loading) { + return ( +
    + +
    + ); + } + + if(sets.length === 0) { + return ( +

    No Token Sets

    + ) + } + return ( - - - {loading ? ( -
    - -
    - ) : ( - <> - - - - )} - -
    + <> + + + + + ); }; diff --git a/src/components/TokenSetsTabs/index.tsx b/src/components/TokenSetsTabs/index.tsx new file mode 100644 index 00000000..4c8a5cc2 --- /dev/null +++ b/src/components/TokenSetsTabs/index.tsx @@ -0,0 +1,43 @@ +import { useState } from "react"; +import { Tab } from "react-bootstrap"; +import { useTranslation } from "react-i18next"; + +import TokenSetsTable from "../TokenSetsTable"; +import TokenSetTab from "./TokenSetTab"; +import * as Styled from "./styleds"; + +const TokenSetsTabs = () => { + const { t } = useTranslation(); + const [activeKey, setActiveKey] = useState("portfolios"); + + return ( + // @ts-ignore + setActiveKey(k)}> + + + {t("tokensets.portfolios")} + + + {t("tokensets.rebalancingSets")} + + + {t("tokensets.otherSets")} + + + + + + + + + + + + + + + + ); +}; + +export default TokenSetsTabs; diff --git a/src/components/TokenSetsTabs/styleds.tsx b/src/components/TokenSetsTabs/styleds.tsx new file mode 100644 index 00000000..6c5165f3 --- /dev/null +++ b/src/components/TokenSetsTabs/styleds.tsx @@ -0,0 +1,115 @@ +import styled from "styled-components"; +import { Nav } from "react-bootstrap"; + +export const CustomNav = styled(Nav)` + padding: 0 1rem; + margin-bottom: 1rem; +`; + +export const NavItem = styled(Nav.Item)` + margin-right: 12px; +`; + +export const NavLink = styled(Nav.Link)` + background-color: ${({ theme }) => theme.primaryLight}; + border-radius: 18px !important; + color: ${({ theme }) => theme.primary}; + font-weight: 500; + padding: 12px 24px; + display: flex; + align-items: center; + justify-content: center; + white-space: nowrap; + + &:hover, + &:focus { + color: ${({ theme }) => theme.primary}; + } + + &.active { + background-color: ${({ theme }) => theme.primary}; + color: ${({ theme }) => theme.text1}; + } +`; + +export const TokenSetsTableWrap = styled.div` + .table { + position: relative; + width: 100%; + border-collapse: collapse; + color: white; + + th, + td { + border: 0; + border-bottom: 1px solid ${({ theme }) => theme.borderColor}; + color: ${({ theme }) => theme.text1}; + } + + th { + font-weight: 500; + font-size: 0.875rem; + + &:focus { + outline: none; + } + } + + td { + cursor: pointer; + padding: 1rem 0.75rem; + vertical-align: middle; + } + + tr:last-child td { + border-bottom-width: 0; + } + } +`; + +export const Title = styled.span` + font-size: 1.125rem; + font-weight: bold; + margin-left: 20px; + line-height: 1.35; + display: block; + + @media (max-width: 991px) { + margin-left: 0; + margin-right: 12px; + font-size: 0.875rem; + } +`; + +export const LogoContainer = styled.div` + max-width: 40px; + max-height: 40px; + min-width: 40px; + min-height: 40px; + height: 40px; + width: 40px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; + + & img { + width: 40px; + height: auto; + } +`; + +export const CellText = styled.span` + font-size: 0.875rem; + font-weight: 500; + line-height: 1; + + @media (max-width: 991px) { + font-size: 0.75rem; + } +`; + +export const CellBoldText = styled(CellText)` + font-weight: 700; +`; diff --git a/src/components/TokenWarningModal/index.tsx b/src/components/TokenWarningModal/index.tsx index c4cb6869..5146e02a 100644 --- a/src/components/TokenWarningModal/index.tsx +++ b/src/components/TokenWarningModal/index.tsx @@ -13,7 +13,7 @@ import { AutoColumn } from "../Column"; import { AlertTriangle } from "react-feather"; const Wrapper = styled.div<{ error: boolean }>` - background: ${({ theme }) => transparentize(0.6, theme.bg2)}; + background: ${({ theme }) => transparentize(0.6, theme.bg1)}; padding: 0.75rem; border-radius: 0.42rem; `; @@ -57,7 +57,7 @@ function TokenWarningCard({ token }: TokenWarningCardProps) { - +
    diff --git a/src/components/TransactionConfirmationModal/index.tsx b/src/components/TransactionConfirmationModal/index.tsx index fb43c5b4..3c9eeb6f 100644 --- a/src/components/TransactionConfirmationModal/index.tsx +++ b/src/components/TransactionConfirmationModal/index.tsx @@ -28,7 +28,7 @@ const Section = styled(AutoColumn)` `; const BottomSection = styled(Section)` - background-color: ${({ theme }) => theme.bg2}; + background-color: ${({ theme }) => theme.bg1}; border-bottom-left-radius: 20px; border-bottom-right-radius: 20px; flex: 1; diff --git a/src/components/TransactionHistory/index.tsx b/src/components/TransactionHistory/index.tsx new file mode 100644 index 00000000..c993ba0e --- /dev/null +++ b/src/components/TransactionHistory/index.tsx @@ -0,0 +1,173 @@ +import { useEffect, useState } from "react"; +import { Button } from "react-bootstrap"; +import { useTranslation } from "react-i18next"; +import { CSVLink } from "react-csv"; +import moment from "moment"; +import axios from "axios"; + +import { useActiveWeb3React } from "../../hooks"; +import Collapse from "../Collapse"; +import HistorySectionList from "../HistorySectionList"; +import ExchangeIcon from "../Icons/Exchange"; +import * as Styled from "./styleds"; + +const TransactionHistory = () => { + const { t } = useTranslation(); + const { account } = useActiveWeb3React(); + const PAGE_SIZE = 30; + const [loading, setLoading] = useState(false); + const [blockNumber, setBlockNumber] = useState(9999999999); + const [finished, setFinished] = useState(false); + const [sections, setSections] = useState>([]); + const [transactions, setTransactions] = useState([]); + + useEffect(() => { + if (account) { + setFinished(false); + fetchTransactions(); + } else { + setFinished(true); + setLoading(false); + } + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [account]); + + const fetchTransactions = async () => { + if (finished) { + return; + } + + setLoading(true); + let txnRes = await axios.get( + `https://api.etherscan.io/api?module=account&action=txlist&address=${account}&startblock=0&endblock=${blockNumber}&page=1&offset=${PAGE_SIZE}&sort=desc&apikey=${process.env.REACT_APP_ETHERSCAN_API_KEY}` + ); + let erc20Res = await axios.get( + `https://api.etherscan.io/api?module=account&action=tokentx&address=${account}&startblock=0&endblock=${blockNumber}&page=1&offset=${PAGE_SIZE}&sort=desc&apikey=${process.env.REACT_APP_ETHERSCAN_API_KEY}` + ); + + let ethLastBlock = + txnRes.data.status === "1" ? txnRes.data.result[txnRes.data.result.length - 1].blockNumber : 0; + let ercLastBlock = + erc20Res.data.status === "1" ? erc20Res.data.result[erc20Res.data.result.length - 1].blockNumber : 0; + + let lastBlock; + let isFinished = false; + + if (txnRes.data.result.length < PAGE_SIZE && erc20Res.data.result.length < PAGE_SIZE) { + lastBlock = ethLastBlock < ercLastBlock ? ethLastBlock : ercLastBlock; + isFinished = true; + } else { + lastBlock = ethLastBlock > ercLastBlock ? ethLastBlock : ercLastBlock; + } + let mixed = [...txnRes.data.result, ...erc20Res.data.result]; + + let result = {}; + for (let i in mixed) { + const txn = mixed[i]; + if (txn.blockNumber >= lastBlock) { + if (!result.hasOwnProperty(txn.timeStamp)) { + // @ts-ignore + result[txn.timeStamp] = { + [txn.hash]: [txn], + }; + } else { + // @ts-ignore + if (!result[txn.timeStamp].hasOwnProperty(txn.hash)) { + // @ts-ignore + result[txn.timeStamp][txn.hash] = [{ ...txn }]; + } else { + // @ts-ignore + result[txn.timeStamp][txn.hash].push({ ...txn }); + } + } + } + } + + let newSections: Array = []; + for (let i in result) { + const title = moment(i, "X").format("MMMM D, YYYY"); + let item = newSections.findIndex((item) => item.title === title); + // @ts-ignore + let newTransactions = Object.keys(result[i]); + if (item > -1) { + newSections[item].content.push( + ...newTransactions.map((t, ti) => { + // @ts-ignore + const txn = result[i][t]; + return ; + }) + ); + } else { + newSections.push({ + title, + content: newTransactions.map((t, ti) => { + // @ts-ignore + const txn = result[i][t]; + return ; + }), + }); + } + } + + for (let i in newSections) { + newSections[i].content.reverse(); + } + newSections.reverse(); + + setBlockNumber(lastBlock - 1); + setFinished(isFinished); + setSections((prevSections) => { + return [...prevSections, ...newSections]; + }); + setTransactions(transactions.concat(txnRes.data.result, erc20Res.data.result)); + setLoading(false); + }; + + return ( + + {t("history")} + {account && transactions && ( + + + + )} + + } + > + {account ? ( + <> + {finished && sections.length === 0 && ( +
    + +
    {t("errors.noTransaction")}
    + + {t("errors.noTransactionDesc")} + +
    + )} + + {sections.length > 0 && } + + {!finished && ( +
    + +
    + )} + + ) : ( +
    + +
    {t("wallet.notConnected")}
    + {t("errors.walletConnect")} +
    + )} +
    + ); +}; + +export default TransactionHistory; diff --git a/src/components/TransactionHistory/styleds.tsx b/src/components/TransactionHistory/styleds.tsx new file mode 100644 index 00000000..b755fa96 --- /dev/null +++ b/src/components/TransactionHistory/styleds.tsx @@ -0,0 +1,27 @@ +import styled from "styled-components"; +import Card from "../Card"; + +export const CustomCard = styled(Card)` + border: 1px solid ${({ theme }) => theme.borderColor2}; + overflow: hidden; + + .card-header { + border-bottom: 1px solid ${({ theme }) => theme.borderColor2}; + } + .card-body { + padding: 0; + } +`; + +export const Title = styled.h2` + font-weight: 700; + font-size: 1.25rem; + margin-top: 0; + margin-bottom: 0; +`; + +export const Header = styled.div` + display: flex; + align-items: center; + justify-content: space-between; +`; diff --git a/src/components/Uniswap/index.tsx b/src/components/Uniswap/index.tsx new file mode 100644 index 00000000..c144ced8 --- /dev/null +++ b/src/components/Uniswap/index.tsx @@ -0,0 +1,487 @@ +import { useCallback, useEffect, useMemo, useState } from "react"; +import { CurrencyAmount, JSBI, Token, Trade } from "@uniswap/sdk"; +import { Row, Col, Button } from "react-bootstrap"; +import SVG from "react-inlinesvg"; +import { ArrowDown, ArrowUpRight } from "react-feather"; +import { useTranslation } from "react-i18next"; + +import ConfirmSwapModal from "./swap/ConfirmSwapModal"; +import CurrencyInputPanel from "../CurrencyInputPanel"; +import AdvancedSwapDetailsDropdown from "./swap/AdvancedSwapDetailsDropdown"; +import confirmPriceImpactWithoutFee from "./swap/confirmPriceImpactWithoutFee"; +import { Wrapper, ArrowWrapper } from "./swap/styleds"; +import Loader from "../Loader"; +import TokenWarningModal from "../TokenWarningModal"; + +import { useActiveWeb3React } from "../../hooks"; +import { useCurrency } from "../../hooks/Tokens"; +import { ApprovalState, useApproveCallbackFromTrade } from "../../hooks/useApproveCallback"; +import useENSAddress from "../../hooks/useENSAddress"; +import { useSwapCallback } from "../../hooks/useSwapCallback"; +import useToggledVersion, { Version } from "../../hooks/useToggledVersion"; +import useWrapCallback, { WrapType } from "../../hooks/useWrapCallback"; +import { useWalletModalToggle } from "../../state/application/hooks"; +import { Field } from "../../state/swap/actions"; +import { + useDefaultsFromURLSearch, + useDerivedSwapInfo, + useSwapActionHandlers, + useSwapState, +} from "../../state/swap/hooks"; +import { useExpertModeManager, useUserSlippageTolerance } from "../../state/user/hooks"; +import { maxAmountSpend } from "../../utils/maxAmountSpend"; +import { computeTradePriceBreakdown, warningSeverity } from "../../utils/prices"; +import TradePrice from "./swap/TradePrice"; +import SwapHeader from "../SwapHeader"; +import useTheme from "../../hooks/useTheme"; +import ArrowRightLongIcon from "../../assets/images/global/arrow-right-long.svg"; +import ArrowDownLongIcon from "../../assets/images/global/arrow-down-long.svg"; +import { CustomCard, ApproveArrow, StyledClickableText, SwitchCol } from "./styles"; + +const Uniswap = (props: any) => { + const loadedUrlParams = useDefaultsFromURLSearch(); + const { t } = useTranslation(); + const theme = useTheme(); + + // token warning stuff + const [loadedInputCurrency, loadedOutputCurrency] = [ + useCurrency(loadedUrlParams?.inputCurrencyId), + useCurrency(loadedUrlParams?.outputCurrencyId), + ]; + const [dismissTokenWarning, setDismissTokenWarning] = useState(false); + const urlLoadedTokens: Token[] = useMemo( + () => [loadedInputCurrency, loadedOutputCurrency]?.filter((c): c is Token => c instanceof Token) ?? [], + [loadedInputCurrency, loadedOutputCurrency] + ); + const handleConfirmTokenWarning = useCallback(() => { + setDismissTokenWarning(true); + }, []); + + const { account } = useActiveWeb3React(); + + const [showInverted, setShowInverted] = useState(false); + + // toggle wallet when disconnected + const toggleWalletModal = useWalletModalToggle(); + + // for expert mode + const [isExpertMode] = useExpertModeManager(); + + // get custom setting values for user + const [allowedSlippage] = useUserSlippageTolerance(); + + // swap state + const { independentField, typedValue, recipient } = useSwapState(); + const { + v1Trade, + v2Trade, + currencyBalances, + parsedAmount, + currencies, + inputError: swapInputError, + } = useDerivedSwapInfo(); + const { wrapType, execute: onWrap, inputError: wrapInputError } = useWrapCallback( + currencies[Field.INPUT], + currencies[Field.OUTPUT], + typedValue + ); + const showWrap: boolean = wrapType !== WrapType.NOT_APPLICABLE; + const { address: recipientAddress } = useENSAddress(recipient); + const toggledVersion = useToggledVersion(); + const tradesByVersion = { + [Version.v1]: v1Trade, + [Version.v2]: v2Trade, + }; + const trade = showWrap ? undefined : tradesByVersion[toggledVersion]; + + const parsedAmounts = showWrap + ? { + [Field.INPUT]: parsedAmount, + [Field.OUTPUT]: parsedAmount, + } + : { + [Field.INPUT]: independentField === Field.INPUT ? parsedAmount : trade?.inputAmount, + [Field.OUTPUT]: independentField === Field.OUTPUT ? parsedAmount : trade?.outputAmount, + }; + + const { onCurrencySelection, onUserInput, onSwitchTokens } = useSwapActionHandlers(); + const isValid = !swapInputError; + const dependentField: Field = independentField === Field.INPUT ? Field.OUTPUT : Field.INPUT; + + const handleTypeInput = useCallback( + (value: string) => { + onUserInput(Field.INPUT, value); + }, + [onUserInput] + ); + const handleTypeOutput = useCallback( + (value: string) => { + onUserInput(Field.OUTPUT, value); + }, + [onUserInput] + ); + + // modal and loading + const [{ showConfirm, tradeToConfirm, swapErrorMessage, attemptingTxn, txHash }, setSwapState] = useState<{ + showConfirm: boolean; + tradeToConfirm: Trade | undefined; + attemptingTxn: boolean; + swapErrorMessage: string | undefined; + txHash: string | undefined; + }>({ + showConfirm: false, + tradeToConfirm: undefined, + attemptingTxn: false, + swapErrorMessage: undefined, + txHash: undefined, + }); + + const formattedAmounts = { + [independentField]: typedValue, + [dependentField]: showWrap + ? parsedAmounts[independentField]?.toExact() ?? "" + : parsedAmounts[dependentField]?.toSignificant(6) ?? "", + }; + + const route = trade?.route; + const userHasSpecifiedInputOutput = Boolean( + currencies[Field.INPUT] && + currencies[Field.OUTPUT] && + parsedAmounts[independentField]?.greaterThan(JSBI.BigInt(0)) + ); + const noRoute = !route; + + // check whether the user has approved the router on the input token + const [approval, approveCallback] = useApproveCallbackFromTrade(trade, allowedSlippage); + + // check if user has gone through approval process, used to show two step buttons, reset on token change + const [approvalSubmitted, setApprovalSubmitted] = useState(false); + + // mark when a user has submitted an approval, reset onTokenSelection for input field + useEffect(() => { + if (approval === ApprovalState.PENDING) { + setApprovalSubmitted(true); + } + }, [approval, approvalSubmitted]); + + const maxAmountInput: CurrencyAmount | undefined = maxAmountSpend(currencyBalances[Field.INPUT]); + const atMaxAmountInput = Boolean(maxAmountInput && parsedAmounts[Field.INPUT]?.equalTo(maxAmountInput)); + + // the callback to execute the swap + const { callback: swapCallback, error: swapCallbackError } = useSwapCallback(trade, allowedSlippage, recipient); + + const { priceImpactWithoutFee } = computeTradePriceBreakdown(trade); + + const handleSwap = useCallback(() => { + if (priceImpactWithoutFee && !confirmPriceImpactWithoutFee(priceImpactWithoutFee)) { + return; + } + if (!swapCallback) { + return; + } + setSwapState({ + attemptingTxn: true, + tradeToConfirm, + showConfirm, + swapErrorMessage: undefined, + txHash: undefined, + }); + swapCallback() + .then((hash) => { + setSwapState({ + attemptingTxn: false, + tradeToConfirm, + showConfirm, + swapErrorMessage: undefined, + txHash: hash, + }); + }) + .catch((error) => { + setSwapState({ + attemptingTxn: false, + tradeToConfirm, + showConfirm, + swapErrorMessage: error.message, + txHash: undefined, + }); + }); + }, [tradeToConfirm, account, priceImpactWithoutFee, recipient, recipientAddress, showConfirm, swapCallback, trade]); + + // warnings on slippage + const priceImpactSeverity = warningSeverity(priceImpactWithoutFee); + + // show approve flow when: no error on inputs, not approved or pending, or approved in current session + // never show if price impact is above threshold in non expert mode + const showApproveFlow = + !swapInputError && + (approval === ApprovalState.NOT_APPROVED || + approval === ApprovalState.PENDING || + (approvalSubmitted && approval === ApprovalState.APPROVED)) && + !(priceImpactSeverity > 3 && !isExpertMode); + + const handleConfirmDismiss = useCallback(() => { + setSwapState({ showConfirm: false, tradeToConfirm, attemptingTxn, swapErrorMessage, txHash }); + // if there was a tx hash, we want to clear the input + if (txHash) { + onUserInput(Field.INPUT, ""); + } + }, [attemptingTxn, onUserInput, swapErrorMessage, tradeToConfirm, txHash]); + + const handleAcceptChanges = useCallback(() => { + setSwapState({ tradeToConfirm: trade, swapErrorMessage, txHash, attemptingTxn, showConfirm }); + }, [attemptingTxn, showConfirm, swapErrorMessage, trade, txHash]); + + const handleInputSelect = useCallback( + (inputCurrency) => { + setApprovalSubmitted(false); // reset 2 step UI for approvals + onCurrencySelection(Field.INPUT, inputCurrency); + }, + [onCurrencySelection] + ); + + const handleMaxInput = useCallback(() => { + maxAmountInput && onUserInput(Field.INPUT, maxAmountInput.toExact()); + }, [maxAmountInput, onUserInput]); + + const handleOutputSelect = useCallback((outputCurrency) => onCurrencySelection(Field.OUTPUT, outputCurrency), [ + onCurrencySelection, + ]); + + return ( +
    + 0 && !dismissTokenWarning} + tokens={urlLoadedTokens} + onConfirm={handleConfirmTokenWarning} + /> + + + +
    + + + + + + + { + setApprovalSubmitted(false); // reset 2 step UI for approvals + onSwitchTokens(); + }} + color={theme.text2} + /> + + + + + + {showWrap ? null : ( + <> + {Boolean(trade) && ( +
    + + {t("price")} + + +
    + )} + + )} + + {trade && } + +
    + {!account ? ( + + ) : showWrap ? ( + + ) : noRoute && userHasSpecifiedInputOutput ? ( + + ) : showApproveFlow ? ( + + + + + + + + + + + + + ) : ( + + )} +
    +
    + + {trade && ( +
    + +
    + )} + + +
    +
    +
    + ); +}; + +export default Uniswap; diff --git a/src/components/Uniswap/styles.tsx b/src/components/Uniswap/styles.tsx new file mode 100644 index 00000000..f0353750 --- /dev/null +++ b/src/components/Uniswap/styles.tsx @@ -0,0 +1,35 @@ +import styled from "styled-components"; +import { ResponsiveCard } from "../Card"; +import { ClickableText } from "../ExternalLink"; + +export const CustomCard = styled(ResponsiveCard)``; + +export const ApproveArrow = styled.div` + align-self: center; + margin: 24px 0 20px; + + @media (min-width: 991px) { + margin: 0 43px; + } +`; + +export const StyledClickableText = styled(ClickableText)` + color: ${({ theme }) => theme.text1}; +`; + +export const SwitchCol = styled.div` + background: ${({ theme }) => theme.bg3}; + border: 4px solid ${({ theme }) => theme.modalBG}; + border-radius: 50%; + margin-left: 1.75rem; + margin-top: -8px; + margin-bottom: -8px; + display: flex; + align-items: center; + justify-content: center; + position: relative; + height: 44px; + width: 44px; + text-align: center; + z-index: 2; +`; diff --git a/src/components/Uniswap/swap/AdvancedSwapDetails.tsx b/src/components/Uniswap/swap/AdvancedSwapDetails.tsx new file mode 100644 index 00000000..aea91c8d --- /dev/null +++ b/src/components/Uniswap/swap/AdvancedSwapDetails.tsx @@ -0,0 +1,105 @@ +import { Trade, TradeType } from "@uniswap/sdk"; +import styled from "styled-components"; +import { Field } from "../../../state/swap/actions"; +import { useUserSlippageTolerance } from "../../../state/user/hooks"; +import { TYPE, ExternalLink } from "../../../theme"; +import { computeSlippageAdjustedAmounts, computeTradePriceBreakdown } from "../../../utils/prices"; +import { AutoColumn } from "../../Column"; +import { RowFixed } from "../../Row"; +import FormattedPriceImpact from "./FormattedPriceImpact"; +import SwapRoute from "./SwapRoute"; + +export const SummaryRow = styled.div` + border-top: 1px solid ${({ theme }) => theme.borderColor2}; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.75rem 0; + font-size: 0.75rem; + + @media (min-width: 768px) { + font-size: 0.875rem; + } +`; + +export const InfoLink = styled(ExternalLink)` + width: 100%; + border: 1px solid ${({ theme }) => theme.bg1}; + padding: 6px 6px; + border-radius: 8px; + text-align: center; + font-size: 14px; + color: ${({ theme }) => theme.text1}; +`; + +function TradeSummary({ trade, allowedSlippage }: { trade: Trade; allowedSlippage: number }) { + const { priceImpactWithoutFee, realizedLPFee } = computeTradePriceBreakdown(trade); + const isExactIn = trade.tradeType === TradeType.EXACT_INPUT; + const slippageAdjustedAmounts = computeSlippageAdjustedAmounts(trade, allowedSlippage); + + return ( + + + + {isExactIn ? "Minimum received" : "Maximum sold"} + + + + {isExactIn + ? `${slippageAdjustedAmounts[Field.OUTPUT]?.toSignificant(4)} ${ + trade.outputAmount.currency.symbol + }` ?? "-" + : `${slippageAdjustedAmounts[Field.INPUT]?.toSignificant(4)} ${ + trade.inputAmount.currency.symbol + }` ?? "-"} + + + + + + + Price Impact + + + + + + + Liquidity Provider Fee + + + {realizedLPFee ? `${realizedLPFee.toSignificant(4)} ${trade.inputAmount.currency.symbol}` : "-"} + + + + ); +} + +export interface AdvancedSwapDetailsProps { + trade?: Trade; +} + +export function AdvancedSwapDetails({ trade }: AdvancedSwapDetailsProps) { + const [allowedSlippage] = useUserSlippageTolerance(); + const showRoute = Boolean(trade && trade.route.path.length > 2); + + return ( + + {trade && ( + <> + + {showRoute && ( +
    + + + Route + + + +
    + )} + + )} +
    + ); +} diff --git a/src/components/Uniswap/swap/AdvancedSwapDetailsDropdown.tsx b/src/components/Uniswap/swap/AdvancedSwapDetailsDropdown.tsx new file mode 100644 index 00000000..0b977189 --- /dev/null +++ b/src/components/Uniswap/swap/AdvancedSwapDetailsDropdown.tsx @@ -0,0 +1,8 @@ +import { useLastTruthy } from "../../../hooks/useLast"; +import { AdvancedSwapDetails, AdvancedSwapDetailsProps } from "./AdvancedSwapDetails"; + +export default function AdvancedSwapDetailsDropdown({ trade, ...rest }: AdvancedSwapDetailsProps) { + const lastTrade = useLastTruthy(trade); + + return ; +} diff --git a/src/components/swap/BetterTradeLink.tsx b/src/components/Uniswap/swap/BetterTradeLink.tsx similarity index 88% rename from src/components/swap/BetterTradeLink.tsx rename to src/components/Uniswap/swap/BetterTradeLink.tsx index ce5c2e3e..a2b6b2b8 100644 --- a/src/components/swap/BetterTradeLink.tsx +++ b/src/components/Uniswap/swap/BetterTradeLink.tsx @@ -1,20 +1,19 @@ -import { stringify } from "qs"; import React, { useContext, useMemo } from "react"; import { useLocation } from "react-router"; import { Text } from "rebass"; +import { stringify } from "qs"; import { ThemeContext } from "styled-components"; -import useParsedQueryString from "../../hooks/useParsedQueryString"; -import useToggledVersion, { DEFAULT_VERSION, Version } from "../../hooks/useToggledVersion"; - -import { StyledInternalLink } from "../../theme"; -import { AutoColumn } from "../Column"; +import useParsedQueryString from "../../../hooks/useParsedQueryString"; +import useToggledVersion, { DEFAULT_VERSION, Version } from "../../../hooks/useToggledVersion"; +import { StyledInternalLink } from "../../../theme"; +import { AutoColumn } from "../../Column"; function VersionLinkContainer({ children }: { children: React.ReactNode }) { const theme = useContext(ThemeContext); return (
    diff --git a/src/components/swap/ConfirmSwapModal.tsx b/src/components/Uniswap/swap/ConfirmSwapModal.tsx similarity index 96% rename from src/components/swap/ConfirmSwapModal.tsx rename to src/components/Uniswap/swap/ConfirmSwapModal.tsx index 5847850f..75aa16ba 100644 --- a/src/components/swap/ConfirmSwapModal.tsx +++ b/src/components/Uniswap/swap/ConfirmSwapModal.tsx @@ -1,9 +1,9 @@ +import { useCallback, useMemo } from "react"; import { currencyEquals, Trade } from "@uniswap/sdk"; -import React, { useCallback, useMemo } from "react"; import TransactionConfirmationModal, { ConfirmationModalContent, TransactionErrorContent, -} from "../TransactionConfirmationModal"; +} from "../../TransactionConfirmationModal"; import SwapModalFooter from "./SwapModalFooter"; import SwapModalHeader from "./SwapModalHeader"; diff --git a/src/components/swap/FormattedPriceImpact.tsx b/src/components/Uniswap/swap/FormattedPriceImpact.tsx similarity index 83% rename from src/components/swap/FormattedPriceImpact.tsx rename to src/components/Uniswap/swap/FormattedPriceImpact.tsx index 691e1aaa..4bd4dbdd 100644 --- a/src/components/swap/FormattedPriceImpact.tsx +++ b/src/components/Uniswap/swap/FormattedPriceImpact.tsx @@ -1,9 +1,8 @@ import { Percent } from "@uniswap/sdk"; -import React from "react"; -import { ONE_BIPS } from "../../constants"; -import { warningSeverity } from "../../utils/prices"; -import { ErrorText } from "./styleds"; +import { ONE_BIPS } from "../../../constants"; +import { warningSeverity } from "../../../utils/prices"; import styled from "styled-components"; +import { ErrorText } from "./styleds"; export const CustomErrorText = styled(ErrorText)` font-size: 0.75rem; diff --git a/src/components/swap/SwapModalFooter.tsx b/src/components/Uniswap/swap/SwapModalFooter.tsx similarity index 91% rename from src/components/swap/SwapModalFooter.tsx rename to src/components/Uniswap/swap/SwapModalFooter.tsx index 9b4250d1..01502bda 100644 --- a/src/components/swap/SwapModalFooter.tsx +++ b/src/components/Uniswap/swap/SwapModalFooter.tsx @@ -1,20 +1,20 @@ -import { Trade, TradeType } from "@uniswap/sdk"; -import React, { useContext, useMemo, useState } from "react"; +import { useContext, useMemo, useState } from "react"; import { Repeat } from "react-feather"; import { Text } from "rebass"; +import { Trade, TradeType } from "@uniswap/sdk"; import { ThemeContext } from "styled-components"; -import { Field } from "../../state/swap/actions"; -import { TYPE } from "../../theme"; +import { Field } from "../../../state/swap/actions"; +import { TYPE } from "../../../theme"; import { computeSlippageAdjustedAmounts, computeTradePriceBreakdown, formatExecutionPrice, warningSeverity, -} from "../../utils/prices"; -import GradientButton from "../UI/Button"; -import { AutoColumn } from "../Column"; -import QuestionHelper from "../QuestionHelper"; -import { AutoRow, RowBetween, RowFixed } from "../Row"; +} from "../../../utils/prices"; +import { AutoColumn } from "../../Column"; +import QuestionHelper from "../../QuestionHelper"; +import { AutoRow, RowBetween, RowFixed } from "../../Row"; +import GradientButton from "../../UI/Button"; import FormattedPriceImpact from "./FormattedPriceImpact"; import { StyledBalanceMaxMini, SwapCallbackError } from "./styleds"; diff --git a/src/components/swap/SwapModalHeader.tsx b/src/components/Uniswap/swap/SwapModalHeader.tsx similarity index 87% rename from src/components/swap/SwapModalHeader.tsx rename to src/components/Uniswap/swap/SwapModalHeader.tsx index b1b977ea..ede0696d 100644 --- a/src/components/swap/SwapModalHeader.tsx +++ b/src/components/Uniswap/swap/SwapModalHeader.tsx @@ -1,16 +1,16 @@ -import { Trade, TradeType } from "@uniswap/sdk"; -import React, { useContext, useMemo } from "react"; +import { useContext, useMemo } from "react"; import { ArrowDown, AlertTriangle } from "react-feather"; import { Text } from "rebass"; +import { Trade, TradeType } from "@uniswap/sdk"; import { ThemeContext } from "styled-components"; -import { Field } from "../../state/swap/actions"; -import { TYPE } from "../../theme"; -import { ButtonPrimary } from "../Button"; -import { isAddress, shortenAddress } from "../../utils"; -import { computeSlippageAdjustedAmounts, computeTradePriceBreakdown, warningSeverity } from "../../utils/prices"; -import { AutoColumn } from "../Column"; -import CurrencyLogo from "../CurrencyLogo"; -import { RowBetween, RowFixed } from "../Row"; +import { Field } from "../../../state/swap/actions"; +import { TYPE } from "../../../theme"; +import { isAddress, shortenAddress } from "../../../utils"; +import { computeSlippageAdjustedAmounts, computeTradePriceBreakdown, warningSeverity } from "../../../utils/prices"; +import { ButtonPrimary } from "../../Button"; +import { AutoColumn } from "../../Column"; +import CurrencyLogo from "../../CurrencyLogo"; +import { RowBetween, RowFixed } from "../../Row"; import { TruncatedText, SwapShowAcceptChanges } from "./styleds"; export default function SwapModalHeader({ @@ -39,7 +39,7 @@ export default function SwapModalHeader({ - + {formattedPrice ?? "-"} {label} setShowInverted(!showInverted)}> - + ) : ( diff --git a/src/components/swap/confirmPriceImpactWithoutFee.ts b/src/components/Uniswap/swap/confirmPriceImpactWithoutFee.ts similarity index 96% rename from src/components/swap/confirmPriceImpactWithoutFee.ts rename to src/components/Uniswap/swap/confirmPriceImpactWithoutFee.ts index cdb8be0b..20260178 100644 --- a/src/components/swap/confirmPriceImpactWithoutFee.ts +++ b/src/components/Uniswap/swap/confirmPriceImpactWithoutFee.ts @@ -1,5 +1,5 @@ import { Percent } from "@uniswap/sdk"; -import { ALLOWED_PRICE_IMPACT_HIGH, PRICE_IMPACT_WITHOUT_FEE_CONFIRM_MIN } from "../../constants"; +import { ALLOWED_PRICE_IMPACT_HIGH, PRICE_IMPACT_WITHOUT_FEE_CONFIRM_MIN } from "../../../constants"; /** * Given the price impact, get user confirmation. diff --git a/src/components/swap/styleds.tsx b/src/components/Uniswap/swap/styleds.tsx similarity index 88% rename from src/components/swap/styleds.tsx rename to src/components/Uniswap/swap/styleds.tsx index 080d07f1..c80249d9 100644 --- a/src/components/swap/styleds.tsx +++ b/src/components/Uniswap/swap/styleds.tsx @@ -1,9 +1,8 @@ -import { transparentize } from "polished"; -import React from "react"; -import { AlertTriangle } from "react-feather"; import styled, { css } from "styled-components"; import { Text } from "rebass"; -import { AutoColumn } from "../Column"; +import { transparentize } from "polished"; +import { AlertTriangle } from "react-feather"; +import { AutoColumn } from "../../Column"; export const Wrapper = styled.div` position: relative; @@ -20,13 +19,13 @@ export const ArrowWrapper = styled.div<{ clickable: boolean }>` opacity: 0.8; } ` - : null} + : null}; `; export const SectionBreak = styled.div` height: 1px; width: 100%; - background-color: ${({ theme }) => theme.bg2}; + background-color: ${({ theme }) => theme.bg1}; `; export const BottomGrouping = styled.div``; @@ -43,12 +42,10 @@ export const ErrorText = styled(Text)<{ severity?: 0 | 1 | 2 | 3 | 4 }>` `; export const StyledBalanceMaxMini = styled.button` - height: 22px; - width: 22px; - background-color: ${({ theme }) => theme.bg2}; + background-color: ${({ theme }) => theme.bg1}; border: none; border-radius: 50%; - padding: 0.2rem; + padding: 6px; font-size: 0.875rem; font-weight: 400; margin-left: 0.4rem; @@ -57,13 +54,10 @@ export const StyledBalanceMaxMini = styled.button` display: flex; justify-content: center; align-items: center; - float: right; - :hover { - background-color: ${({ theme }) => theme.bg2}; - } - :focus { - background-color: ${({ theme }) => theme.bg2}; + &:hover, + &:focus { + background-color: ${({ theme }) => theme.bg1}; outline: none; } `; diff --git a/src/components/ValueCard/index.tsx b/src/components/ValueCard/index.tsx index f0b468db..588a6b30 100644 --- a/src/components/ValueCard/index.tsx +++ b/src/components/ValueCard/index.tsx @@ -1,11 +1,12 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -import React, { useContext } from "react"; -import styled, { ThemeContext } from "styled-components"; +import { useContext } from "react"; +import { ThemeContext } from "styled-components"; +import AssetIcon from "../../assets/images/account/assets.svg"; +import DebtIcon from "../../assets/images/account/debts.svg"; +import NetWorthIcon from "../../assets/images/account/networth.svg"; import CurrencyText from "../CurrencyText"; -import AssetIcon from "../../assets/images/assets/assets.svg"; -import DebtIcon from "../../assets/images/assets/debts.svg"; -import NetWorthIcon from "../../assets/images/assets/networth.svg"; +import Loading from "../Loading"; +import * as Styled from "./styleds"; const icons: any = { assets: AssetIcon, @@ -13,113 +14,51 @@ const icons: any = { netWorth: NetWorthIcon, }; -const Card = styled.div` - background-color: ${({ theme }) => theme.modalBG}; - color: ${({ theme }) => theme.text1}; - display: flex; - align-items: center; - border-radius: 20px; - padding: 30px; - margin-bottom: 10px; - - @media (min-width: 768px) { - padding: 45px; - margin-bottom: 20px; - } -`; - -const CardIcon = styled.div<{ color: string }>` - width: 80px; - height: 80px; - border-radius: 80px; - background-color: ${({ color }) => color}; - overflow: hidden; - display: flex; - align-items: center; - justify-content: center; - - @media (min-width: 768px) { - width: 100px; - height: 100px; - border-radius: 100px; - } -`; - -const CardBody = styled.div` - display: flex; - align-items: center; - padding: 0; -`; - -const Img = styled.img` - width: 32px; - height: 32px; - - @media (min-width: 768px) { - width: 40px; - height: 40px; - } -`; - -const CardContent = styled.div` - display: flex; - flex-direction: column; - flex-wrap: wrap; - justify-content: center; - padding-left: 20px; - - @media (min-width: 768px) { - padding-left: 30px; - } -`; +export type ValueCardProps = { + className?: string; + color?: any; + value: string; + title: string; + type: string; + show?: boolean; + loading?: boolean; +}; -const Title = styled.span` - font-weight: 500; - font-size: 0.875rem; - color: ${({ theme }) => theme.text1}; - display: block; - margin-bottom: 0.625rem; +function ValueCard({ + className = "", + value, + title, + type, + color = "primary", + show = true, + loading = false, +}: ValueCardProps) { + const theme = useContext(ThemeContext); + // @ts-ignore + const themeColor = theme[color]; - @media (min-width: 768px) { - font-size: 1rem; - margin-bottom: 1rem; + if (loading) { + return ; } -`; - -const Value = styled.span` - font-weight: 700; - font-size: 1.25rem; - color: ${({ theme }) => theme.text1}; - display: block; - margin: 0; - @media (min-width: 768px) { - font-size: 1.75rem; + if (!show) { + return null; } -`; -function ValueCard({ className, value, title, type, color = "primary" }: any) { - const theme = useContext(ThemeContext); - // @ts-ignore - const themeColor = theme[color]; return ( - <> - - {/* begin::Body */} - - - {type} - - - {title} - - {value} - - - - {/* end::Body */} - - + + + + + + + {title} + + {value} + + + + ); } diff --git a/src/components/ValueCard/styleds.tsx b/src/components/ValueCard/styleds.tsx new file mode 100644 index 00000000..39ac015a --- /dev/null +++ b/src/components/ValueCard/styleds.tsx @@ -0,0 +1,50 @@ +import styled from "styled-components"; + +export const Card = styled.div` + background-color: ${({ theme }) => theme.modalBG}; + color: ${({ theme }) => theme.text1}; + display: flex; + align-items: center; + border-radius: 18px; + margin-bottom: 1rem; + padding: 1.5rem; +`; + +export const CardBody = styled.div` + display: flex; + align-items: center; +`; + +export const CardIcon = styled.div<{ color: string }>` + width: 60px; + height: 60px; + border-radius: 50%; + background-color: ${({ color }) => color}; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; +`; + +export const Img = styled.img` + width: 50%; + height: auto; +`; + +export const CardContent = styled.div` + margin-left: 1rem; +`; + +export const Title = styled.p` + font-weight: 500; + font-size: 1rem; + color: ${({ theme }) => theme.text1}; + margin-bottom: 0; +`; + +export const Value = styled.p` + font-weight: 600; + font-size: 1.5rem; + color: ${({ theme }) => theme.text1}; + margin: 0; +`; diff --git a/src/components/VoteItem/index.js b/src/components/VoteItem/index.js index d9675dc2..86df15c1 100644 --- a/src/components/VoteItem/index.js +++ b/src/components/VoteItem/index.js @@ -53,7 +53,7 @@ const Wrapper = styled.tr` } &:hover { - background-color: ${({ theme }) => theme.bg2}; + background-color: ${({ theme }) => theme.bg1}; border-color: ${({ theme }) => theme.text3}; } } diff --git a/src/pages/Wallet/NftTab.js b/src/components/WalletCard/NftTab.js similarity index 93% rename from src/pages/Wallet/NftTab.js rename to src/components/WalletCard/NftTab.js index b87a168f..7ad5a6e9 100644 --- a/src/pages/Wallet/NftTab.js +++ b/src/components/WalletCard/NftTab.js @@ -1,13 +1,13 @@ import React, { useEffect, useMemo, useRef, useState } from "react"; import styled from "styled-components"; +import { useTranslation } from "react-i18next"; +import moment from "moment"; import OpenSeaApi from "../../http/opensea"; -import { WalletPageTable } from "./WalletPageTable"; import { useActiveWeb3React } from "../../hooks"; -import Loading from "../../components/Loading"; -import { CustomText, LogoContainer, Title, TradeButton } from "./index"; -import moment from "moment"; -import { useTranslation } from "react-i18next"; +import { WalletPageTable } from "./WalletPageTable"; +import Loading from "../Loading"; +import { CustomText, LogoContainer, Title, TradeButton } from "./styleds"; const Logo = styled.img` width: 100%; @@ -72,7 +72,11 @@ const NftTab = (props) => { )}
    {row.name} - {row?.description?.length > 120 ? row?.description?.slice(0, 120) + '...' : row?.description} + + {row?.description?.length > 120 + ? row?.description?.slice(0, 120) + "..." + : row?.description} +
    ); @@ -106,7 +110,9 @@ const NftTab = (props) => { formatter: (cellContent, row) => { return (
    - {moment(row.asset_contract.created_date).format("YYYY-MM-DD
    HH:mm:ss")}
    + + {moment(row.asset_contract.created_date).format("YYYY-MM-DD
    HH:mm:ss")} +
    ); }, diff --git a/src/pages/Wallet/UnlockModal.js b/src/components/WalletCard/UnlockModal.js similarity index 85% rename from src/pages/Wallet/UnlockModal.js rename to src/components/WalletCard/UnlockModal.js index a3d677cc..6c9a9321 100644 --- a/src/pages/Wallet/UnlockModal.js +++ b/src/components/WalletCard/UnlockModal.js @@ -1,6 +1,6 @@ -import { ConfirmationPendingContent, TransactionSubmittedContent } from "../../components/TransactionConfirmationModal"; +import { ConfirmationPendingContent, TransactionSubmittedContent } from "../TransactionConfirmationModal"; import React from "react"; -import { Modal } from "../../components/Modal/bootstrap"; +import { Modal } from "../Modal/bootstrap"; import { CHAIN_ID } from "../../constants"; const UnlockModal = (props) => { diff --git a/src/pages/Wallet/WalletPageTable.js b/src/components/WalletCard/WalletPageTable.js similarity index 83% rename from src/pages/Wallet/WalletPageTable.js rename to src/components/WalletCard/WalletPageTable.js index 5cde7214..61768b64 100644 --- a/src/pages/Wallet/WalletPageTable.js +++ b/src/components/WalletCard/WalletPageTable.js @@ -9,7 +9,7 @@ export function WalletPageTable({ entities, columns }) { { + const theme = useTheme(); + const { account } = useActiveWeb3React(); + const [query, setQuery] = useState(""); + const [unlocking, setUnlocking] = useState(false); + const [showUnlockModal, setShowUnlockModal] = useState(false); + const [done, setDone] = useState(false); + const overview = useSelector((state) => state.balances.overview); + const loading = useSelector((state) => state.balances.loading); + const { selected, currenciesRate } = useSelector((state) => state.currency); + const balances = useSelector((state) => state.balances.data); + const { ETH } = useSelector((state) => state.currency.currenciesRate); + const dispatch = useDispatch(); + const walletBalances = useMemoTokenBalances(); + const { t } = useTranslation(); + + useEffect(() => { + if (account) { + dispatch(fetchBalances(account)); + } + }, [account, dispatch]); + + useEffect(() => { + dispatch(fetchTransformedBalances(balances, walletBalances, ETH)); + }, [balances, walletBalances, ETH, dispatch]); + + useEffect(() => { + web3 = new Web3(Web3.givenProvider || new Web3.providers.HttpProvider(process.env.REACT_APP_NETWORK_URL)); + if (web3.currentProvider) { + web3Wrapper = new Web3Wrapper(web3.currentProvider); + } + }, []); + + const unlockHandler = async (token) => { + setShowUnlockModal(true); + try { + if (token.address) { + const contractWrappers = await getContractWrappers(web3.currentProvider || window.ethereum); + const approveAddress = token.address ? token.address : contractWrappers.contractAddresses.erc20Proxy; + + const erc20Token = new ERC20TokenContract(token.address, contractWrappers.getProvider()); + const amount = UNLIMITED_ALLOWANCE_IN_BASE_UNITS; + + const tx = await erc20Token.approve(approveAddress, amount).sendTransactionAsync({ + from: account, + }); + setUnlocking(true); + await web3Wrapper.awaitTransactionSuccessAsync(tx); + + if (tx) { + setUnlocking(false); + setDone(true); + } + } else if (token.symbol === "ETH") { + throw new Error("Unnecessary Approve for ethereum"); + } else { + throw new Error("Token is invalid"); + } + } catch (e) { + toast.error("Unnecessary Approve for ethereum or token is invalid"); + setUnlocking(false); + setShowUnlockModal(false); + setDone(false); + } + }; + + let tokensData = overview.wallet.balances || []; + + let filteredTokensData = useMemo(() => { + if (query === "") { + return tokensData; + } else { + const lowerQuery = query.toLowerCase(); + return tokensData.filter((token) => JSON.stringify(token.metadata).toLowerCase().includes(lowerQuery)); + } + }, [tokensData, query]); + + const TokensColumns = [ + { + dataField: "token", + text: t("token"), + formatter: (cellContent, row) => { + const isLoading = row.loading || false; + return ( +
    + + {isLoading ? ( + + ) : ( + + )} + + + {isLoading ? : row.metadata.symbol} + +
    + ); + }, + }, + { + dataField: "balance", + text: t("balanceTitle"), + formatter: (cellContent, row) => { + const isLoading = row.loading || false; + return ( +
    + {isLoading ? ( + + ) : ( + + {row.balance ? row.balance.toSignificant(6) : 0} + + )} +
    + ); + }, + }, + { + dataField: "value", + text: t("totalValue"), + formatter: (cellContent, row) => { + const isLoading = row.loading || false; + return ( +
    + {isLoading ? ( +
    + + +
    + ) : ( + + {row.balanceUSD} + + )} +
    + ); + }, + }, + { + dataField: "action", + text: t("table.actions"), + formatter: (cellContent, row, rowIndex, { unlockHandler }) => { + const isLoading = row.loading || false; + // const value = row.balanceUSD * (currenciesRate["BTC"] || 1); + return ( +
    + {isLoading ? ( +
    + +
    + ) : ( + <> + + {t("buttons.buy")} + + + + {t("buttons.unlock")} + + + + {t("buttons.sell")} + + + {/* {value <= 0.001 ? ( + + {t("buttons.convertTo", { symbol: "OCTO" })} + + ) : ( + + {t("buttons.convertTo", { symbol: "OCTO" })} + + )} */} + + )} +
    + ); + }, + isAction: true, + formatExtraData: { + unlockHandler, + }, + }, + ]; + + return ( +
    + { + setDone(false); + setUnlocking(false); + setShowUnlockModal(false); + }} + /> + + + +
    + + + + + + + + + setQuery(e.target.value)} + /> + +
    + + + {loading ? ( +
    + +
    + ) : ( + + )} +
    + + + +
    +
    +
    +
    + ); +}; + +export default WalletCard; diff --git a/src/pages/Wallet/style.scss b/src/components/WalletCard/style.scss similarity index 75% rename from src/pages/Wallet/style.scss rename to src/components/WalletCard/style.scss index b7d4bb7e..535cb306 100644 --- a/src/pages/Wallet/style.scss +++ b/src/components/WalletCard/style.scss @@ -1,5 +1,5 @@ .wallet-page__table { - border-collapse: separate; + border-collapse: collapse; border-spacing: 0 0; thead th { @@ -11,6 +11,7 @@ white-space: nowrap; min-height: 56px; padding: 1rem 1.5rem !important; + border: 0 !important; .dark-mode & { background-color: rgba(white, 0.1); @@ -29,24 +30,22 @@ &:last-child { border-top-right-radius: 18px; border-bottom-right-radius: 18px; + text-align: right; } } th, td { - vertical-align: middle; - - &:last-child { - text-align: left; - } + vertical-align: middle !important; } td { - border-bottom: 1px solid rgba(#202020, 0.5); + border-top: 0 !important; + border-bottom: 1px solid rgba(black, 0.15); padding: 1.25rem 1.5rem; .dark-mode & { - border-color: rgba(white, 0.5); + border-color: rgba(white, 0.15); } } diff --git a/src/components/WalletCard/styleds.tsx b/src/components/WalletCard/styleds.tsx new file mode 100644 index 00000000..a506a439 --- /dev/null +++ b/src/components/WalletCard/styleds.tsx @@ -0,0 +1,151 @@ +import styled from "styled-components"; +import { Link } from "react-router-dom"; +import { Button, Nav } from "react-bootstrap"; +import { InputGroup } from "../Form"; + +export const CustomNavItem = styled(Nav.Item)` + margin-right: 0.5rem; +`; + +export const CustomInputGroup = styled(InputGroup)` + margin-bottom: 2rem; +`; + +export const CustomNavLink = styled(Nav.Link)` + border-radius: 18px !important; + color: ${({ theme }) => theme.primary}; + background-color: ${({ theme }) => theme.primaryLight}; + white-space: nowrap; + padding: 0.75rem 1.5rem; + min-height: 56px; + font-weight: 500; + display: flex; + align-items: center; + justify-content: center; + + @media (max-width: 767px) { + padding: 6px 15px; + font-size: 1rem; + min-height: 32px; + border-radius: 12px !important; + } + + &:hover { + color: ${({ theme }) => theme.primary}; + } + + &.active { + color: ${({ theme }) => theme.text1}; + background-color: ${({ theme }) => theme.primary}; + } +`; + +export const LogoContainer = styled.div` + max-width: 24px; + max-height: 24px; + height: 24px; + width: 24px; + min-width: 24px; + margin-left: 1rem; + + @media (min-width: 992px) { + max-width: 32px; + max-height: 32px; + height: 32px; + width: 32px; + min-width: 32px; + margin-right: 1.5rem; + margin-left: 0; + } +`; + +export const Title = styled.span` + font-weight: bold; + font-size: 1rem; + color: ${({ theme }) => theme.text1}; + + @media (max-width: 991px) { + font-size: 0.875rem; + } +`; + +export const CustomText = styled.span` + color: ${({ theme }) => theme.text1}; + font-weight: 400; + font-size: 0.875rem; + + @media (max-width: 991px) { + font-size: 0.75rem; + } +`; + +export const PoolsButton = styled(Button)` + border-radius: 12px; + background-color: ${({ theme }) => theme.bg1}; + padding: 6px 20px; + max-height: 40px; + min-height: 40px; + display: flex; + align-items: center; + justify-content: center; + white-space: nowrap; + font-size: 1rem; + font-family: inherit; + font-weight: 500; + border: none; + outline: none; + text-decoration: none; + + &:hover, + &:focus, + &:active { + text-decoration: none; + outline: none; + box-shadow: none; + } +`; + +export const TradeButton = styled(PoolsButton)<{ variant: string }>` + color: ${({ theme, variant }) => (variant ? variant : theme.primary)} !important; + margin-left: 8px; + + @media (max-width: 991px) { + width: 100%; + margin-bottom: 0.5rem; + } + + &:not(:disabled):hover { + color: ${({ theme }) => theme.bg1} !important; + background-color: ${({ theme, variant }) => (variant ? variant : theme.primary)}; + } + + &:disabled { + cursor: not-allowed; + opacity: 0.5; + } +`; + +export const StyledLink = styled(Link)` + text-decoration: none; + display: inline-flex; + margin-right: 20px; + + &:last-child { + margin-right: 0; + } + + @media (max-width: 991px) { + margin-right: 0; + &:not(:last-child) { + margin-bottom: 14px; + } + } + + &:hover, + &:focus, + &:active { + text-decoration: none; + outline: none; + box-shadow: none; + } +`; diff --git a/src/components/WalletConnectStatus/index.tsx b/src/components/WalletConnectStatus/index.tsx new file mode 100644 index 00000000..83942102 --- /dev/null +++ b/src/components/WalletConnectStatus/index.tsx @@ -0,0 +1,35 @@ +import SVG from "react-inlinesvg"; +import { useTranslation } from "react-i18next"; +import { useActiveWeb3React } from "../../hooks"; +import AccountWalletIcon from "../../assets/images/account/wallet.svg"; +import * as Styled from "./styles"; + +export default function WalletConnectStatus({ hidden }: { hidden?: boolean }) { + const { account } = useActiveWeb3React(); + const { t } = useTranslation(); + + return ( +
    + {account ? ( + + + + {t("wallet.connected")} + + {t("wallet.connectedTo")} {account} + + + + ) : ( + + + + + {t("wallet.notConnected")} + + + + )} +
    + ); +} diff --git a/src/components/WalletConnectStatus/styles.tsx b/src/components/WalletConnectStatus/styles.tsx new file mode 100644 index 00000000..811c5421 --- /dev/null +++ b/src/components/WalletConnectStatus/styles.tsx @@ -0,0 +1,32 @@ +import styled from "styled-components"; + +export const AccountState = styled.div<{ type?: string }>` + color: ${({ theme, type }) => (type === "success" ? theme.success : theme.danger)}; + display: flex; + align-items: center; + justify-content: ${({ type }) => (type === "success" ? "flex-start" : "center")}; + padding: ${({ type }) => (type === "success" ? "14px 20px" : "24px 20px")}; + border-radius: 18px; +`; + +export const AccountStateContent = styled.div` + margin-left: 20px; +`; + +export const AccountStateTitle = styled.h3` + color: currentColor; + font-size: 1rem; + font-weight: 500; + margin-bottom: 0.5rem; +`; + +export const AccountStateDesc = styled.span` + color: rgba(74, 200, 170, 0.5); + font-size: 0.875rem; + font-weight: 500; + + strong { + color: ${({ theme }) => theme.success}; + font-weight: 500; + } +`; diff --git a/src/components/WalletModal/index.tsx b/src/components/WalletModal/index.tsx index 1aee8590..4934bb2f 100644 --- a/src/components/WalletModal/index.tsx +++ b/src/components/WalletModal/index.tsx @@ -342,7 +342,7 @@ export default function WalletModal() {
    - {t("seeAllTransactions")} + {t("seeAllTransactions")}
    ); @@ -358,7 +358,7 @@ export default function WalletModal() {
    - {t("seeAllTransactions")} + {t("seeAllTransactions")}
    ) diff --git a/src/components/swap/AdvancedSwapDetails.tsx b/src/components/swap/AdvancedSwapDetails.tsx deleted file mode 100644 index 244fd61a..00000000 --- a/src/components/swap/AdvancedSwapDetails.tsx +++ /dev/null @@ -1,126 +0,0 @@ -import { Trade, TradeType } from "@uniswap/sdk"; -import React, { useContext } from "react"; -import styled, { ThemeContext } from "styled-components"; -import { Field } from "../../state/swap/actions"; -import { useUserSlippageTolerance } from "../../state/user/hooks"; -import { TYPE, ExternalLink } from "../../theme"; -import { computeSlippageAdjustedAmounts, computeTradePriceBreakdown } from "../../utils/prices"; -import { AutoColumn } from "../Column"; -import { RowFixed } from "../Row"; -import FormattedPriceImpact from "./FormattedPriceImpact"; -import SwapRoute from "./SwapRoute"; - -const CustomTypeBlack = styled(TYPE.Black)` - font-size: 0.75rem; - - @media (min-width: 768px) { - font-size: 1rem; - } -`; - -export const InfoLink = styled(ExternalLink)` - width: 100%; - border: 1px solid ${({ theme }) => theme.bg2}; - padding: 6px 6px; - border-radius: 8px; - text-align: center; - font-size: 14px; - color: ${({ theme }) => theme.text1}; -`; - -export const SummaryRow = styled.div` - display: flex; - align-items: center; - justify-content: space-between; - padding: 1rem 0; - border-bottom: 1px solid ${({ theme }) => theme.text3}; - font-size: 1rem; - font-weight: 500; -`; - -function TradeSummary({ trade, allowedSlippage }: { trade: Trade; allowedSlippage: number }) { - const theme = useContext(ThemeContext); - const { priceImpactWithoutFee, realizedLPFee } = computeTradePriceBreakdown(trade); - const isExactIn = trade.tradeType === TradeType.EXACT_INPUT; - const slippageAdjustedAmounts = computeSlippageAdjustedAmounts(trade, allowedSlippage); - - return ( - <> - - - - - {isExactIn ? "Minimum received" : "Maximum sold"} - - - - - {isExactIn - ? `${slippageAdjustedAmounts[Field.OUTPUT]?.toSignificant(4)} ${ - trade.outputAmount.currency.symbol - }` ?? "-" - : `${slippageAdjustedAmounts[Field.INPUT]?.toSignificant(4)} ${ - trade.inputAmount.currency.symbol - }` ?? "-"} - - - - - - - Price Impact - - - - - - - - - Liquidity Provider Fee - - - - {realizedLPFee ? `${realizedLPFee.toSignificant(4)} ${trade.inputAmount.currency.symbol}` : "-"} - - - - - ); -} - -export interface AdvancedSwapDetailsProps { - trade?: Trade; -} - -export function AdvancedSwapDetails({ trade }: AdvancedSwapDetailsProps) { - const theme = useContext(ThemeContext); - const [allowedSlippage] = useUserSlippageTolerance(); - - const showRoute = Boolean(trade && trade.route.path.length > 2); - - return ( - - {trade && ( - <> - - {showRoute && ( -
    - - - Route - - - -
    - )} - - )} -
    - ); -} diff --git a/src/components/swap/AdvancedSwapDetailsDropdown.tsx b/src/components/swap/AdvancedSwapDetailsDropdown.tsx deleted file mode 100644 index fd78d137..00000000 --- a/src/components/swap/AdvancedSwapDetailsDropdown.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from "react"; -import styled from "styled-components"; -import { useLastTruthy } from "../../hooks/useLast"; -import { AdvancedSwapDetails, AdvancedSwapDetailsProps } from "./AdvancedSwapDetails"; - -const AdvancedDetailsFooter = styled.div<{ show: boolean }>` - width: 100%; - border-bottom-left-radius: 0.42rem; - border-bottom-right-radius: 0.42rem; - color: ${({ theme }) => theme.text2}; - background-color: ${({ theme }) => theme.modalBG}; - padding-bottom: 50px; -`; - -export default function AdvancedSwapDetailsDropdown({ trade, ...rest }: AdvancedSwapDetailsProps) { - const lastTrade = useLastTruthy(trade); - - return ( - - - - ); -} diff --git a/src/constants/fiatOffList.js b/src/constants/fiatOffList.js deleted file mode 100644 index 0ffa4745..00000000 --- a/src/constants/fiatOffList.js +++ /dev/null @@ -1,168 +0,0 @@ -import GiftImage from '../assets/images/fiatOff/gift.svg'; -import PassportImage from '../assets/images/fiatOff/passport.svg'; -import ShoppingImage from '../assets/images/fiatOff/shopping.svg'; -import BitrefillImage from '../assets/images/fiatOff/bitrefill.png'; -import BitdialsImage from '../assets/images/fiatOff/bitdials.png'; -import BitcarsImage from '../assets/images/fiatOff/bitcars.png'; -import egifterImage from '../assets/images/fiatOff/egifter.png'; -import coinsbeeImage from '../assets/images/fiatOff/coinsbee.png'; -import bitgildImage from '../assets/images/fiatOff/bitgild.png'; -import TravalaImage from '../assets/images/fiatOff/travala.png'; -import GivingImage from '../assets/images/fiatOff/giving.png'; - -const items = [ - { - thumbnail: BitrefillImage, - title: "Bitrefill", - url: 'https://www.bitrefill.com', - items: [ - { - title: "Gift Cards", - icon: GiftImage - }, - { - title: "BTC / ETH", - icon: ShoppingImage - }, - { - title: "No KYC", - icon: PassportImage - }, - ] - }, - { - thumbnail: BitdialsImage, - title: "BitDials", - url: 'https://www.bitdials.eu', - items: [ - { - title: "Luxury Goods", - icon: GiftImage - }, - { - title: "BTC / Alts", - icon: ShoppingImage - }, - { - title: "No KYC", - icon: PassportImage - }, - ] - }, - { - thumbnail: BitcarsImage, - title: "BitCars", - url: 'https://bitcars.eu', - items: [ - { - title: "Cars", - icon: GiftImage - }, - { - title: "BTC / Alts", - icon: ShoppingImage - }, - { - title: "No KYC", - icon: PassportImage - }, - ] - }, - { - thumbnail: egifterImage, - title: "egifter", - url: 'https://www.egifter.com', - items: [ - { - title: "Gift Cards", - icon: GiftImage - }, - { - title: "BTC / ETH", - icon: ShoppingImage - }, - { - title: "No KYC", - icon: PassportImage - }, - ] - }, - { - thumbnail: coinsbeeImage, - title: "Coinsbee", - url: 'https://www.coinsbee.com/en', - items: [ - { - title: "Gift Cards", - icon: GiftImage - }, - { - title: "BTC / Alts", - icon: ShoppingImage - }, - { - title: "No KYC", - icon: PassportImage - }, - ] - }, - { - thumbnail: bitgildImage, - title: "Bitgild", - url: 'https://www.bitgild.com', - items: [ - { - title: "Gold & Silver", - icon: GiftImage - }, - { - title: "BTC / ETH", - icon: ShoppingImage - }, - { - title: "KYC", - icon: PassportImage - }, - ] - }, - { - thumbnail: TravalaImage, - title: "Travala", - url: 'https://www.travala.com', - items: [ - { - title: "Travel", - icon: GiftImage - }, - { - title: "BTC / Alts", - icon: ShoppingImage - }, - { - title: "No KYC", - icon: PassportImage - }, - ] - }, - { - thumbnail: GivingImage, - title: "GivingB", - url: 'https://www.thegivingblock.com/donate-bitcoin', - items: [ - { - title: "Charity", - icon: GiftImage - }, - { - title: "BTC / ETH", - icon: ShoppingImage - }, - { - title: "No KYC", - icon: PassportImage - }, - ] - }, -] - -export default items; diff --git a/src/constants/headerRoutes.js b/src/constants/headerRoutes.js index e792cdcc..f6c04299 100644 --- a/src/constants/headerRoutes.js +++ b/src/constants/headerRoutes.js @@ -1,65 +1,50 @@ export const routes = { - fiat: { - title: "buy", + dashboard: { + title: "dashboard", + path: "/dashboard", + state: "success", + }, + history: { + title: "history", + path: "/history", + state: "success", + }, + instantSwap: { + title: "exchange", + path: "/exchange", + state: "success", + }, + invest: { + title: "invest", + path: "/invest", + state: "success", + }, + pools: { + title: "pools", + path: "/invest/pools", + state: "success", + }, + more: { + title: "more", routes: { - instantSwap: { - title: "swap", - path: "/swap/all", - state: "success", - }, - swap: { - title: "uniswap", - path: "/swap/uni", + governance: { + title: "governance", + path: "/governance", state: "success", }, - cross: { - title: "cross", - path: "/cross/anyswap", + nft: { + title: "nft", + path: "/nft", state: "success", }, on: { title: "crypto", - path: "/fiat/on", + path: "/onramp", state: "success", }, off: { title: "giftCards", - path: "/fiat/off-cards", - state: "success", - }, - }, - }, - cross: { - title: "cross", - routes: { - bridges: { - title: "bridges", - path: "/cross/bridges", - state: "success", - }, - anySwap: { - title: "anySwap", - path: "/cross/anyswap", - state: "success", - }, - crossBalance: { - title: "crossBalance", - path: "/cross/balance", - state: "success", - }, - }, - }, - invest: { - title: "invest", - routes: { - pools: { - title: "pools", - path: "/invest/pools", - state: "success", - }, - tokenSets: { - title: "tokenSets", - path: "/invest/tokensets", + path: "/offramp", state: "success", }, loans: { @@ -67,56 +52,21 @@ export const routes = { path: "/invest/loans", state: "success", }, - nft: { - title: "nft", - path: "/invest/nft", - state: "success", - }, launchpad: { title: "launchpad", - path: "/invest/launchpad", - state: "success", - } - }, - }, - tools: { - title: "tools", - routes: { - governance: { - title: "governance", - path: "/tools/governance", + path: "/launchpad", state: "success", }, - rankings: { - title: "rankings", - path: "/tools/explore/tokens", + swap: { + title: "uniswap", + path: "/uniswap", state: "success", }, - explore: { - title: "explore", - path: "/tools/explore", + cross: { + title: "cross", + path: "/cross/anyswap", state: "success", }, }, }, - account: { - title: "Account", - routes: { - dashboard: { - title: "dashboard", - path: "/dashboard", - state: "success", - }, - wallet: { - title: "wallet", - path: "/account/wallet", - state: "success", - }, - history: { - title: "history", - path: "/account/history", - state: "success", - }, - } - } }; diff --git a/src/constants/index.ts b/src/constants/index.ts index f0ee17e1..f3e587c8 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -45,6 +45,8 @@ export const REMOVE_CONTRACTS = { export const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"; export const DEFAULT_DECIMALS = 18; +export const ADDRESS_PATTERN = /^[13][a-km-zA-HJ-NP-Z1-9]{25,80}$|^(bc1)[0-9A-Za-z]{25,80}$|^(0x[a-fA-F0-9]{40})$|^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$/; + // a list of tokens by chain type ChainTokenList = { readonly [chainId in ChainId]: Token[]; @@ -63,6 +65,7 @@ export const COMP = new Token(ChainId.MAINNET, "0xc00e94Cb662C3520282E6f57172140 export const MKR = new Token(ChainId.MAINNET, "0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2", 18, "MKR", "Maker"); export const AMPL = new Token(ChainId.MAINNET, "0xD46bA6D942050d489DBd938a2C909A5d5039A161", 9, "AMPL", "Ampleforth"); export const WBTC = new Token(ChainId.MAINNET, "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", 18, "WBTC", "Wrapped BTC"); +export const OCTO = new Token(ChainId.MAINNET, "0x7240aC91f01233BaAf8b064248E80feaA5912BA3", 18, "OCTO", "Octo.fi"); export const BalanceToken = new Token( ChainId.MAINNET, @@ -502,7 +505,7 @@ export const exploreSections = { src: "/coins/" + row.id, }; }, - seeMore: "/tools/explore/tokens", + seeMore: "/#/invest/tokens", }, tokenSets: { title: "Token Sets", @@ -520,7 +523,7 @@ export const exploreSections = { title: false, }; }, - seeMore: "/tools/explore/tokenSets", + seeMore: "/#/invest/tokensets", }, pools: { title: "Pools", @@ -538,7 +541,7 @@ export const exploreSections = { title: "Total Liquidity", }; }, - seeMore: "/invest/pools", + seeMore: "/#/invest/pools", }, trending: { title: "Trending on Coingecko", @@ -572,7 +575,7 @@ export const exploreSections = { symbol: row?.symbol?.toUpperCase() || "-", price: row?.volume_24h, priceDiff: row.price_percentage_change_24h, - seeMore: "/tools/explore/derivatives", + seeMore: "/#/invest/derivatives", title: "Volume (24h)", }; }, @@ -854,21 +857,6 @@ export const ApprovalState = { APPROVED: 3, }; -export const banners = [ - { - image: require("../assets/images/banners/banner_1.png"), - url: "https://den.octo.fi", - }, - { - image: require("../assets/images/banners/banner_2.png"), - url: "https://dyor.octo.fi", - }, - { - image: require("../assets/images/banners/banner_3.png"), - url: "https://doc.octo.fi", - }, -]; - export enum StepKind { WrapEth = "WrapEth", ToggleTokenLock = "ToggleTokenLock", diff --git a/src/data/FiatOffList.ts b/src/data/FiatOffList.ts new file mode 100644 index 00000000..3e137748 --- /dev/null +++ b/src/data/FiatOffList.ts @@ -0,0 +1,169 @@ +import { FiatOffItem } from "../typings"; +import GiftImage from "../assets/images/fiatOff/gift.svg"; +import PassportImage from "../assets/images/fiatOff/passport.svg"; +import ShoppingImage from "../assets/images/fiatOff/shopping.svg"; +import BitrefillImage from "../assets/images/fiatOff/bitrefill.png"; +import BitdialsImage from "../assets/images/fiatOff/bitdials.png"; +import BitcarsImage from "../assets/images/fiatOff/bitcars.png"; +import EgifterImage from "../assets/images/fiatOff/egifter.png"; +import CoinsbeeImage from "../assets/images/fiatOff/coinsbee.png"; +import BitgildImage from "../assets/images/fiatOff/bitgild.png"; +import TravalaImage from "../assets/images/fiatOff/travala.png"; +import GivingImage from "../assets/images/fiatOff/giving.png"; + +const items: Array = [ + { + thumbnail: BitrefillImage, + title: "Bitrefill", + url: "https://www.bitrefill.com", + traits: [ + { + title: "Gift Cards", + icon: GiftImage, + }, + { + title: "BTC / ETH", + icon: ShoppingImage, + }, + { + title: "No KYC", + icon: PassportImage, + }, + ], + }, + { + thumbnail: BitdialsImage, + title: "BitDials", + url: "https://www.bitdials.eu", + traits: [ + { + title: "Luxury Goods", + icon: GiftImage, + }, + { + title: "BTC / Alts", + icon: ShoppingImage, + }, + { + title: "No KYC", + icon: PassportImage, + }, + ], + }, + { + thumbnail: BitcarsImage, + title: "BitCars", + url: "https://bitcars.eu", + traits: [ + { + title: "Cars", + icon: GiftImage, + }, + { + title: "BTC / Alts", + icon: ShoppingImage, + }, + { + title: "No KYC", + icon: PassportImage, + }, + ], + }, + { + thumbnail: EgifterImage, + title: "eGifter", + url: "https://www.egifter.com", + traits: [ + { + title: "Gift Cards", + icon: GiftImage, + }, + { + title: "BTC / ETH", + icon: ShoppingImage, + }, + { + title: "No KYC", + icon: PassportImage, + }, + ], + }, + { + thumbnail: CoinsbeeImage, + title: "Coinsbee", + url: "https://www.coinsbee.com/en/", + traits: [ + { + title: "Gift Cards", + icon: GiftImage, + }, + { + title: "BTC / Alts", + icon: ShoppingImage, + }, + { + title: "No KYC", + icon: PassportImage, + }, + ], + }, + { + thumbnail: BitgildImage, + title: "Bitgild", + url: "https://www.bitgild.com", + traits: [ + { + title: "Gold & Silver", + icon: GiftImage, + }, + { + title: "BTC / ETH", + icon: ShoppingImage, + }, + { + title: "KYC", + icon: PassportImage, + }, + ], + }, + { + thumbnail: TravalaImage, + title: "Travala", + url: "https://www.travala.com", + traits: [ + { + title: "Travel", + icon: GiftImage, + }, + { + title: "BTC / Alts", + icon: ShoppingImage, + }, + { + title: "No KYC", + icon: PassportImage, + }, + ], + }, + { + thumbnail: GivingImage, + title: "The Giving Block", + url: "https://www.thegivingblock.com/donate-bitcoin", + traits: [ + { + title: "Charity", + icon: GiftImage, + }, + { + title: "BTC / ETH", + icon: ShoppingImage, + }, + { + title: "No KYC", + icon: PassportImage, + }, + ], + }, +]; + +export default items; diff --git a/src/data/banners.ts b/src/data/banners.ts new file mode 100644 index 00000000..60e52621 --- /dev/null +++ b/src/data/banners.ts @@ -0,0 +1,19 @@ +import { Banner } from '../typings'; +import Banner1 from '../assets/images/banners/banner_1.png'; +import Banner2 from '../assets/images/banners/banner_2.png'; +import Banner3 from '../assets/images/banners/banner_3.png'; + +export const banners: Array = [ + { + image: Banner1, + url: "https://den.octo.fi", + }, + { + image: Banner2, + url: "https://dyor.octo.fi", + }, + { + image: Banner3, + url: "https://doc.octo.fi", + }, +]; diff --git a/src/data/features.ts b/src/data/features.ts new file mode 100644 index 00000000..e07cf288 --- /dev/null +++ b/src/data/features.ts @@ -0,0 +1,28 @@ +import { Feature } from '../typings'; + +export const features: Array = [ + { + href: "https://help.octo.fi", + iconName: "support", + title: "app.features.support.title", + desc: "app.features.support.desc", + }, + { + href: "https://blog.octo.fi", + iconName: "blog", + title: "app.features.blog.title", + desc: "app.features.blog.desc", + }, + { + href: "https://den.octo.fi", + iconName: "community", + title: "app.features.community.title", + desc: "app.features.community.desc", + }, + { + href: "https://yolo.octo.fi", + iconName: "blog", + title: "app.features.careers.title", + desc: "app.features.careers.desc", + }, +]; \ No newline at end of file diff --git a/src/data/socials.ts b/src/data/socials.ts new file mode 100644 index 00000000..cda4b1b5 --- /dev/null +++ b/src/data/socials.ts @@ -0,0 +1,28 @@ +import { SocialLink } from '../typings'; +import Github from "../assets/images/socials/github.svg"; +import Youtube from "../assets/images/socials/youtube.svg"; +import Telegram from "../assets/images/socials/telegram.svg"; +import Twitter from "../assets/images/socials/twitter.svg"; + +export const socials: Array = [ + { + name: "Twitter", + image: Twitter, + url: "https://twitter.com/octofinance", + }, + { + name: "Telegram", + image: Telegram, + url: "https://t.me/OctoFi", + }, + { + name: "Youtube", + image: Youtube, + url: "https://www.youtube.com/channel/UCQ8TelmjLpFKQAsZCXIs5Tw", + }, + { + name: "Github", + image: Github, + url: "https://github.com/octofi", + }, +]; \ No newline at end of file diff --git a/src/http/tokenSet.js b/src/http/tokenSet.js index eb117dbd..b0a03b10 100644 --- a/src/http/tokenSet.js +++ b/src/http/tokenSet.js @@ -56,7 +56,7 @@ class TokenSetApi { }); } - async getTokenSetsHitorical(ids) { + async getTokenSetsHistorical(ids) { const promises = []; for (let i in ids) { promises.push(this.promisify(() => this.fetchHistoricalData(ids[i]))); diff --git a/src/http/transak.js b/src/http/transak.js index 1207dec3..fb5ef8fe 100644 --- a/src/http/transak.js +++ b/src/http/transak.js @@ -19,6 +19,9 @@ export default class TransakApi { case "price": { return this.fetchConversionPrice(payload); } + default: { + return this.fetchCryptoCurrencies(); + } } } diff --git a/src/pages/Borrow/BorrowBalance/index.js b/src/pages/Borrow/BorrowBalance/index.js index 97e905ce..9b54e9ec 100644 --- a/src/pages/Borrow/BorrowBalance/index.js +++ b/src/pages/Borrow/BorrowBalance/index.js @@ -25,7 +25,7 @@ import { } from "../../../state/spotUI/actions"; import CurrencyLogo from "../../../components/CurrencyLogo"; import Skeleton from "react-loading-skeleton"; -import { StyledLink, TradeButton } from "../../Wallet"; +import { StyledLink, TradeButton } from "../../../components/WalletCard/styleds"; import BootstrapTable from "react-bootstrap-table-next"; import ResponsiveTable from "../../../components/ResponsiveTable"; import BorrowTokenModal from "./BorrowTokenModal"; @@ -33,6 +33,7 @@ import RepayTokenModal from "./RepayTokenModal"; import { Token } from "@uniswap/sdk"; import { sortedData } from "../../../lib/helper"; import { useTranslation } from "react-i18next"; +import useTheme from "../../../hooks/useTheme"; const LogoContainer = styled.div` width: 32px; @@ -84,6 +85,7 @@ const CellText = styled.span` `; const BorrowBalance = (props) => { + const theme = useTheme(); const [isEthState, setIsEthState] = useState(false); const [isHideZeroBalance, setIsHideZeroBalance] = useState(false); const [isStableCoin, setIsStableCoin] = useState(false); @@ -401,7 +403,7 @@ const BorrowBalance = (props) => { diff --git a/src/pages/Borrow/LendingBalance/index.js b/src/pages/Borrow/LendingBalance/index.js index 8e97b94e..e0c9a5a1 100644 --- a/src/pages/Borrow/LendingBalance/index.js +++ b/src/pages/Borrow/LendingBalance/index.js @@ -9,17 +9,17 @@ import withBalance from "../../../components/hoc/withBalance"; import { useActiveWeb3React } from "../../../hooks"; import { setAaveCurrency } from "../../../state/aave/actions"; import { getKnownTokens, isWethToken } from "../../../utils/known_tokens"; -import { formatTokenSymbol, tokenAmountInUnits } from "../../../utils/spot/tokens"; +import { tokenAmountInUnits } from "../../../utils/spot/tokens"; import { Protocol } from "../../../utils/aave/types"; import { startLendingTokenSteps, startUnLendingTokenSteps } from "../../../state/spotUI/actions"; import CurrencyLogo from "../../../components/CurrencyLogo"; -import Skeleton from "react-loading-skeleton"; -import { StyledLink, TradeButton } from "../../Wallet"; +import { StyledLink, TradeButton } from "../../../components/WalletCard/styleds"; import BootstrapTable from "react-bootstrap-table-next"; import ResponsiveTable from "../../../components/ResponsiveTable"; import LendingModalContainer from "./LendingModalContainer"; import { sortedData } from "../../../lib/helper"; import { useTranslation } from "react-i18next"; +import useTheme from "../../../hooks/useTheme"; const LogoContainer = styled.div` width: 32px; @@ -71,6 +71,7 @@ const CellText = styled.span` `; const LendingBalance = (props) => { + const theme = useTheme(); const [isEthState, setIsEthState] = useState(false); const [isHideZeroBalance, setIsHideZeroBalance] = useState(false); const [isStableCoin, setIsStableCoin] = useState(false); @@ -284,7 +285,13 @@ const LendingBalance = (props) => { dataField: "displayBalance", text: t("borrow.balance"), formatter: (cellContent, row, rowIndex) => { - return {typeof row.displayBalance === 'number' ? `${row.displayBalance} ${row.tokenSymbol}` : row.displayBalance}; + return ( + + {typeof row.displayBalance === "number" + ? `${row.displayBalance} ${row.tokenSymbol}` + : row.displayBalance} + + ); }, sort: true, }, @@ -292,7 +299,13 @@ const LendingBalance = (props) => { dataField: "displayDepositBalance", text: t("borrow.depositBalance"), formatter: (cellContent, row, rowIndex) => { - return {typeof row.displayDepositBalance === 'number' ? `${row.displayDepositBalance} ${row.tokenSymbol}` : row.displayDepositBalance}; + return ( + + {typeof row.displayDepositBalance === "number" + ? `${row.displayDepositBalance} ${row.tokenSymbol}` + : row.displayDepositBalance} + + ); }, sort: true, }, @@ -326,7 +339,7 @@ const LendingBalance = (props) => { diff --git a/src/pages/Borrow/index.js b/src/pages/Borrow/index.js index 9d621bb8..09b84c89 100644 --- a/src/pages/Borrow/index.js +++ b/src/pages/Borrow/index.js @@ -1,34 +1,17 @@ -import { Row, Col } from "react-bootstrap"; - import GraphQlProvider from "./Provider"; import Page from "../../components/Page"; import GlobalOverall from "./GlobalOverall"; import BorrowCommon from "./Common"; import "./style.scss"; import ModalProvider from "./ModalProvider"; -import styled from "styled-components"; - -const StyledRow = styled(Row)` - margin-top: 40px; - - @media (max-width: 767px) { - margin-top: 20px; - } -`; -const Borrow = (props) => { +const Borrow = () => { return ( - + - - - - - - - - + + ); diff --git a/src/pages/CoinDetails/index.js b/src/pages/CoinDetails/index.js deleted file mode 100644 index 48878797..00000000 --- a/src/pages/CoinDetails/index.js +++ /dev/null @@ -1,804 +0,0 @@ -import React, {useContext, useEffect, useMemo, useState} from "react"; -import { Row, Col } from "react-bootstrap"; -import { useSelector, useDispatch } from "react-redux"; -import { Link } from "react-router-dom"; - -import Card, { ResponsiveCard } from "../../components/Card"; -import HistoricalChart from "../../components/HistoricalChart"; -import styled, { ThemeContext } from "styled-components"; -import { useIsDarkMode } from "../../state/user/hooks"; -import { fetchSelectedCoin, fetchHistoricalData } from "../../state/market/actions"; -import ArrowUp from "../../components/Icons/Coin/ArrowUp"; -import ArrowDown from "../../components/Icons/Coin/ArrowDown"; -import { useActiveWeb3React } from "../../hooks"; -import { useTokenContract } from "../../hooks/useContract"; -import Page from "../../components/Page"; -import Loading from "../../components/Loading"; -import GradientButton from "../../components/UI/Button"; -import CurrencyText from "../../components/CurrencyText"; -import dompurify from "dompurify"; - -const ChartResponsiveCard = styled(ResponsiveCard)` - @media (max-width: 991px) { - margin-bottom: -1px !important; - border: none; - - & .card-body { - padding-top: 15px; - padding-bottom: 15px; - } - } -`; - -const ResponsiveCol = styled.div` - @media (max-width: 991px) { - margin: -1px -30px 0; - padding: 15px 30px; - background-color: ${({ theme }) => theme.modalBG}; - } -`; - -const BalanceCard = styled(Card)` - & .card-body { - padding: 20px 30px; - } - - @media (max-width: 991px) { - margin-bottom: 40px; - & .card-body { - padding: 20px 15px; - } - } -`; - -const StyledCard = styled(Card)` - @media (max-width: 991px) { - border: 1px solid ${({ theme }) => theme.text4}; - margin-bottom: 0 !important; - - & .card-header, - & .card-body { - padding: 20px 15px; - } - - & .card-header { - min-height: 57px; - } - - h4 { - font-size: 0.875rem; - } - } - - @media (min-width: 992px) { - & .card-header { - padding: 15px 30px; - position: relative; - border: none; - - &::before { - content: ""; - position: absolute; - left: 30px; - right: 30px; - bottom: 0; - border-bottom: 1px solid ${({ theme }) => theme.text3}; - } - } - - h4 { - font-size: 1rem; - } - } -`; - -const StyledChangesCard = styled(StyledCard)` - @media (max-width: 991px) { - .card-body { - padding-top: 34px; - padding-bottom: 34px; - } - } -`; - -const About = styled.div` - font-weight: 400; - font-size: 1rem; - line-height: 19px; - - @media (max-width: 991px) { - font-size: 0.875rem; - line-height: 17px; - text-align: justify; - } -`; - -const DetailsCol = styled(Col)` - &:not(:last-child) { - margin-bottom: 20px; - } -`; - -const DetailsInnerCol = styled(Col)` - width: initial !important; - flex: 1; - max-width: 100%; -`; - -const DetailsDesc = styled.span` - font-weight: 400; - font-size: 1rem; - - @media (max-width: 991px) { - font-size: 0.75rem; - } -`; - -const DetailsValue = styled.span` - font-weight: 700; - font-size: 1rem; - text-align: right; - width: 100%; - - @media (max-width: 991px) { - font-size: 0.875rem; - } -`; - -const DetailsLink = styled(DetailsValue)` - color: ${({ theme }) => theme.primary}; - text-decoration: ${({ withUnderline }) => (withUnderline ? "underline" : "none")}; - - @media (max-width: 991px) { - font-weight: 400; - max-width: 180px; - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - } -`; - -const StatsDesc = styled.span` - color: ${({ theme }) => theme.text3}; - font-size: 0.875rem; - display: block; - margin-bottom: 1.25rem; - - @media (max-width: 991px) { - font-size: 0.75rem; - color: ${({ theme }) => theme.text1}; - margin-bottom: ${({ last }) => (!last ? "0.875rem" : "0")}; - } -`; - -const StatsValue = styled.span` - color: ${({ theme }) => theme.text1}; - font-size: 1rem; - font-weight: 700; - - @media (max-width: 991px) { - font-size: 0.875rem; - - margin-bottom: ${({ last }) => (!last ? "0.875rem" : "0")}; - } -`; - -const ChangesTitle = styled.span` - font-weight: 700; - font-size: 1.25rem; - margin-left: 1rem; -`; - -const ChangesSubtitle = styled.span` - font-weight: 400; - font-size: 0.875rem; - display: block; - margin-top: 18px; -`; - -const BalanceText = styled.span` - font-weight: 400; - font-size: 0.875rem; - margin-bottom: 20px; - - @media (min-width: 991px) { - margin-bottom: 0; - font-size: 1rem; - } -`; - -const BalanceValue = styled.span` - font-weight: 700; - font-size: 1rem; -`; - -const BuyHelper = styled.span` - font-weight: 400; - font-size: 1rem; - - @media (max-width: 991px) { - margin-bottom: 30px; - } -`; - -const BuyLink = styled(Link)` - flex: 1; - display: flex; - flex-direction: column; -`; - -const CoinDetails = (props) => { - const { account } = useActiveWeb3React(); - const theme = useContext(ThemeContext); - const darkMode = useIsDarkMode(); - const dispatch = useDispatch(); - const marketData = useSelector((state) => state.market); - const [walletBalance, setWalletBalance] = useState(false); - const selected = marketData.selected.data || false; - const tokenContract = useTokenContract(selected.contract_address); - - useEffect(() => { - if (tokenContract) { - tokenContract.decimals().then((decimals) => { - tokenContract.balanceOf(account).then((response) => { - const balance = response.toString(); - setWalletBalance(balance / 10 ** decimals); - }); - }); - } - }, [tokenContract, account]); - - useEffect(() => { - dispatch(fetchSelectedCoin(props.match.params.id)); - - dispatch(fetchHistoricalData(props.match.params.id)); - }, [dispatch, props.match.params.id]); - - const coinAbout = useMemo(() => { - if(selected) { - return dompurify.sanitize(selected?.description?.en); - } - - return '' - }, [selected?.description?.en]) - - return marketData.selected.loading ? ( - - - - -
    - -
    -
    - -
    -
    - ) : ( - - - {selected && typeof walletBalance !== "boolean" && ( - - -
    - Your "{selected.name}" Balance - - {selected.symbol.toUpperCase()} {walletBalance.toFixed(6)} ( - - {walletBalance * selected.market_data.current_price.usd} - - ) - -
    -
    - - )} - - - {selected && selected.contract_address && ( -
    - - Do you wanna Exchange {selected.name}? - -
    - - Buy - - - Sell - -
    -
    - )} - - -
    - -
    - - - - - - - - - - - - - - - - -
    - {selected && selected.market_data.price_change_percentage_24h >= 0 ? ( - - ) : ( - - )} - = 0 - ? "text-success" - : "text-danger" - } - > - {selected && Number(selected.market_data.price_change_percentage_24h).toFixed(4)}% - -
    - Daily Changes Percentage -
    -
    - - - - -
    - {selected && selected.market_data.price_change_percentage_7d >= 0 ? ( - - ) : ( - - )} - = 0 - ? "text-success" - : "text-danger" - } - > - {selected && Number(selected.market_data.price_change_percentage_7d).toFixed(4)}% - -
    - Weekly Changes Percentage -
    -
    - - - - -
    - {selected && selected.market_data.price_change_percentage_30d >= 0 ? ( - - ) : ( - - )} - = 0 - ? "text-success" - : "text-danger" - } - > - {selected && Number(selected.market_data.price_change_percentage_30d).toFixed(4)}% - -
    - Monthly Changes Percentage -
    -
    - - - - -
    - {selected && selected.market_data.price_change_percentage_1y >= 0 ? ( - - ) : ( - - )} - = 0 - ? "text-success" - : "text-danger" - } - > - {selected && Number(selected.market_data.price_change_percentage_1y).toFixed(4)}% - -
    - Yearly Changes Percentage -
    -
    - -
    - - - - -
    - - Market Cap - ${selected && selected.market_data.market_cap.usd} - - - All time High - - ${selected && selected.market_data.ath.usd} - - - - All Time Low - - ${selected && selected.market_data.atl.usd} - - -
    -
    -
    - -
    - - - - - - - - - - - - - - - - Currency Name - - - {selected && selected.name} - - - - - - - Symbol - - - {selected && selected.symbol.toUpperCase()} - - - - - - - Website - - - - {selected && selected.links.homepage[0]} - - - - - - - - Whitepaper - - - - {selected && selected.ico_data - ? selected.ico_data.links.whitepaper - : "-"} - - - - - - - - Block Explorer - - - - {selected && (selected.links.blockchain_site[0] || "-")} - - - - - - - - Github - - - - {selected && (selected.links.repos_url.github[0] || "-")} - - - - - - - - Twitter - - - - {selected && - (selected.links.twitter_screen_name - ? `https://twitter.com/${selected.links.twitter_screen_name}` - : "-")} - - - - - - - - Facebook - - - - {selected && - (selected.links.facebook_username - ? `https://facebook.com/${selected.links.facebook_username}` - : "-")} - - - - - - - - Reddit - - - - {selected && (selected.links.subreddit_url || "-")} - - - - - - - - Telegram - - - - {selected && - (selected.links.telegram_channel_identifier - ? `https://t.me/${selected.links.telegram_channel_identifier}` - : "-")} - - - - - - - - Bitcoin Talk - - - - {selected && - (selected.links.bitcointalk_thread_identifier - ? `https://bitcointalk.org/index.php?topic=${selected.links.bitcointalk_thread_identifier}` - : "-")} - - - - - - - - - -
    - ); -}; - -export default CoinDetails; diff --git a/src/pages/CoinDetailsPage/index.tsx b/src/pages/CoinDetailsPage/index.tsx new file mode 100644 index 00000000..a34f4e39 --- /dev/null +++ b/src/pages/CoinDetailsPage/index.tsx @@ -0,0 +1,12 @@ +import Page from "../../components/Page"; +import CoinDetails from "../../components/CoinDetails"; + +const CoinDetailsPage = (props: any) => { + return ( + + + + ); +}; + +export default CoinDetailsPage; diff --git a/src/pages/ContractDetails/index.js b/src/pages/ContractDetails/index.js index 0475e114..d905a9cf 100644 --- a/src/pages/ContractDetails/index.js +++ b/src/pages/ContractDetails/index.js @@ -103,13 +103,13 @@ const ContractDetails = (props) => {
    Buy Sell diff --git a/src/pages/CreateProposal/index.js b/src/pages/CreateProposal/index.js index 0ab244da..9566ac2b 100644 --- a/src/pages/CreateProposal/index.js +++ b/src/pages/CreateProposal/index.js @@ -17,15 +17,6 @@ import { toast } from "react-hot-toast"; import { useWalletModalToggle } from "../../state/application/hooks"; import { useTranslation } from "react-i18next"; -const StyledResponsiveCard = styled(ResponsiveCard)` - border: none; - margin-top: -70px; - - @media (min-width: 768px) { - margin-top: -30px; - } -`; - const Header = styled.div` padding: 0; display: flex; @@ -64,7 +55,7 @@ const FormLabel = styled(Form.Label)` const FormControl = styled(Form.Control)` border-radius: 18px; - background-color: ${({ theme }) => theme.bg2}; + background-color: ${({ theme }) => theme.bg1}; min-height: 56px; padding: 18px 20px; color: ${({ theme }) => theme.text1}; @@ -84,7 +75,7 @@ const FormControl = styled(Form.Control)` &:focus, &:active { outline: none; - background-color: ${({ theme }) => theme.bg2}; + background-color: ${({ theme }) => theme.bg1}; } `; @@ -191,7 +182,7 @@ const CreateProposals = (props) => { setLoading(false); toast.success("Your Proposal is in!"); if (res.hasOwnProperty("ipfsHash")) { - props.history.push(`/tools/governance/${id}`); + props.history.push(`/governance/${id}`); } } catch (error) { toast.error("Something went wrong!"); @@ -207,7 +198,7 @@ const CreateProposals = (props) => { dispatch(fetchSpaces()); } else { if (!spaces.hasOwnProperty(id)) { - props.history.push("/tools/governance"); + props.history.push("/governance"); } } }, [spaces, id, dispatch, props.history]); @@ -239,10 +230,10 @@ const CreateProposals = (props) => { ); return ( - + - +
    {t("governance.createProposal")}
    @@ -252,8 +243,7 @@ const CreateProposals = (props) => { {t("governance.question")} handleChange("name", e.target.value)} @@ -263,7 +253,8 @@ const CreateProposals = (props) => { {t("governance.whatIsYourProposal")} handleChange("body", e.target.value)} @@ -390,7 +381,7 @@ const CreateProposals = (props) => {
    - +
    diff --git a/src/pages/CrossAnySwap/index.js b/src/pages/CrossAnySwap/index.js index 0ce24678..cff5ac05 100644 --- a/src/pages/CrossAnySwap/index.js +++ b/src/pages/CrossAnySwap/index.js @@ -1,35 +1,35 @@ -import styled from 'styled-components'; -import { Row, Col, Button as BS } from 'react-bootstrap'; +import styled from "styled-components"; +import { Row, Col, Button as BS, ListGroup } from "react-bootstrap"; import SVG from "react-inlinesvg"; -import {ETHER} from "@uniswap/sdk"; -import {useCallback, useEffect, useReducer, useState} from "react"; +import { ETHER } from "@uniswap/sdk"; +import { useCallback, useEffect, useReducer, useState } from "react"; -import EXCHANGE_ABI from '../../constants/abis/exchange.json'; +import EXCHANGE_ABI from "../../constants/abis/exchange.json"; import Page from "../../components/Page"; import DefaultCard from "../../components/Card"; import BridgeInputPanel from "../../components/BridgeInputPanel"; -import SwapIcon from '../../assets/images/cross/swap.svg'; +import SwapIcon from "../../assets/images/cross/swap.svg"; import AddressInputPanel from "../../components/AddressInputPanel"; import BigNumber from "bignumber.js"; -import {brokenTokens, ZERO} from "../../constants"; +import { brokenTokens, ZERO } from "../../constants"; import getNetConfig from "../../config"; -import {useTranslation} from "react-i18next"; -import {useActiveWeb3React} from "../../hooks"; -import {useBetaMessageManager} from "../../contexts/LocalStorage"; -import {amountFormatter, getAllQueryParams, useExchangeContract} from "../../utils/cross"; -import {useTransactionAdder} from "../../state/transactions/hooks"; -import {INITIAL_TOKENS_CONTEXT, useTokenDetails} from "../../contexts/Tokens"; -import {useAddressAllowance} from "../../contexts/Allowances"; -import {useAddressBalance, useExchangeReserves} from "../../contexts/Balances"; -import {ethers} from "ethers"; -import {getWeb3BaseInfo, getWeb3ConTract} from "../../utils/web3/txns"; -import {recordTxns} from "../../utils/record"; -import {isAddress} from "../../utils"; -import {useWalletModalToggle} from "../../state/application/hooks"; +import { useTranslation } from "react-i18next"; +import { useActiveWeb3React } from "../../hooks"; +import { useBetaMessageManager } from "../../contexts/LocalStorage"; +import { amountFormatter, getAllQueryParams, useExchangeContract } from "../../utils/cross"; +import { useTransactionAdder } from "../../state/transactions/hooks"; +import { INITIAL_TOKENS_CONTEXT, useTokenDetails } from "../../contexts/Tokens"; +import { useAddressAllowance } from "../../contexts/Allowances"; +import { useAddressBalance, useExchangeReserves } from "../../contexts/Balances"; +import { ethers } from "ethers"; +import { getWeb3BaseInfo, getWeb3ConTract } from "../../utils/web3/txns"; +import { recordTxns } from "../../utils/record"; +import { isAddress } from "../../utils"; +import { useWalletModalToggle } from "../../state/application/hooks"; import HardwareTip from "../../components/HardwareTips"; -import {Modal as BSModal} from "../../components/Modal/bootstrap"; +import { Modal as BSModal } from "../../components/Modal/bootstrap"; import WarningTip from "../../components/WarningTip"; -import {toast} from "react-hot-toast"; +import { toast } from "react-hot-toast"; const config = getNetConfig(); @@ -39,46 +39,46 @@ const config = getNetConfig(); // } const Modal = styled(BSModal)` - & .modal-dialog { - max-width: 800px; - } - + & .modal-dialog { + max-width: 800px; + } + & .modal-body { - padding: 20px 30px 30px; + padding: 20px 30px 30px; } & .modal-header { - padding: 30px; + padding: 30px; } -` +`; const TabButton = styled(BS)` - min-height: 48px; - height: 48px; - border-radius: 18px; - font-weight: 500; - font-size: 1rem; - min-width: 127px; - - @media (max-width: 991px) { - width: 100%; - display: flex; - align-items: center; - justify-content: center; - line-height: 1; - font-size: .875rem; - min-height: 40px; - height: 40px; - padding: 0; - border-radius: 12px; - min-width: 105px; - } -` + min-height: 48px; + height: 48px; + border-radius: 18px; + font-weight: 500; + font-size: 1rem; + min-width: 127px; + + @media (max-width: 991px) { + width: 100%; + display: flex; + align-items: center; + justify-content: center; + line-height: 1; + font-size: 0.875rem; + min-height: 40px; + height: 40px; + padding: 0; + border-radius: 12px; + min-width: 105px; + } +`; const Button = styled(BS)` - min-height: 48px; - height: 48px; - min-width: 205px; -` + min-height: 48px; + height: 48px; + min-width: 205px; +`; // const AlertButton = styled(BS)` // min-height: 48px; @@ -95,18 +95,17 @@ const Title = styled.h1` color: ${({ theme }) => theme.text1}; line-height: 3rem; - @media (max-width: 1199px) { font-size: 2.25rem; } @media (max-width: 991px) { font-size: 2rem; - margin-bottom: 0.5rem; + margin-bottom: 0.5rem; } @media (max-width: 767px) { font-size: 1.5rem; } -` +`; const Header = styled.div` display: flex; @@ -114,33 +113,33 @@ const Header = styled.div` justify-content: space-between; margin-bottom: 20px; margin-top: 24px; - - @media(max-width: 991px) { + + @media (max-width: 991px) { margin-top: 10px; - flex-direction: column; - align-items: stretch; + flex-direction: column; + align-items: stretch; } -` +`; const Card = styled(DefaultCard)` - margin-bottom: 1rem; - - & > .card-body { - padding: 36px 64px 24px; + margin-bottom: 1rem; - @media (max-width: 991px) { - padding: 24px; - } + & > .card-body { + padding: 36px 64px 24px; - @media (max-width: 576px) { - padding: 16px; - } - } -` + @media (max-width: 991px) { + padding: 24px; + } + + @media (max-width: 576px) { + padding: 16px; + } + } +`; // const SlippageCard = styled(DefaultCard)` // margin-bottom: 1rem; - + // & > .card-body { // padding: 0 64px; // min-height: 56px; @@ -156,59 +155,58 @@ const Card = styled(DefaultCard)` // ` const TabHeader = styled.div` - display: grid; - grid-column-gap: 25px; - grid-template-columns: 127px 127px; - border-radius: 20px; - - @media (max-width: 991px) { - padding: 8px; - background-color: ${({ theme }) => theme.modalBG}; - grid-template-columns: 1fr 1fr; - grid-column-gap: 16px; - border: 1px solid ${({ theme }) => theme.borderColor}; - } - - @media (max-width: 576px) { - grid-template-columns: 1fr; - grid-row-gap: 12px; - grid-column-gap: 0; - } -` + display: grid; + grid-column-gap: 25px; + grid-template-columns: 127px 127px; + border-radius: 20px; -const SwapCurrencies = styled.div` - display: flex; - align-items: center; - justify-content: center; - padding: 12px; - cursor: pointer; + @media (max-width: 991px) { + padding: 8px; + background-color: ${({ theme }) => theme.modalBG}; + grid-template-columns: 1fr 1fr; + grid-column-gap: 16px; + border: 1px solid ${({ theme }) => theme.borderColor}; + } + @media (max-width: 576px) { + grid-template-columns: 1fr; + grid-row-gap: 12px; + grid-column-gap: 0; + } +`; - @media (max-width: 576px) { - padding: 8px; - } -` +const SwapCurrencies = styled.div` + display: flex; + align-items: center; + justify-content: center; + padding: 12px; + cursor: pointer; + + @media (max-width: 576px) { + padding: 8px; + } +`; const SubmitButtonContainer = styled.div` - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - margin-top: 32px; - - @media (max-width: 1199px) { - margin-top: 28px; - } - - @media (max-width: 991px) { - margin-top: 24px; - } - - @media (max-width: 767px) { - margin-top: 16px; - align-items: stretch; - } -` + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + margin-top: 32px; + + @media (max-width: 1199px) { + margin-top: 28px; + } + + @media (max-width: 991px) { + margin-top: 24px; + } + + @media (max-width: 767px) { + margin-top: 16px; + align-items: stretch; + } +`; // const AlertContainer = styled.div` // border-radius: 18px; @@ -217,7 +215,7 @@ const SubmitButtonContainer = styled.div` // justify-content: space-between; // padding: 4px 4px 4px 64px; // background-color: ${({ theme }) => theme.primaryLight}; - + // @media (max-width: 1199px) { // padding-left: 48px; // } @@ -240,181 +238,187 @@ const SubmitButtonContainer = styled.div` // ` const TransactionInfo = styled.div` - padding: 0 0 1.5625rem; - border-bottom: 0.0625rem solid ${({ theme }) => theme.text3}; - @media screen and (max-width: 960px) { - padding: 0 0.625rem 0.625rem; - height: auto; - } -` + padding: 0 0 1.5625rem; + border-bottom: 0.0625rem solid ${({ theme }) => theme.text3}; + @media screen and (max-width: 960px) { + padding: 0 0.625rem 0.625rem; + height: auto; + } +`; const LastSummaryText = styled.div` - display: flex; - justify-content: flex-start; - align-items:center; - font-size: 0.75rem; - font-weight: normal; - font-stretch: normal; - font-style: normal; - line-height: 1.17; - letter-spacing: normal; - color: ${({ theme }) => theme.text1}; - height: 32px; - margin-bottom: 0.625rem; - - .icon { - width: 32px; - height: 32px; - padding: 8px; - object-fit: contain; - border: solid 0.5px ${({ theme }) => theme.text2}; - background-color: ${({ theme }) => theme.text2}; - border-radius: 100%; - margin-right: 0.625rem; - - img { - height: 100%; - display:block; - } - } -` + display: flex; + justify-content: flex-start; + align-items: center; + font-size: 0.75rem; + font-weight: normal; + font-stretch: normal; + font-style: normal; + line-height: 1.17; + letter-spacing: normal; + color: ${({ theme }) => theme.text1}; + height: 32px; + margin-bottom: 0.625rem; + + .icon { + width: 32px; + height: 32px; + padding: 8px; + object-fit: contain; + border: solid 0.5px ${({ theme }) => theme.text2}; + background-color: ${({ theme }) => theme.text2}; + border-radius: 100%; + margin-right: 0.625rem; + + img { + height: 100%; + display: block; + } + } +`; const ValueWrapper = styled.span` - display: flex; - justify-content: center; - align-items: center; - padding: 0.375rem 0.3rem 0.375rem 0.3rem; - background-color: ${({ theme }) => theme.bg1}; - border-radius: 1rem; - font-variant: tabular-nums; -` - -const INPUT = 0 -const OUTPUT = 1 -const ETH_TO_TOKEN = 0 -const TOKEN_TO_ETH = 1 -const TOKEN_TO_TOKEN = 2 + display: flex; + justify-content: center; + align-items: center; + padding: 0.375rem 0.3rem 0.375rem 0.3rem; + background-color: ${({ theme }) => theme.bg1}; + border-radius: 1rem; + font-variant: tabular-nums; +`; + +const INPUT = 0; +const OUTPUT = 1; +const ETH_TO_TOKEN = 0; +const TOKEN_TO_ETH = 1; +const TOKEN_TO_TOKEN = 2; // denominated in bips -const ALLOWED_SLIPPAGE_DEFAULT = 50 -const TOKEN_ALLOWED_SLIPPAGE_DEFAULT = 50 +const ALLOWED_SLIPPAGE_DEFAULT = 50; +const TOKEN_ALLOWED_SLIPPAGE_DEFAULT = 50; // 15 minutes, denominated in seconds -const DEFAULT_DEADLINE_FROM_NOW = 60 * 15 +const DEFAULT_DEADLINE_FROM_NOW = 60 * 15; // % above the calculated gas cost that we actually send, denominated in bips -const GAS_MARGIN = new BigNumber(1000) +const GAS_MARGIN = new BigNumber(1000); function calculateSlippageBounds(value, token = false, tokenAllowedSlippage, allowedSlippage) { if (value) { - const offset = value.times(token ? tokenAllowedSlippage : allowedSlippage).dividedBy(new BigNumber(10000)) - const minimum = value.minus(offset) - const maximum = value.plus(offset) + const offset = value.times(token ? tokenAllowedSlippage : allowedSlippage).dividedBy(new BigNumber(10000)); + const minimum = value.minus(offset); + const maximum = value.plus(offset); return { minimum: minimum.lt(ZERO) ? ZERO : new BigNumber(minimum.toFixed(0)), - maximum: maximum.gt(new BigNumber(2).pow(256)) ? new BigNumber(2).pow(256) : new BigNumber(maximum.toFixed(0)) - } + maximum: maximum.gt(new BigNumber(2).pow(256)) + ? new BigNumber(2).pow(256) + : new BigNumber(maximum.toFixed(0)), + }; } else { - return {} + return {}; } } function getSwapType(inputCurrency, outputCurrency) { if (!inputCurrency || !outputCurrency) { - return null + return null; } else if (inputCurrency === config.symbol) { - return ETH_TO_TOKEN + return ETH_TO_TOKEN; } else if (outputCurrency === config.symbol) { - return TOKEN_TO_ETH + return TOKEN_TO_ETH; } else { - return TOKEN_TO_TOKEN + return TOKEN_TO_TOKEN; } } // this mocks the getInputPrice function, and calculates the required output function calculateEtherTokenOutputFromInput(inputAmount, inputReserve, outputReserve) { - const inputAmountWithFee = inputAmount.times(new BigNumber(997)) - const numerator = inputAmountWithFee.times(outputReserve) - const denominator = inputReserve.times(new BigNumber(1000)).plus(inputAmountWithFee) - return numerator.dividedBy(denominator) + const inputAmountWithFee = inputAmount.times(new BigNumber(997)); + const numerator = inputAmountWithFee.times(outputReserve); + const denominator = inputReserve.times(new BigNumber(1000)).plus(inputAmountWithFee); + return numerator.dividedBy(denominator); } // this mocks the getOutputPrice function, and calculates the required input function calculateEtherTokenInputFromOutput(outputAmount, inputReserve, outputReserve) { - const numerator = inputReserve.times(outputAmount).times(new BigNumber(1000)) - const denominator = outputReserve.minus(outputAmount).times(new BigNumber(997)) - return numerator.dividedBy(denominator).plus(new BigNumber(1)) + const numerator = inputReserve.times(outputAmount).times(new BigNumber(1000)); + const denominator = outputReserve.minus(outputAmount).times(new BigNumber(997)); + return numerator.dividedBy(denominator).plus(new BigNumber(1)); } function getInitialSwapState(state) { return { - independentValue: state.exactFieldURL && state.exactAmountURL ? state.exactAmountURL : '', // this is a user input - dependentValue: '', // this is a calculated number - independentField: state.exactFieldURL === 'output' ? OUTPUT : INPUT, - inputCurrency: state.inputCurrencyURL ? state.inputCurrencyURL : state.outputCurrencyURL === config.symbol ? '' : config.symbol, + independentValue: state.exactFieldURL && state.exactAmountURL ? state.exactAmountURL : "", // this is a user input + dependentValue: "", // this is a calculated number + independentField: state.exactFieldURL === "output" ? OUTPUT : INPUT, + inputCurrency: state.inputCurrencyURL + ? state.inputCurrencyURL + : state.outputCurrencyURL === config.symbol + ? "" + : config.symbol, outputCurrency: state.outputCurrencyURL ? state.outputCurrencyURL === config.symbol ? !state.inputCurrencyURL || (state.inputCurrencyURL && state.inputCurrencyURL !== config.symbol) ? config.symbol - : '' + : "" : state.outputCurrencyURL : state.initialCurrency - ? state.initialCurrency - : config.initToken - } + ? state.initialCurrency + : config.initToken, + }; } function swapStateReducer(state, action) { switch (action.type) { - case 'FLIP_INDEPENDENT': { - const { independentField, inputCurrency, outputCurrency } = state + case "FLIP_INDEPENDENT": { + const { independentField, inputCurrency, outputCurrency } = state; return { ...state, - dependentValue: '', + dependentValue: "", independentField: independentField === INPUT ? OUTPUT : INPUT, inputCurrency: outputCurrency, - outputCurrency: inputCurrency - } + outputCurrency: inputCurrency, + }; } - case 'SELECT_CURRENCY': { - const { inputCurrency, outputCurrency } = state - const { field, currency } = action.payload + case "SELECT_CURRENCY": { + const { inputCurrency, outputCurrency } = state; + const { field, currency } = action.payload; - const newInputCurrency = field === INPUT ? currency : inputCurrency - const newOutputCurrency = field === OUTPUT ? currency : outputCurrency + const newInputCurrency = field === INPUT ? currency : inputCurrency; + const newOutputCurrency = field === OUTPUT ? currency : outputCurrency; if (newInputCurrency === newOutputCurrency) { return { ...state, - inputCurrency: field === INPUT ? currency : '', - outputCurrency: field === OUTPUT ? currency : '' - } + inputCurrency: field === INPUT ? currency : "", + outputCurrency: field === OUTPUT ? currency : "", + }; } else { return { ...state, inputCurrency: newInputCurrency, - outputCurrency: newOutputCurrency - } + outputCurrency: newOutputCurrency, + }; } } - case 'UPDATE_INDEPENDENT': { - const { field, value } = action.payload - const { dependentValue, independentValue } = state + case "UPDATE_INDEPENDENT": { + const { field, value } = action.payload; + const { dependentValue, independentValue } = state; return { ...state, independentValue: value, - dependentValue: value === independentValue ? dependentValue : '', - independentField: field - } + dependentValue: value === independentValue ? dependentValue : "", + independentField: field, + }; } - case 'UPDATE_DEPENDENT': { + case "UPDATE_DEPENDENT": { return { ...state, - dependentValue: action.payload - } + dependentValue: action.payload, + }; } default: { - return getInitialSwapState() + return getInitialSwapState(); } } } @@ -427,27 +431,27 @@ function getExchangeRate(inputValue, inputDecimals, outputValue, outputDecimals, outputValue && (outputDecimals || outputDecimals === 0) ) { - const factor = new BigNumber(10).pow(new BigNumber(18)) + const factor = new BigNumber(10).pow(new BigNumber(18)); if (invert) { return inputValue .times(factor) .times(new BigNumber(10).pow(new BigNumber(outputDecimals))) .dividedBy(new BigNumber(10).pow(new BigNumber(inputDecimals))) - .dividedBy(outputValue) + .dividedBy(outputValue); } else { return outputValue .times(factor) .times(new BigNumber(10).pow(new BigNumber(inputDecimals))) .dividedBy(new BigNumber(10).pow(new BigNumber(outputDecimals))) - .dividedBy(inputValue) + .dividedBy(inputValue); } } } catch {} } function calculateGasMargin(value, margin) { - const offset = value.times(margin).dividedBy(new BigNumber(10000)) - return value.plus(offset) + const offset = value.times(margin).dividedBy(new BigNumber(10000)); + return value.plus(offset); } function getMarketRate( @@ -461,76 +465,73 @@ function getMarketRate( invert = false ) { if (swapType === ETH_TO_TOKEN) { - return getExchangeRate(outputReserveETH, 18, outputReserveToken, outputDecimals, invert) + return getExchangeRate(outputReserveETH, 18, outputReserveToken, outputDecimals, invert); } else if (swapType === TOKEN_TO_ETH) { - return getExchangeRate(inputReserveToken, inputDecimals, inputReserveETH, 18, invert) + return getExchangeRate(inputReserveToken, inputDecimals, inputReserveETH, 18, invert); } else if (swapType === TOKEN_TO_TOKEN) { - const factor = new BigNumber(10).pow(new BigNumber(18)) - const firstRate = getExchangeRate(inputReserveToken, inputDecimals, inputReserveETH, 18) - const secondRate = getExchangeRate(outputReserveETH, 18, outputReserveToken, outputDecimals) + const factor = new BigNumber(10).pow(new BigNumber(18)); + const firstRate = getExchangeRate(inputReserveToken, inputDecimals, inputReserveETH, 18); + const secondRate = getExchangeRate(outputReserveETH, 18, outputReserveToken, outputDecimals); try { - return !!(firstRate && secondRate) ? firstRate.times(secondRate).dividedBy(factor) : undefined + return !!(firstRate && secondRate) ? firstRate.times(secondRate).dividedBy(factor) : undefined; } catch {} } } - -const CrossAnySwap = props => { - +const CrossAnySwap = (props) => { // @todo: remove old data const [inputCurrencySwap, setInputCurrency] = useState(ETHER); const [outputCurrencySwap, setOutputCurrency] = useState(undefined); - const { t } = useTranslation() + const { t } = useTranslation(); const { account, chainId, error } = useActiveWeb3React(); - const [showBetaMessage] = useBetaMessageManager() - let walletType = sessionStorage.getItem('walletType') + const [showBetaMessage] = useBetaMessageManager(); + let walletType = sessionStorage.getItem("walletType"); - let params = getAllQueryParams() - params = params ? params : {} + let params = getAllQueryParams(); + params = params ? params : {}; - const urlAddedTokens = {} + const urlAddedTokens = {}; if (params && params.inputCurrency) { - urlAddedTokens[params.inputCurrency] = true + urlAddedTokens[params.inputCurrency] = true; } if (params && params.outputCurrency) { - urlAddedTokens[params.outputCurrency] = true + urlAddedTokens[params.outputCurrency] = true; } if (params && params.tokenAddress) { - urlAddedTokens[params.tokenAddress] = true + urlAddedTokens[params.tokenAddress] = true; } - const addTransaction = useTransactionAdder() + const addTransaction = useTransactionAdder(); const initialSlippage = (token = false) => { - let slippage = Number.parseInt(params.slippage) + let slippage = Number.parseInt(params.slippage); if (!isNaN(slippage) && (slippage === 0 || slippage >= 1)) { - return slippage // round to match custom input availability + return slippage; // round to match custom input availability } // check for token <-> token slippage option - return token ? TOKEN_ALLOWED_SLIPPAGE_DEFAULT : ALLOWED_SLIPPAGE_DEFAULT - } + return token ? TOKEN_ALLOWED_SLIPPAGE_DEFAULT : ALLOWED_SLIPPAGE_DEFAULT; + }; const initialRecipient = () => { if (sending && params.recipient) { - return params.recipient + return params.recipient; } - return '' - } + return ""; + }; + const [sending, setSending] = useState(false); - const [sending, setSending] = useState(false) + const [brokenTokenWarning, setBrokenTokenWarning] = useState(); - const [brokenTokenWarning, setBrokenTokenWarning] = useState() + const [deadlineFromNow, setDeadlineFromNow] = useState(DEFAULT_DEADLINE_FROM_NOW); - const [deadlineFromNow, setDeadlineFromNow] = useState(DEFAULT_DEADLINE_FROM_NOW) + const [rawSlippage, setRawSlippage] = useState(() => initialSlippage()); + const [rawTokenSlippage, setRawTokenSlippage] = useState(() => initialSlippage(true)); - const [rawSlippage, setRawSlippage] = useState(() => initialSlippage()) - const [rawTokenSlippage, setRawTokenSlippage] = useState(() => initialSlippage(true)) + const [slippageView, setSlippageView] = useState(false); - const [slippageView, setSlippageView] = useState(false) - - const allowedSlippageBig = new BigNumber(rawSlippage) - const tokenAllowedSlippageBig = new BigNumber(rawTokenSlippage) + const allowedSlippageBig = new BigNumber(rawSlippage); + const tokenAllowedSlippageBig = new BigNumber(rawTokenSlippage); const [swapState, dispatchSwapState] = useReducer( swapStateReducer, @@ -539,109 +540,122 @@ const CrossAnySwap = props => { inputCurrencyURL: params.inputCurrency, outputCurrencyURL: params.outputCurrency || params.tokenAddress, exactFieldURL: params.exactField, - exactAmountURL: params.exactAmount + exactAmountURL: params.exactAmount, }, getInitialSwapState - ) + ); - const { independentValue, dependentValue, independentField, inputCurrency, outputCurrency } = swapState + const { independentValue, dependentValue, independentField, inputCurrency, outputCurrency } = swapState; useEffect(() => { - setBrokenTokenWarning(false) + setBrokenTokenWarning(false); for (let i = 0; i < brokenTokens.length; i++) { if ( brokenTokens[i].toLowerCase() === outputCurrency.toLowerCase() || brokenTokens[i].toLowerCase() === inputCurrency.toLowerCase() ) { - setBrokenTokenWarning(true) + setBrokenTokenWarning(true); } } - }, [outputCurrency, inputCurrency]) + }, [outputCurrency, inputCurrency]); const [recipient, setRecipient] = useState({ address: initialRecipient(), - name: '' - }) - const [recipientError, setRecipientError] = useState(null) + name: "", + }); + const [recipientError, setRecipientError] = useState(null); // get swap type from the currency types - const swapType = getSwapType(inputCurrency, outputCurrency) - + const swapType = getSwapType(inputCurrency, outputCurrency); // get decimals and exchange address for each of the currency types - const { symbol: inputSymbol, decimals: inputDecimals, exchangeAddress: inputExchangeAddress, isSwitch: inputIsSwitch } = useTokenDetails( - inputCurrency - ) - const { symbol: outputSymbol, decimals: outputDecimals, exchangeAddress: outputExchangeAddress, isSwitch: outputIsSwitch } = useTokenDetails( - outputCurrency - ) - - const inputExchangeContract = useExchangeContract(inputExchangeAddress) - const outputExchangeContract = useExchangeContract(outputExchangeAddress) - const contract = swapType === ETH_TO_TOKEN ? outputExchangeContract : inputExchangeContract + const { + symbol: inputSymbol, + decimals: inputDecimals, + exchangeAddress: inputExchangeAddress, + isSwitch: inputIsSwitch, + } = useTokenDetails(inputCurrency); + const { + symbol: outputSymbol, + decimals: outputDecimals, + exchangeAddress: outputExchangeAddress, + isSwitch: outputIsSwitch, + } = useTokenDetails(outputCurrency); + + const inputExchangeContract = useExchangeContract(inputExchangeAddress); + const outputExchangeContract = useExchangeContract(outputExchangeAddress); + const contract = swapType === ETH_TO_TOKEN ? outputExchangeContract : inputExchangeContract; // get input allowance - const inputAllowance = useAddressAllowance(account, inputCurrency, inputExchangeAddress) + const inputAllowance = useAddressAllowance(account, inputCurrency, inputExchangeAddress); // fetch reserves for each of the currency types - const { reserveETH: inputReserveETH, reserveToken: inputReserveToken } = useExchangeReserves(inputCurrency) - const { reserveETH: outputReserveETH, reserveToken: outputReserveToken } = useExchangeReserves(outputCurrency) + const { reserveETH: inputReserveETH, reserveToken: inputReserveToken } = useExchangeReserves(inputCurrency); + const { reserveETH: outputReserveETH, reserveToken: outputReserveToken } = useExchangeReserves(outputCurrency); // get balances for each of the currency types - const inputBalance = useAddressBalance(account, inputCurrency) - const outputBalance = useAddressBalance(account, outputCurrency) + const inputBalance = useAddressBalance(account, inputCurrency); + const outputBalance = useAddressBalance(account, outputCurrency); const inputBalanceFormatted = !!(inputBalance && Number.isInteger(inputDecimals)) ? amountFormatter(inputBalance, inputDecimals, Math.min(6, inputDecimals)) - : '' + : ""; const outputBalanceFormatted = !!(outputBalance && Number.isInteger(outputDecimals)) ? amountFormatter(outputBalance, outputDecimals, Math.min(6, outputDecimals)) - : '' + : ""; // compute useful transforms of the data above - const independentDecimals = independentField === INPUT ? inputDecimals : outputDecimals - const dependentDecimals = independentField === OUTPUT ? inputDecimals : outputDecimals + const independentDecimals = independentField === INPUT ? inputDecimals : outputDecimals; + const dependentDecimals = independentField === OUTPUT ? inputDecimals : outputDecimals; // declare/get parsed and formatted versions of input/output values - const [independentValueParsed, setIndependentValueParsed] = useState() + const [independentValueParsed, setIndependentValueParsed] = useState(); const dependentValueFormatted = !!(dependentValue && (dependentDecimals || dependentDecimals === 0)) ? amountFormatter(dependentValue, dependentDecimals, dependentDecimals, false) - // ? amountFormatter(dependentValue, dependentDecimals, Math.min(6, dependentDecimals), false) - : '' - const inputValueParsed = independentField === INPUT ? independentValueParsed : dependentValue - const outputValueParsed = independentField === OUTPUT ? independentValueParsed : dependentValue - let inputValueFormatted = independentField === INPUT ? independentValue : dependentValueFormatted - let outputValueFormatted = independentField === OUTPUT ? independentValue : dependentValueFormatted + : // ? amountFormatter(dependentValue, dependentDecimals, Math.min(6, dependentDecimals), false) + ""; + const inputValueParsed = independentField === INPUT ? independentValueParsed : dependentValue; + const outputValueParsed = independentField === OUTPUT ? independentValueParsed : dependentValue; + let inputValueFormatted = independentField === INPUT ? independentValue : dependentValueFormatted; + let outputValueFormatted = independentField === OUTPUT ? independentValue : dependentValueFormatted; if (independentField) { - inputValueFormatted *= 1 + 0.001 - inputValueFormatted = Number(inputValueFormatted.toFixed(dependentDecimals)) ? Number(inputValueFormatted.toFixed(Math.min(8, dependentDecimals))) : '' + inputValueFormatted *= 1 + 0.001; + inputValueFormatted = Number(inputValueFormatted.toFixed(dependentDecimals)) + ? Number(inputValueFormatted.toFixed(Math.min(8, dependentDecimals))) + : ""; } else { - outputValueFormatted *= 1 - 0.001 - outputValueFormatted = Number(outputValueFormatted.toFixed(dependentDecimals)) ? Number(outputValueFormatted.toFixed(Math.min(8, dependentDecimals))) : '' - + outputValueFormatted *= 1 - 0.001; + outputValueFormatted = Number(outputValueFormatted.toFixed(dependentDecimals)) + ? Number(outputValueFormatted.toFixed(Math.min(8, dependentDecimals))) + : ""; } // validate + parse independent value - const [independentError, setIndependentError] = useState() + const [independentError, setIndependentError] = useState(); useEffect(() => { if (independentValue && (independentDecimals || independentDecimals === 0)) { try { - const parsedValue = new BigNumber(ethers.utils.parseUnits(independentValue, independentDecimals).toString()); - - if (parsedValue.lte(ethers.constants.Zero.toString()) || parsedValue.gte(ethers.constants.MaxUint256.toString())) { - throw Error() + const parsedValue = new BigNumber( + ethers.utils.parseUnits(independentValue, independentDecimals).toString() + ); + + if ( + parsedValue.lte(ethers.constants.Zero.toString()) || + parsedValue.gte(ethers.constants.MaxUint256.toString()) + ) { + throw Error(); } else { - setIndependentValueParsed(new BigNumber(parsedValue.toFixed(0))) - setIndependentError(null) + setIndependentValueParsed(new BigNumber(parsedValue.toFixed(0))); + setIndependentError(null); } } catch { - setIndependentError(t('inputNotValid')) + setIndependentError(t("inputNotValid")); } return () => { - setIndependentValueParsed() - setIndependentError() - } + setIndependentValueParsed(); + setIndependentError(); + }; } - }, [independentValue, independentDecimals, t]) + }, [independentValue, independentDecimals, t]); // calculate slippage from target rate const { minimum: dependentValueMinumum, maximum: dependentValueMaximum } = calculateSlippageBounds( @@ -649,136 +663,152 @@ const CrossAnySwap = props => { swapType === TOKEN_TO_TOKEN, tokenAllowedSlippageBig, allowedSlippageBig - ) + ); // validate input allowance + balance - const [inputError, setInputError] = useState() - const [showUnlock, setShowUnlock] = useState(false) + const [inputError, setInputError] = useState(); + const [showUnlock, setShowUnlock] = useState(false); useEffect(() => { - const inputValueCalculation = independentField === INPUT ? independentValueParsed : dependentValueMaximum + const inputValueCalculation = independentField === INPUT ? independentValueParsed : dependentValueMaximum; if (inputBalance && (inputAllowance || inputCurrency === config.symbol) && inputValueCalculation) { if (inputBalance.lt(inputValueCalculation)) { - setInputError('Insufficient balance') + setInputError("Insufficient balance"); } else if (inputCurrency !== config.symbol && inputAllowance.lt(inputValueCalculation.toFixed(0))) { - setInputError("Approve") - setShowUnlock(true) + setInputError("Approve"); + setShowUnlock(true); } else { - setInputError(null) - setShowUnlock(false) + setInputError(null); + setShowUnlock(false); } return () => { - setInputError() - setShowUnlock(false) - } + setInputError(); + setShowUnlock(false); + }; } - }, [independentField, independentValueParsed, dependentValueMaximum, inputBalance, inputCurrency, inputAllowance, t]) + }, [ + independentField, + independentValueParsed, + dependentValueMaximum, + inputBalance, + inputCurrency, + inputAllowance, + t, + ]); // calculate dependent value useEffect(() => { - const amount = independentValueParsed + const amount = independentValueParsed; if (swapType === ETH_TO_TOKEN) { - const reserveETH = outputReserveETH - const reserveToken = outputReserveToken + const reserveETH = outputReserveETH; + const reserveToken = outputReserveToken; if (amount && reserveETH && reserveToken) { try { const calculatedDependentValue = independentField === INPUT ? calculateEtherTokenOutputFromInput(amount, reserveETH, reserveToken) - : calculateEtherTokenInputFromOutput(amount, reserveETH, reserveToken) + : calculateEtherTokenInputFromOutput(amount, reserveETH, reserveToken); if (calculatedDependentValue.lte(ethers.constants.Zero.toString())) { - throw Error() + throw Error(); } dispatchSwapState({ - type: 'UPDATE_DEPENDENT', - payload: calculatedDependentValue - }) + type: "UPDATE_DEPENDENT", + payload: calculatedDependentValue, + }); } catch { - setIndependentError(t('insufficientLiquidity')) + setIndependentError(t("insufficientLiquidity")); } return () => { - dispatchSwapState({ type: 'UPDATE_DEPENDENT', payload: '' }) - } + dispatchSwapState({ type: "UPDATE_DEPENDENT", payload: "" }); + }; } } else if (swapType === TOKEN_TO_ETH) { - const reserveETH = inputReserveETH - const reserveToken = inputReserveToken + const reserveETH = inputReserveETH; + const reserveToken = inputReserveToken; if (amount && reserveETH && reserveToken) { try { const calculatedDependentValue = independentField === INPUT ? calculateEtherTokenOutputFromInput(amount, reserveToken, reserveETH) - : calculateEtherTokenInputFromOutput(amount, reserveToken, reserveETH) + : calculateEtherTokenInputFromOutput(amount, reserveToken, reserveETH); if (calculatedDependentValue.lte(ethers.constants.Zero.toString())) { - throw Error() + throw Error(); } dispatchSwapState({ - type: 'UPDATE_DEPENDENT', - payload: calculatedDependentValue - }) + type: "UPDATE_DEPENDENT", + payload: calculatedDependentValue, + }); } catch { - setIndependentError(t('insufficientLiquidity')) + setIndependentError(t("insufficientLiquidity")); } return () => { - dispatchSwapState({ type: 'UPDATE_DEPENDENT', payload: '' }) - } + dispatchSwapState({ type: "UPDATE_DEPENDENT", payload: "" }); + }; } } else if (swapType === TOKEN_TO_TOKEN) { - const reserveETHFirst = inputReserveETH - const reserveTokenFirst = inputReserveToken + const reserveETHFirst = inputReserveETH; + const reserveTokenFirst = inputReserveToken; - const reserveETHSecond = outputReserveETH - const reserveTokenSecond = outputReserveToken + const reserveETHSecond = outputReserveETH; + const reserveTokenSecond = outputReserveToken; if (amount && reserveETHFirst && reserveTokenFirst && reserveETHSecond && reserveTokenSecond) { try { if (independentField === INPUT) { - const intermediateValue = calculateEtherTokenOutputFromInput(amount, reserveTokenFirst, reserveETHFirst) + const intermediateValue = calculateEtherTokenOutputFromInput( + amount, + reserveTokenFirst, + reserveETHFirst + ); if (intermediateValue.lte(ethers.constants.Zero.toString())) { - throw Error() + throw Error(); } const calculatedDependentValue = calculateEtherTokenOutputFromInput( intermediateValue, reserveETHSecond, reserveTokenSecond - ) + ); if (calculatedDependentValue.lte(ethers.constants.Zero.toString())) { - throw Error() + throw Error(); } dispatchSwapState({ - type: 'UPDATE_DEPENDENT', - payload: calculatedDependentValue - }) + type: "UPDATE_DEPENDENT", + payload: calculatedDependentValue, + }); } else { - const intermediateValue = calculateEtherTokenInputFromOutput(amount, reserveETHSecond, reserveTokenSecond) + const intermediateValue = calculateEtherTokenInputFromOutput( + amount, + reserveETHSecond, + reserveTokenSecond + ); if (intermediateValue.lte(ethers.constants.Zero.toString())) { - throw Error() + throw Error(); } const calculatedDependentValue = calculateEtherTokenInputFromOutput( intermediateValue, reserveTokenFirst, reserveETHFirst - ) + ); if (calculatedDependentValue.lte(ethers.constants.Zero.toString())) { - throw Error() + throw Error(); } dispatchSwapState({ - type: 'UPDATE_DEPENDENT', - payload: calculatedDependentValue - }) + type: "UPDATE_DEPENDENT", + payload: calculatedDependentValue, + }); } } catch { - setIndependentError("Insufficient liquidity") + setIndependentError("Insufficient liquidity"); } return () => { - dispatchSwapState({ type: 'UPDATE_DEPENDENT', payload: '' }) - } + dispatchSwapState({ type: "UPDATE_DEPENDENT", payload: "" }); + }; } } }, [ @@ -789,13 +819,18 @@ const CrossAnySwap = props => { inputReserveETH, inputReserveToken, independentField, - t - ]) - + t, + ]); - const [inverted, setInverted] = useState(false) - const exchangeRate = getExchangeRate(inputValueParsed, inputDecimals, outputValueParsed, outputDecimals) - const exchangeRateInverted = getExchangeRate(inputValueParsed, inputDecimals, outputValueParsed, outputDecimals, true) + const [inverted, setInverted] = useState(false); + const exchangeRate = getExchangeRate(inputValueParsed, inputDecimals, outputValueParsed, outputDecimals); + const exchangeRateInverted = getExchangeRate( + inputValueParsed, + inputDecimals, + outputValueParsed, + outputDecimals, + true + ); const marketRate = getMarketRate( swapType, @@ -805,395 +840,501 @@ const CrossAnySwap = props => { outputReserveETH, outputReserveToken, outputDecimals - ) + ); const percentSlippage = exchangeRate && marketRate && !marketRate.isZero() ? exchangeRate - .minus(marketRate) - .abs() - .times(new BigNumber(10).pow(new BigNumber(18))) - .dividedBy(marketRate) - .minus(new BigNumber(3).times(new BigNumber(10).pow(new BigNumber(15)))) - : undefined - const percentSlippageFormatted = percentSlippage && amountFormatter(percentSlippage, 16, 2) + .minus(marketRate) + .abs() + .times(new BigNumber(10).pow(new BigNumber(18))) + .dividedBy(marketRate) + .minus(new BigNumber(3).times(new BigNumber(10).pow(new BigNumber(15)))) + : undefined; + const percentSlippageFormatted = percentSlippage && amountFormatter(percentSlippage, 16, 2); const slippageWarning = percentSlippage && - percentSlippage.gte(ethers.utils.parseEther('.05').toString()) && - percentSlippage.lt(ethers.utils.parseEther('.2').toString()) // [5% - 20%) - const highSlippageWarning = percentSlippage && percentSlippage.gte(ethers.utils.parseEther('.2').toString()) // [20+% + percentSlippage.gte(ethers.utils.parseEther(".05").toString()) && + percentSlippage.lt(ethers.utils.parseEther(".2").toString()); // [5% - 20%) + const highSlippageWarning = percentSlippage && percentSlippage.gte(ethers.utils.parseEther(".2").toString()); // [20+% const isValid = sending ? exchangeRate && inputError === null && independentError === null && recipientError === null && deadlineFromNow - : exchangeRate && inputError === null && independentError === null && deadlineFromNow + : exchangeRate && inputError === null && independentError === null && deadlineFromNow; - const estimatedText = `(${t('estimated')})` + const estimatedText = `(${t("estimated")})`; function formatBalance(value) { - return `Balance: ${value}` + return `Balance: ${value}`; } - const [isDisabled, setIsDisableed] = useState(true) + const [isDisabled, setIsDisableed] = useState(true); const onSwapValid = useCallback(() => { if (!isNaN(percentSlippageFormatted) && Number(percentSlippageFormatted) >= 5) { - setSlippageView(true) + setSlippageView(true); } else { - onSwap() + onSwap(); } - }, [percentSlippageFormatted, onSwap]) + }, [percentSlippageFormatted, onSwap]); async function onSwap() { - if (!isDisabled) return - setIsDisableed(false) + if (!isDisabled) return; + setIsDisableed(false); setTimeout(() => { - setIsDisableed(true) - }, 3000) - const deadline = Math.ceil(Date.now() / 1000) + deadlineFromNow - - let estimate, method, args, value - let txnsType = sending ? 'SEND' : 'SWAP' + setIsDisableed(true); + }, 3000); + const deadline = Math.ceil(Date.now() / 1000) + deadlineFromNow; + let estimate, method, args, value; + let txnsType = sending ? "SEND" : "SWAP"; // if (config.supportWallet.includes(walletType)) { if (config.supportWallet.includes(walletType)) { - setIsHardwareError(false) - setIsHardwareTip(true) - setHardwareTxnsInfo(inputValueFormatted + inputSymbol) - let contractAddress = swapType === ETH_TO_TOKEN ? outputExchangeAddress : inputExchangeAddress - let web3Contract = getWeb3ConTract(EXCHANGE_ABI, contractAddress) - let data = '' + setIsHardwareError(false); + setIsHardwareTip(true); + setHardwareTxnsInfo(inputValueFormatted + inputSymbol); + let contractAddress = swapType === ETH_TO_TOKEN ? outputExchangeAddress : inputExchangeAddress; + let web3Contract = getWeb3ConTract(EXCHANGE_ABI, contractAddress); + let data = ""; if (independentField === INPUT) { - if (swapType === ETH_TO_TOKEN) { value = new BigNumber(independentValueParsed).toFixed(0); - data = sending ? - // web3Contract.ethToTokenTransferInput.getData(dependentValueMinumum.toString(), deadline, recipient.address) - web3Contract.methods.ethToTokenTransferInput(dependentValueMinumum.toString(), deadline, recipient.address).encodeABI() - : - // web3Contract.ethToTokenSwapInput.getData(dependentValueMinumum.toString(), deadline) - web3Contract.methods.ethToTokenSwapInput(dependentValueMinumum.toString(), deadline).encodeABI() + data = sending + ? // web3Contract.ethToTokenTransferInput.getData(dependentValueMinumum.toString(), deadline, recipient.address) + web3Contract.methods + .ethToTokenTransferInput(dependentValueMinumum.toString(), deadline, recipient.address) + .encodeABI() + : // web3Contract.ethToTokenSwapInput.getData(dependentValueMinumum.toString(), deadline) + web3Contract.methods + .ethToTokenSwapInput(dependentValueMinumum.toString(), deadline) + .encodeABI(); } else if (swapType === TOKEN_TO_ETH) { - value = new BigNumber(ethers.constants.Zero.toString()) - data = sending ? - // web3Contract.tokenToEthTransferInput.getData(independentValueParsed.toString(), dependentValueMinumum.toString(), deadline, recipient.address) - web3Contract.methods.tokenToEthTransferInput(independentValueParsed.toString(), dependentValueMinumum.toString(), deadline, recipient.address).encodeABI() - : - // web3Contract.tokenToEthSwapInput.getData(independentValueParsed.toString(), dependentValueMinumum.toString(), deadline) - web3Contract.methods.tokenToEthSwapInput(independentValueParsed.toString(), dependentValueMinumum.toString(), deadline).encodeABI() + value = new BigNumber(ethers.constants.Zero.toString()); + data = sending + ? // web3Contract.tokenToEthTransferInput.getData(independentValueParsed.toString(), dependentValueMinumum.toString(), deadline, recipient.address) + web3Contract.methods + .tokenToEthTransferInput( + independentValueParsed.toString(), + dependentValueMinumum.toString(), + deadline, + recipient.address + ) + .encodeABI() + : // web3Contract.tokenToEthSwapInput.getData(independentValueParsed.toString(), dependentValueMinumum.toString(), deadline) + web3Contract.methods + .tokenToEthSwapInput( + independentValueParsed.toString(), + dependentValueMinumum.toString(), + deadline + ) + .encodeABI(); } else if (swapType === TOKEN_TO_TOKEN) { - value = new BigNumber(ethers.constants.Zero.toString()) - data = sending ? - // web3Contract.tokenToTokenTransferInput.getData( - // independentValueParsed?.toString(), - // dependentValueMinumum.toString(), - // ethers.constants.One.toHexString(), - // deadline, - // recipient.address, - // outputCurrency) - web3Contract.methods.tokenToTokenTransferInput( - independentValueParsed?.toString(), - dependentValueMinumum.toString(), - ethers.constants.One.toHexString(), - deadline, - recipient.address, - outputCurrency).encodeABI() - : - // web3Contract.tokenToTokenSwapInput.getData(independentValueParsed?.toString(), dependentValueMinumum.toString(), ethers.constants.One.toHexString(), deadline, outputCurrency) - web3Contract.methods.tokenToTokenSwapInput(independentValueParsed?.toString(), dependentValueMinumum.toString(), ethers.constants.One.toHexString(), deadline, outputCurrency).encodeABI() + value = new BigNumber(ethers.constants.Zero.toString()); + data = sending + ? // web3Contract.tokenToTokenTransferInput.getData( + // independentValueParsed?.toString(), + // dependentValueMinumum.toString(), + // ethers.constants.One.toHexString(), + // deadline, + // recipient.address, + // outputCurrency) + web3Contract.methods + .tokenToTokenTransferInput( + independentValueParsed?.toString(), + dependentValueMinumum.toString(), + ethers.constants.One.toHexString(), + deadline, + recipient.address, + outputCurrency + ) + .encodeABI() + : // web3Contract.tokenToTokenSwapInput.getData(independentValueParsed?.toString(), dependentValueMinumum.toString(), ethers.constants.One.toHexString(), deadline, outputCurrency) + web3Contract.methods + .tokenToTokenSwapInput( + independentValueParsed?.toString(), + dependentValueMinumum.toString(), + ethers.constants.One.toHexString(), + deadline, + outputCurrency + ) + .encodeABI(); } } else if (independentField === OUTPUT) { - if (swapType === ETH_TO_TOKEN) { - value = new BigNumber(dependentValueMaximum.toFixed(0)) - data = sending ? - // web3Contract.ethToTokenTransferOutput.getData(independentValueParsed?.toString(), deadline, recipient.address) - web3Contract.methods.ethToTokenTransferOutput(independentValueParsed?.toString(), deadline, recipient.address).encodeABI() - : - web3Contract.methods.ethToTokenSwapOutput(independentValueParsed?.toString(), deadline).encodeABI() + value = new BigNumber(dependentValueMaximum.toFixed(0)); + data = sending + ? // web3Contract.ethToTokenTransferOutput.getData(independentValueParsed?.toString(), deadline, recipient.address) + web3Contract.methods + .ethToTokenTransferOutput( + independentValueParsed?.toString(), + deadline, + recipient.address + ) + .encodeABI() + : web3Contract.methods + .ethToTokenSwapOutput(independentValueParsed?.toString(), deadline) + .encodeABI(); } else if (swapType === TOKEN_TO_ETH) { - value = new BigNumber(ethers.constants.Zero.toString()) - data = sending ? - // web3Contract.tokenToEthTransferOutput.getData(independentValueParsed?.toString(), dependentValueMaximum.toString(), deadline, recipient.address) - web3Contract.methods.tokenToEthTransferOutput(independentValueParsed?.toString(), dependentValueMaximum.toString(), deadline, recipient.address).encodeABI() - : - // web3Contract.tokenToEthSwapOutput.getData(independentValueParsed?.toString(), dependentValueMaximum.toString(), deadline) - web3Contract.methods.tokenToEthSwapOutput(independentValueParsed?.toString(), dependentValueMaximum.toString(), deadline).encodeABI() + value = new BigNumber(ethers.constants.Zero.toString()); + data = sending + ? // web3Contract.tokenToEthTransferOutput.getData(independentValueParsed?.toString(), dependentValueMaximum.toString(), deadline, recipient.address) + web3Contract.methods + .tokenToEthTransferOutput( + independentValueParsed?.toString(), + dependentValueMaximum.toString(), + deadline, + recipient.address + ) + .encodeABI() + : // web3Contract.tokenToEthSwapOutput.getData(independentValueParsed?.toString(), dependentValueMaximum.toString(), deadline) + web3Contract.methods + .tokenToEthSwapOutput( + independentValueParsed?.toString(), + dependentValueMaximum.toString(), + deadline + ) + .encodeABI(); } else if (swapType === TOKEN_TO_TOKEN) { - value = new BigNumber(ethers.constants.Zero.toString()) - data = sending ? - web3Contract.methods.tokenToTokenTransferOutput( - independentValueParsed?.toString(), - dependentValueMaximum.toString(), - ethers.constants.MaxUint256.toHexString(), - deadline, - recipient.address, - outputCurrency - ).encodeABI() - : - // web3Contract.tokenToTokenSwapOutput.getData(independentValueParsed?.toString(), dependentValueMaximum.toString(), ethers.constants.MaxUint256.toHexString(), deadline, outputCurrency) - web3Contract.methods.tokenToTokenSwapOutput(independentValueParsed?.toString(), dependentValueMaximum.toString(), ethers.constants.MaxUint256.toHexString(), deadline, outputCurrency).encodeABI() + value = new BigNumber(ethers.constants.Zero.toString()); + data = sending + ? web3Contract.methods + .tokenToTokenTransferOutput( + independentValueParsed?.toString(), + dependentValueMaximum.toString(), + ethers.constants.MaxUint256.toHexString(), + deadline, + recipient.address, + outputCurrency + ) + .encodeABI() + : // web3Contract.tokenToTokenSwapOutput.getData(independentValueParsed?.toString(), dependentValueMaximum.toString(), ethers.constants.MaxUint256.toHexString(), deadline, outputCurrency) + web3Contract.methods + .tokenToTokenSwapOutput( + independentValueParsed?.toString(), + dependentValueMaximum.toString(), + ethers.constants.MaxUint256.toHexString(), + deadline, + outputCurrency + ) + .encodeABI(); } } - value = swapType === ETH_TO_TOKEN ? value.toString() : 0 - getWeb3BaseInfo(contractAddress, data, account, value).then(res => { - if (res.msg === 'Success') { - addTransaction(res.info) - recordTxns(res.info, txnsType, inputSymbol + '/' + outputSymbol, account, recipient.address) - setIsHardwareTip(false) + value = swapType === ETH_TO_TOKEN ? value.toString() : 0; + getWeb3BaseInfo(contractAddress, data, account, value).then((res) => { + if (res.msg === "Success") { + addTransaction(res.info); + recordTxns(res.info, txnsType, inputSymbol + "/" + outputSymbol, account, recipient.address); + setIsHardwareTip(false); dispatchSwapState({ - type: 'UPDATE_INDEPENDENT', - payload: { value: '', field: INPUT } - }) + type: "UPDATE_INDEPENDENT", + payload: { value: "", field: INPUT }, + }); dispatchSwapState({ - type: 'UPDATE_INDEPENDENT', - payload: { value: '', field: OUTPUT } - }) - setIsViewTxnsDtil(false) + type: "UPDATE_INDEPENDENT", + payload: { value: "", field: OUTPUT }, + }); + setIsViewTxnsDtil(false); } else { - setIsHardwareError(true) + setIsHardwareError(true); } - }) - return + }); + return; } if (independentField === INPUT) { if (swapType === ETH_TO_TOKEN) { - estimate = sending ? contract.estimateGas.ethToTokenTransferInput : contract.estimateGas.ethToTokenSwapInput - method = sending ? contract.ethToTokenTransferInput : contract.ethToTokenSwapInput - args = sending ? [dependentValueMinumum.toString(), deadline, recipient.address] : [dependentValueMinumum.toString(), deadline] + estimate = sending + ? contract.estimateGas.ethToTokenTransferInput + : contract.estimateGas.ethToTokenSwapInput; + method = sending ? contract.ethToTokenTransferInput : contract.ethToTokenSwapInput; + args = sending + ? [dependentValueMinumum.toString(), deadline, recipient.address] + : [dependentValueMinumum.toString(), deadline]; value = new BigNumber(independentValueParsed.toFixed(0)); } else if (swapType === TOKEN_TO_ETH) { - estimate = sending ? contract.estimateGas.tokenToEthTransferInput : contract.estimateGas.tokenToEthSwapInput - method = sending ? contract.tokenToEthTransferInput : contract.tokenToEthSwapInput + estimate = sending + ? contract.estimateGas.tokenToEthTransferInput + : contract.estimateGas.tokenToEthSwapInput; + method = sending ? contract.tokenToEthTransferInput : contract.tokenToEthSwapInput; args = sending ? [independentValueParsed.toString(), dependentValueMinumum.toString(), deadline, recipient.address] - : [independentValueParsed.toString(), dependentValueMinumum.toString(), deadline] - value = new BigNumber(ethers.constants.Zero.toString()) - + : [independentValueParsed.toString(), dependentValueMinumum.toString(), deadline]; + value = new BigNumber(ethers.constants.Zero.toString()); } else if (swapType === TOKEN_TO_TOKEN) { - estimate = sending ? contract.estimateGas.tokenToTokenTransferInput : contract.estimateGas.tokenToTokenSwapInput - method = sending ? contract.tokenToTokenTransferInput : contract.tokenToTokenSwapInput + estimate = sending + ? contract.estimateGas.tokenToTokenTransferInput + : contract.estimateGas.tokenToTokenSwapInput; + method = sending ? contract.tokenToTokenTransferInput : contract.tokenToTokenSwapInput; args = sending ? [ - independentValueParsed.toString(), - dependentValueMinumum.toString(), - ethers.constants.One, - deadline, - recipient.address, - outputCurrency - ] - : [independentValueParsed.toString(), dependentValueMinumum.toString(), ethers.constants.One, deadline, outputCurrency] - value = new BigNumber(ethers.constants.Zero.toString()) + independentValueParsed.toString(), + dependentValueMinumum.toString(), + ethers.constants.One, + deadline, + recipient.address, + outputCurrency, + ] + : [ + independentValueParsed.toString(), + dependentValueMinumum.toString(), + ethers.constants.One, + deadline, + outputCurrency, + ]; + value = new BigNumber(ethers.constants.Zero.toString()); } } else if (independentField === OUTPUT) { - if (swapType === ETH_TO_TOKEN) { - estimate = sending ? contract.estimateGas.ethToTokenTransferOutput : contract.estimateGas.ethToTokenSwapOutput - method = sending ? contract.ethToTokenTransferOutput : contract.ethToTokenSwapOutput - args = sending ? [independentValueParsed.toString(), deadline, recipient.address] : [independentValueParsed.toString(), deadline] + estimate = sending + ? contract.estimateGas.ethToTokenTransferOutput + : contract.estimateGas.ethToTokenSwapOutput; + method = sending ? contract.ethToTokenTransferOutput : contract.ethToTokenSwapOutput; + args = sending + ? [independentValueParsed.toString(), deadline, recipient.address] + : [independentValueParsed.toString(), deadline]; value = new BigNumber(dependentValueMaximum.toFixed(0)); } else if (swapType === TOKEN_TO_ETH) { - estimate = sending ? contract.estimateGas.tokenToEthTransferOutput : contract.estimateGas.tokenToEthSwapOutput - method = sending ? contract.tokenToEthTransferOutput : contract.tokenToEthSwapOutput + estimate = sending + ? contract.estimateGas.tokenToEthTransferOutput + : contract.estimateGas.tokenToEthSwapOutput; + method = sending ? contract.tokenToEthTransferOutput : contract.tokenToEthSwapOutput; args = sending ? [independentValueParsed.toString(), dependentValueMaximum.toString(), deadline, recipient.address] - : [independentValueParsed.toString(), dependentValueMaximum.toString(), deadline] - value = new BigNumber(ethers.constants.Zero.toString()) + : [independentValueParsed.toString(), dependentValueMaximum.toString(), deadline]; + value = new BigNumber(ethers.constants.Zero.toString()); } else if (swapType === TOKEN_TO_TOKEN) { - estimate = sending ? contract.estimateGas.tokenToTokenTransferOutput : contract.estimateGas.tokenToTokenSwapOutput - method = sending ? contract.tokenToTokenTransferOutput : contract.tokenToTokenSwapOutput + estimate = sending + ? contract.estimateGas.tokenToTokenTransferOutput + : contract.estimateGas.tokenToTokenSwapOutput; + method = sending ? contract.tokenToTokenTransferOutput : contract.tokenToTokenSwapOutput; args = sending ? [ - independentValueParsed.toString(), - dependentValueMaximum.toString(), - ethers.constants.MaxUint256, - deadline, - recipient.address, - outputCurrency - ] - : [independentValueParsed.toString(), dependentValueMaximum.toString(), ethers.constants.MaxUint256, deadline, outputCurrency] - value = new BigNumber(ethers.constants.Zero.toString()) + independentValueParsed.toString(), + dependentValueMaximum.toString(), + ethers.constants.MaxUint256, + deadline, + recipient.address, + outputCurrency, + ] + : [ + independentValueParsed.toString(), + dependentValueMaximum.toString(), + ethers.constants.MaxUint256, + deadline, + outputCurrency, + ]; + value = new BigNumber(ethers.constants.Zero.toString()); } } - let estimatedGasLimit + let estimatedGasLimit; try { - estimatedGasLimit = await estimate(...args, { value: value.toFixed(0) }) - } catch(e) { + estimatedGasLimit = await estimate(...args, { value: value.toFixed(0) }); + } catch (e) { const errorCode = e?.code; - if(errorCode === -32000) { + if (errorCode === -32000) { toast.error("Insufficient funds for gas."); return false; } estimatedGasLimit = new BigNumber(40000); } - method(...args, { value: value.toFixed(0), - gasLimit: calculateGasMargin(estimatedGasLimit, GAS_MARGIN).toFixed(0) - }).then(response => { - addTransaction(response) - recordTxns(response, txnsType, inputSymbol + '/' + outputSymbol, account, recipient.address) - dispatchSwapState({ - type: 'UPDATE_INDEPENDENT', - payload: { value: '', field: INPUT } - }) - dispatchSwapState({ - type: 'UPDATE_INDEPENDENT', - payload: { value: '', field: OUTPUT } - }) - setIsViewTxnsDtil(false) - }).catch(err => { - console.log(err) + gasLimit: calculateGasMargin(estimatedGasLimit, GAS_MARGIN).toFixed(0), }) + .then((response) => { + addTransaction(response); + recordTxns(response, txnsType, inputSymbol + "/" + outputSymbol, account, recipient.address); + dispatchSwapState({ + type: "UPDATE_INDEPENDENT", + payload: { value: "", field: INPUT }, + }); + dispatchSwapState({ + type: "UPDATE_INDEPENDENT", + payload: { value: "", field: OUTPUT }, + }); + setIsViewTxnsDtil(false); + }) + .catch((err) => { + console.log(err); + }); } - const [customSlippageError, setcustomSlippageError] = useState('') + const [customSlippageError, setcustomSlippageError] = useState(""); - const toggleWalletModal = useWalletModalToggle() + const toggleWalletModal = useWalletModalToggle(); const newInputDetected = - inputCurrency !== config.symbol && inputCurrency && !INITIAL_TOKENS_CONTEXT[chainId].hasOwnProperty(inputCurrency) + inputCurrency !== config.symbol && + inputCurrency && + !INITIAL_TOKENS_CONTEXT[chainId].hasOwnProperty(inputCurrency); const newOutputDetected = - outputCurrency !== config.symbol && outputCurrency && !INITIAL_TOKENS_CONTEXT[chainId].hasOwnProperty(outputCurrency) + outputCurrency !== config.symbol && + outputCurrency && + !INITIAL_TOKENS_CONTEXT[chainId].hasOwnProperty(outputCurrency); - const [showInputWarning, setShowInputWarning] = useState(false) - const [showOutputWarning, setShowOutputWarning] = useState(false) - - const [isHardwareTip, setIsHardwareTip] = useState(false) - const [isHardwareError, setIsHardwareError] = useState(false) - const [hardwareTxnsInfo, setHardwareTxnsInfo] = useState('') - const [isViewTxnsDtil, setIsViewTxnsDtil] = useState(false) + const [showInputWarning, setShowInputWarning] = useState(false); + const [showOutputWarning, setShowOutputWarning] = useState(false); + const [isHardwareTip, setIsHardwareTip] = useState(false); + const [isHardwareError, setIsHardwareError] = useState(false); + const [hardwareTxnsInfo, setHardwareTxnsInfo] = useState(""); + const [isViewTxnsDtil, setIsViewTxnsDtil] = useState(false); useEffect(() => { if (newInputDetected) { - setShowInputWarning(true) + setShowInputWarning(true); } else { - setShowInputWarning(false) + setShowInputWarning(false); } - }, [newInputDetected, setShowInputWarning]) + }, [newInputDetected, setShowInputWarning]); useEffect(() => { if (newOutputDetected) { - setShowOutputWarning(true) + setShowOutputWarning(true); } else { - setShowOutputWarning(false) + setShowOutputWarning(false); } - }, [newOutputDetected, setShowOutputWarning]) + }, [newOutputDetected, setShowOutputWarning]); console.log(recipientError); return ( - + - + + + + {t("menu.anySwap")} + + + {t("menu.bridges")} + + + {t("menu.crossBalance")} + + +
    {!sending ? "Swap" : "Send"} Swap Send
    - - { - setIsHardwareTip(false) - }} - error={isHardwareError} - txnsInfo={hardwareTxnsInfo} - coinType={inputSymbol} - /> - - - - - {t('youAreSelling')}{' '} - - {`${amountFormatter( + { + setIsHardwareTip(false); + }} + error={isHardwareError} + txnsInfo={hardwareTxnsInfo} + coinType={inputSymbol} + /> + + + + + + {t("youAreSelling")}{" "} + + {`${amountFormatter( independentValueParsed, independentDecimals, Math.min(6, independentDecimals) )} ${inputSymbol}`} - {' '} - {t('forAtLeast')} - - {`${amountFormatter( + {" "} + {t("forAtLeast")} + + {`${amountFormatter( dependentValueMinumum, dependentDecimals, Math.min(6, dependentDecimals) )} ${outputSymbol}`} - - - - {t('priceChange')} {`${percentSlippageFormatted}%`} - - - - - + + + + {t("priceChange")} {`${percentSlippageFormatted}%`} + + + + + { if (inputBalance && inputDecimals) { - const valueToSet = inputCurrency === config.symbol ? inputBalance.minus(ethers.utils.parseEther('.1').toString()) : inputBalance + const valueToSet = + inputCurrency === config.symbol + ? inputBalance.minus(ethers.utils.parseEther(".1").toString()) + : inputBalance; if (valueToSet.gt(ethers.constants.Zero.toString())) { dispatchSwapState({ - type: 'UPDATE_INDEPENDENT', + type: "UPDATE_INDEPENDENT", payload: { - value: amountFormatter(valueToSet, inputDecimals, inputDecimals, false), - field: INPUT - } - }) + value: amountFormatter( + valueToSet, + inputDecimals, + inputDecimals, + false + ), + field: INPUT, + }, + }); } } }} - onCurrencySelect={inputCurrency => { + onCurrencySelect={(inputCurrency) => { dispatchSwapState({ - type: 'SELECT_CURRENCY', - payload: { currency: inputCurrency, field: INPUT } - }) + type: "SELECT_CURRENCY", + payload: { currency: inputCurrency, field: INPUT }, + }); }} - onUserInput={inputValue => { + onUserInput={(inputValue) => { dispatchSwapState({ - type: 'UPDATE_INDEPENDENT', - payload: { value: inputValue, field: INPUT } - }) + type: "UPDATE_INDEPENDENT", + payload: { value: inputValue, field: INPUT }, + }); }} showUnlock={showUnlock} selectedTokens={[inputCurrency, outputCurrency]} selectedTokenAddress={inputCurrency} value={inputValueFormatted} - errorMessage={inputError ? inputError : independentField === INPUT ? independentError : ''} + errorMessage={ + inputError ? inputError : independentField === INPUT ? independentError : "" + } label={"Input"} withoutMargin={true} /> - - { - dispatchSwapState({ type: 'FLIP_INDEPENDENT' }) - }}> - + + { + dispatchSwapState({ type: "FLIP_INDEPENDENT" }); + }} + > + @@ -1201,73 +1342,70 @@ const CrossAnySwap = props => { label={"Output"} currency={outputCurrencySwap} otherCurrency={inputCurrencySwap} - id={'bridge-output-currency'} + id={"bridge-output-currency"} showCommonBases={false} withoutMargin={true} - description={outputValueFormatted && independentField === INPUT ? estimatedText : ''} + description={ + outputValueFormatted && independentField === INPUT ? estimatedText : "" + } extraText={outputBalanceFormatted && formatBalance(outputBalanceFormatted)} urlAddedTokens={urlAddedTokens} - onCurrencySelect={outputCurrency => { + onCurrencySelect={(outputCurrency) => { dispatchSwapState({ - type: 'SELECT_CURRENCY', - payload: { currency: outputCurrency, field: OUTPUT } - }) + type: "SELECT_CURRENCY", + payload: { currency: outputCurrency, field: OUTPUT }, + }); }} - onUserInput={outputValue => { + onUserInput={(outputValue) => { dispatchSwapState({ - type: 'UPDATE_INDEPENDENT', - payload: { value: outputValue, field: OUTPUT } - }) + type: "UPDATE_INDEPENDENT", + payload: { value: outputValue, field: OUTPUT }, + }); }} selectedTokens={[inputCurrency, outputCurrency]} selectedTokenAddress={outputCurrency} value={outputValueFormatted} - errorMessage={independentField === OUTPUT ? independentError : ''} + errorMessage={independentField === OUTPUT ? independentError : ""} disableUnlock /> {sending ? ( - + { - setRecipient(r => { + onChange={(val) => { + setRecipient((r) => { return { ...r, - address: val - } - }) + address: val, + }; + }); }} - withoutMargin={true} - label={"Recipient Address"} /> ) : null} - - - + + {config.dirSwitchFn(inputIsSwitch) && config.dirSwitchFn(outputIsSwitch) ? ( ) : ( - + )}
    - ) -} + ); +}; export default CrossAnySwap; - diff --git a/src/pages/CrossBalance/index.js b/src/pages/CrossBalance/index.js index 64563336..5df29ebc 100644 --- a/src/pages/CrossBalance/index.js +++ b/src/pages/CrossBalance/index.js @@ -1,56 +1,56 @@ import styled from "styled-components"; -import { Row, Col, Button as BS } from 'react-bootstrap'; +import { Row, Col, Button as BS, ListGroup } from "react-bootstrap"; import SVG from "react-inlinesvg"; -import React, {useEffect, useMemo, useState} from "react"; -import { Link } from 'react-router-dom'; -import {useTranslation} from "react-i18next"; +import React, { useEffect, useMemo, useState } from "react"; +import { Link } from "react-router-dom"; +import { useTranslation } from "react-i18next"; -import TokenLogo from '../../components/CrossTokenLogo'; -import getConfig from '../../config'; +import TokenLogo from "../../components/CrossTokenLogo"; +import getConfig from "../../config"; import Page from "../../components/Page"; -import DefaultCard from '../../components/Card'; +import DefaultCard from "../../components/Card"; import { useIsDarkMode } from "../../state/user/hooks"; import { InputGroup, InputGroupFormControl as FormControl, InputGroupPrepend, - InputGroupText + InputGroupText, } from "../../components/Form"; import SearchIcon from "../../assets/images/search.svg"; import CurrencyText from "../../components/CurrencyText"; -import {Table} from "./Table"; -import {darken} from "polished"; -import {useActiveWeb3React} from "../../hooks"; -import {useAllBalances} from "../../contexts/Balances"; -import {useAllTokenDetails} from "../../contexts/Tokens"; -import {amountFormatter, formatEthBalance, formatNum, formatTokenBalance} from "../../utils/cross"; +import { Table } from "./Table"; +import { darken } from "polished"; +import { useActiveWeb3React } from "../../hooks"; +import { useAllBalances } from "../../contexts/Balances"; +import { useAllTokenDetails } from "../../contexts/Tokens"; +import { amountFormatter, formatEthBalance, formatNum, formatTokenBalance } from "../../utils/cross"; import BigNumber from "bignumber.js"; -import {showData} from "../../utils/showData"; -import {getPoolInfo} from "../../utils/crossBalance"; +import { showData } from "../../utils/showData"; +import { getPoolInfo } from "../../utils/crossBalance"; const config = getConfig(); const Button = styled(BS)` - min-height: 48px; - height: 48px; - border-radius: 18px; - font-weight: 500; - font-size: 1rem; - min-width: 105px; - - @media (max-width: 991px) { - width: 100%; - display: flex; - align-items: center; - justify-content: center; - line-height: 1; - font-size: .875rem; - min-height: 40px; - height: 40px; - padding: 0; - border-radius: 12px; - } -` + min-height: 48px; + height: 48px; + border-radius: 18px; + font-weight: 500; + font-size: 1rem; + min-width: 105px; + + @media (max-width: 991px) { + width: 100%; + display: flex; + align-items: center; + justify-content: center; + line-height: 1; + font-size: 0.875rem; + min-height: 40px; + height: 40px; + padding: 0; + border-radius: 12px; + } +`; const Title = styled.h1` margin: 0; @@ -59,18 +59,17 @@ const Title = styled.h1` color: ${({ theme }) => theme.text1}; line-height: 3rem; - @media (max-width: 1199px) { font-size: 2.25rem; } @media (max-width: 991px) { font-size: 2rem; - margin-bottom: 0.5rem; + margin-bottom: 0.5rem; } @media (max-width: 767px) { font-size: 1.5rem; } -` +`; const Header = styled.div` display: flex; @@ -78,287 +77,282 @@ const Header = styled.div` justify-content: space-between; margin-bottom: 24px; margin-top: 40px; - - @media(max-width: 991px) { + + @media (max-width: 991px) { margin-top: 10px; - flex-direction: column; - align-items: stretch; + flex-direction: column; + align-items: stretch; } -` +`; const Card = styled(DefaultCard)` & > .card-body { padding: 30px 30px 24px; - - @media (max-width: 576px) { - padding: 16px; - } + + @media (max-width: 576px) { + padding: 16px; + } } -` +`; const CellText = styled.span` - font-size: 1rem; - font-weight: 500; - color: ${({ theme }) => theme.text1}; - display: block; - line-height: 1.25rem; - - @media (max-width: 991px) { - font-size: .875rem; - } - @media (max-width: 767px) { - font-size: .75rem; - } -` + font-size: 1rem; + font-weight: 500; + color: ${({ theme }) => theme.text1}; + display: block; + line-height: 1.25rem; -const CurrencySymbol = styled(CellText)` - margin-bottom: 5px; + @media (max-width: 991px) { + font-size: 0.875rem; + } + @media (max-width: 767px) { + font-size: 0.75rem; + } +`; +const CurrencySymbol = styled(CellText)` + margin-bottom: 5px; - @media (max-width: 991px) { - margin-bottom: 0; - } -` + @media (max-width: 991px) { + margin-bottom: 0; + } +`; const CurrencyName = styled.span` - font-size: .875rem; - font-weight: 400; - line-height: 1.125rem; - color: ${({ theme }) => theme.text3}; - + font-size: 0.875rem; + font-weight: 400; + line-height: 1.125rem; + color: ${({ theme }) => theme.text3}; - @media (max-width: 991px) { - font-size: .75rem; - } - @media (max-width: 767px) { - font-size: .625rem; - } -` + @media (max-width: 991px) { + font-size: 0.75rem; + } + @media (max-width: 767px) { + font-size: 0.625rem; + } +`; const CoinContent = styled.div` - margin-left: 45px; - - @media (max-width: 1199px) { - margin-left: 24px; - } - - @media (max-width: 991px) { - margin-left: 0; - margin-right: 24px; - } - - @media (max-width: 991px) { - margin-right: 16px; - } -` + margin-left: 45px; + + @media (max-width: 1199px) { + margin-left: 24px; + } + + @media (max-width: 991px) { + margin-left: 0; + margin-right: 24px; + } + + @media (max-width: 991px) { + margin-right: 16px; + } +`; const ShowMoreWrap = styled.div` border-top: 1px solid rgba(255, 255, 255, 0.5); padding-top: 2rem; text-align: center; -` +`; -function isBaseUSD (coin) { +function isBaseUSD(coin) { if ( - (coin === 'aUSDT' && config.symbol === 'FSN') || - (coin === 'anyUSDT' && config.symbol === 'FTM') || - (coin === 'HUSD' && config.symbol === 'HT') || - (coin === 'USDTB' && config.symbol === 'BNB') || - (coin === 'USDC' && config.symbol === 'ETH') + (coin === "aUSDT" && config.symbol === "FSN") || + (coin === "anyUSDT" && config.symbol === "FTM") || + (coin === "HUSD" && config.symbol === "HT") || + (coin === "USDTB" && config.symbol === "BNB") || + (coin === "USDC" && config.symbol === "ETH") ) { - return true - } else if (coin === 'anyUSDT') { - return true + return true; + } else if (coin === "anyUSDT") { + return true; } - return false + return false; } -const CrossBalance = props => { +const CrossBalance = (props) => { const { account } = useActiveWeb3React(); const darkMode = useIsDarkMode(); - const allBalances = useAllBalances() - const allTokens = useAllTokenDetails() - const { t } = useTranslation() - const [query, setQuery] = useState(''); + const allBalances = useAllBalances(); + const allTokens = useAllTokenDetails(); + const { t } = useTranslation(); + const [query, setQuery] = useState(""); const [showFull, setShowFull] = useState(false); - const [poolList, setPoolList] = useState([]) - const [poolObj, setPoolObj] = useState({}) - const [baseMarket, setBaseMarket] = useState() - const [pagecount, setPagecount] = useState(0) + const [poolList, setPoolList] = useState([]); + const [poolObj, setPoolObj] = useState({}); + const [baseMarket, setBaseMarket] = useState(); + const [pagecount, setPagecount] = useState(0); const changeSearchInput = (e) => { setQuery(e.target.value); - } + }; const poolArr = useMemo(() => { - const arr = [] + const arr = []; for (const obj in allTokens) { arr.push({ token: obj, exchangeAddress: allTokens[obj].exchangeAddress, - ...allTokens[obj] - }) + ...allTokens[obj], + }); } - return arr - }, [allTokens]) - + return arr; + }, [allTokens]); - const totalCount = poolArr.length - const poolInfoObj = {} + const totalCount = poolArr.length; + const poolInfoObj = {}; - function formatData (res) { - let arr = [] - let baseAccountBalance = new BigNumber(0) - let rwArr = [] + function formatData(res) { + let arr = []; + let baseAccountBalance = new BigNumber(0); + let rwArr = []; for (let obj of res) { console.log(obj); - obj.pecent = amountFormatter(obj.pecent, 18, Math.min(config.keepDec, obj.decimals)) - obj.balance = amountFormatter(obj.balance, obj.decimals, Math.min(config.keepDec, obj.decimals)) + obj.pecent = amountFormatter(obj.pecent, 18, Math.min(config.keepDec, obj.decimals)); + obj.balance = amountFormatter(obj.balance, obj.decimals, Math.min(config.keepDec, obj.decimals)); if (obj.Basebalance) { - baseAccountBalance = baseAccountBalance.plus(obj.Basebalance) + baseAccountBalance = baseAccountBalance.plus(obj.Basebalance); } // if (obj.exchangeETHBalance) { // baseAllBalance = baseAllBalance.add(obj.exchangeETHBalance) // } if (isBaseUSD(obj.symbol)) { - console.log(obj.market, 18, Math.min(config.keepDec, obj.decimals)) - setBaseMarket(Number(amountFormatter( obj.market, 18, Math.min(config.keepDec, obj.decimals) ))) + console.log(obj.market, 18, Math.min(config.keepDec, obj.decimals)); + setBaseMarket(Number(amountFormatter(obj.market, 18, Math.min(config.keepDec, obj.decimals)))); } - poolInfoObj[obj.symbol] = obj - arr.push(obj) + poolInfoObj[obj.symbol] = obj; + arr.push(obj); if (obj.exchangeETHBalance && obj.exchangeTokenBalancem && obj.market) { rwArr.push({ coin: obj.symbol, market: obj.market ? obj.market.toString() : 0, baseAmount: obj.exchangeETHBalance ? obj.exchangeETHBalance.toString() : 0, - tokenAmount: obj.exchangeTokenBalancem ? obj.exchangeTokenBalancem.toString() : 0 - }) + tokenAmount: obj.exchangeTokenBalancem ? obj.exchangeTokenBalancem.toString() : 0, + }); } } if (arr[0].symbol === config.symbol) { - arr[0].Basebalance = baseAccountBalance - poolInfoObj[config.symbol].Basebalance = baseAccountBalance + arr[0].Basebalance = baseAccountBalance; + poolInfoObj[config.symbol].Basebalance = baseAccountBalance; } - setPoolObj(poolInfoObj) - return arr + setPoolObj(poolInfoObj); + return arr; } - - async function getData (account) { - let arr = [] + async function getData(account) { + let arr = []; for (let i = 0; i <= totalCount; i++) { - const resArr = poolArr + const resArr = poolArr; - const result = await getPoolInfo(resArr, account) - arr.push(...formatData(result)) - setPoolList(arr) + const result = await getPoolInfo(resArr, account); + arr.push(...formatData(result)); + setPoolList(arr); } } useEffect(() => { if (poolArr.length > 0) { if (poolList.length <= 0) { - setPoolList(poolArr) + setPoolList(poolArr); } - getData(account) + getData(account); } - }, [poolArr, account]) + }, [poolArr, account]); function getPrice(market, coin) { if (isBaseUSD(coin)) { - return '1' + return "1"; } - if (!market) return '-' - let mt1 = Number(amountFormatter( market, 18 , config.keepDec)) - if (!mt1) return '0' - return formatNum((baseMarket / mt1), config.keepDec) + if (!market) return "-"; + let mt1 = Number(amountFormatter(market, 18, config.keepDec)); + if (!mt1) return "0"; + return formatNum(baseMarket / mt1, config.keepDec); } const columns = useMemo(() => { return [ { - dataField: 'coin', - text: t('cross.coin'), + dataField: "coin", + text: t("cross.coin"), formatter: (cell, row, rowIndex) => { return (
    - + {showData(row?.symbol?.toUpperCase())} {showData(row?.name)}
    - ) - } + ); + }, }, { - dataField: 'price', - text: t('cross.price'), + dataField: "price", + text: t("cross.price"), formatter: (cell, row, rowIndex) => { return ( {showData(row?.price)} - ) - } + ); + }, }, { - dataField: 'balance', - text: t('cross.balance'), + dataField: "balance", + text: t("cross.balance"), formatter: (cell, row, rowIndex) => { - return ( - {account ? showData(row?.balance) : '-'} - ) - } + return {account ? showData(row?.balance) : "-"}; + }, }, { - dataField: 'totalBalance', - text: t('cross.totalBalance'), + dataField: "totalBalance", + text: t("cross.totalBalance"), formatter: (cell, row, rowIndex) => { - return ( - {account ? showData(row?.balance) : '-'} - ) - } + return {account ? showData(row?.balance) : "-"}; + }, }, { - dataField: 'action', - text: t('cross.action'), + dataField: "action", + text: t("cross.action"), formatter: (cell, row, rowIndex) => { return ( - - ) + + ); }, - isAction: true + isAction: true, }, - ] - }, []) + ]; + }, []); function getMyAccount() { - if(!account) return {}; + if (!account) return {}; - const myAccount = account ? allBalances[account] : '' + const myAccount = account ? allBalances[account] : ""; - let tokenList = Object.keys(allTokens).map(k => { - let balance = '-' - let price = '-' - let tvl = 0 + let tokenList = Object.keys(allTokens).map((k) => { + let balance = "-"; + let price = "-"; + let tvl = 0; if (k === config.symbol && myAccount && myAccount[k] && myAccount[k].value) { - balance = formatEthBalance(new BigNumber(myAccount[k].value)) + balance = formatEthBalance(new BigNumber(myAccount[k].value)); } else if (myAccount && myAccount[k] && myAccount[k].value) { - balance = formatTokenBalance(new BigNumber(myAccount[k].value), allTokens[k].decimals) + balance = formatTokenBalance(new BigNumber(myAccount[k].value), allTokens[k].decimals); } if (poolObj[allTokens[k].symbol] && baseMarket) { if (allTokens[k].symbol === config.symbol) { - price = formatNum(baseMarket, config.keepDec) + price = formatNum(baseMarket, config.keepDec); } else { - price = getPrice(poolObj[allTokens[k].symbol].market, allTokens[k].symbol) + price = getPrice(poolObj[allTokens[k].symbol].market, allTokens[k].symbol); } if (!isNaN(balance) && !isNaN(price)) { - tvl = Number(balance) * Number(price) + tvl = Number(balance) * Number(price); } } return { @@ -368,44 +362,59 @@ const CrossBalance = props => { balance: balance, isSwitch: allTokens[k].isSwitch, price: price, - tvl: tvl - } - }) + tvl: tvl, + }; + }); tokenList.sort((a, b) => { if (!isNaN(a.tvl) && !isNaN(b.tvl) && Number(a.tvl) > Number(b.tvl)) { - return -1 + return -1; } - }) + }); if (config.isChangeDashboard) { - let ANYItem = {} - for (let i = 0,len = tokenList.length; i < len; i++) { - if (tokenList[i].symbol === 'ANY') { - ANYItem = tokenList[i] - tokenList.splice(i, 1) - break + let ANYItem = {}; + for (let i = 0, len = tokenList.length; i < len; i++) { + if (tokenList[i].symbol === "ANY") { + ANYItem = tokenList[i]; + tokenList.splice(i, 1); + break; } } - tokenList.unshift(ANYItem) + tokenList.unshift(ANYItem); } - return tokenList.length > 0 ? tokenList.filter(item => { - return !query - || item.name.toLowerCase().indexOf(query.toLowerCase()) !== -1 - || item.symbol.toLowerCase().indexOf(query.toLowerCase()) !== -1 - || item.address.toLowerCase().indexOf(query.toLowerCase()) !== -1; - - }) : [] + return tokenList.length > 0 + ? tokenList.filter((item) => { + return ( + !query || + item.name.toLowerCase().indexOf(query.toLowerCase()) !== -1 || + item.symbol.toLowerCase().indexOf(query.toLowerCase()) !== -1 || + item.address.toLowerCase().indexOf(query.toLowerCase()) !== -1 + ); + }) + : []; } const data = getMyAccount(); return ( - + - + + + + {t("menu.anySwap")} + + + {t("menu.bridges")} + + + {t("menu.crossBalance")} + + +
    - Crosstopus Balances + Balance @@ -420,8 +429,7 @@ const CrossBalance = props => { />
    - - +
    { - ) -} + ); +}; -export default CrossBalance; \ No newline at end of file +export default CrossBalance; diff --git a/src/pages/CrossBridge/index.js b/src/pages/CrossBridge/index.js index 9d208c5e..fe5d68ad 100644 --- a/src/pages/CrossBridge/index.js +++ b/src/pages/CrossBridge/index.js @@ -1,96 +1,93 @@ -import styled from 'styled-components'; -import { Row, Col, Button as BS } from 'react-bootstrap'; +import styled from "styled-components"; +import { Row, Col, Button as BS, ListGroup } from "react-bootstrap"; import SVG from "react-inlinesvg"; -import {ETHER} from "@uniswap/sdk"; -import {useCallback, useEffect, useMemo, useReducer, useState} from "react"; -import { ethers } from 'ethers'; -import { transparentize } from 'polished'; +import { ETHER } from "@uniswap/sdk"; +import { useCallback, useEffect, useMemo, useReducer, useState } from "react"; +import { ethers } from "ethers"; +import { transparentize } from "polished"; - -import ScheduleIcon from '../../assets/images/icon/schedule.svg' +import ScheduleIcon from "../../assets/images/icon/schedule.svg"; import Page from "../../components/Page"; import DefaultCard from "../../components/Card"; import BridgeInputPanel from "../../components/BridgeInputPanel"; -import SwapIcon from '../../assets/images/cross/swap.svg'; +import SwapIcon from "../../assets/images/cross/swap.svg"; import AddressInputPanel from "../../components/AddressInputPanel"; -import getConfig from '../../config'; -import {formatCoin, thousandBit} from "../../utils/bridge/tools"; -import {useActiveWeb3React} from "../../hooks"; -import {isAddress} from "../../utils"; -import {useBetaMessageManager} from "../../contexts/LocalStorage"; -import {INITIAL_TOKENS_CONTEXT, useAllTokenDetails, useTokenDetails} from "../../contexts/Tokens"; -import {amountFormatter, formatDecimal, getAllQueryParams} from "../../utils/cross"; -import {getAllOutBalance, getLocalOutBalance, getTokenBalance} from "../../utils/bridge/getOutBalance"; -import {getAllowanceInfo} from "../../utils/bridge/approve"; -import {getRegisterInfo, getServerInfo, RegisterAddress, removeLocalConfig} from "../../utils/bridge/getServerInfo"; +import getConfig from "../../config"; +import { formatCoin, thousandBit } from "../../utils/bridge/tools"; +import { useActiveWeb3React } from "../../hooks"; +import { isAddress } from "../../utils"; +import { useBetaMessageManager } from "../../contexts/LocalStorage"; +import { INITIAL_TOKENS_CONTEXT, useAllTokenDetails, useTokenDetails } from "../../contexts/Tokens"; +import { amountFormatter, formatDecimal, getAllQueryParams } from "../../utils/cross"; +import { getAllOutBalance, getLocalOutBalance, getTokenBalance } from "../../utils/bridge/getOutBalance"; +import { getAllowanceInfo } from "../../utils/bridge/approve"; +import { getRegisterInfo, getServerInfo, RegisterAddress, removeLocalConfig } from "../../utils/bridge/getServerInfo"; import BridgeTokens from "../../contexts/BridgeTokens"; -import {createAddress, GetBTChashStatus, GetBTCtxnsAll, isBTCAddress} from "../../utils/bridge/BTC"; -import {useAddressBalance} from "../../contexts/Balances"; +import { createAddress, GetBTChashStatus, GetBTCtxnsAll, isBTCAddress } from "../../utils/bridge/BTC"; +import { useAddressBalance } from "../../contexts/Balances"; import BigNumber from "bignumber.js"; -import {useWalletModalToggle} from "../../state/application/hooks"; -import {useTransactionAdder} from "../../state/transactions/hooks"; -import {useSwapTokenContract} from "../../hooks/useSwapTokenContract"; - -import swapBTCABI from '../../constants/abis/swapBTCABI' -import swapETHABI from '../../constants/abis/swapETHABI' -import erc20 from '../../constants/abis/erc20.json' -import {recordTxns} from "../../utils/record"; -import {getWeb3BaseInfo, getWeb3ConTract} from "../../utils/web3/txns"; -import {getHashStatus, getWithdrawHashStatus, HDsendERC20Txns, MMsendERC20Txns} from "../../utils/web3/BridgeWeb3"; -import {shortenAddress} from "../../utils"; +import { useWalletModalToggle } from "../../state/application/hooks"; +import { useTransactionAdder } from "../../state/transactions/hooks"; +import { useSwapTokenContract } from "../../hooks/useSwapTokenContract"; + +import swapBTCABI from "../../constants/abis/swapBTCABI"; +import swapETHABI from "../../constants/abis/swapETHABI"; +import erc20 from "../../constants/abis/erc20.json"; +import { recordTxns } from "../../utils/record"; +import { getWeb3BaseInfo, getWeb3ConTract } from "../../utils/web3/txns"; +import { getHashStatus, getWithdrawHashStatus, HDsendERC20Txns, MMsendERC20Txns } from "../../utils/web3/BridgeWeb3"; +import { shortenAddress } from "../../utils"; import HardwareTip from "../../components/HardwareTips"; import WarningTip from "../../components/WarningTip"; import TokenLogo from "../../components/CrossTokenLogo"; -import {useTranslation} from "react-i18next"; -import Copy from '../../components/AccountDetails/Copy'; -import {Modal as BSModal} from "../../components/Modal/bootstrap"; +import { useTranslation } from "react-i18next"; +import Copy from "../../components/AccountDetails/Copy"; +import { Modal as BSModal } from "../../components/Modal/bootstrap"; import WalletConnectData from "../../components/WalletConnectData"; - const config = getConfig(); const Modal = styled(BSModal)` - & .modal-dialog { - max-width: 800px; - } - + & .modal-dialog { + max-width: 800px; + } + & .modal-body { - padding: 20px 30px 30px; + padding: 20px 30px 30px; } & .modal-header { - padding: 30px; + padding: 30px; } -` - +`; const TabButton = styled(BS)` - min-height: 48px; - height: 48px; - border-radius: 18px; - font-weight: 500; - font-size: 1rem; - min-width: 127px; - - @media (max-width: 991px) { - width: 100%; - display: flex; - align-items: center; - justify-content: center; - line-height: 1; - font-size: .875rem; - min-height: 40px; - height: 40px; - padding: 0; - border-radius: 12px; - min-width: 105px; - } -` + min-height: 48px; + height: 48px; + border-radius: 18px; + font-weight: 500; + font-size: 1rem; + min-width: 127px; + + @media (max-width: 991px) { + width: 100%; + display: flex; + align-items: center; + justify-content: center; + line-height: 1; + font-size: 0.875rem; + min-height: 40px; + height: 40px; + padding: 0; + border-radius: 12px; + min-width: 105px; + } +`; const Button = styled(BS)` - min-height: 48px; - height: 48px; - min-width: 205px; -` + min-height: 48px; + height: 48px; + min-width: 205px; +`; const Title = styled.h1` margin: 0; @@ -99,19 +96,17 @@ const Title = styled.h1` color: ${({ theme }) => theme.text1}; line-height: 3rem; - @media (max-width: 1199px) { font-size: 2.25rem; } @media (max-width: 991px) { font-size: 2rem; - margin-bottom: 0.5rem; + margin-bottom: 0.5rem; } @media (max-width: 767px) { font-size: 1.5rem; } -` - +`; const Header = styled.div` display: flex; @@ -119,635 +114,634 @@ const Header = styled.div` justify-content: space-between; margin-bottom: 20px; margin-top: 24px; - - @media(max-width: 991px) { + + @media (max-width: 991px) { margin-top: 10px; - flex-direction: column; - align-items: stretch; + flex-direction: column; + align-items: stretch; } -` +`; const Card = styled(DefaultCard)` - & > .card-body { - padding: 36px 64px; - + & > .card-body { + padding: 36px 64px; - @media (max-width: 991px) { - padding: 24px; - } + @media (max-width: 991px) { + padding: 24px; + } - @media (max-width: 576px) { - padding: 16px; - } - } -` + @media (max-width: 576px) { + padding: 16px; + } + } +`; const TabHeader = styled.div` - display: grid; - grid-column-gap: 25px; - grid-template-columns: 127px 127px; - border-radius: 20px; - - @media (max-width: 991px) { - padding: 8px; - background-color: ${({ theme }) => theme.modalBG}; - grid-template-columns: 1fr 1fr; - grid-column-gap: 16px; - border: 1px solid ${({ theme }) => theme.borderColor}; - } - - @media (max-width: 576px) { - grid-template-columns: 1fr; - grid-row-gap: 12px; - grid-column-gap: 0; - - } -` + display: grid; + grid-column-gap: 25px; + grid-template-columns: 127px 127px; + border-radius: 20px; + + @media (max-width: 991px) { + padding: 8px; + background-color: ${({ theme }) => theme.modalBG}; + grid-template-columns: 1fr 1fr; + grid-column-gap: 16px; + border: 1px solid ${({ theme }) => theme.borderColor}; + } + + @media (max-width: 576px) { + grid-template-columns: 1fr; + grid-row-gap: 12px; + grid-column-gap: 0; + } +`; const SwapCurrencies = styled.div` display: flex; - align-items: center; - justify-content: center; - padding: 12px; - cursor: pointer; + align-items: center; + justify-content: center; + padding: 12px; + cursor: pointer; + @media (max-width: 576px) { + padding: 8px; + } +`; - @media (max-width: 576px) { - padding: 8px; +const SubmitButtonContainer = styled.div` + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + margin-top: 40px; - } - -` + @media (max-width: 1199px) { + margin-top: 32px; + } -const SubmitButtonContainer = styled.div` - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - margin-top: 40px; - - @media (max-width: 1199px) { - margin-top: 32px; - } - - @media (max-width: 991px) { - margin-top: 28px; - - } - - @media (max-width: 767px) { - margin-top: 24px; - align-items: stretch; - } - -` + @media (max-width: 991px) { + margin-top: 28px; + } + @media (max-width: 767px) { + margin-top: 24px; + align-items: stretch; + } +`; const AlertContainer = styled.div` - border-radius: 18px; - display: flex; - align-items: center; - justify-content: space-between; - padding: 4px 4px 4px 64px; - background-color: ${({ theme }) => theme.primaryLight}; - - - @media (max-width: 1199px) { - padding-left: 48px; - } - - @media (max-width: 991px) { - padding-left: 36px; - } - - @media (max-width: 767px) { - padding-left: 16px; - } - -` + border-radius: 18px; + display: flex; + align-items: center; + justify-content: space-between; + padding: 4px 4px 4px 64px; + background-color: ${({ theme }) => theme.primaryLight}; -const AlertText = styled.span` - font-weight: 500; - font-size: 1rem; - color: ${({ theme }) => theme.primary}; - line-height: 21px; - padding-right: 1rem; - padding-top: 9px; - padding-bottom: 9px; -` + @media (max-width: 1199px) { + padding-left: 48px; + } + + @media (max-width: 991px) { + padding-left: 36px; + } + @media (max-width: 767px) { + padding-left: 16px; + } +`; -const AlertButton = styled(BS)` - min-height: 48px; - height: 48px; - border-radius: 18px; - font-weight: 500; - min-width: 105px; -` +const AlertText = styled.span` + font-weight: 500; + font-size: 1rem; + color: ${({ theme }) => theme.primary}; + line-height: 21px; + padding-right: 1rem; + padding-top: 9px; + padding-bottom: 9px; +`; +const AlertButton = styled(BS)` + min-height: 48px; + height: 48px; + border-radius: 18px; + font-weight: 500; + min-width: 105px; +`; const MintListVal = styled.div` - display: flex; - justify-content: flex-start; - align-items:center; - width: 100%; - cursor:pointer; - color:${({ theme }) => theme.text1}; - font-size:12px; - .green { - color: ${({ theme }) => theme.success}; - } - .red { - color: ${({ theme }) => theme.danger}; - } - .link { - color:${({ theme }) => theme.text1}; - } -` + display: flex; + justify-content: flex-start; + align-items: center; + width: 100%; + cursor: pointer; + color: ${({ theme }) => theme.text1}; + font-size: 12px; + .green { + color: ${({ theme }) => theme.success}; + } + .red { + color: ${({ theme }) => theme.danger}; + } + .link { + color: ${({ theme }) => theme.text1}; + } +`; const MintTip = styled.div` - border-radius: 0.25rem; - z-index: 99; - cursor:pointer; - color: ${({ theme }) => theme.warning}; - .txt { - width: 0;height: 100%;white-space: nowrap;overflow: hidden;transition: width 0.5s; - } - &:hover { - .txt { - width: 150px;padding: 0 1.25rem; - } - } -` + border-radius: 0.25rem; + z-index: 99; + cursor: pointer; + color: ${({ theme }) => theme.warning}; + .txt { + width: 0; + height: 100%; + white-space: nowrap; + overflow: hidden; + transition: width 0.5s; + } + &:hover { + .txt { + width: 150px; + padding: 0 1.25rem; + } + } +`; const HashStatus = styled.div` - display: flex; - justify-content: space-between; - align-items:center; - width: 100%; - font-size:12px; - color: ${({ theme }) => theme.text1}; - font-weight:bold; - padding: 12px 15px; - border-radius:9px; - margin-top:15px; - &.yellow { - border: 1px solid ${({ theme }) => theme.warning}; - background: ${({ theme }) => transparentize(0.15, theme.warning)}; - } - &.green{ - border: 1px solid ${({ theme }) => theme.success}; - background: ${({ theme }) => transparentize(0.15, theme.success)}; - } - &.red{ - border: 1px solid ${({ theme }) => theme.danger}; - background: ${({ theme }) => transparentize(0.15, theme.danger)}; - } -` + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + font-size: 12px; + color: ${({ theme }) => theme.text1}; + font-weight: bold; + padding: 12px 15px; + border-radius: 9px; + margin-top: 15px; + &.yellow { + border: 1px solid ${({ theme }) => theme.warning}; + background: ${({ theme }) => transparentize(0.15, theme.warning)}; + } + &.green { + border: 1px solid ${({ theme }) => theme.success}; + background: ${({ theme }) => transparentize(0.15, theme.success)}; + } + &.red { + border: 1px solid ${({ theme }) => theme.danger}; + background: ${({ theme }) => transparentize(0.15, theme.danger)}; + } +`; const MintList = styled.div` - padding: 8px 8px; - font-size: 0.875rem; - margin-bottom: 1rem; -` - + padding: 8px 8px; + font-size: 0.875rem; + margin-bottom: 1rem; +`; const MintDiv = styled.div` - width: 100%; - padding: 0; -` + width: 100%; + padding: 0; +`; const MintListCenter = styled(MintList)` - display: flex; - justify-content: center; - align-items: center; - margin-bottom: 1.875rem; -` + display: flex; + justify-content: center; + align-items: center; + margin-bottom: 1.875rem; +`; const DepositValue = styled.div` - width:100%; + width: 100%; text-align: center; p { - font-size:12px; - color:#96989e; - margin: 8px 0 8px; + font-size: 12px; + color: #96989e; + margin: 8px 0 8px; } span { - color:${({ theme }) => theme.text1}; - font-size:22px; + color: ${({ theme }) => theme.text1}; + font-size: 22px; } -` +`; const MintListLabel = styled.div` - width: 100%; - font-size:12px; - color: ${({ theme }) => theme.text3}; -` - + width: 100%; + font-size: 12px; + color: ${({ theme }) => theme.text3}; +`; const TokenLogoBox1 = styled.div` - ${({ theme }) => theme.flexColumnNoWrap}; - height: 46px; - background: ${ ({theme}) => theme.white}; - box-sizing:border-box; - border-radius: 100%; - margin-top: 15px; - border:1px solid ${({ theme }) => theme.text1}; -` + ${({ theme }) => theme.flexColumnNoWrap}; + height: 46px; + background: ${({ theme }) => theme.white}; + box-sizing: border-box; + border-radius: 100%; + margin-top: 15px; + border: 1px solid ${({ theme }) => theme.text1}; +`; const MintHahshList = styled.div` - position:fixed; - top:100px; - right:20px; - z-index: 99; - cursor:pointer; - margin:0; - ul { - list-style:none; - cursor:pointer; - margin:0; - padding:15px; - max-height: 200px; - overflow:auto; - li { - border-radius: 0.25rem; - box-shadow:0 0 5px 0 #E1902E; - margin:0 0 20px; - padding: 5px; - position:relative; - img { - display:block; - } - .txt { - width: 0;height: 100%;white-space: nowrap;overflow: hidden;transition: width 0.5s; - } - .del { - ${({ theme }) => theme.flexColumnNoWrap}; - position:absolute; - top: -9px; - right:-9px; - width: 18px; - height: 18px; - border:1px solid #ddd; - border-radius:100%; - background: rgba(0,0,0,.1); - line-height:1; - font-size:12px; - color:#fff; - opacity: 0; - } - &:hover { - .txt { - width: 150px;padding: 0 1.25rem; - } - .del { - opacity: 1; - } - } - } - } - .delete { - ${({ theme }) => theme.flexColumnNoWrap}; - width:100%; - background: rgba(0,0,0,.1); - } -` + position: fixed; + top: 100px; + right: 20px; + z-index: 99; + cursor: pointer; + margin: 0; + ul { + list-style: none; + cursor: pointer; + margin: 0; + padding: 15px; + max-height: 200px; + overflow: auto; + li { + border-radius: 0.25rem; + box-shadow: 0 0 5px 0 #e1902e; + margin: 0 0 20px; + padding: 5px; + position: relative; + img { + display: block; + } + .txt { + width: 0; + height: 100%; + white-space: nowrap; + overflow: hidden; + transition: width 0.5s; + } + .del { + ${({ theme }) => theme.flexColumnNoWrap}; + position: absolute; + top: -9px; + right: -9px; + width: 18px; + height: 18px; + border: 1px solid #ddd; + border-radius: 100%; + background: rgba(0, 0, 0, 0.1); + line-height: 1; + font-size: 12px; + color: #fff; + opacity: 0; + } + &:hover { + .txt { + width: 150px; + padding: 0 1.25rem; + } + .del { + opacity: 1; + } + } + } + } + .delete { + ${({ theme }) => theme.flexColumnNoWrap}; + width: 100%; + background: rgba(0, 0, 0, 0.1); + } +`; const Flex = styled.div` - display: flex; - justify-content: center; - padding: 2rem; - - button { - max-width: 20rem; - } - &.pd0 { - padding: 0 - } -` + display: flex; + justify-content: center; + padding: 2rem; + button { + max-width: 20rem; + } + &.pd0 { + padding: 0; + } +`; -const DEPOSIT_HISTORY = 'DEPOSIT_HISTORY' -const WITHDRAW_HISTORY = 'WITHDRAW_HISTORY' +const DEPOSIT_HISTORY = "DEPOSIT_HISTORY"; +const WITHDRAW_HISTORY = "WITHDRAW_HISTORY"; -const INPUT = 0 -const OUTPUT = 1 +const INPUT = 0; +const OUTPUT = 1; -function isArray(o){ - return Object.prototype.toString.call(o) === '[object Array]' +function isArray(o) { + return Object.prototype.toString.call(o) === "[object Array]"; } function getInitialSwapState(state) { - let wdInit = sessionStorage.getItem('WITHDRAW_HISTORY') && sessionStorage.getItem('WITHDRAW_HISTORY') !== 'undefined' ? JSON.parse(sessionStorage.getItem('WITHDRAW_HISTORY')) : {} - let wdArr = [] + let wdInit = + sessionStorage.getItem("WITHDRAW_HISTORY") && sessionStorage.getItem("WITHDRAW_HISTORY") !== "undefined" + ? JSON.parse(sessionStorage.getItem("WITHDRAW_HISTORY")) + : {}; + let wdArr = []; if (isArray(wdInit)) { - wdArr = wdInit + wdArr = wdInit; } else { - wdArr = wdInit[config.chainID] ? wdInit[config.chainID] : [] + wdArr = wdInit[config.chainID] ? wdInit[config.chainID] : []; } - let dpInit = sessionStorage.getItem('DEPOSIT_HISTORY') && sessionStorage.getItem('DEPOSIT_HISTORY') !== 'undefined' ? JSON.parse(sessionStorage.getItem('DEPOSIT_HISTORY')) : {} - let dpArr = [] + let dpInit = + sessionStorage.getItem("DEPOSIT_HISTORY") && sessionStorage.getItem("DEPOSIT_HISTORY") !== "undefined" + ? JSON.parse(sessionStorage.getItem("DEPOSIT_HISTORY")) + : {}; + let dpArr = []; if (isArray(dpInit)) { - dpArr = dpInit + dpArr = dpInit; } else { - dpArr = dpInit[config.chainID] ? dpInit[config.chainID] : [] + dpArr = dpInit[config.chainID] ? dpInit[config.chainID] : []; } return { - independentValue: state.exactFieldURL && state.exactAmountURL ? state.exactAmountURL : '', // this is a user input - dependentValue: '', // this is a calculated number - independentField: state.exactFieldURL === 'output' ? OUTPUT : INPUT, + independentValue: state.exactFieldURL && state.exactAmountURL ? state.exactAmountURL : "", // this is a user input + dependentValue: "", // this is a calculated number + independentField: state.exactFieldURL === "output" ? OUTPUT : INPUT, inputCurrency: state.inputCurrencyURL ? state.inputCurrencyURL : config.initBridge, outputCurrency: state.outputCurrencyURL ? state.outputCurrencyURL === config.symbol ? !state.inputCurrencyURL || (state.inputCurrencyURL && state.inputCurrencyURL !== config.symbol) ? config.symbol - : '' + : "" : state.outputCurrencyURL : state.initialCurrency - ? state.initialCurrency - : config.initBridge, + ? state.initialCurrency + : config.initBridge, // hashArr: sessionStorage.getItem('DEPOSIT_HISTORY') ? JSON.parse(sessionStorage.getItem('DEPOSIT_HISTORY')) : [], // withdrawArr: sessionStorage.getItem('WITHDRAW_HISTORY') && sessionStorage.getItem('WITHDRAW_HISTORY') !== 'undefined' ? JSON.parse(sessionStorage.getItem('WITHDRAW_HISTORY')) : [], hashArr: dpArr, withdrawArr: wdArr, - bridgeType: 'mint' - } + bridgeType: "mint", + }; } - function swapStateReducer(state, action) { switch (action.type) { - case 'FLIP_INDEPENDENT': { - const { independentField, inputCurrency, outputCurrency } = state + case "FLIP_INDEPENDENT": { + const { independentField, inputCurrency, outputCurrency } = state; return { ...state, - dependentValue: '', + dependentValue: "", independentField: independentField === INPUT ? OUTPUT : INPUT, inputCurrency: outputCurrency, - outputCurrency: inputCurrency - } + outputCurrency: inputCurrency, + }; } - case 'SELECT_CURRENCY': { - const { inputCurrency, outputCurrency } = state - const { field, currency } = action.payload + case "SELECT_CURRENCY": { + const { inputCurrency, outputCurrency } = state; + const { field, currency } = action.payload; - const newInputCurrency = field === INPUT ? currency : inputCurrency - const newOutputCurrency = field === OUTPUT ? currency : outputCurrency + const newInputCurrency = field === INPUT ? currency : inputCurrency; + const newOutputCurrency = field === OUTPUT ? currency : outputCurrency; if (newInputCurrency === newOutputCurrency) { return { ...state, - inputCurrency: field === INPUT ? currency : '', - outputCurrency: field === OUTPUT ? currency : '' - } + inputCurrency: field === INPUT ? currency : "", + outputCurrency: field === OUTPUT ? currency : "", + }; } else { return { ...state, inputCurrency: newInputCurrency, - outputCurrency: newOutputCurrency - } + outputCurrency: newOutputCurrency, + }; } } - case 'UPDATE_INDEPENDENT': { - const { field, value, realyValue } = action.payload - const { dependentValue, independentValue } = state + case "UPDATE_INDEPENDENT": { + const { field, value, realyValue } = action.payload; + const { dependentValue, independentValue } = state; return { ...state, independentValue: value, - dependentValue: value === independentValue ? dependentValue : '', + dependentValue: value === independentValue ? dependentValue : "", independentField: field, - realyValue: realyValue - } + realyValue: realyValue, + }; } - case 'UPDATE_DEPENDENT': { + case "UPDATE_DEPENDENT": { return { ...state, - dependentValue: action.payload - } + dependentValue: action.payload, + }; } - case 'UPDATE_BREDGETYPE': { + case "UPDATE_BREDGETYPE": { return { ...state, - bridgeType: action.payload - } + bridgeType: action.payload, + }; } - case 'UPDATE_SWAPREGISTER': { + case "UPDATE_SWAPREGISTER": { if (action.token && state.inputCurrency.toLowerCase() !== action.token.toLowerCase()) { - return state + return state; } return { ...state, - registerAddress: action.payload ? action.payload : '', - PlusGasPricePercentage: action.PlusGasPricePercentage ? action.PlusGasPricePercentage : '', + registerAddress: action.payload ? action.payload : "", + PlusGasPricePercentage: action.PlusGasPricePercentage ? action.PlusGasPricePercentage : "", isDeposit: action.isDeposit, - depositMaxNum: action.depositMaxNum ? action.depositMaxNum : '', - depositMinNum: action.depositMinNum ? action.depositMinNum : '', + depositMaxNum: action.depositMaxNum ? action.depositMaxNum : "", + depositMinNum: action.depositMinNum ? action.depositMinNum : "", isRedeem: action.isRedeem, - redeemMaxNum: action.redeemMaxNum ? action.redeemMaxNum : '', - redeemMinNum: action.redeemMinNum ? action.redeemMinNum : '', - maxFee: action.maxFee ? action.maxFee : '', - minFee: action.minFee ? action.minFee : '', - fee: action.fee ? action.fee : '', + redeemMaxNum: action.redeemMaxNum ? action.redeemMaxNum : "", + redeemMinNum: action.redeemMinNum ? action.redeemMinNum : "", + maxFee: action.maxFee ? action.maxFee : "", + minFee: action.minFee ? action.minFee : "", + fee: action.fee ? action.fee : "", dMaxFee: action.dMaxFee ? action.dMaxFee : 0, dMinFee: action.dMinFee ? action.dMinFee : 0, dFee: action.dFee ? action.dFee : 0, - redeemBigValMoreTime: action.redeemBigValMoreTime ? action.redeemBigValMoreTime : '', - depositBigValMoreTime: action.depositBigValMoreTime ? action.depositBigValMoreTime : '', - pairid: action.pairid ? action.pairid : '', - } + redeemBigValMoreTime: action.redeemBigValMoreTime ? action.redeemBigValMoreTime : "", + depositBigValMoreTime: action.depositBigValMoreTime ? action.depositBigValMoreTime : "", + pairid: action.pairid ? action.pairid : "", + }; } - case 'UPDATE_MINTTYPE': { + case "UPDATE_MINTTYPE": { return { ...state, - isViewMintModel: action.payload - } + isViewMintModel: action.payload, + }; } - case 'UPDATE_MINTHISTORY': { + case "UPDATE_MINTHISTORY": { return { ...state, - mintHistory: action.payload - } + mintHistory: action.payload, + }; } - case 'UPDATE_MINTINFOTYPE': { + case "UPDATE_MINTINFOTYPE": { return { ...state, - isViewMintInfo: action.payload - } + isViewMintInfo: action.payload, + }; } - case 'UPDATE_HASH_STATUS': { - const { hashData, type, NewHashCount } = action.payload - const { hashArr, hashCount } = state + case "UPDATE_HASH_STATUS": { + const { hashData, type, NewHashCount } = action.payload; + const { hashArr, hashCount } = state; if (!type) { - hashArr.push(hashData) + hashArr.push(hashData); } - let arr = type ? hashData : hashArr - let initObj = sessionStorage.getItem(DEPOSIT_HISTORY) ? JSON.parse(sessionStorage.getItem(DEPOSIT_HISTORY)) : {} - let obj = {} + let arr = type ? hashData : hashArr; + let initObj = sessionStorage.getItem(DEPOSIT_HISTORY) + ? JSON.parse(sessionStorage.getItem(DEPOSIT_HISTORY)) + : {}; + let obj = {}; if (isArray(initObj)) { - obj[config.chainID] = arr + obj[config.chainID] = arr; } else { - obj = initObj - obj[config.chainID] = arr + obj = initObj; + obj[config.chainID] = arr; } - sessionStorage.setItem(DEPOSIT_HISTORY, JSON.stringify(obj)) - let count = 0 + sessionStorage.setItem(DEPOSIT_HISTORY, JSON.stringify(obj)); + let count = 0; if ((hashCount || hashCount === 0) && NewHashCount) { - count = hashCount + NewHashCount + count = hashCount + NewHashCount; } return { ...state, hashArr: arr, - hashCount: count - } + hashCount: count, + }; } - case 'UPDATE_WITHDRAW_STATUS': { - const { withdrawData, type, NewHashCount } = action.payload - const { withdrawArr, withdrawCount } = state + case "UPDATE_WITHDRAW_STATUS": { + const { withdrawData, type, NewHashCount } = action.payload; + const { withdrawArr, withdrawCount } = state; if (!type) { - withdrawArr.push(withdrawData) + withdrawArr.push(withdrawData); } - let arr = type ? withdrawData : withdrawArr - let initObj = sessionStorage.getItem(WITHDRAW_HISTORY) ? JSON.parse(sessionStorage.getItem(WITHDRAW_HISTORY)) : {} - let obj = {} + let arr = type ? withdrawData : withdrawArr; + let initObj = sessionStorage.getItem(WITHDRAW_HISTORY) + ? JSON.parse(sessionStorage.getItem(WITHDRAW_HISTORY)) + : {}; + let obj = {}; if (isArray(initObj)) { - obj[config.chainID] = arr + obj[config.chainID] = arr; } else { - obj = initObj - obj[config.chainID] = arr + obj = initObj; + obj[config.chainID] = arr; } // console.log(obj) - sessionStorage.setItem(WITHDRAW_HISTORY, JSON.stringify(obj)) - let count = 0 + sessionStorage.setItem(WITHDRAW_HISTORY, JSON.stringify(obj)); + let count = 0; if ((withdrawCount || withdrawCount === 0) && NewHashCount) { - count = withdrawCount + NewHashCount + count = withdrawCount + NewHashCount; } return { ...state, withdrawArr: arr, - withdrawCount: count - } + withdrawCount: count, + }; } - default: { //UPDATE_MINTINFOTYPE - return getInitialSwapState() + default: { + //UPDATE_MINTINFOTYPE + return getInitialSwapState(); } } } -function isSpecialCoin (coin) { - if (formatCoin(coin) === 'BTC') { - return 1 - } else if (formatCoin(coin) === 'LTC') { - return 2 - } else if (formatCoin(coin) === 'BLOCK') { - return 3 +function isSpecialCoin(coin) { + if (formatCoin(coin) === "BTC") { + return 1; + } else if (formatCoin(coin) === "LTC") { + return 2; + } else if (formatCoin(coin) === "BLOCK") { + return 3; } else { - return 0 + return 0; } } -function formatName (name, extendObj) { +function formatName(name, extendObj) { // console.log(name) if (name) { - if ( - config.symbol === 'BNB' || - config.symbol === 'ETH' || - config.symbol === 'FTM' - ) { - if (name.indexOf('Anyswap') !== -1) { - name = name.replace(config.suffix, '') - return name + '(Fusion)' + if (config.symbol === "BNB" || config.symbol === "ETH" || config.symbol === "FTM") { + if (name.indexOf("Anyswap") !== -1) { + name = name.replace(config.suffix, ""); + return name + "(Fusion)"; } else { - return formatOutName(name, extendObj) + return formatOutName(name, extendObj); } } else { - if (name.indexOf('Anyswap') !== -1) { - return name + '(Fusion)' + if (name.indexOf("Anyswap") !== -1) { + return name + "(Fusion)"; } else { - return formatOutName(name, extendObj) + return formatOutName(name, extendObj); } } } else { - return name + return name; } } - -function formatOutName (name, extendObj) { - let srcChainId = extendObj && extendObj.BRIDGE && extendObj.BRIDGE.length > 0 ? extendObj.BRIDGE[0].type : '' - name = name.replace(config.namePrefix, '').replace(config.suffix, '') +function formatOutName(name, extendObj) { + let srcChainId = extendObj && extendObj.BRIDGE && extendObj.BRIDGE.length > 0 ? extendObj.BRIDGE[0].type : ""; + name = name.replace(config.namePrefix, "").replace(config.suffix, ""); if (srcChainId) { - if (Number(srcChainId) === 1 && name.indexOf('Ethereum') === -1) { - if (name === 'Frapped USDT') { - name = 'Tether-ERC20' + if (Number(srcChainId) === 1 && name.indexOf("Ethereum") === -1) { + if (name === "Frapped USDT") { + name = "Tether-ERC20"; } else { - name = name + '-ERC20' + name = name + "-ERC20"; } - } else if (Number(srcChainId) === 56 && name.indexOf('Binance') === -1) { - name = name + '-BEP20' - } else if (Number(srcChainId) === 128 && name.indexOf('Huobi') === -1) { - name = name + '-HECO' - } else if (Number(srcChainId) === 250 && name.indexOf('Fantom') === -1) { - name = name + '-FRC20' - } else if (Number(srcChainId) === 32659 && name.indexOf('Fusion') === -1) { - name = name + '(Fusion)' + } else if (Number(srcChainId) === 56 && name.indexOf("Binance") === -1) { + name = name + "-BEP20"; + } else if (Number(srcChainId) === 128 && name.indexOf("Huobi") === -1) { + name = name + "-HECO"; + } else if (Number(srcChainId) === 250 && name.indexOf("Fantom") === -1) { + name = name + "-FRC20"; + } else if (Number(srcChainId) === 32659 && name.indexOf("Fusion") === -1) { + name = name + "(Fusion)"; } } // console.log(name) - return name + return name; } -const selfUseAllToken = config.noSupportBridge -let hashInterval - +const selfUseAllToken = config.noSupportBridge; +let hashInterval; -const CrossBridge = props => { - let { account, chainId, error, library } = useActiveWeb3React() - const [showBetaMessage] = useBetaMessageManager() - const allTokens = useAllTokenDetails() - let walletType = sessionStorage.getItem('walletType') +const CrossBridge = (props) => { + let { account, chainId, error, library } = useActiveWeb3React(); + const [showBetaMessage] = useBetaMessageManager(); + const allTokens = useAllTokenDetails(); + let walletType = sessionStorage.getItem("walletType"); const { t } = useTranslation(); // @todo: delete this variables const [inputCurrencySwap, setInputCurrency] = useState(ETHER); const [outputCurrencySwap, setOutputCurrency] = useState(undefined); - const [inputAmount, setInputAmount] = useState(''); - const [outputAmount, setOutputAmount] = useState(''); + const [inputAmount, setInputAmount] = useState(""); + const [outputAmount, setOutputAmount] = useState(""); - let params = getAllQueryParams() - params = params ? params : {} + let params = getAllQueryParams(); + params = params ? params : {}; - const urlAddedTokens = {} + const urlAddedTokens = {}; if (params.inputCurrency) { - urlAddedTokens[params.inputCurrency] = true + urlAddedTokens[params.inputCurrency] = true; } if (params.outputCurrency) { - urlAddedTokens[params.outputCurrency] = true + urlAddedTokens[params.outputCurrency] = true; } const getAllOutBalanceFn = useCallback(() => { - let tokenClass = {} + let tokenClass = {}; for (let tk in allTokens) { if (allTokens[tk].extendObj && allTokens[tk].extendObj.BRIDGE) { for (let cd of allTokens[tk].extendObj.BRIDGE) { if (cd.isSwitch) { if (!tokenClass[cd.type]) { - tokenClass[cd.type] = {} + tokenClass[cd.type] = {}; } - tokenClass[cd.type][tk] = allTokens[tk] + tokenClass[cd.type][tk] = allTokens[tk]; } } } } - getAllOutBalance(tokenClass, account) - }, [allTokens, account]) + getAllOutBalance(tokenClass, account); + }, [allTokens, account]); useEffect(() => { if (account) { - getAllOutBalanceFn() + getAllOutBalanceFn(); } - }, [account, getAllOutBalanceFn]) + }, [account, getAllOutBalanceFn]); const [swapState, dispatchSwapState] = useReducer( swapStateReducer, @@ -756,10 +750,10 @@ const CrossBridge = props => { inputCurrencyURL: params.inputCurrency, outputCurrencyURL: params.outputCurrency, exactFieldURL: params.exactField, - exactAmountURL: params.exactAmount + exactAmountURL: params.exactAmount, }, getInitialSwapState - ) + ); const { independentValue, @@ -792,20 +786,19 @@ const CrossBridge = props => { withdrawCount, redeemBigValMoreTime, depositBigValMoreTime, - pairid - } = swapState - + pairid, + } = swapState; const [recipient, setRecipient] = useState({ - address: '', - name: '' - }) + address: "", + name: "", + }); const recipientCount = useMemo(() => { - return Date.now() + inputCurrency + bridgeType - }, [inputCurrency, bridgeType]) + return Date.now() + inputCurrency + bridgeType; + }, [inputCurrency, bridgeType]); - const [recipientError, setRecipientError] = useState() + const [recipientError, setRecipientError] = useState(); // get decimals and exchange address for each of the currency types const { @@ -822,60 +815,59 @@ const CrossBridge = props => { maxFee: initMaxFee, minFee: initMinFee, fee: initFee, - extendObj - } = useTokenDetails( inputCurrency ) - + extendObj, + } = useTokenDetails(inputCurrency); - const [isRegister, setIsRegister] = useState(false) + const [isRegister, setIsRegister] = useState(false); - const [isHardwareTip, setIsHardwareTip] = useState(false) - const [isHardwareError, setIsHardwareError] = useState(false) - const [hardwareTxnsInfo, setHardwareTxnsInfo] = useState('') - const [isDisabled, setIsDisableed] = useState(true) - const [isMintBtn, setIsMintBtn] = useState(false) - const [isRedeemBtn, setIsRedeem] = useState(false) + const [isHardwareTip, setIsHardwareTip] = useState(false); + const [isHardwareError, setIsHardwareError] = useState(false); + const [hardwareTxnsInfo, setHardwareTxnsInfo] = useState(""); + const [isDisabled, setIsDisableed] = useState(true); + const [isMintBtn, setIsMintBtn] = useState(false); + const [isRedeemBtn, setIsRedeem] = useState(false); const [mintDtil, setMintDtil] = useState({ - coin: '', - value: '', - hash: '', - from: '', - to: '', + coin: "", + value: "", + hash: "", + from: "", + to: "", status: 0, - timestamp: '' - }) - const [mintDtilView, setMintDtilView] = useState(false) - const [mintSureBtn, setMintSureBtn] = useState(false) - const [mintModelTitle, setMintModelTitle] = useState() - const [mintModelTip, setMintModelTip] = useState() - const [balanceError, setBalanceError] = useState() - const [bridgeNode, setBridgeNode] = useState() - const [approveNum, setApproveNum] = useState() - const [approveBtnView, setApproveNumBtnView] = useState(1) - - function setInit (disabled) { - setIsRedeem(true) - setIsMintBtn(true) + timestamp: "", + }); + const [mintDtilView, setMintDtilView] = useState(false); + const [mintSureBtn, setMintSureBtn] = useState(false); + const [mintModelTitle, setMintModelTitle] = useState(); + const [mintModelTip, setMintModelTip] = useState(); + const [balanceError, setBalanceError] = useState(); + const [bridgeNode, setBridgeNode] = useState(); + const [approveNum, setApproveNum] = useState(); + const [approveBtnView, setApproveNumBtnView] = useState(1); + + function setInit(disabled) { + setIsRedeem(true); + setIsMintBtn(true); dispatchSwapState({ - type: 'UPDATE_SWAPREGISTER', - payload: '', - PlusGasPricePercentage: '', + type: "UPDATE_SWAPREGISTER", + payload: "", + PlusGasPricePercentage: "", isDeposit: disabled, - depositMaxNum: '', - depositMinNum: '', - depositBigValMoreTime: '', + depositMaxNum: "", + depositMinNum: "", + depositBigValMoreTime: "", isRedeem: disabled, - redeemMaxNum: '', - redeemMinNum: '', - maxFee: '', - minFee: '', - fee: '', - dMaxFee: '', - dMinFee: '', - dFee: '', - redeemBigValMoreTime: '', - token: '', - pairid: '' - }) + redeemMaxNum: "", + redeemMinNum: "", + maxFee: "", + minFee: "", + fee: "", + dMaxFee: "", + dMinFee: "", + dFee: "", + redeemBigValMoreTime: "", + token: "", + pairid: "", + }); } const fetchPoolTokens = useCallback(() => { @@ -883,95 +875,102 @@ const CrossBridge = props => { if (extendObj && extendObj.APPROVE && account) { // if (true) { // getAllowanceInfo(account, '', chainId, inputCurrency).then(res => { - getAllowanceInfo(account, extendObj.APPROVE, chainId, inputCurrency).then(res => { + getAllowanceInfo(account, extendObj.APPROVE, chainId, inputCurrency).then((res) => { // console.log(res) - if (res.msg === 'Success') { - setApproveNum(res.info.approve) + if (res.msg === "Success") { + setApproveNum(res.info.approve); } else { - setApproveNum('') + setApproveNum(""); } - setApproveNumBtnView(1) - }) + setApproveNumBtnView(1); + }); } else { - setApproveNum('') - setApproveNumBtnView(1) + setApproveNum(""); + setApproveNumBtnView(1); } - }, [inputCurrency, account, extendObj, chainId]) - + }, [inputCurrency, account, extendObj, chainId]); useEffect(() => { - fetchPoolTokens() - library.on('block', fetchPoolTokens) + fetchPoolTokens(); + library.on("block", fetchPoolTokens); return () => { - library.removeListener('block', fetchPoolTokens) - } - }, [inputCurrency, library, account, fetchPoolTokens]) + library.removeListener("block", fetchPoolTokens); + }; + }, [inputCurrency, library, account, fetchPoolTokens]); useEffect(() => { - let node = extendObj && extendObj.BRIDGE ? extendObj.BRIDGE[0].type : '' - let version = extendObj && extendObj.VERSION ? extendObj.VERSION : '' - let tokenOnlyOne = inputCurrency + let node = extendObj && extendObj.BRIDGE ? extendObj.BRIDGE[0].type : ""; + let version = extendObj && extendObj.VERSION ? extendObj.VERSION : ""; + let tokenOnlyOne = inputCurrency; - setInit('') - let coin = formatCoin(inputSymbol) + setInit(""); + let coin = formatCoin(inputSymbol); if (account && initIsDeposit && initIsRedeem) { - getServerInfo(account, tokenOnlyOne, inputSymbol, chainId, version).then(res => { - console.log(res) - if (res.msg === 'Success' && res.info) { - let serverInfo = res.info + getServerInfo(account, tokenOnlyOne, inputSymbol, chainId, version).then((res) => { + console.log(res); + if (res.msg === "Success" && res.info) { + let serverInfo = res.info; // setIsRegister(true) try { - let DepositAddress = '' + let DepositAddress = ""; if (!isSpecialCoin(coin)) { - let erc20Token = BridgeTokens[node] && BridgeTokens[node][coin] && BridgeTokens[node][coin].token ? BridgeTokens[node][coin].token : '' + let erc20Token = + BridgeTokens[node] && BridgeTokens[node][coin] && BridgeTokens[node][coin].token + ? BridgeTokens[node][coin].token + : ""; if ( - (initDepositAddress.toLowerCase() !== serverInfo.depositAddress.toLowerCase()) - || (tokenOnlyOne.toLowerCase() !== serverInfo.token.toLowerCase()) - || ( - erc20Token && erc20Token.toLowerCase() !== serverInfo.outnetToken.toLowerCase() - ) + initDepositAddress.toLowerCase() !== serverInfo.depositAddress.toLowerCase() || + tokenOnlyOne.toLowerCase() !== serverInfo.token.toLowerCase() || + (erc20Token && erc20Token.toLowerCase() !== serverInfo.outnetToken.toLowerCase()) ) { - console.log(1) + console.log(1); // removeRegisterInfo(account, tokenOnlyOne) - removeLocalConfig(account, tokenOnlyOne, chainId) - setInit(0) - return + removeLocalConfig(account, tokenOnlyOne, chainId); + setInit(0); + return; } - DepositAddress = serverInfo.depositAddress + DepositAddress = serverInfo.depositAddress; } else { if ( - serverInfo.dcrmAddress.toLowerCase() !== config[formatCoin(coin).toLowerCase()].initAddr.toLowerCase() + serverInfo.dcrmAddress.toLowerCase() !== + config[formatCoin(coin).toLowerCase()].initAddr.toLowerCase() ) { - console.log(2) + console.log(2); // removeRegisterInfo(account, tokenOnlyOne) - removeLocalConfig(account, tokenOnlyOne, chainId) - setInit(0) - return + removeLocalConfig(account, tokenOnlyOne, chainId); + setInit(0); + return; } - let p2pAddress = serverInfo.p2pAddress ? serverInfo.p2pAddress : getRegisterInfo(account, tokenOnlyOne, chainId, version, coin).p2pAddress + let p2pAddress = serverInfo.p2pAddress + ? serverInfo.p2pAddress + : getRegisterInfo(account, tokenOnlyOne, chainId, version, coin).p2pAddress; if (p2pAddress) { - DepositAddress = p2pAddress - let localBTCAddr = createAddress(account, coin, config[formatCoin(coin).toLowerCase()].initAddr) - console.log('DepositAddress', DepositAddress) - console.log('localBTCAddr', localBTCAddr) + DepositAddress = p2pAddress; + let localBTCAddr = createAddress( + account, + coin, + config[formatCoin(coin).toLowerCase()].initAddr + ); + console.log("DepositAddress", DepositAddress); + console.log("localBTCAddr", localBTCAddr); if (p2pAddress !== localBTCAddr) { - console.log(3) + console.log(3); // removeRegisterInfo(account, tokenOnlyOne) - removeLocalConfig(account, tokenOnlyOne, chainId) - setInit(0) - return + removeLocalConfig(account, tokenOnlyOne, chainId); + setInit(0); + return; } } else { - console.log(4) + console.log(4); // removeRegisterInfo(account, tokenOnlyOne) // removeLocalConfig(account, tokenOnlyOne, chainId) - setInit('') - return + setInit(""); + return; } } let serverObj = { - type: 'UPDATE_SWAPREGISTER', + type: "UPDATE_SWAPREGISTER", payload: DepositAddress, PlusGasPricePercentage: serverInfo.PlusGasPricePercentage, isDeposit: serverInfo.isDeposit, @@ -990,275 +989,337 @@ const CrossBridge = props => { redeemBigValMoreTime: serverInfo.redeemBigValMoreTime, token: serverInfo.token, pairid: serverInfo.pairid, - } - dispatchSwapState(serverObj) + }; + dispatchSwapState(serverObj); } catch (error) { - console.log(error) - setInit('') - return + console.log(error); + setInit(""); + return; } } else { - setInit('') + setInit(""); // setIsRegister(false) } - }) + }); } else { - setInit('') + setInit(""); } - }, [inputCurrency, account, initDepositAddress, initIsDeposit, initDepositMaxNum, initDepositMinNum, initIsRedeem, initRedeemMaxNum, initRedeemMinNum, initMaxFee, initMinFee, initFee, inputSymbol, isRegister, chainId, extendObj]) - + }, [ + inputCurrency, + account, + initDepositAddress, + initIsDeposit, + initDepositMaxNum, + initDepositMinNum, + initIsRedeem, + initRedeemMaxNum, + initRedeemMinNum, + initMaxFee, + initMinFee, + initFee, + inputSymbol, + isRegister, + chainId, + extendObj, + ]); useEffect(() => { - let version = extendObj && extendObj.VERSION ? extendObj.VERSION : '' - let tokenOnlyOne = inputCurrency + let version = extendObj && extendObj.VERSION ? extendObj.VERSION : ""; + let tokenOnlyOne = inputCurrency; - setIsRegister(false) - let coin = formatCoin(inputSymbol) + setIsRegister(false); + let coin = formatCoin(inputSymbol); if (account && initIsDeposit && initIsRedeem) { - RegisterAddress(account, tokenOnlyOne, coin, chainId, version).then(res => { - if (res && res.msg === 'Success') { - setIsRegister(true) + RegisterAddress(account, tokenOnlyOne, coin, chainId, version).then((res) => { + if (res && res.msg === "Success") { + setIsRegister(true); } else { - setIsRegister(false) + setIsRegister(false); } - }) + }); } else { - setIsRegister(false) + setIsRegister(false); } - }, [inputSymbol, initIsDeposit, initIsRedeem, account, extendObj, chainId, inputCurrency]) + }, [inputSymbol, initIsDeposit, initIsRedeem, account, extendObj, chainId, inputCurrency]); - const [outNetBalance, setOutNetBalance] = useState() - const [outNetETHBalance, setOutNetETHBalance] = useState() + const [outNetBalance, setOutNetBalance] = useState(); + const [outNetETHBalance, setOutNetETHBalance] = useState(); // get balances for each of the currency types - const inputBalance = useAddressBalance(account, inputCurrency) - const FSNBalance = useAddressBalance(account, config.symbol) - const FSNBalanceNum = FSNBalance ? amountFormatter(FSNBalance) : 0 + const inputBalance = useAddressBalance(account, inputCurrency); + const FSNBalance = useAddressBalance(account, config.symbol); + const FSNBalanceNum = FSNBalance ? amountFormatter(FSNBalance) : 0; const setOutBalance = useCallback(() => { - let node = extendObj && extendObj.BRIDGE ? extendObj.BRIDGE[0].type : '' + let node = extendObj && extendObj.BRIDGE ? extendObj.BRIDGE[0].type : ""; if (node && account) { - let lob = getLocalOutBalance(node, account, inputCurrency) + let lob = getLocalOutBalance(node, account, inputCurrency); if (lob && lob.info) { - let bl = amountFormatter(new BigNumber(lob.info.balance), inputDecimals, Math.min(8, inputDecimals)) - setOutNetBalance(bl) + let bl = amountFormatter(new BigNumber(lob.info.balance), inputDecimals, Math.min(8, inputDecimals)); + setOutNetBalance(bl); } else { - setOutNetBalance('') + setOutNetBalance(""); } - let lobBase = getLocalOutBalance(node, account, 'BASE') + let lobBase = getLocalOutBalance(node, account, "BASE"); if (lobBase && lobBase.info) { - let bl = amountFormatter(new BigNumber(lobBase.info.balance), 18, 8) - setOutNetETHBalance(bl) + let bl = amountFormatter(new BigNumber(lobBase.info.balance), 18, 8); + setOutNetETHBalance(bl); } else { - setOutNetETHBalance('') + setOutNetETHBalance(""); } } - }, [inputCurrency, account, extendObj, inputDecimals]) + }, [inputCurrency, account, extendObj, inputDecimals]); useEffect(() => { // getOutBalance() - setOutNetBalance('') - setOutNetETHBalance('') - setOutBalance() - }, [inputCurrency, account, extendObj, inputBalance, hashCount, hashArr, FSNBalance, setOutBalance]) + setOutNetBalance(""); + setOutNetETHBalance(""); + setOutBalance(); + }, [inputCurrency, account, extendObj, inputBalance, hashCount, hashArr, FSNBalance, setOutBalance]); const inputBalanceFormatted = !!(inputBalance && Number.isInteger(inputDecimals)) ? amountFormatter(inputBalance, inputDecimals, inputDecimals) - : '' + : ""; const dependentValueFormatted = !!(dependentValue && (inputDecimals || inputDecimals === 0)) ? amountFormatter(dependentValue, inputDecimals, Math.min(8, inputDecimals), false) - : '' + : ""; - let inputValueFormatted = independentField === INPUT ? independentValue : dependentValueFormatted + let inputValueFormatted = independentField === INPUT ? independentValue : dependentValueFormatted; // console.log(inputValueFormatted) - inputValueFormatted = inputValueFormatted || inputValueFormatted === 0 ? Number(formatDecimal(inputValueFormatted, inputDecimals)) : '' - - const [isLimitAction, setIsLimitAction] = useState(true) - const [limitAmount, setLimitAmount] = useState(0) + inputValueFormatted = + inputValueFormatted || inputValueFormatted === 0 + ? Number(formatDecimal(inputValueFormatted, inputDecimals)) + : ""; + const [isLimitAction, setIsLimitAction] = useState(true); + const [limitAmount, setLimitAmount] = useState(0); useEffect(() => { if (extendObj && extendObj.APPROVE) { - let node = extendObj && extendObj.BRIDGE ? extendObj.BRIDGE[0].type : '' - if (bridgeType && bridgeType === 'redeem') { - let coin = formatCoin(inputSymbol) - let erc20Token = BridgeTokens[node] && BridgeTokens[node][coin] && BridgeTokens[node][coin].token ? BridgeTokens[node][coin].token : '' - getTokenBalance(node, erc20Token, initDepositAddress, 0).then(res => { + let node = extendObj && extendObj.BRIDGE ? extendObj.BRIDGE[0].type : ""; + if (bridgeType && bridgeType === "redeem") { + let coin = formatCoin(inputSymbol); + let erc20Token = + BridgeTokens[node] && BridgeTokens[node][coin] && BridgeTokens[node][coin].token + ? BridgeTokens[node][coin].token + : ""; + getTokenBalance(node, erc20Token, initDepositAddress, 0).then((res) => { // console.log(initDepositAddress) // console.log(res) - let amount = amountFormatter(new BigNumber(res), inputDecimals) - amount = Number(amount) - let num = Number(inputValueFormatted) - setLimitAmount(amount) - if ((amount - extendObj.APPROVELIMIT) > num) { - setIsLimitAction(true) + let amount = amountFormatter(new BigNumber(res), inputDecimals); + amount = Number(amount); + let num = Number(inputValueFormatted); + setLimitAmount(amount); + if (amount - extendObj.APPROVELIMIT > num) { + setIsLimitAction(true); } else { - setIsLimitAction(false) + setIsLimitAction(false); } - }) + }); } else { - getTokenBalance(config.nodeRpc, inputCurrency, extendObj.APPROVE, 1).then(res => { - console.log(res) - let amount = amountFormatter(new BigNumber(res), inputDecimals) - amount = Number(amount) - let num = Number(inputValueFormatted) + getTokenBalance(config.nodeRpc, inputCurrency, extendObj.APPROVE, 1).then((res) => { + console.log(res); + let amount = amountFormatter(new BigNumber(res), inputDecimals); + amount = Number(amount); + let num = Number(inputValueFormatted); // console.log(amount) - setLimitAmount(amount) - if ((amount - extendObj.APPROVELIMIT) > num) { - setIsLimitAction(true) + setLimitAmount(amount); + if (amount - extendObj.APPROVELIMIT > num) { + setIsLimitAction(true); } else { - setIsLimitAction(false) + setIsLimitAction(false); } - }) + }); } } else { - setIsLimitAction(true) - setLimitAmount('') + setIsLimitAction(true); + setLimitAmount(""); } - }, [account, inputCurrency, inputValueFormatted, bridgeType, extendObj, initDepositAddress, inputDecimals, inputSymbol]) + }, [ + account, + inputCurrency, + inputValueFormatted, + bridgeType, + extendObj, + initDepositAddress, + inputDecimals, + inputSymbol, + ]); function formatBalance(value) { - return `Balance: ${formatDecimal(value, Math.min(config.keepDec, inputDecimals))}` + return `Balance: ${formatDecimal(value, Math.min(config.keepDec, inputDecimals))}`; } - const toggleWalletModal = useWalletModalToggle() + const toggleWalletModal = useWalletModalToggle(); const newInputDetected = - inputCurrency !== config.symbol && inputCurrency && !INITIAL_TOKENS_CONTEXT[chainId].hasOwnProperty(inputCurrency) + inputCurrency !== config.symbol && + inputCurrency && + !INITIAL_TOKENS_CONTEXT[chainId].hasOwnProperty(inputCurrency); - const [showInputWarning, setShowInputWarning] = useState(false) + const [showInputWarning, setShowInputWarning] = useState(false); useEffect(() => { if (newInputDetected) { - setShowInputWarning(true) + setShowInputWarning(true); } else { - setShowInputWarning(false) + setShowInputWarning(false); } - }, [newInputDetected, setShowInputWarning]) + }, [newInputDetected, setShowInputWarning]); - const addTransaction = useTransactionAdder() - - const tokenContract = useSwapTokenContract(extendObj && extendObj.APPROVE ? extendObj.APPROVE : inputCurrency, swapBTCABI) - const tokenETHContract = useSwapTokenContract(extendObj && extendObj.APPROVE ? extendObj.APPROVE : inputCurrency, swapETHABI) - const tokenERC20Contract = useSwapTokenContract(inputCurrency, erc20) + const addTransaction = useTransactionAdder(); + const tokenContract = useSwapTokenContract( + extendObj && extendObj.APPROVE ? extendObj.APPROVE : inputCurrency, + swapBTCABI + ); + const tokenETHContract = useSwapTokenContract( + extendObj && extendObj.APPROVE ? extendObj.APPROVE : inputCurrency, + swapETHABI + ); + const tokenERC20Contract = useSwapTokenContract(inputCurrency, erc20); useEffect(() => { - if (bridgeType && bridgeType === 'redeem') { + if (bridgeType && bridgeType === "redeem") { if ( - !error - && isDisabled - && isRedeem - && !showBetaMessage - && inputValueFormatted - && recipient.address - && Number(inputBalanceFormatted) >= Number(inputValueFormatted) - && Number(inputValueFormatted) <= Number(redeemMaxNum) - && Number(inputValueFormatted) >= Number(redeemMinNum) - && isLimitAction + !error && + isDisabled && + isRedeem && + !showBetaMessage && + inputValueFormatted && + recipient.address && + Number(inputBalanceFormatted) >= Number(inputValueFormatted) && + Number(inputValueFormatted) <= Number(redeemMaxNum) && + Number(inputValueFormatted) >= Number(redeemMinNum) && + isLimitAction ) { if (isSpecialCoin(inputSymbol) && isBTCAddress(recipient.address, inputSymbol)) { if (extendObj && extendObj.APPROVE && (!approveNum || !Number(approveNum))) { - setIsRedeem(true) + setIsRedeem(true); } else { - setIsRedeem(false) + setIsRedeem(false); } - setBalanceError('') + setBalanceError(""); } else if (!isSpecialCoin(inputSymbol) && isAddress(recipient.address)) { if (extendObj && extendObj.APPROVE && (!approveNum || !Number(approveNum))) { - setIsRedeem(true) + setIsRedeem(true); } else { - setIsRedeem(false) + setIsRedeem(false); } - setBalanceError('') + setBalanceError(""); } else { - setIsRedeem(true) - if (inputValueFormatted === '' - || ( - Number(inputBalanceFormatted) >= Number(inputValueFormatted) - && Number(inputValueFormatted) <= Number(redeemMaxNum) - && Number(inputValueFormatted) >= Number(redeemMinNum) - ) + setIsRedeem(true); + if ( + inputValueFormatted === "" || + (Number(inputBalanceFormatted) >= Number(inputValueFormatted) && + Number(inputValueFormatted) <= Number(redeemMaxNum) && + Number(inputValueFormatted) >= Number(redeemMinNum)) ) { - setBalanceError('') + setBalanceError(""); } else { - setBalanceError('Error') + setBalanceError("Error"); } } } else { - setIsRedeem(true) - if (inputValueFormatted === '' - || ( - Number(inputBalanceFormatted) >= Number(inputValueFormatted) - && Number(inputValueFormatted) <= Number(redeemMaxNum) - && Number(inputValueFormatted) >= Number(redeemMinNum) - ) + setIsRedeem(true); + if ( + inputValueFormatted === "" || + (Number(inputBalanceFormatted) >= Number(inputValueFormatted) && + Number(inputValueFormatted) <= Number(redeemMaxNum) && + Number(inputValueFormatted) >= Number(redeemMinNum)) ) { - setBalanceError('') + setBalanceError(""); } else { - setBalanceError('Error') + setBalanceError("Error"); } } } else { if ( - isDisabled - && isDeposit - && !showBetaMessage - && inputValueFormatted - && registerAddress - && isRegister - && Number(inputValueFormatted) <= depositMaxNum - && Number(inputValueFormatted) >= depositMinNum - && isLimitAction + isDisabled && + isDeposit && + !showBetaMessage && + inputValueFormatted && + registerAddress && + isRegister && + Number(inputValueFormatted) <= depositMaxNum && + Number(inputValueFormatted) >= depositMinNum && + isLimitAction ) { - if ( isSpecialCoin(inputSymbol)) { - setIsMintBtn(false) - setBalanceError('') + if (isSpecialCoin(inputSymbol)) { + setIsMintBtn(false); + setBalanceError(""); } else if ( - !isSpecialCoin(inputSymbol) - && Number(inputValueFormatted) <= Number(outNetBalance) - && Number(outNetETHBalance) >= 0.01 + !isSpecialCoin(inputSymbol) && + Number(inputValueFormatted) <= Number(outNetBalance) && + Number(outNetETHBalance) >= 0.01 ) { - setIsMintBtn(false) - setBalanceError('') + setIsMintBtn(false); + setBalanceError(""); } else { - setIsMintBtn(true) - if (inputValueFormatted === '' || ( Number(inputValueFormatted) <= depositMaxNum && Number(inputValueFormatted) >= depositMinNum ) ) { - setBalanceError('') + setIsMintBtn(true); + if ( + inputValueFormatted === "" || + (Number(inputValueFormatted) <= depositMaxNum && Number(inputValueFormatted) >= depositMinNum) + ) { + setBalanceError(""); } else { - setBalanceError('Error') + setBalanceError("Error"); } } } else { - setIsMintBtn(true) - if (inputValueFormatted === '' || ( Number(inputValueFormatted) <= depositMaxNum && Number(inputValueFormatted) >= depositMinNum ) ) { - setBalanceError('') + setIsMintBtn(true); + if ( + inputValueFormatted === "" || + (Number(inputValueFormatted) <= depositMaxNum && Number(inputValueFormatted) >= depositMinNum) + ) { + setBalanceError(""); } else { - setBalanceError('Error') + setBalanceError("Error"); } } } - }, [account, isDisabled, isRedeem, showBetaMessage, recipient.address, independentValue, inputSymbol, isDeposit, registerAddress, outNetBalance, bridgeType, depositMaxNum, depositMinNum, isLimitAction, approveNum, error, extendObj, inputBalanceFormatted, inputValueFormatted, isRegister, outNetETHBalance, redeemMaxNum, redeemMinNum]) - + }, [ + account, + isDisabled, + isRedeem, + showBetaMessage, + recipient.address, + independentValue, + inputSymbol, + isDeposit, + registerAddress, + outNetBalance, + bridgeType, + depositMaxNum, + depositMinNum, + isLimitAction, + approveNum, + error, + extendObj, + inputBalanceFormatted, + inputValueFormatted, + isRegister, + outNetETHBalance, + redeemMaxNum, + redeemMinNum, + ]); - function cleanInput () { + function cleanInput() { dispatchSwapState({ - type: 'UPDATE_INDEPENDENT', + type: "UPDATE_INDEPENDENT", payload: { - value: '', + value: "", field: INPUT, - realyValue: '' - } - }) + realyValue: "", + }, + }); } - - - function sendTxnsEnd (data, value, address, node) { - addTransaction(data) - recordTxns(data, 'WITHDRAW', inputSymbol, account, address, node) + function sendTxnsEnd(data, value, address, node) { + addTransaction(data); + recordTxns(data, "WITHDRAW", inputSymbol, account, address, node); dispatchSwapState({ - type: 'UPDATE_WITHDRAW_STATUS', + type: "UPDATE_WITHDRAW_STATUS", payload: { type: 0, withdrawData: { @@ -1270,93 +1331,99 @@ const CrossBridge = props => { to: address, status: 0, timestamp: Date.now(), - swapHash: '', - swapStatus: 'pending', - swapTime: '', + swapHash: "", + swapStatus: "pending", + swapTime: "", node: node, - bridgeVersion: extendObj.VERSION ? extendObj.VERSION : 'V1', + bridgeVersion: extendObj.VERSION ? extendObj.VERSION : "V1", chainID: config.chainID, bindAddr: recipient.address, - pairid: pairid - } - } - }) - cleanInput() + pairid: pairid, + }, + }, + }); + cleanInput(); } - function sendTxns (node) { + function sendTxns(node) { if (isSpecialCoin(inputSymbol) && !isBTCAddress(recipient.address, inputSymbol)) { - alert('Illegal address!') - return + alert("Illegal address!"); + return; } - if (!isDisabled) return - setIsDisableed(false) + if (!isDisabled) return; + setIsDisableed(false); setTimeout(() => { - setIsDisableed(true) - }, 3000) + setIsDisableed(true); + }, 3000); - let amountVal = ethers.utils.parseUnits(inputValueFormatted.toString(), inputDecimals) + let amountVal = ethers.utils.parseUnits(inputValueFormatted.toString(), inputDecimals); if (amountVal.gt(inputBalance)) { - amountVal = inputBalance + amountVal = inputBalance; } - let address = recipient.address - let token = extendObj && extendObj.APPROVE ? extendObj.APPROVE : inputCurrency - console.log(token) + let address = recipient.address; + let token = extendObj && extendObj.APPROVE ? extendObj.APPROVE : inputCurrency; + console.log(token); if (config.supportWallet.includes(walletType)) { - setIsHardwareError(false) - setIsHardwareTip(true) - setHardwareTxnsInfo(amountFormatter(amountVal, inputDecimals, inputDecimals) + " " + inputSymbol) + setIsHardwareError(false); + setIsHardwareTip(true); + setHardwareTxnsInfo(amountFormatter(amountVal, inputDecimals, inputDecimals) + " " + inputSymbol); - let web3Contract = getWeb3ConTract(swapETHABI, token) + let web3Contract = getWeb3ConTract(swapETHABI, token); if (isSpecialCoin(inputSymbol)) { - web3Contract = getWeb3ConTract(swapBTCABI, token) + web3Contract = getWeb3ConTract(swapBTCABI, token); } - let data = web3Contract.methods.Swapout(amountVal, address).encodeABI() - getWeb3BaseInfo(token, data, account).then(res => { - if (res.msg === 'Success') { + let data = web3Contract.methods.Swapout(amountVal, address).encodeABI(); + getWeb3BaseInfo(token, data, account).then((res) => { + if (res.msg === "Success") { // console.log(res.info) - sendTxnsEnd(res.info, amountVal, address, node) + sendTxnsEnd(res.info, amountVal, address, node); } else { - alert(res.error) + alert(res.error); } - setIsHardwareTip(false) - }) - return + setIsHardwareTip(false); + }); + return; } if (isSpecialCoin(inputSymbol) === 0) { - tokenETHContract.Swapout(amountVal, address).then(res => { - sendTxnsEnd(res, amountVal, address, node) - setIsHardwareTip(false) - }).catch(err => { - console.log(err) - setIsHardwareTip(false) - }) + tokenETHContract + .Swapout(amountVal, address) + .then((res) => { + sendTxnsEnd(res, amountVal, address, node); + setIsHardwareTip(false); + }) + .catch((err) => { + console.log(err); + setIsHardwareTip(false); + }); } else { - tokenContract.Swapout(amountVal, address).then(res => { - sendTxnsEnd(res, amountVal, address, node) - setIsHardwareTip(false) - }).catch(err => { - console.log(err) - setIsHardwareTip(false) - }) + tokenContract + .Swapout(amountVal, address) + .then((res) => { + sendTxnsEnd(res, amountVal, address, node); + setIsHardwareTip(false); + }) + .catch((err) => { + console.log(err); + setIsHardwareTip(false); + }); } } - function MintModelView () { - if (!registerAddress) return + function MintModelView() { + if (!registerAddress) return; dispatchSwapState({ - type: 'UPDATE_MINTTYPE', - payload: isViewMintModel ? false : true - }) + type: "UPDATE_MINTTYPE", + payload: isViewMintModel ? false : true, + }); } - function MintInfoModelView () { + function MintInfoModelView() { dispatchSwapState({ - type: 'UPDATE_MINTINFOTYPE', - payload: isViewMintInfo ? false : true - }) + type: "UPDATE_MINTINFOTYPE", + payload: isViewMintInfo ? false : true, + }); } - function changeMorR (type) { + function changeMorR(type) { // let bt = '' // if () // if (bridgeType && bridgeType === 'redeem') { @@ -1364,12 +1431,11 @@ const CrossBridge = props => { // } else { // bt = 'redeem' // } - dispatchSwapState({ type: 'UPDATE_BREDGETYPE', payload: type }) - cleanInput() + dispatchSwapState({ type: "UPDATE_BREDGETYPE", payload: type }); + cleanInput(); } - - function insertMintHistory (pairid, coin, value, hash, from, to, node, status, swapHash, swapStatus, swapTime) { + function insertMintHistory(pairid, coin, value, hash, from, to, node, status, swapHash, swapStatus, swapTime) { let data = { account: account, coin: coin, @@ -1379,381 +1445,481 @@ const CrossBridge = props => { to: to, status: status ? status : 0, timestamp: Date.now(), - swapHash: swapHash ? swapHash : '', - swapStatus: swapStatus ? swapStatus : '', - swapTime: swapTime ? swapTime : '', + swapHash: swapHash ? swapHash : "", + swapStatus: swapStatus ? swapStatus : "", + swapTime: swapTime ? swapTime : "", node: node, - bridgeVersion: extendObj.VERSION ? extendObj.VERSION : 'V1', + bridgeVersion: extendObj.VERSION ? extendObj.VERSION : "V1", chainID: config.chainID, - pairid: pairid - } - console.log(data) + pairid: pairid, + }; + console.log(data); dispatchSwapState({ - type: 'UPDATE_HASH_STATUS', + type: "UPDATE_HASH_STATUS", payload: { type: 0, - hashData: data - } - }) + hashData: data, + }, + }); } - - - function mintAmount (mintAddress, mintCoin) { - let coin = formatCoin(mintCoin) + function mintAmount(mintAddress, mintCoin) { + let coin = formatCoin(mintCoin); if (initDepositAddress.toLowerCase() !== mintAddress.toLowerCase()) { - alert('Data error, please refresh and try again!') - setIsHardwareTip(false) - setMintSureBtn(false) - setMintModelTitle('') - setMintModelTip('') - return + alert("Data error, please refresh and try again!"); + setIsHardwareTip(false); + setMintSureBtn(false); + setMintModelTitle(""); + setMintModelTip(""); + return; } // let token = extendObj && extendObj.APPROVE ? extendObj.APPROVE : inputCurrency - let token = inputCurrency + let token = inputCurrency; - if (walletType === 'Ledger') { - setHardwareTxnsInfo(inputValueFormatted + ' ' + coin) - setIsHardwareTip(true) - setMintSureBtn(false) + if (walletType === "Ledger") { + setHardwareTxnsInfo(inputValueFormatted + " " + coin); + setIsHardwareTip(true); + setMintSureBtn(false); // MintModelView() - HDsendERC20Txns(coin, account, mintAddress, inputValueFormatted, PlusGasPricePercentage, bridgeNode, token).then(res => { + HDsendERC20Txns( + coin, + account, + mintAddress, + inputValueFormatted, + PlusGasPricePercentage, + bridgeNode, + token + ).then((res) => { // console.log(res) - if (res.msg === 'Success') { - recordTxns(res.info, 'DEPOSIT', inputSymbol, account, mintAddress, bridgeNode) - insertMintHistory(pairid, coin, inputValueFormatted, res.info.hash, account, mintAddress, bridgeNode) - cleanInput() - setIsHardwareTip(false) - setMintModelTitle('') - setMintModelTip('') - setMintSureBtn(false) + if (res.msg === "Success") { + recordTxns(res.info, "DEPOSIT", inputSymbol, account, mintAddress, bridgeNode); + insertMintHistory( + pairid, + coin, + inputValueFormatted, + res.info.hash, + account, + mintAddress, + bridgeNode + ); + cleanInput(); + setIsHardwareTip(false); + setMintModelTitle(""); + setMintModelTip(""); + setMintSureBtn(false); } else { - setIsHardwareError(true) - alert(res.error.toString()) + setIsHardwareError(true); + alert(res.error.toString()); } - }) + }); } else { - setMintSureBtn(false) - MMsendERC20Txns(coin, account, mintAddress, inputValueFormatted, PlusGasPricePercentage, bridgeNode, token).then(res => { + setMintSureBtn(false); + MMsendERC20Txns( + coin, + account, + mintAddress, + inputValueFormatted, + PlusGasPricePercentage, + bridgeNode, + token + ).then((res) => { // console.log(res) - if (res.msg === 'Success') { - console.log(bridgeNode) - recordTxns(res.info, 'DEPOSIT', inputSymbol, account, mintAddress, bridgeNode) - insertMintHistory(pairid, coin, inputValueFormatted, res.info.hash, account, mintAddress, bridgeNode) - cleanInput() + if (res.msg === "Success") { + console.log(bridgeNode); + recordTxns(res.info, "DEPOSIT", inputSymbol, account, mintAddress, bridgeNode); + insertMintHistory( + pairid, + coin, + inputValueFormatted, + res.info.hash, + account, + mintAddress, + bridgeNode + ); + cleanInput(); } else { - console.log(res.error) - alert(res.error.toString()) + console.log(res.error); + alert(res.error.toString()); } - setIsHardwareTip(false) - setMintSureBtn(false) - setMintModelTitle('') - setMintModelTip('') - }) + setIsHardwareTip(false); + setMintSureBtn(false); + setMintModelTitle(""); + setMintModelTip(""); + }); } } - const [removeHashStatus, setRemoveHashStatus] = useState() - function removeHash (index) { - let arr = [] - if (bridgeType === 'redeem') { + const [removeHashStatus, setRemoveHashStatus] = useState(); + function removeHash(index) { + let arr = []; + if (bridgeType === "redeem") { for (let i = 0, len = withdrawArr.length; i < len; i++) { - if (index === i) continue - arr.push(withdrawArr[i]) + if (index === i) continue; + arr.push(withdrawArr[i]); } dispatchSwapState({ - type: 'UPDATE_WITHDRAW_STATUS', + type: "UPDATE_WITHDRAW_STATUS", payload: { type: 1, withdrawData: arr, - } - }) + }, + }); } else { for (let i = 0, len = hashArr.length; i < len; i++) { - if (index === i) continue - arr.push(hashArr[i]) + if (index === i) continue; + arr.push(hashArr[i]); } dispatchSwapState({ - type: 'UPDATE_HASH_STATUS', + type: "UPDATE_HASH_STATUS", payload: { type: 1, hashData: arr, - } - }) + }, + }); } - setRemoveHashStatus(Date.now()) + setRemoveHashStatus(Date.now()); } - - const updateHashStatusData = useCallback((res) => { - if (hashArr[res.index] && res.hash === hashArr[res.index].hash) { - hashArr[res.index].status = res.status - hashArr[res.index].swapHash = res.swapHash ? res.swapHash : '' - hashArr[res.index].swapStatus = res.swapStatus ? res.swapStatus : '' - hashArr[res.index].swapTime = res.swapTime ? res.swapTime : '' - dispatchSwapState({ - type: 'UPDATE_HASH_STATUS', - payload: { - type: 1, - hashData: hashArr, - NewHashCount: 1 - } - }) - } - }, [hashArr]) + const updateHashStatusData = useCallback( + (res) => { + if (hashArr[res.index] && res.hash === hashArr[res.index].hash) { + hashArr[res.index].status = res.status; + hashArr[res.index].swapHash = res.swapHash ? res.swapHash : ""; + hashArr[res.index].swapStatus = res.swapStatus ? res.swapStatus : ""; + hashArr[res.index].swapTime = res.swapTime ? res.swapTime : ""; + dispatchSwapState({ + type: "UPDATE_HASH_STATUS", + payload: { + type: 1, + hashData: hashArr, + NewHashCount: 1, + }, + }); + } + }, + [hashArr] + ); const updateHashStatus = useCallback(() => { if (hashArr.length > 0) { - for (let i = 0, len = hashArr.length; i < len; i ++) { - if (hashArr[i].chainID && hashArr[i].chainID !== config.chainID) continue + for (let i = 0, len = hashArr.length; i < len; i++) { + if (hashArr[i].chainID && hashArr[i].chainID !== config.chainID) continue; if ( - !hashArr[i].status - || !hashArr[i].swapStatus - || hashArr[i].swapStatus === 'pending' - || hashArr[i].swapStatus === 'confirming' - || hashArr[i].swapStatus === 'minting' + !hashArr[i].status || + !hashArr[i].swapStatus || + hashArr[i].swapStatus === "pending" || + hashArr[i].swapStatus === "confirming" || + hashArr[i].swapStatus === "minting" ) { if (isSpecialCoin(hashArr[i].coin)) { - GetBTChashStatus(hashArr[i].hash, i, hashArr[i].coin, hashArr[i].status, hashArr[i].account, hashArr[i].bridgeVersion, hashArr[i].pairid).then(res => { - updateHashStatusData(res) - }) + GetBTChashStatus( + hashArr[i].hash, + i, + hashArr[i].coin, + hashArr[i].status, + hashArr[i].account, + hashArr[i].bridgeVersion, + hashArr[i].pairid + ).then((res) => { + updateHashStatusData(res); + }); } else { - getHashStatus(hashArr[i].hash, i, hashArr[i].coin, hashArr[i].status, hashArr[i].node, hashArr[i].account, hashArr[i].bridgeVersion, hashArr[i].pairid).then(res => { - updateHashStatusData(res) - }) + getHashStatus( + hashArr[i].hash, + i, + hashArr[i].coin, + hashArr[i].status, + hashArr[i].node, + hashArr[i].account, + hashArr[i].bridgeVersion, + hashArr[i].pairid + ).then((res) => { + updateHashStatusData(res); + }); } } } } - }, [hashArr, updateHashStatusData]) + }, [hashArr, updateHashStatusData]); const updateWithdrawStatus = useCallback(() => { if (withdrawArr.length > 0) { - for (let i = 0, len = withdrawArr.length; i < len; i ++) { - if (withdrawArr[i].chainID && withdrawArr[i].chainID !== config.chainID) continue + for (let i = 0, len = withdrawArr.length; i < len; i++) { + if (withdrawArr[i].chainID && withdrawArr[i].chainID !== config.chainID) continue; if ( - !withdrawArr[i].status - || !withdrawArr[i].swapStatus - || withdrawArr[i].swapStatus === 'pending' - || withdrawArr[i].swapStatus === 'confirming' - || withdrawArr[i].swapStatus === 'minting' + !withdrawArr[i].status || + !withdrawArr[i].swapStatus || + withdrawArr[i].swapStatus === "pending" || + withdrawArr[i].swapStatus === "confirming" || + withdrawArr[i].swapStatus === "minting" ) { - let binAddr = withdrawArr[i].bindAddr - getWithdrawHashStatus(withdrawArr[i].hash, i, withdrawArr[i].coin, withdrawArr[i].status, withdrawArr[i].node, binAddr, withdrawArr[i].bridgeVersion, withdrawArr[i].pairid).then(res => { + let binAddr = withdrawArr[i].bindAddr; + getWithdrawHashStatus( + withdrawArr[i].hash, + i, + withdrawArr[i].coin, + withdrawArr[i].status, + withdrawArr[i].node, + binAddr, + withdrawArr[i].bridgeVersion, + withdrawArr[i].pairid + ).then((res) => { // console.log(res) if (withdrawArr[res.index] && res.hash === withdrawArr[res.index].hash) { - withdrawArr[res.index].status = res.status ? res.status : 0 - withdrawArr[res.index].swapHash = res.swapHash ? res.swapHash : '' - withdrawArr[res.index].swapStatus = res.swapStatus ? res.swapStatus : 'pending' - withdrawArr[res.index].swapTime = res.swapTime ? res.swapTime : '' + withdrawArr[res.index].status = res.status ? res.status : 0; + withdrawArr[res.index].swapHash = res.swapHash ? res.swapHash : ""; + withdrawArr[res.index].swapStatus = res.swapStatus ? res.swapStatus : "pending"; + withdrawArr[res.index].swapTime = res.swapTime ? res.swapTime : ""; dispatchSwapState({ - type: 'UPDATE_WITHDRAW_STATUS', + type: "UPDATE_WITHDRAW_STATUS", payload: { type: 1, withdrawData: withdrawArr, - withdrawCount: 1 - } - }) + withdrawCount: 1, + }, + }); } - }) + }); } } } - }, [withdrawArr]) - + }, [withdrawArr]); useEffect(() => { - if (!account) return - clearInterval(hashInterval) - updateHashStatus() - updateWithdrawStatus() + if (!account) return; + clearInterval(hashInterval); + updateHashStatus(); + updateWithdrawStatus(); hashInterval = setInterval(() => { - if (window.location.pathname.indexOf('bridge') !== -1) { - updateHashStatus() - updateWithdrawStatus() + if (window.location.pathname.indexOf("bridge") !== -1) { + updateHashStatus(); + updateWithdrawStatus(); } else { - clearInterval(hashInterval) + clearInterval(hashInterval); } - }, 1000 * 30) - }, [removeHashStatus, account, updateHashStatus, updateWithdrawStatus]) - - const [mintBTCErrorTip, setMintBTCErrorTip] = useState() - const [loadingState, setLoadingState] = useState(false) - - function getBTCtxns () { - setLoadingState(true) - GetBTCtxnsAll(registerAddress, account, formatCoin(inputSymbol), (extendObj.VERSION ? extendObj.VERSION : 'V1')).then(res => { + }, 1000 * 30); + }, [removeHashStatus, account, updateHashStatus, updateWithdrawStatus]); + + const [mintBTCErrorTip, setMintBTCErrorTip] = useState(); + const [loadingState, setLoadingState] = useState(false); + + function getBTCtxns() { + setLoadingState(true); + GetBTCtxnsAll( + registerAddress, + account, + formatCoin(inputSymbol), + extendObj.VERSION ? extendObj.VERSION : "V1" + ).then((res) => { // console.log(res) if (res) { for (let obj of hashArr) { if (res.hash === obj.hash) { - setMintBTCErrorTip(`No new deposit transaction found. Please send ${formatCoin(inputSymbol)} to deposit address first.`) - MintModelView() - return + setMintBTCErrorTip( + `No new deposit transaction found. Please send ${formatCoin( + inputSymbol + )} to deposit address first.` + ); + MintModelView(); + return; } } - insertMintHistory(pairid, res.coin, res.value, res.hash, account, res.to, 0, res.status, res.swapHash, res.swapStatus, res.swapTime) - cleanInput() - setMintDtil(res) - setMintDtilView(true) + insertMintHistory( + pairid, + res.coin, + res.value, + res.hash, + account, + res.to, + 0, + res.status, + res.swapHash, + res.swapStatus, + res.swapTime + ); + cleanInput(); + setMintDtil(res); + setMintDtilView(true); } else { - setMintBTCErrorTip(`No new deposit transaction found. Please send ${formatCoin(inputSymbol)} to deposit address first.`) - MintModelView() + setMintBTCErrorTip( + `No new deposit transaction found. Please send ${formatCoin(inputSymbol)} to deposit address first.` + ); + MintModelView(); } - setLoadingState(false) - }) + setLoadingState(false); + }); } - - function walletTip () { - let node = extendObj && extendObj.BRIDGE ? extendObj.BRIDGE[0] : '' + function walletTip() { + let node = extendObj && extendObj.BRIDGE ? extendObj.BRIDGE[0] : ""; if (node) { - let coin = config.bridgeAll[node.type].symbol + let coin = config.bridgeAll[node.type].symbol; return ( // eslint-disable-next-line -
    💀 {`ONLY the deposits from your ${coin} wallet ${account} will be credited!!!`}
    - ) +
    + 💀 {`ONLY the deposits from your ${coin} wallet ${account} will be credited!!!`} +
    + ); } } - - function txnsList (arr, count) { + function txnsList(arr, count) { return (
      {arr.map((item, index) => { if (item.account !== account) { - return '' + return ""; } return (
    • - - { - setMintDtil(item) - setMintDtilView(true) - }}/> + + { + setMintDtil(item); + setMintDtilView(true); + }} + />
      { - removeHash(index) + removeHash(index); }} - >x
      + > + x +
    • - ) + ); })}
    - ) + ); } - - function txnsListDtil () { - if (!mintDtil || !mintDtil.hash) return '' - let hashCurObj = {} - let hashOutObj = {} - if (bridgeType === 'redeem') { + function txnsListDtil() { + if (!mintDtil || !mintDtil.hash) return ""; + let hashCurObj = {}; + let hashOutObj = {}; + if (bridgeType === "redeem") { hashCurObj = { hash: mintDtil.hash, - url: config.bridgeAll[chainId].lookHash + mintDtil.hash - } + url: config.bridgeAll[chainId].lookHash + mintDtil.hash, + }; if (isSpecialCoin(mintDtil.coin)) { hashOutObj = { hash: mintDtil.swapHash, - url: config[formatCoin(mintDtil.coin).toLowerCase()].lookHash + mintDtil.swapHash - } + url: config[formatCoin(mintDtil.coin).toLowerCase()].lookHash + mintDtil.swapHash, + }; } else { hashOutObj = { hash: mintDtil.swapHash, - url: config.bridgeAll[mintDtil.node].lookHash + mintDtil.swapHash - } + url: config.bridgeAll[mintDtil.node].lookHash + mintDtil.swapHash, + }; } } else { hashOutObj = { hash: mintDtil.swapHash, - url: config.bridgeAll[chainId].lookHash + mintDtil.swapHash - } + url: config.bridgeAll[chainId].lookHash + mintDtil.swapHash, + }; if (isSpecialCoin(mintDtil.coin)) { hashCurObj = { hash: mintDtil.hash, - url: config[formatCoin(mintDtil.coin).toLowerCase()].lookHash + mintDtil.hash - } + url: config[formatCoin(mintDtil.coin).toLowerCase()].lookHash + mintDtil.hash, + }; } else { hashCurObj = { hash: mintDtil.hash, - url: config.bridgeAll[mintDtil.node].lookHash + mintDtil.hash - } + url: config.bridgeAll[mintDtil.node].lookHash + mintDtil.hash, + }; } } - let outNodeName = '', curNodeName = config.bridgeAll[chainId].name + let outNodeName = "", + curNodeName = config.bridgeAll[chainId].name; if (!mintDtil.node) { if (isSpecialCoin(mintDtil.coin) === 1) { - outNodeName = 'Bitcoin' + outNodeName = "Bitcoin"; } else if (isSpecialCoin(mintDtil.coin) === 2) { - outNodeName = 'Litecoin' + outNodeName = "Litecoin"; } else if (isSpecialCoin(mintDtil.coin) === 3) { - outNodeName = 'Blocknet' + outNodeName = "Blocknet"; } } else { - outNodeName = config.bridgeAll[mintDtil.node].name + outNodeName = config.bridgeAll[mintDtil.node].name; } let curHash = ( - {hashCurObj.hash} + + {hashCurObj.hash} + - ) + ); let outHash = ( - {hashOutObj.hash} + + {hashOutObj.hash} + - ) + ); let outStatus = ( - +
    - - {outNodeName + ' ' + t('txnsStatus')} + + {outNodeName + " " + t("txnsStatus")}
    - {!mintDtil.status ? ({bridgeType === 'redeem' ? 'Redeeming' : 'Pending'}) : ''} - {mintDtil.status === 1 ? (Success) : ''} - {mintDtil.status === 2 ? (Failure) : ''} + {!mintDtil.status ? ( + {bridgeType === "redeem" ? "Redeeming" : "Pending"} + ) : ( + "" + )} + {mintDtil.status === 1 ? Success : ""} + {mintDtil.status === 2 ? Failure : ""}
    - ) - const fromView = - {bridgeType === 'redeem' ? t('from') : t('to')}: - - {mintDtil.from ? mintDtil.from : account} - - - - const toView = - {bridgeType === 'redeem' ? t('to') : t('from')}: - - {mintDtil.to} - - - + ); + const fromView = ( + + {bridgeType === "redeem" ? t("from") : t("to")}: + + {mintDtil.from ? mintDtil.from : account} + + + + ); + const toView = ( + + {bridgeType === "redeem" ? t("to") : t("from")}: + + {mintDtil.to} + + + + ); return ( - {(bridgeType === 'redeem' ? curNodeName : outNodeName) + ' ' + t('hash')}: + + {(bridgeType === "redeem" ? curNodeName : outNodeName) + " " + t("hash")}: + {curHash} - { - hashOutObj.hash ? ( - - {(bridgeType === 'redeem' ? outNodeName : curNodeName) + ' ' + t('hash')}: - {outHash} - - ) : '' - } - {bridgeType === 'redeem' ? ( + {hashOutObj.hash ? ( + + + {(bridgeType === "redeem" ? outNodeName : curNodeName) + " " + t("hash")}: + + {outHash} + + ) : ( + "" + )} + {bridgeType === "redeem" ? ( <> {fromView} {toView} @@ -1765,104 +1931,108 @@ const CrossBridge = props => { )} - {t('value')}: + {t("value")}: {Number(mintDtil.value)} - + - + - + -

    {bridgeType === 'redeem' ? t('ValueWithdraw') : t('ValueDeposited')}

    - {Number(mintDtil.value)} {mintDtil.coin} +

    {bridgeType === "redeem" ? t("ValueWithdraw") : t("ValueDeposited")}

    + + {Number(mintDtil.value)} {mintDtil.coin} +
    - { - bridgeType && bridgeType === 'redeem' ? '' : outStatus - } - { - mintDtil.swapStatus ? ( - -
    - - {curNodeName + ' ' + t('txnsStatus')} -
    - {mintDtil.swapStatus} -
    - ) : '' - } - { - bridgeType && bridgeType === 'redeem' && mintDtil.swapStatus === 'success' ? outStatus : '' - } + {bridgeType && bridgeType === "redeem" ? "" : outStatus} + {mintDtil.swapStatus ? ( + +
    + + {curNodeName + " " + t("txnsStatus")} +
    + {mintDtil.swapStatus} +
    + ) : ( + "" + )} + {bridgeType && bridgeType === "redeem" && mintDtil.swapStatus === "success" ? outStatus : ""}
    - ) + ); } + function approve() { + let _userTokenBalance = ethers.constants.MaxUint256.toString(); - - function approve () { - let _userTokenBalance = ethers.constants.MaxUint256.toString() - - let token = extendObj.APPROVE - let sourceToken = inputCurrency - setApproveNumBtnView('') + let token = extendObj.APPROVE; + let sourceToken = inputCurrency; + setApproveNumBtnView(""); // let token = '0xe23edd629f264c14333b1d7cb3374259e9df5d55' // let sourceToken = '0xd5190a1C83B7cf3566098605E00fA0C0fD5F3778' if (config.supportWallet.includes(walletType)) { // setIsHardwareTip(true) // setHardwareTxnsInfo('Approve ' + inputSymbol) - setIsHardwareError(false) - setIsHardwareTip(true) - setHardwareTxnsInfo( "Unlock " + inputSymbol) - let web3Contract = getWeb3ConTract(erc20, sourceToken) - const data = web3Contract.methods.approve(token, _userTokenBalance).encodeABI() - getWeb3BaseInfo(sourceToken, data, account).then(res => { - if (res.msg === 'Success') { - console.log(res.info) - addTransaction(res.info) + setIsHardwareError(false); + setIsHardwareTip(true); + setHardwareTxnsInfo("Unlock " + inputSymbol); + let web3Contract = getWeb3ConTract(erc20, sourceToken); + const data = web3Contract.methods.approve(token, _userTokenBalance).encodeABI(); + getWeb3BaseInfo(sourceToken, data, account).then((res) => { + if (res.msg === "Success") { + console.log(res.info); + addTransaction(res.info); } else { - alert(res.error) - setApproveNumBtnView(1) + alert(res.error); + setApproveNumBtnView(1); } - setIsHardwareTip(false) - }) - return + setIsHardwareTip(false); + }); + return; } - tokenERC20Contract.approve(token, _userTokenBalance).then(res => { - console.log(res) - addTransaction(res) - setIsHardwareTip(false) - }).catch(err => { - setIsHardwareTip(false) - setApproveNumBtnView(1) - console.log(err) - }) + tokenERC20Contract + .approve(token, _userTokenBalance) + .then((res) => { + console.log(res); + addTransaction(res); + setIsHardwareTip(false); + }) + .catch((err) => { + setIsHardwareTip(false); + setApproveNumBtnView(1); + console.log(err); + }); } - - function redeemBtn (type, index) { + function redeemBtn(type, index) { return ( <> - ) + ); } - function mintBtn (type, index) { + function mintBtn(type, index) { return ( <> - ) + ); } - function viewBtn (type) { - let btn = '' - if (type === 'redeem') { + function viewBtn(type) { + let btn = ""; + if (type === "redeem") { if (extendObj && extendObj.BRIDGE) { btn = extendObj.BRIDGE.map((item, index) => { if (item.isSwitch) { - return redeemBtn(item.type, index) + return redeemBtn(item.type, index); } else { - return '' + return ""; } - }) + }); } else { - btn = redeemBtn('') + btn = redeemBtn(""); } } else { if (loadingState) { btn = ( - - ) + ); } else { if (extendObj && extendObj.BRIDGE) { btn = extendObj.BRIDGE.map((item, index) => { if (item.isSwitch) { // return mintBtn(item.type, index) - return mintBtn(item.type, index) + return mintBtn(item.type, index); } else { - return '' + return ""; } - }) + }); } else { - btn = mintBtn('') + btn = mintBtn(""); } } } - return btn + return btn; } - - function noBalanceTip () { - let node = extendObj && extendObj.BRIDGE && extendObj.BRIDGE[0] && extendObj.BRIDGE[0].type ? extendObj.BRIDGE[0].type : '' + function noBalanceTip() { + let node = + extendObj && extendObj.BRIDGE && extendObj.BRIDGE[0] && extendObj.BRIDGE[0].type + ? extendObj.BRIDGE[0].type + : ""; if ( - (bridgeType && bridgeType === 'redeem') - || !account - || !registerAddress - || isSpecialCoin(inputSymbol) - || (Number(outNetETHBalance) >= 0.02 && Number(outNetBalance) > Number(depositMinNum)) - || !node + (bridgeType && bridgeType === "redeem") || + !account || + !registerAddress || + isSpecialCoin(inputSymbol) || + (Number(outNetETHBalance) >= 0.02 && Number(outNetBalance) > Number(depositMinNum)) || + !node ) { - return '' + return ""; } else { - let coin = formatCoin(inputSymbol) + let coin = formatCoin(inputSymbol); if (node === 1 || node === 4) { - if (coin !== 'ETH') { - coin = coin + '-ERC20' + if (coin !== "ETH") { + coin = coin + "-ERC20"; } } return (
    - + - You need have {coin} and 0.02 {config.bridgeAll[node].symbol} gas fee on your same eth wallet first: {shortenAddress(account)} + You need have{" "} + + {coin} and 0.02 {config.bridgeAll[node].symbol} gas fee + {" "} + on your same eth wallet first: {shortenAddress(account)} - ) + ); } } - return ( - + - + + + + {t("menu.anySwap")} + + + {t("menu.bridges")} + + + {t("menu.crossBalance")} + + +
    - {bridgeType && bridgeType === 'redeem' ? "Redeem" : "Deposit"} + {bridgeType && bridgeType === "redeem" ? "Redeem" : "Deposit"} changeMorR('mint')} - variant={bridgeType && bridgeType !== 'redeem' ? 'primary' : 'light-primary'} + onClick={() => changeMorR("mint")} + variant={bridgeType && bridgeType !== "redeem" ? "primary" : "light-primary"} > Deposit changeMorR('redeem')} - variant={bridgeType && bridgeType === 'redeem' ? 'primary' : 'light-primary'} + onClick={() => changeMorR("redeem")} + variant={bridgeType && bridgeType === "redeem" ? "primary" : "light-primary"} > Redeem
    - - { - setIsHardwareTip(false) - setIsHardwareError(false) - }} - error={isHardwareError} - txnsInfo={hardwareTxnsInfo} - isSelfBtn={mintSureBtn} - onSure={() => { - mintAmount(registerAddress, inputSymbol) - }} - title={mintModelTitle} - tipInfo={mintModelTip} - coin={inputSymbol} - > - - - - - Deposit - - - - {inputValueFormatted ? ( - <> + + { + setIsHardwareTip(false); + setIsHardwareError(false); + }} + error={isHardwareError} + txnsInfo={hardwareTxnsInfo} + isSelfBtn={mintSureBtn} + onSure={() => { + mintAmount(registerAddress, inputSymbol); + }} + title={mintModelTitle} + tipInfo={mintModelTip} + coin={inputSymbol} + /> + + + + Deposit + + + + {inputValueFormatted && ( - Deposit {inputSymbol && formatCoin(inputSymbol)} amount: + + Deposit {inputSymbol && formatCoin(inputSymbol)} amount: + {inputValueFormatted} - - ) : ''} - - - Deposit {inputSymbol && formatCoin(inputSymbol)} address: - {registerAddress ? registerAddress : ''} - - - - - - { - mintBTCErrorTip ? ( - <> - - - {mintBTCErrorTip} - - - - ) : '' - } - - - - - - - - - {t('hash')}: - {mintHistory && mintHistory?.mintHash ? mintHistory?.mintHash : ''} - - - {t('from')}: - {mintHistory && mintHistory?.from ? mintHistory?.from : ''} - - - {t('to')}: - {registerAddress ? registerAddress : ''} - - - {t('value')}: - {mintHistory && mintHistory?.mintValue ? mintHistory?.mintValue : ''} - - - {t('fee')}: - {mintHistory && mintHistory?.mintValue && (fee || fee === 0) ? Number(mintHistory?.mintValue) * Number(fee) : 0} - - - {t('receive')}: - {mintHistory && mintHistory?.mintValue && (fee || fee === 0) ? Number(mintHistory?.mintValue) * (1 - Number(fee)) : ''} - - - {t('receive')} {config?.symbol} {t('address')}: - {account} - - - - - - - - Transaction Details - - - {mintDtil ? txnsListDtil() : ''} - - - - { (mintHistory && mintHistory?.mintTip) ? - ( - - - - - - Waiting for deposit + )} + + + + Deposit {inputSymbol && formatCoin(inputSymbol)} address: + + + {registerAddress ? registerAddress : ""} + + + + + + + + {mintBTCErrorTip && ( + + {mintBTCErrorTip} + + )} + + + + + + + + + {t("hash")}: + + {mintHistory && mintHistory?.mintHash ? mintHistory?.mintHash : ""} + + + + {t("from")}: + + {mintHistory && mintHistory?.from ? mintHistory?.from : ""} + + + + {t("to")}: + {registerAddress ? registerAddress : ""} + + + {t("value")}: + + {mintHistory && mintHistory?.mintValue ? mintHistory?.mintValue : ""} + + + + {t("fee")}: + + {mintHistory && mintHistory?.mintValue && (fee || fee === 0) + ? Number(mintHistory?.mintValue) * Number(fee) + : 0} + + + + {t("receive")}: + + {mintHistory && mintHistory?.mintValue && (fee || fee === 0) + ? Number(mintHistory?.mintValue) * (1 - Number(fee)) + : ""} + + + + + {t("receive")} {config?.symbol} {t("address")}: + + {account} + + + + + + + + Transaction Details + + {mintDtil ? txnsListDtil() : ""} + + + {mintHistory && mintHistory?.mintTip && ( + + + + + - - - - ) - : - '' - } - + + Waiting for deposit + + + + + )} + - - - { - let iValue = formatDecimal(inputValue, inputDecimals) - let inputVal = iValue && Number(iValue) ? new BigNumber(ethers.utils.parseUnits(iValue.toString(), inputDecimals).toString()) : new BigNumber(0) - let _fee = inputVal.times(ethers.utils.parseUnits(dFee.toString(), 18).toString()).dividedBy(new BigNumber(10).pow(new BigNumber(18)).toString()); - let _minFee = new BigNumber(ethers.utils.parseUnits(dMinFee.toString(), inputDecimals).toString()); - let _maxFee = new BigNumber(ethers.utils.parseUnits(dMaxFee.toString(), inputDecimals).toString()); - if (bridgeType && bridgeType === 'redeem') { - _fee = inputVal.times(ethers.utils.parseUnits(fee.toString(), 18).toString()).dividedBy(new BigNumber(10).pow(new BigNumber(18)).toString()); - _minFee = new BigNumber(ethers.utils.parseUnits(minFee.toString(), inputDecimals).toString()); - _maxFee = new BigNumber(ethers.utils.parseUnits(maxFee.toString(), inputDecimals).toString()); - } - if (_fee.isZero()) { - // inputVal = inputVal - } else { - if (_fee.lt(_minFee)) { - _fee = _minFee - } else if (_fee.gt(_maxFee)) { - _fee = _maxFee - } - inputVal = inputVal.minus(_fee) - } - if ((inputVal || inputVal === 0) && inputValue !== '') { - inputVal = amountFormatter(inputVal, inputDecimals, Math.min(10, inputDecimals)) - } else { - inputVal = '' - } - dispatchSwapState({ - type: 'UPDATE_INDEPENDENT', - payload: { - value: inputValue, - field: INPUT, - // realyValue: bridgeType && bridgeType === 'redeem' ? inputVal : inputValue - realyValue: inputVal ? Number(Number(inputVal).toFixed(8)) : '' - } - }) - }} - onMax={setInputAmount} - label={bridgeType && bridgeType !== 'redeem' ? "Deposit" : "Redeem"} - onCurrencySelect={inputCurrency => { - dispatchSwapState({ - type: 'SELECT_CURRENCY', - payload: { currency: inputCurrency, field: INPUT } - }) - }} - currency={inputCurrencySwap} - otherCurrency={outputCurrencySwap} - id={'bridge-input-currency'} - showCommonBases={false} - withoutMargin={true} - hideETH={true} - /> - - - { - if (bridgeType && bridgeType === 'redeem') { - changeMorR('mint') + { + let iValue = formatDecimal(inputValue, inputDecimals); + let inputVal = + iValue && Number(iValue) + ? new BigNumber( + ethers.utils.parseUnits(iValue.toString(), inputDecimals).toString() + ) + : new BigNumber(0); + let _fee = inputVal + .times(ethers.utils.parseUnits(dFee.toString(), 18).toString()) + .dividedBy(new BigNumber(10).pow(new BigNumber(18)).toString()); + let _minFee = new BigNumber( + ethers.utils.parseUnits(dMinFee.toString(), inputDecimals).toString() + ); + let _maxFee = new BigNumber( + ethers.utils.parseUnits(dMaxFee.toString(), inputDecimals).toString() + ); + if (bridgeType && bridgeType === "redeem") { + _fee = inputVal + .times(ethers.utils.parseUnits(fee.toString(), 18).toString()) + .dividedBy(new BigNumber(10).pow(new BigNumber(18)).toString()); + _minFee = new BigNumber( + ethers.utils.parseUnits(minFee.toString(), inputDecimals).toString() + ); + _maxFee = new BigNumber( + ethers.utils.parseUnits(maxFee.toString(), inputDecimals).toString() + ); + } + if (_fee.isZero()) { + // inputVal = inputVal + } else { + if (_fee.lt(_minFee)) { + _fee = _minFee; + } else if (_fee.gt(_maxFee)) { + _fee = _maxFee; + } + inputVal = inputVal.minus(_fee); + } + if ((inputVal || inputVal === 0) && inputValue !== "") { + inputVal = amountFormatter(inputVal, inputDecimals, Math.min(10, inputDecimals)); + } else { + inputVal = ""; + } + dispatchSwapState({ + type: "UPDATE_INDEPENDENT", + payload: { + value: inputValue, + field: INPUT, + // realyValue: bridgeType && bridgeType === 'redeem' ? inputVal : inputValue + realyValue: inputVal ? Number(Number(inputVal).toFixed(8)) : "", + }, + }); + }} + onMax={setInputAmount} + label={bridgeType && bridgeType !== "redeem" ? "Deposit" : "Redeem"} + onCurrencySelect={(inputCurrency) => { + dispatchSwapState({ + type: "SELECT_CURRENCY", + payload: { currency: inputCurrency, field: INPUT }, + }); + }} + currency={inputCurrencySwap} + otherCurrency={outputCurrencySwap} + id={"bridge-input-currency"} + showCommonBases={false} + withoutMargin={true} + hideETH={true} + /> + +
    + { + if (bridgeType && bridgeType === "redeem") { + changeMorR("mint"); } else { - changeMorR('redeem') + changeMorR("redeem"); } - }}> - - - -
    - { - dispatchSwapState({ - type: 'SELECT_CURRENCY', - payload: { currency: inputCurrency, field: INPUT } - }) - }} - isSelfSymbol={bridgeType && bridgeType === 'redeem' && inputSymbol ? formatCoin(inputSymbol) : inputSymbol} - isSelfLogo={bridgeType && bridgeType === 'redeem' && inputSymbol ? formatCoin(inputSymbol) : ''} - isSelfName={bridgeType && bridgeType === 'redeem' && inputName ? formatName(inputName, extendObj) : ''} - showUnlock={false} - disableUnlock={true} - onUserInput={setOutputAmount} - onMax={setOutputAmount} - label={"Receive"} - currency={outputCurrencySwap} - otherCurrency={inputCurrencySwap} - id={'bridge-output-currency'} - showCommonBases={false} - withoutMargin={true} - selectedTokens={[inputCurrency, outputCurrency]} - selectedTokenAddress={inputCurrency} - value={realyValue ? realyValue : ''} - hideETH={true} - selfUseAllToken={selfUseAllToken} - /> - - {bridgeType && bridgeType === 'redeem' ? ( - - setRecipient(r => { + }} + > + + + + + { + dispatchSwapState({ + type: "SELECT_CURRENCY", + payload: { currency: inputCurrency, field: INPUT }, + }); + }} + isSelfSymbol={ + bridgeType && bridgeType === "redeem" && inputSymbol + ? formatCoin(inputSymbol) + : inputSymbol + } + isSelfLogo={ + bridgeType && bridgeType === "redeem" && inputSymbol ? formatCoin(inputSymbol) : "" + } + isSelfName={ + bridgeType && bridgeType === "redeem" && inputName + ? formatName(inputName, extendObj) + : "" + } + showUnlock={false} + disableUnlock={true} + onUserInput={setOutputAmount} + onMax={setOutputAmount} + label={"Receive"} + currency={outputCurrencySwap} + otherCurrency={inputCurrencySwap} + id={"bridge-output-currency"} + showCommonBases={false} + withoutMargin={true} + selectedTokens={[inputCurrency, outputCurrency]} + selectedTokenAddress={inputCurrency} + value={realyValue ? realyValue : ""} + hideETH={true} + selfUseAllToken={selfUseAllToken} + /> + + {bridgeType && bridgeType === "redeem" ? ( +
    + + setRecipient((r) => { return { ...r, - address: val - } - })} - withoutMargin={true} - label={`Recipient ${inputSymbol ? formatCoin(inputSymbol) : inputSymbol} Address`} - /> - - ) : (isSpecialCoin(inputSymbol) && account && registerAddress) ? ( -
    - setRecipient(val)} - withoutMargin={true} - label={'Deposit ' + (inputSymbol ? formatCoin(inputSymbol) : inputSymbol) + ' Address'} - /> - - ) : null} - - { - bridgeType && bridgeType === 'redeem' && Number(FSNBalanceNum) < 0.001 && account ? ( - - - Insufficient Balance - - - ) : ('') - } - + address: val, + }; + }) + } + label={`Recipient ${inputSymbol ? formatCoin(inputSymbol) : inputSymbol} Address`} + /> + + ) : isSpecialCoin(inputSymbol) && account && registerAddress ? ( +
    + setRecipient(val)} + label={ + "Deposit " + (inputSymbol ? formatCoin(inputSymbol) : inputSymbol) + " Address" + } + /> +
    + ) : null} - { - isLimitAction ? '' : ( - - - The liquidity of the cross chain bridge is insufficient, please try again later. Balance: {thousandBit(limitAmount,2)} {inputSymbol} - - - ) - } + {bridgeType && bridgeType === "redeem" && Number(FSNBalanceNum) < 0.001 && account && ( +
    + + Insufficient Balance + +
    + )} + + {!isLimitAction && ( +
    + + + The liquidity of the cross chain bridge is insufficient, please try again later. + Balance: {thousandBit(limitAmount, 2)} {inputSymbol} + + +
    + )} - { - (isDeposit === 0 || isRedeem === 0 ? - ( - - - Deposit and Withdraw stopped, node maintenance! - - - ) - : - null + {isDeposit === 0 || isRedeem === 0 ? ( +
    + + Deposit and Withdraw stopped, node maintenance! + +
    + ) : null} + + {noBalanceTip()} + + + {isDeposit || isRedeem ? ( + account ? ( + viewBtn(bridgeType) + ) : ( + ) - } - - {noBalanceTip()} - - - - {isDeposit || isRedeem ? - account ? viewBtn(bridgeType) : ( - - ) : bridgeType && bridgeType === 'redeem' && extendObj && extendObj.APPROVE && (!approveNum || !Number(approveNum)) ? ( - - ) : isDeposit === 0 && isRedeem === 0 ? ( - - ) : ( - - )} - - - + ) : bridgeType && + bridgeType === "redeem" && + extendObj && + extendObj.APPROVE && + (!approveNum || !Number(approveNum)) ? ( + + ) : isDeposit === 0 && isRedeem === 0 ? ( + + ) : ( + + )} + + + - - ) -} + ); +}; export default CrossBridge; - diff --git a/src/pages/Dashboard/index.js b/src/pages/Dashboard/index.js index f3f6340f..0616d20d 100644 --- a/src/pages/Dashboard/index.js +++ b/src/pages/Dashboard/index.js @@ -2,57 +2,30 @@ import React, { useEffect } from "react"; import { Row, Col } from "react-bootstrap"; import { Route } from "react-router-dom"; import { connect, useDispatch, useSelector } from "react-redux"; -import SVG from "react-inlinesvg"; -import styled from "styled-components"; +import { useTranslation } from "react-i18next"; import { useActiveWeb3React } from "../../hooks"; -import ValueCard from "../../components/ValueCard"; -import OverviewCard from "../../components/OverviewCard"; -import ChartCard from "../../components/ChartCard"; -import AssetModal from "../../components/AssetModal"; -import WalletModal from "../../components/AssetModal/wallet"; -import Page from "../../components/Page"; -import Loading from "../../components/Loading"; -import AccountCard from "../../components/AccountCard"; import { emitter } from "../../lib/helper"; import { fetchBalances, fetchTransformedBalances } from "../../state/balances/actions"; import { useMemoTokenBalances } from "../../state/balances/hooks"; -import { useTranslation } from "react-i18next"; - -const LoadingCol = styled.div` - background-color: ${({ theme }) => theme.modalBG}; - border-radius: 20px; - min-height: 550px; -`; - -const Card = styled.div` - background-color: ${({ theme }) => theme.modalBG}; - color: ${({ theme }) => theme.text1}; - display: flex; - align-items: center; - border-radius: 20px; - justify-content: center; - flex-direction: column; - padding: 45px; -`; - -const RowTitle = styled.h4` - margin-top: 30px; - margin-bottom: 20px; - - @media (min-width: 768px) { - margin-top: 60px; - margin-bottom: 30px; - } -`; +import Page from "../../components/Page"; +import AccountCard from "../../components/AccountCard"; +import AssetModal from "../../components/AssetModal"; +import WalletModal from "../../components/AssetModal/WalletModal"; +import AssetTable from "../../components/AssetTable"; +// import WalletTable from "../../components/AssetTable/WalletTable"; +import WalletCard from "../../components/WalletCard"; +import ChartCard from "../../components/ChartCard"; +import Platforms from "../../components/Platforms"; +import * as Styled from "./styleds"; const Dashboard = (props) => { + const { t } = useTranslation(); const { account } = useActiveWeb3React(); const balances = useSelector((state) => state.balances.data); const { ETH } = useSelector((state) => state.currency.currenciesRate); const dispatch = useDispatch(); const walletBalances = useMemoTokenBalances(); - const { t } = useTranslation(); useEffect(() => { if (account) { @@ -64,137 +37,119 @@ const Dashboard = (props) => { dispatch(fetchTransformedBalances(balances, walletBalances, ETH)); }, [balances, walletBalances, ETH, dispatch]); - const clickOnAsset = (asset) => { + const onClickToken = (token) => { + if (token.metadata.symbol === "ETH") { + props.history.push("/coins/ethereum"); + } else { + props.history.push(`/coins/${token.metadata.address}`); + // props.history.push(`/coins/contract/${token.metadata.address}`); + } + }; + + const onSelectCard = (asset) => { emitter.emit("open-modal", { action: () => { - props.history.push(`/dashboard`); + props.history.push("/dashboard"); emitter.emit("close-modal"); }, }); if (asset === "wallet") { - props.history.push("/dashboard/wallet"); + props.history.push("/dashboard/assets"); } else { - props.history.push(`/dashboard/asset/${asset}`); + props.history.push(`/dashboard/account/${asset}`); } }; - const showPlatform = (platform) => { + const onSelectPlatform = (platform) => { props.history.push(`/platforms/${platform}`); }; return ( - - {props.loading ? ( - - - - - - - - ) : ( - <> - - - - - - - - - - - - - - - - - - - {t("accountOverview")} - - - - {props.overview && - Object.keys(props.overview).map((key) => { - const account = props.overview[key]; - return ( - - - - ); - })} - - - - - {t("platforms")} - - - - {props.transformedBalance.length > 0 ? ( - props.transformedBalance.map((b, index) => { - return ( - - - - ); - }) - ) : ( - - - -
    - {t("errors.noPlatform")} -
    - {t("errors.noPlatformDesc")} -
    - - )} - - - - - )} + + + + {/* TODO: replace with a Portfolio Balance Chart */} + + + {/* */} + + + + + + + {/* + onSelectCard(props.overview.wallet.slug)} + assets={props.overview.wallet} + > + + + */} + + onSelectCard(props.overview.deposits.slug)} + assets={props.overview.deposits} + > + + + + onSelectCard(props.overview.debts.slug)} + assets={props.overview.debts} + > + + + + + + {t("platforms")} + + + + ); }; diff --git a/src/pages/Dashboard/styleds.tsx b/src/pages/Dashboard/styleds.tsx new file mode 100644 index 00000000..1496830d --- /dev/null +++ b/src/pages/Dashboard/styleds.tsx @@ -0,0 +1,17 @@ +import styled from "styled-components"; + +export const LoadingWrap = styled.div` + background-color: ${({ theme }) => theme.modalBG}; + border-radius: 20px; + min-height: 550px; +`; + +export const RowTitle = styled.h4` + margin-top: 30px; + margin-bottom: 20px; + + @media (min-width: 768px) { + margin-top: 60px; + margin-bottom: 30px; + } +`; diff --git a/src/pages/Exchange/index.tsx b/src/pages/Exchange/index.tsx new file mode 100644 index 00000000..d108d2fe --- /dev/null +++ b/src/pages/Exchange/index.tsx @@ -0,0 +1,15 @@ +import { useTranslation } from "react-i18next"; +import Page from "../../components/Page"; +import InstantSwap from "../../components/InstantSwap"; + +const History = () => { + const { t } = useTranslation(); + + return ( + + + + ); +}; + +export default History; diff --git a/src/pages/Explore/index.js b/src/pages/Explore/index.js index d3a15f80..3e930c69 100644 --- a/src/pages/Explore/index.js +++ b/src/pages/Explore/index.js @@ -1,12 +1,8 @@ import React from "react"; -import { Row, Col } from "react-bootstrap"; -import styled from "styled-components"; import { connect } from "react-redux"; import Slider from "react-slick"; import { isMobile } from "react-device-detect"; -import "slick-carousel/slick/slick.css"; -import "slick-carousel/slick/slick-theme.css"; -import "./style.scss"; +import { Button } from "react-bootstrap"; import { fetchTokens } from "../../state/explore/actions"; import SectionList from "../../components/SectionList"; @@ -14,54 +10,8 @@ import InnerCard from "../../components/InnerCard"; import ExchangeIcon from "../../components/Icons/Exchange"; import Loading from "../../components/Loading"; import Page from "../../components/Page"; -import { Link } from "react-router-dom"; - -const TypeIcon = styled.div` - width: 24px; - min-width: 24px; - height: 24px; - display: flex; - align-items: center; - justify-content: center; - background-color: #1bc5bd20; - border-radius: 32px; - - @media (min-width: 768px) { - width: 48px; - height: 48px; - min-width: 48px; - border-radius: 48px; - } -`; - -const Container = styled.div` - display: block; - width: 100%; - overflow: hidden; -`; - -const ContainerInner = styled.div` - width: calc(100% + 18px); -`; - -const StyledLink = styled(Link)` - @media (max-width: 767px) { - background-color: transparent !important; - padding: 0; - text-decoration: underline; - white-space: nowrap; - - &:focus, - &:active { - color: ${({ theme }) => theme.primary} !important; - } - } - - @media (min-width: 768px) { - padding: 0.625rem 1.875rem; - height: 48px; - } -`; +import "./style.scss"; +import * as Styled from "./styleds"; class Explore extends React.Component { componentDidMount() { @@ -105,10 +55,10 @@ class Explore extends React.Component { title: data.title, description: data.description, headerAction: data.seeMore && ( -
    - +
    +
    ), content: data.loading ? ( @@ -120,13 +70,14 @@ class Explore extends React.Component { {[...Array(5)].map((item, index) => { let row = data.data[index]; let imageComponent = sec === "derivatives" && ( - + - + ); + let dataItem = data.schema(row, imageComponent); return ( -
    +
    ); @@ -137,16 +88,8 @@ class Explore extends React.Component { }); return ( - - -
    - - - - - - - + + ); } diff --git a/src/pages/Explore/style.scss b/src/pages/Explore/style.scss index 3e5ece33..66d067eb 100644 --- a/src/pages/Explore/style.scss +++ b/src/pages/Explore/style.scss @@ -1,4 +1,5 @@ @import "../../styles/abstracts/variables"; + .slick-track { display: flex !important; } @@ -11,46 +12,44 @@ } } -.explore { - &__dots { - list-style: none; - padding-top: 0; - padding-left: 0; - margin: 0; - display: flex !important; - align-items: center; - position: relative; - justify-content: center; - - @media (min-width: 576px) { - padding-top: 10px; - } +.explore__dots { + list-style: none; + padding-top: 0; + padding-left: 0; + margin: 0; + display: flex !important; + align-items: center; + position: relative; + justify-content: center; - & li { - button { - width: 8px !important; - height: 8px !important; - border-radius: 8px; - opacity: 0.5; - background-color: $primary; - transition: opacity 0.4s ease; - margin: 0 5px; - padding: 0; - - @media (min-width: 576px) { - width: 10px !important; - height: 10px !important; - border-radius: 10px; - } - - &::before { - display: none; - } + @media (min-width: 576px) { + padding-top: 10px; + } + + & li { + button { + width: 8px !important; + height: 8px !important; + border-radius: 8px; + opacity: 0.5; + background-color: $primary; + transition: opacity 0.4s ease; + margin: 0 5px; + padding: 0; + + @media (min-width: 576px) { + width: 10px !important; + height: 10px !important; + border-radius: 10px; } - &.slick-active button { - opacity: 1; + &::before { + display: none; } } + + &.slick-active button { + opacity: 1; + } } } diff --git a/src/pages/Explore/styleds.tsx b/src/pages/Explore/styleds.tsx new file mode 100644 index 00000000..a9b53c7f --- /dev/null +++ b/src/pages/Explore/styleds.tsx @@ -0,0 +1,19 @@ +import styled from "styled-components"; + +export const TypeIcon = styled.div` + width: 24px; + min-width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + background-color: #1bc5bd20; + border-radius: 32px; + + @media (min-width: 768px) { + width: 48px; + height: 48px; + min-width: 48px; + border-radius: 48px; + } +`; diff --git a/src/pages/ExploreTypeList/index.js b/src/pages/ExploreTypeList/index.js deleted file mode 100644 index ac9e6e83..00000000 --- a/src/pages/ExploreTypeList/index.js +++ /dev/null @@ -1,239 +0,0 @@ -import React, { useEffect, useState } from "react"; -import { Row, Col } from "react-bootstrap"; -import BootstrapTable from "react-bootstrap-table-next"; -import styled from "styled-components"; -import { useSelector, useDispatch } from "react-redux"; -import { ResponsiveCard } from "../../components/Card"; -import CurrencyLogo from "../../components/CurrencyLogo"; -import CurrencyText from "../../components/CurrencyText"; -import { fetchTokens } from "../../state/explore/actions"; -import Loading from "../../components/Loading"; -import Page from "../../components/Page"; -import "./style.scss"; -import ResponsiveTable from "../../components/ResponsiveTable"; -import MarketTokens from "./MarketTokens"; - -const CardTitle = styled.h2` - font-size: 1rem; - font-weight: 500; - margin-top: 0; - margin-bottom: 20px; - - @media (min-width: 768px) { - font-size: 1.25rem; - } -`; - -const Logo = styled.img` - width: 32px; - height: 32px; - border-radius: 32px; - background-color: ${({ theme }) => theme.text1}; - border: 2px solid ${({ theme }) => theme.text1}; - - @media (max-width: 991px) { - width: 24px; - height: 24px; - border-radius: 24px; - } -`; -const LogoContainer = styled.div` - width: 32px; - height: 32px; - border-radius: 32px; - - @media (max-width: 991px) { - width: 24px; - height: 24px; - border-radius: 24px; - } -`; - -const CellText = styled.span` - font-weight: 500; - font-size: 0.875rem; - - &.font-size-base { - font-size: 1rem; - } - - @media (max-width: 991px) { - font-weight: 700; - - &.label { - font-weight: 500; - } - } -`; - -// const SymbolText = styled.span` -// font-weight: 500; -// font-size: 1rem; - -// @media (max-width: 991px) { -// font-size: 0.875rem; -// font-weight: 400; -// } -// `; - -const CustomTitle = styled.h4` - color: ${({ theme }) => theme.text1} - font-size: 1.25rem; - - @media (max-width: 991px) { - font-size: 0.875rem; - } -`; - -const TokenSetCustomTitle = styled(CustomTitle)` - font-size: 1rem; -`; - -const tokenSetsColumns = [ - { - dataField: "id", - text: "ID", - formatter: (cellContent, row, rowIndex) => {rowIndex + 1}, - }, - - { - dataField: "name", - text: "NAME", - formatter: (cellContent, row, rowIndex) => ( -
    - {row.image ? ( - - ) : ( - - - - )} -
    - {row.name} -
    -
    - ), - style: { - maxWidth: 250, - }, - notCentered: true, - }, - { - dataField: "price_usd", - text: "CURRENT PRICE", - formatter: (cellContent, row) => ( - - {row.price_usd} - - ), - }, - { - dataField: "components", - text: "ASSETS", - formatter: (cellContent, row) => ( -
    - {row.components.map((c) => { - return {c.symbol}; - })} -
    - ), - }, - { - dataField: "natural_units", - text: "NATURAL UNITS", - formatter: (cellContent, row) => {row.natural_unit}, - }, - { - dataField: "unit_shares", - text: "UNIT SHARES", - formatter: (cellContent, row) => {row.unit_shares}, - }, - { - dataField: "market_cap", - text: "MARKET CAP", - formatter: (cellContent, row) => ( - - {row.market_cap} - - ), - }, -]; -const ExploreTypeList = (props) => { - const [columns, setColumns] = useState([]); - const [data, setData] = useState({ data: [], loading: true, title: "" }); - const dispatch = useDispatch(); - const exploreSets = useSelector((state) => state.explore); - - useEffect(() => { - const type = props.match.params.type; - if (type && ["tokens", "tokenSets"].includes(type)) { - if (exploreSets[type].data.length === 0) { - dispatch(fetchTokens()); - } - - setData(exploreSets[type]); - - if (type === "tokenSets") { - setColumns(tokenSetsColumns); - } - } else { - props.history.push("/tools/explore"); - } - }, [props.match.params.type, exploreSets, dispatch, props.history]); - - return ( - - - - - {props.match.params.type === "tokens" ? ( - - ) : ( - <> - {data.title} - {data.loading ? ( -
    - -
    - ) : ( - <> - - - - - )} - - )} -
    - - - - ); -}; - -export default ExploreTypeList; diff --git a/src/pages/ExploreTypeList/style.scss b/src/pages/ExploreTypeList/style.scss deleted file mode 100644 index 094946b0..00000000 --- a/src/pages/ExploreTypeList/style.scss +++ /dev/null @@ -1,69 +0,0 @@ -.explore { - &__table { - border-collapse: separate; - border-spacing: 0 0; - margin-bottom: 0 !important; - - thead th { - background-color: rgba(#202020, 0.1); - color: #202020; - font-size: 0.875rem; - font-weight: 500; - text-overflow: ellipsis; - white-space: nowrap; - padding: 1.25rem 0.75rem; - min-height: 56px; - - .dark-mode & { - background-color: rgba(white, 0.1); - color: white; - } - - &:focus { - outline: none; - } - - &:first-child { - border-top-left-radius: 18px; - border-bottom-left-radius: 18px; - } - - &:last-child { - border-top-right-radius: 18px; - border-bottom-right-radius: 18px; - } - } - - th, - td { - vertical-align: middle !important; - - &:first-child { - padding: 1.25rem 1.375rem; - } - - &:last-child { - padding: 1.25rem 0.5rem; - } - } - - td { - cursor: pointer; - color: #202020; - - .dark-mode & { - color: white; - } - } - - tr:not(:last-child) { - td { - border-bottom: 1px solid rgba(#202020, 0.5) !important; - - .dark-mode & { - border-color: rgba(white, 0.5) !important; - } - } - } - } -} diff --git a/src/pages/FiatOff/index.js b/src/pages/FiatOff/index.js index fa0e098e..263805c4 100644 --- a/src/pages/FiatOff/index.js +++ b/src/pages/FiatOff/index.js @@ -1,145 +1,11 @@ -import { Row, Col, Button as BS } from "react-bootstrap"; -import styled from "styled-components"; -import SVG from "react-inlinesvg"; - import Page from "../../components/Page"; -import Card from "../../components/Card"; -import Img from "../../components/UI/Img"; -import fiatOffList from "../../constants/fiatOffList"; - -const StyledCard = styled(Card)` - margin-top: 36px; - - & > .card-body { - padding: 40px 20px; - } -`; - -const InnerCard = styled.div` - display: flex; - flex-direction: column; - border-radius: 12px; - background-color: ${({ theme }) => theme.bg1}; - padding: 10px; - align-items: stretch; - border: 1px solid ${({ theme }) => theme.borderColor}; - margin-bottom: 20px; -`; - -const ItemImageContainer = styled.div` - position: relative; - width: 100%; - padding-top: 100%; - border-radius: 12px; - overflow: hidden; - background-color: ${({ theme }) => theme.modalBG}; -`; - -const ItemImage = styled(Img)` - width: 100%; - height: 100%; - position: absolute; - top: 0; - left: 0; - right: 0; - object-fit: cover; -`; - -const ItemContent = styled.div` - display: flex; - align-items: stretch; - flex-direction: column; -`; - -const ItemList = styled.div` - flex: 1; - padding: 20px 5px 24px; - display: grid; - grid-column-gap: 8px; - grid-row-gap: 8px; - grid-template-columns: 35px 1fr; - align-items: center; -`; - -const Button = styled(BS)` - border-radius: 10px; - height: 48px; - min-height: 48px; -`; - -const ListIcon = styled.div` - display: flex; - align-items: center; - justify-content: center; - width: 35px; - height: 32px; - color: ${({theme}) => theme.primary}; -`; - -const ListTitle = styled.span` - font-weight: 500; - color: ${({ theme }) => theme.text1}; - font-size: 0.875rem; - line-height: 1.125rem; -`; - -const Title = styled.h1` - margin-top: 0; - margin-bottom: 40px; - font-size: 1.25rem; - font-weight: 700; - color: ${({ theme }) => theme.text1}; - line-height: 1.5rem; -`; +import fiatOffList from "../../data/FiatOffList"; +import OfframpList from "../../components/OfframpList"; const FiatOff = (props) => { return ( - - - - - - - How to spend your crypto - - {fiatOffList?.map((item, index) => { - return ( - - - - - - - - {item?.items?.map((listItem) => { - return ( - <> - - - - {listItem?.title} - - ); - })} - - - - - - ); - })} - - - - + + ); }; diff --git a/src/pages/FiatOn/index.js b/src/pages/FiatOn/index.js deleted file mode 100644 index 6fd9a3aa..00000000 --- a/src/pages/FiatOn/index.js +++ /dev/null @@ -1,506 +0,0 @@ -import { Component } from "react"; -import { Row, Col, Button } from "react-bootstrap"; -import TransakSDK from "@transak/transak-sdk"; - -import Page from "../../components/Page"; -import Card from "../../components/Card"; -import Loading from "../../components/Loading"; -import CryptoInput from "../../components/CryptoInput"; -import styled from "styled-components"; -import TransakApi from "../../http/transak"; -import { TYPING_INTERVAL } from "../../constants"; -import withWeb3Account from "../../components/hoc/withWeb3Account"; -import { toast } from "react-hot-toast"; -import { withTranslation } from "react-i18next"; - -const Title = styled.h3` - margin-top: 0; - margin-bottom: 30px; - font-weight: 700; - font-size: 0.875rem; - color: ${({ theme }) => theme.text1}; - - @media (min-width: 768px) { - font-size: 1.25rem; - - .card-body { - padding: 0; - } - } -`; - -const CustomCard = styled(Card)` - padding: 20px; - margin-top: 86px; - - @media (max-width: 767px) { - padding: 0; - border: none; - margin-top: 40px; - - .card-body { - padding: 0; - } - } -`; - -const PriceText = styled.span` - display: flex; - padding-top: 6px; - color: ${({ theme }) => theme.text1}; - font-size: 0.875rem; - font-weight: 400; - - @media (max-width: 767px) { - padding-bottom: 9px; - } -`; - -const PriceCol = styled(Col)` - margin-bottom: 36px; - - @media (min-width: 768px) { - margin-bottom: 66px; - } -`; - -const BuyButton = styled(Button)` - width: 100%; - - @media (min-width: 768px) { - width: auto; - min-width: 250px; - } -`; - -class FiatOn extends Component { - constructor(props) { - super(props); - - this.api = new TransakApi(process.env.REACT_APP_TRANSAK_ENVIRONMENT); - this.typeTimeout = undefined; - - this.state = { - loading: true, - priceLoading: false, - cryptoCurrencies: [], - fiatCurrencies: [], - selected: { - crypto: { - currency: null, - value: "", - }, - fiat: { - currency: null, - value: "", - }, - }, - conversionRate: 0, - }; - } - - async componentDidMount() { - try { - const cryptoRes = await this.api.get("crypto"); - const fiatRes = await this.api.get("fiat"); - this.setState((prevState) => { - return { - cryptoCurrencies: cryptoRes.data.response, - fiatCurrencies: fiatRes.data.response, - selected: { - ...prevState.selected, - crypto: { - ...prevState.selected.crypto, - currency: - cryptoRes.data.response.find( - (item) => item.symbol === process.env.REACT_APP_TRANSAK_CRYPTO_SYMBOL - ) || null, - }, - fiat: { - ...prevState.selected.fiat, - currency: - fiatRes.data.response.find( - (item) => item.symbol === process.env.REACT_APP_TRANSAK_FIAT_SYMBOL - ) || null, - }, - }, - loading: false, - }; - }); - } catch (e) { - toast.error("Something went wrong. please try again later."); - this.setState({ - loading: false, - }); - } - } - - fetchPrices = async (value, type) => { - try { - this.setState({ - priceLoading: true, - }); - const prices = await this.api.get("price", { - type: "BUY", - fiat: this.state.selected.fiat.currency.symbol, - crypto: this.state.selected.crypto.currency.symbol, - amount: Number(value) || 300, - amountType: type, - network: type === "crypto" ? value.network.name : this.state.selected.crypto.currency.network.name, - }); - - let fiatValue = 0; - let cryptoValue = 0; - if (type === "fiat") { - fiatValue = value; - cryptoValue = prices.data.response.cryptoAmount; - } else { - cryptoValue = value; - fiatValue = prices.data.response.fiatAmount; - } - this.setState((prevState) => { - return { - selected: { - ...prevState.selected, - fiat: { - ...prevState.selected.fiat, - value: fiatValue, - }, - crypto: { - ...prevState.selected.crypto, - value: cryptoValue, - }, - }, - priceLoading: false, - }; - }); - } catch (e) { - if (e.hasOwnProperty("response")) { - toast.error(e?.response?.data?.error?.message); - } - this.setState((prevState) => { - return { - selected: { - ...prevState.selected, - [type]: { - ...prevState.selected[type], - value, - }, - }, - priceLoading: false, - }; - }); - } - }; - - onUserInputHandler = async (value, type) => { - if (this.state.selected.fiat.currency && this.state.selected.crypto.currency) { - clearTimeout(this.typeTimeout); - this.typeTimeout = setTimeout(() => { - this.fetchPrices(value, type); - }, TYPING_INTERVAL); - - this.setState((prevState) => { - return { - selected: { - ...prevState.selected, - [type]: { - ...prevState.selected[type], - value, - }, - }, - }; - }); - } else { - this.setState((prevState) => { - return { - selected: { - ...prevState.selected, - [type]: { - ...prevState.selected[type], - value, - }, - }, - priceLoading: false, - }; - }); - } - }; - - onSelect = async (value, type) => { - this.setState({ - priceLoading: true, - }); - - let cryptoValue = ""; - if ( - (this.state.selected.crypto.currency !== null || type === "crypto") && - (this.state.selected.fiat.currency !== null || type === "fiat") - ) { - try { - const prices = await this.api.get("price", { - type: "BUY", - fiat: type === "fiat" ? value.symbol : this.state.selected.fiat.currency.symbol, - crypto: type === "crypto" ? value.symbol : this.state.selected.crypto.currency.symbol, - amount: this.state.selected.fiat.value || 300, - amountType: "fiat", - network: type === "crypto" ? value.network.name : this.state.selected.crypto.currency.network.name, - }); - - if (this.state.selected.fiat.value) { - cryptoValue = prices.data.response.cryptoAmount; - } - - if (type === "crypto") { - this.setState((prevState) => { - return { - selected: { - ...prevState.selected, - [type]: { - ...prevState.selected[type], - currency: value, - value: cryptoValue, - }, - }, - priceLoading: false, - conversionRate: prices.data.response.conversionPrice, - }; - }); - } else { - this.setState((prevState) => { - return { - selected: { - ...prevState.selected, - [type]: { - ...prevState.selected[type], - currency: value, - }, - crypto: { - ...prevState.selected.crypto, - value: cryptoValue, - }, - }, - priceLoading: false, - conversionRate: prices.data.response.conversionPrice, - }; - }); - } - } catch (e) { - if (e.hasOwnProperty("response")) { - toast.error(e?.response?.data?.error?.message); - } - this.setState((prevState) => { - return { - selected: { - ...prevState.selected, - [type]: { - ...prevState.selected[type], - currency: value, - }, - }, - priceLoading: false, - conversionRate: 0, - }; - }); - } - } - }; - - buyHandler = () => { - if (!this.props.web3.account) { - this.props.toggleWalletModal(); - } else { - const { selected } = this.state; - let transak = new TransakSDK({ - environment: process.env.REACT_APP_TRANSAK_ENVIRONMENT, - defaultCryptoCurrency: selected.crypto.currency.symbol, - walletAddress: this.props.web3.account, - fiatCurrency: selected.fiat.currency.symbol, - fiatAmount: Number(selected.fiat.value), - hostURL: window.location.origin, - widgetHeight: "550px", - widgetWidth: "450px", - apiKey: process.env.REACT_APP_TRANSAK_API_KEY, - }); - - transak.init(); - - transak.on(transak.EVENTS.TRANSAK_ORDER_SUCCESSFUL, (orderData) => { - toast.success("Your Purchase was completed Successfully, You can see your assets in dashboard"); - transak.close(); - }); - transak.on(transak.EVENTS.ORDER_COMPLETED, (orderData) => { - toast.success("Your Purchase was completed Successfully, You can see your assets in dashboard"); - transak.close(); - }); - transak.on(transak.EVENTS.TRANSAK_ORDER_CANCELLED, (orderData) => { - toast.error("You Canceled The Purchase Progress, Maybe the next time!"); - transak.close(); - }); - transak.on(transak.EVENTS.TRANSAK_ORDER_FAILED, (orderData) => { - toast.error("Your order process failed unfortunately, Please try again later"); - transak.close(); - }); - transak.on(transak.EVENTS.ORDER_FAILED, (orderData) => { - toast.error("Your order process failed unfortunately, Please try again later"); - transak.close(); - }); - } - }; - - checkLimits = () => { - const { selected } = this.state; - if (selected.fiat.currency.symbol === "INR") { - return ( - !selected.crypto.currency || - !selected.crypto.value || - !selected.fiat.value || - !selected.fiat.currency || - selected.fiat.value < 750 - ); - } else { - return ( - !selected.crypto.currency || - !selected.crypto.value || - !selected.fiat.value || - !selected.fiat.currency || - selected.fiat.value < 10 - ); - } - }; - - render() { - const { cryptoCurrencies: cryptoList, fiatCurrencies: fiatList, selected, conversionRate } = this.state; - const { t } = this.props; - - return ( - - - - - {this.state.loading ? ( -
    - -
    - ) : ( - -
    - 1. {t("fiatOn.currencyLabel")} - - - - - - - - 2. {t("fiatOn.cryptoLabel")} - - - 0 - ? 0 - : 66, - }} - > - - - - {selected.crypto.currency && - selected.crypto.value && - selected.fiat.currency && - selected.crypto.value && - selected.crypto.value > 0 && ( - - - - - {selected.fiat.currency && selected.crypto.currency - ? `${t("exchange.rate")}: 1 ${ - selected.crypto.currency.symbol - } = ${(conversionRate > 0 - ? 1 / conversionRate - : 1 / selected.crypto.currency.priceUSD - ).toFixed(4)} ${selected.fiat.currency.symbol}` - : ""} - - - - - {t("exchange.youWillGet")}: {selected.crypto.value}{" "} - {selected.crypto.currency.symbol} - - - - - )} - - - - {this.state.priceLoading ? ( - - ) : !this.props.web3.account ? ( - t("wallet.connect") - ) : !selected.crypto.currency ? ( - t("exchange.selectCrypto") - ) : !selected.fiat.currency ? ( - t("exchange.selectFiat") - ) : !selected.fiat.value || !selected.crypto.value ? ( - t("exchange.enterAmount") - ) : this.checkLimits() ? ( - t("exchange.lowValue") - ) : ( - t("fiatOn.buyButton") - )} - - - - )} - - - - - ); - } -} - -export default withTranslation()(withWeb3Account(FiatOn)); diff --git a/src/pages/FiatOn/index.tsx b/src/pages/FiatOn/index.tsx new file mode 100644 index 00000000..89cfd582 --- /dev/null +++ b/src/pages/FiatOn/index.tsx @@ -0,0 +1,28 @@ +import { Row, Col } from "react-bootstrap"; +import Page from "../../components/Page"; +import FiatOnramp from "../../components/FiatOnramp"; +import * as Styled from "./styleds"; + +const FiatOn = () => { + return ( + + + +

    + Buy Crypto with Transak +

    + +
  • Use credit, debit, or bank transfer to purchase crypto.
  • +
  • Receive it in your wallet.
  • +
  • Start trading and investing instantly.
  • +
    + + + + + + + ); +}; + +export default FiatOn; diff --git a/src/pages/FiatOn/styleds.tsx b/src/pages/FiatOn/styleds.tsx new file mode 100644 index 00000000..f525ab16 --- /dev/null +++ b/src/pages/FiatOn/styleds.tsx @@ -0,0 +1,10 @@ +import styled from "styled-components"; + +export const List = styled.ul` + font-size: 1.25rem; +`; + +export const Company = styled.span` + // color: ${({ theme }) => theme.primary}; + color: #2575fc; +`; diff --git a/src/pages/Governance/index.js b/src/pages/Governance/index.js index 8c1abf8e..6e9387ce 100644 --- a/src/pages/Governance/index.js +++ b/src/pages/Governance/index.js @@ -1,49 +1,27 @@ import React, { useEffect, useState } from "react"; -import { Row, Col } from "react-bootstrap"; import { useDispatch, useSelector } from "react-redux"; -import styled from "styled-components"; +import { useTranslation } from "react-i18next"; +import SVG from "react-inlinesvg"; import _ from "lodash"; -import SpaceCard from "../../components/SpaceCard"; +import SearchIcon from "../../assets/images/search.svg"; import { fetchSpaces } from "../../state/governance/actions"; import Page from "../../components/Page"; -import SearchIcon from "../../assets/images/search.svg"; -import { useTranslation } from "react-i18next"; -import SVG from "react-inlinesvg"; import { - InputGroupText, InputGroup, + InputGroupText, InputGroupPrepend, InputGroupFormControl as FormControl, } from "../../components/Form"; +import SnapshotSpaceList from "../../components/SnapshotSpaceList"; +import * as Styled from "./styleds"; -const PageTitle = styled.h2` - color: ${({ theme }) => theme.text1}; - font-weight: 700; - font-size: 1.25rem; - margin: -20px 0 45px; - - @media (max-width: 991px) { - font-size: 0.875rem; - color: ${({ theme }) => theme.text3}; - margin: -10px 0 20px; - } -`; - -const StyledInputGroup = styled(InputGroup)` - margin: -148px 0 45px; - - @media (max-width: 991px) { - margin: 0 0 20px; - } -`; - -const Governance = (props) => { +const Governance = () => { + const { t } = useTranslation(); const dispatch = useDispatch(); const [search, setSearch] = useState(""); const { loading, spaces } = useSelector((state) => state.governance); const [transformedSpaces, setTransformedSpaces] = useState([]); - const { t } = useTranslation(); useEffect(() => { dispatch(fetchSpaces()); @@ -52,12 +30,14 @@ const Governance = (props) => { useEffect(() => { const pinnedSpaces = process.env.REACT_APP_GOVERNANCE_PINNED.split(",").map((space) => space.trim()); const list = Object.keys(spaces).map((key) => { + const space = spaces[key]; return { - ...spaces[key], + space, pinned: !!pinnedSpaces.includes(key), key, }; }); + const newSpaces = _.orderBy(list, ["pinned"], ["desc"]).filter((space) => JSON.stringify(space).toLowerCase().includes(search.toLowerCase()) ); @@ -69,61 +49,20 @@ const Governance = (props) => { }; return ( - - - - {t("governance.spaces")} - - - - - - - - - - - {loading ? ( - - {[...Array(12)].map((value, i) => { - return ( - - - - ); - })} - - ) : ( - - {transformedSpaces.map((space, i) => { - return ( - - - - ); - })} - - )} - - + +
    + {t("governance.spaces")} + + + + + + + + +
    + +
    ); }; diff --git a/src/pages/Governance/styleds.tsx b/src/pages/Governance/styleds.tsx new file mode 100644 index 00000000..61a789a9 --- /dev/null +++ b/src/pages/Governance/styleds.tsx @@ -0,0 +1,12 @@ +import styled from "styled-components"; + +export const Title = styled.h2` + color: ${({ theme }) => theme.text1}; + font-weight: 700; + font-size: 1.5rem; + margin-bottom: 0; + + ${({ theme }) => theme.mediaWidth.upToMedium` + margin-bottom: 1.5rem; + `}; +`; diff --git a/src/pages/History/index.js b/src/pages/History/index.js deleted file mode 100644 index 8a73b235..00000000 --- a/src/pages/History/index.js +++ /dev/null @@ -1,245 +0,0 @@ -import React from "react"; -import { Row, Col } from "react-bootstrap"; -import moment from "moment"; -import axios from "axios"; -import { CSVLink } from "react-csv"; - -import Card from "../../components/Card"; -import SectionList from "../../components/HistorySectionList"; -import Collapse from "../../components/Collapse"; -import withWeb3Account from "../../components/hoc/withWeb3Account"; -import ExchangeIcon from "../../components/Icons/Exchange"; -import Loading from "../../components/Loading"; -import Page from "../../components/Page"; -import styled from "styled-components"; -import { withTranslation } from "react-i18next"; - -const PAGE_SIZE = 30; - -const Title = styled.h2` - font-weight: 700; - font-size: 1.25rem; - margin-top: 0; - margin-bottom: 0; -`; - -const Header = styled.div` - display: flex; - align-items: center; - justify-content: space-between; - margin: -10px 0; -`; - -const Body = styled.div` - padding-top: 10px; - @media (max-width: 767px) { - padding-top: 20px; - } -`; - -const CustomCard = styled(Card)` - @media (max-width: 767px) { - padding: 0; - border: none; - - .card-body { - padding: 0; - } - - .card-header { - display: none; - } - } -`; - -class History extends React.Component { - constructor(props) { - super(props); - - this.loader = React.createRef(); - - this.state = { - loading: false, - blockNumber: null, - finished: false, - sections: [], - transactions: [], - }; - } - - componentDidMount() { - const options = { - root: null, - rootMargin: "0px", - threshold: 0, - }; - // initialize IntersectionObserver - // and attaching to Load More div - const observer = new IntersectionObserver(this.handleObserver, options); - if (this.loader.current) { - observer.observe(this.loader.current); - } - } - - handleObserver = (entities) => { - const target = entities[0]; - if (target.isIntersecting && !this.state.finished) { - this.fetchTransactions(); - } - }; - - getBlockNumber() { - return new Promise((resolve, reject) => { - resolve(this.props.web3.library.blockNumber > 0 ? this.props.web3.library.blockNumber : 9999999999); - }); - } - - fetchTransactions = async () => { - if (this.state.finished) { - return; - } - this.setState({ - loading: true, - }); - const lastBlockNumber = this.state.blockNumber || "99999999"; - let txnRes = await axios.get( - `https://api.etherscan.io/api?module=account&action=txlist&address=${this.props.web3.account}&startblock=0&endblock=${lastBlockNumber}&page=1&offset=${PAGE_SIZE}&sort=desc&apikey=KM1S3UP6BGICACR5X2IE5E3ZIADV33DKA1` - ); - let erc20Res = await axios.get( - `https://api.etherscan.io/api?module=account&action=tokentx&address=${this.props.web3.account}&startblock=0&endblock=${lastBlockNumber}&page=1&offset=${PAGE_SIZE}&sort=desc&apikey=KM1S3UP6BGICACR5X2IE5E3ZIADV33DKA1` - ); - - let ethLastBlock = - txnRes.data.status === "1" ? txnRes.data.result[txnRes.data.result.length - 1].blockNumber : 0; - let ercLastBlock = - erc20Res.data.status === "1" ? erc20Res.data.result[erc20Res.data.result.length - 1].blockNumber : 0; - - let lastBlock; - let isFinished = false; - - if (txnRes.data.result.length < PAGE_SIZE && erc20Res.data.result.length < PAGE_SIZE) { - lastBlock = ethLastBlock < ercLastBlock ? ethLastBlock : ercLastBlock; - isFinished = true; - } else { - lastBlock = ethLastBlock > ercLastBlock ? ethLastBlock : ercLastBlock; - } - let mixed = [...txnRes.data.result, ...erc20Res.data.result]; - - this.setState((prevState) => { - let result = {}; - for (let i in mixed) { - const txn = mixed[i]; - if (txn.blockNumber >= lastBlock) { - if (!result.hasOwnProperty(txn.timeStamp)) { - result[txn.timeStamp] = { - [txn.hash]: [txn], - }; - } else { - if (!result[txn.timeStamp].hasOwnProperty(txn.hash)) { - result[txn.timeStamp][txn.hash] = [{ ...txn }]; - } else { - result[txn.timeStamp][txn.hash].push({ ...txn }); - } - } - } - } - - let sections = []; - for (let i in result) { - const title = moment(i, "X").format("MMMM D, YYYY"); - let item = sections.findIndex((item) => item.title === title); - let transactions = Object.keys(result[i]); - if (item > -1) { - sections[item].content.push( - ...transactions.map((t) => { - const txn = result[i][t]; - return ; - }) - ); - } else { - sections.push({ - title, - content: transactions.map((t) => { - const txn = result[i][t]; - return ; - }), - }); - } - } - for (let i in sections) { - sections[i].content.reverse(); - } - sections.reverse(); - - let transformedSections = prevState.sections.concat(sections); - - return { - loading: false, - blockNumber: lastBlock - 1, - finished: isFinished, - sections: transformedSections, - transactions: prevState.transactions.concat(txnRes.data.result, erc20Res.data.result), - }; - }); - }; - - render() { - const { t } = this.props; - - return ( - - -
    - - {t("history")} - - {t("download", { file: "CSV" })} - - - } - > - - {(!this.state.loading || this.state.sections.length > 1) && ( - - )} - {this.state.finished && this.state.sections.length === 0 && ( -
    - -
    - {t("errors.noTransaction")} -
    - - {t("errors.noTransactionDesc")} - -
    - )} -
    - {!this.state.finished && ( -
    - -
    - )} -
    - -
    - - - - ); - } -} - -export default withWeb3Account(withTranslation()(History)); diff --git a/src/pages/History/index.tsx b/src/pages/History/index.tsx new file mode 100644 index 00000000..1241810d --- /dev/null +++ b/src/pages/History/index.tsx @@ -0,0 +1,15 @@ +import { useTranslation } from "react-i18next"; +import Page from "../../components/Page"; +import TransactionHistory from "../../components/TransactionHistory"; + +const History = () => { + const { t } = useTranslation(); + + return ( + + + + ); +}; + +export default History; diff --git a/src/pages/Home/Banners/index.tsx b/src/pages/Home/Banners/index.tsx new file mode 100644 index 00000000..4c91197d --- /dev/null +++ b/src/pages/Home/Banners/index.tsx @@ -0,0 +1,56 @@ +import Slider from "react-slick"; + +import { banners } from "../../../data/banners"; +import * as Styled from "./styleds"; + +const Banners = () => { + const settings = { + className: "slider w-100", + dots: true, + arrows: false, + infinite: false, + centerMode: false, + slidesToShow: 3, + slidesToScroll: 3, + centerPadding: "30px", + responsive: [ + { + breakpoint: 1199, + settings: { + slidesToShow: 2, + slidesToScroll: 2, + infinite: true, + }, + }, + { + breakpoint: 767, + settings: { + slidesToShow: 1, + slidesToScroll: 1, + infinite: true, + }, + }, + ], + dotsClass: "banner__dots slick-dots", + }; + + return ( + + + + {banners.map((banner, i) => { + return ( +
    + + + +
    + ); + })} +
    +
    +
    + ); +}; + +export default Banners; diff --git a/src/pages/Home/Banners/styleds.tsx b/src/pages/Home/Banners/styleds.tsx new file mode 100644 index 00000000..43d1fffc --- /dev/null +++ b/src/pages/Home/Banners/styleds.tsx @@ -0,0 +1,59 @@ +import styled from "styled-components"; + +export const Banners = styled.section` + margin: 40px 0 25px; + overflow: hidden; + + @media (min-width: 576px) { + margin: 80px 0; + } +`; + +export const BannerLink = styled.a` + display: block; + padding: 0 27px; + width: 100%; + + &:focus { + outline: none; + } +`; + +export const BannerImage = styled.img` + width: 100%; + display: block; +`; + +export const BannersInner = styled.div` + width: calc(100% + 54px); + margin-left: -27px; + padding-bottom: 2.5rem; + + .banner__dots { + bottom: -35px; + + li { + width: 12px; + height: 12px; + + button { + width: 12px; + height: 12px; + border-radius: 50%; + opacity: 0.5; + background-color: ${({ theme }) => theme.primary}; + transition: opacity 0.4s ease; + padding: 0; + margin: 0 5px; + + &::before { + display: none; + } + } + + &.slick-active button { + opacity: 1; + } + } + } +`; diff --git a/src/pages/Home/sections/Currencies.js b/src/pages/Home/Currencies/index.js similarity index 64% rename from src/pages/Home/sections/Currencies.js rename to src/pages/Home/Currencies/index.js index cfe802c3..e3e80fe9 100644 --- a/src/pages/Home/sections/Currencies.js +++ b/src/pages/Home/Currencies/index.js @@ -1,85 +1,25 @@ -import { Col } from "react-bootstrap"; import React, { useState, useEffect, useCallback, useMemo } from "react"; -import Table from "react-bootstrap-table-next"; -import Skeleton from "react-loading-skeleton"; -import styled from "styled-components"; +import { Link, withRouter } from "react-router-dom"; +import { useTranslation } from "react-i18next"; import { isMobile } from "react-device-detect"; +import SVG from "react-inlinesvg"; +import Skeleton from "react-loading-skeleton"; +import Table from "react-bootstrap-table-next"; +import { Button } from "react-bootstrap"; -import "./currencies.scss"; +import { CustomCard } from "../../../components/Card"; import SearchIcon from "../../../assets/images/search.svg"; -import CustomCard from "../../../components/CustomCard"; import MarketApi from "../../../http/market"; -import SparklineChart from "../../../components/SparklineChart"; -import ResponsiveTable from "../../../components/ResponsiveTable"; -import { Link, withRouter } from "react-router-dom"; import CurrencyText from "../../../components/CurrencyText"; -import { useTranslation } from "react-i18next"; +import ResponsiveTable from "../../../components/ResponsiveTable"; +import SparklineChart from "../../../components/SparklineChart"; import { InputGroupFormControl as FormControl, InputGroup, InputGroupPrepend, InputGroupText, } from "../../../components/Form"; -import SVG from "react-inlinesvg"; - -const GotoMarketContainer = styled.div` - display: flex; - align-items: center; - justify-content: center; - padding: 12px 24px; - margin-top: 1rem; - - @media (min-width: 992px) { - margin-top: 0; - } -`; - -const PoolsButton = styled.button` - border-radius: 12px; - background-color: ${({ theme }) => theme.bg2}; - padding: 6px 20px; - max-height: 40px; - min-height: 40px; - display: flex; - align-items: center; - justify-content: center; - white-space: nowrap; - font-size: 1rem; - font-family: inherit; - font-weight: 500; - border: none; - outline: none; - text-decoration: none; - - &:hover, - &:focus, - &:active { - text-decoration: none; - outline: none; - box-shadow: none; - } -`; - -const TradeButton = styled(PoolsButton)` - color: ${({ theme }) => theme.primary}; - width: 100%; - - &:hover { - color: ${({ theme }) => theme.bg2}; - background-color: ${({ theme }) => theme.primary}; - } -`; - -const StyledLink = styled(Link)` - text-decoration: none; - &:hover, - &:focus, - &:active { - text-decoration: none; - outline: none; - box-shadow: none; - } -`; +import * as Styled from "./styleds"; const marketApi = new MarketApi(); @@ -147,9 +87,9 @@ const Currencies = (props) => { return (
    {row.hasOwnProperty("image") ? ( -
    + {row?.name} -
    + ) : ( { /> )} - + {row.hasOwnProperty("symbol") ? ( row?.symbol?.toUpperCase() ) : ( )} - - + + {row.hasOwnProperty("name") ? row?.name : } - +
    ); }, @@ -177,13 +117,13 @@ const Currencies = (props) => { text: t("table.price"), formatter: (cell, row) => { return ( - + {row.hasOwnProperty("current_price") ? ( {row?.current_price} ) : ( )} - + ); }, }, @@ -211,9 +151,12 @@ const Currencies = (props) => { text: t("last7Days"), formatter: (cell, row, index) => { const data = row?.sparkline_in_7d?.price; - + return row.hasOwnProperty("sparkline_in_7d") ? ( - = 0 ? "primary" : "secondary"} /> + = 0 ? "primary" : "secondary"} + /> ) : ( ); @@ -232,9 +175,9 @@ const Currencies = (props) => { formatter(cellContent, row) { return (
    - - View More - + + View More +
    ); }, @@ -244,24 +187,17 @@ const Currencies = (props) => { const rowEvents = { onClick: (e, row) => { - props.history.push(`/tools/market/${row?.id}`); + props.history.push(`/market/${row?.id}`); }, }; return ( -
    -
    + +

    {t("tokens.assets")}

    - - + +
    @@ -278,15 +214,16 @@ const Currencies = (props) => { className={"form-control--currency"} /> - -
    - - {coins.length > 0 ? ( - <> + + + + {coins.length > 0 ? ( + <> +
    { columns={columns} data={coins} /> - - - - - - - - ) : ( - - {t("tokens.noToken")} - - )} - - - + + + + + + + + + + + ) : ( +
    + {t("tokens.noToken")} +
    + )} + + ); }; diff --git a/src/pages/Home/Currencies/styleds.tsx b/src/pages/Home/Currencies/styleds.tsx new file mode 100644 index 00000000..8334673f --- /dev/null +++ b/src/pages/Home/Currencies/styleds.tsx @@ -0,0 +1,179 @@ +import styled from "styled-components"; +import { Link } from "react-router-dom"; + +export const GotoMarketContainer = styled.div` + display: flex; + align-items: center; + justify-content: center; + padding-top: 0.5rem; + padding-bottom: 1rem; +`; + +export const PoolsButton = styled.button` + border-radius: 12px; + background-color: ${({ theme }) => theme.bg1}; + padding: 6px 20px; + max-height: 40px; + min-height: 40px; + display: flex; + align-items: center; + justify-content: center; + white-space: nowrap; + font-size: 1rem; + font-family: inherit; + font-weight: 500; + border: none; + outline: none; + text-decoration: none; + + &:hover, + &:focus, + &:active { + text-decoration: none; + outline: none; + box-shadow: none; + } +`; + +export const TradeButton = styled(PoolsButton)` + color: ${({ theme }) => theme.primary}; + width: 100%; + + &:hover { + color: ${({ theme }) => theme.bg1}; + background-color: ${({ theme }) => theme.primary}; + } +`; + +export const StyledLink = styled(Link)` + text-decoration: none; + + &:hover, + &:focus, + &:active { + text-decoration: none; + outline: none; + box-shadow: none; + } +`; + +export const CurrencySection = styled.section` + margin-top: 1.5rem; + margin-bottom: 1.25rem; + + @media (min-width: 576px) { + margin-top: 3.5rem; + margin-bottom: 2rem; + } + + @media (max-width: 576px) { + h2 { + font-size: 1.75rem; + margin-bottom: 1.25rem; + } + } +`; + +export const CurrenciesTable = styled.div` + .table { + position: relative; + width: 100%; + border-collapse: collapse; + color: ${({ theme }) => theme.text1}; + margin-bottom: 0.5rem; + } + + .table thead th { + border-bottom: 0; + border-top: 0; + font-weight: 400; + padding: 1.5rem 1rem; + color: ${({ theme }) => theme.text1}; + + &:focus { + outline: none; + } + + &:first-child { + padding-left: 2rem; + } + } + + .table tbody td { + vertical-align: middle; + border: 1px solid ${({ theme }) => theme.borderColor}; + border-left-width: 0; + border-right-width: 0; + color: white; + cursor: pointer; + padding: 1rem; + + &:first-child { + padding-left: 2rem; + } + } +`; + +export const CoinIcon = styled.div` + margin-right: 10px; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + background-color: white; + border-radius: 6px; + flex-basis: 24px; + + @media (min-width: 992px) { + border-radius: 12px; + margin-right: 24px; + width: 48px; + height: 48px; + flex-basis: 48px; + } + + img { + width: 18px; + height: 18px; + object-fit: contain; + + @media (min-width: 992px) { + width: 36px; + height: 36px; + } + } +`; + +export const CoinSymbol = styled.span` + margin-right: 0.625rem; + font-size: 1rem; + font-weight: 700; + color: ${({ theme }) => theme.text1}; + + @media (min-width: 992px) { + margin-right: 2.25rem; + font-size: 1.125rem; + } +`; + +export const CoinName = styled.span` + font-weight: 500; + font-size: 0.75rem; + color: ${({ theme }) => theme.text1}; + + @media (min-width: 992px) { + font-size: 1rem; + } +`; + +export const CoinPrice = styled.span` + font-size: 1rem; + font-weight: 700; + color: ${({ theme }) => theme.text1}; + + @media (min-width: 992px) { + font-size: 1.25rem; + font-weight: 500; + } +`; diff --git a/src/pages/Home/Features/index.tsx b/src/pages/Home/Features/index.tsx new file mode 100644 index 00000000..7a094d66 --- /dev/null +++ b/src/pages/Home/Features/index.tsx @@ -0,0 +1,67 @@ +import { useTranslation } from "react-i18next"; +// @ts-ignore +import Slider from "react-slick"; + +import { features } from "../../../data/features"; +import FeatureItem from "../../../components/FeatureItem"; +import * as Styled from "./styleds"; + +const Features = () => { + const { t } = useTranslation(); + + const settings = { + className: "slider w-100", + dots: true, + arrows: false, + infinite: false, + centerMode: false, + slidesToShow: 4, + slidesToScroll: 1, + centerPadding: "20px", + responsive: [ + { + breakpoint: 1199, + settings: { + slidesToShow: 2, + slidesToScroll: 2, + infinite: true, + }, + }, + { + breakpoint: 767, + settings: { + slidesToShow: 1, + slidesToScroll: 1, + infinite: true, + }, + }, + ], + dotsClass: "features__dots slick-dots d-lg-none", + }; + + return ( + +

    {t("app.getInTouch")}

    + + + + {features.map((feature, index) => { + return ( + + {t(feature.desc)} + + ); + })} + + + +
    + ); +}; + +export default Features; diff --git a/src/pages/Home/sections/features.scss b/src/pages/Home/Features/styleds.tsx similarity index 69% rename from src/pages/Home/sections/features.scss rename to src/pages/Home/Features/styleds.tsx index 855e700f..302a7b84 100644 --- a/src/pages/Home/sections/features.scss +++ b/src/pages/Home/Features/styleds.tsx @@ -1,30 +1,28 @@ -@import "../../../styles/abstracts/variables"; +import styled from "styled-components"; -.features { +export const Features = styled.section` padding: 20px 0 0; @media (min-width: 576px) { padding: 35px 0 0; } +`; + +export const Container = styled.div` + overflow: hidden; +`; + +export const Inner = styled.div` + width: calc(100% + 20px); + margin-left: -10px; + padding-bottom: 24px; .slick-slide { height: 100%; padding: 0 10px; } - &__container { - width: 100%; - display: block; - overflow: hidden; - } - - &__inner { - width: calc(100% + 20px); - margin-left: -10px; - padding-bottom: 24px; - } - - &__dots { + .features__dots { list-style: none; padding-top: 0; padding-left: 0; @@ -38,13 +36,13 @@ padding-top: 10px; } - & li { + li { button { width: 5px !important; height: 5px !important; border-radius: 5px; opacity: 0.5; - background-color: $primary; + background-color: ${({ theme }) => theme.primary}; transition: opacity 0.4s ease; margin: 0 5px; padding: 0; @@ -65,4 +63,4 @@ } } } -} +`; diff --git a/src/pages/Home/Hero/index.tsx b/src/pages/Home/Hero/index.tsx new file mode 100644 index 00000000..af012ed1 --- /dev/null +++ b/src/pages/Home/Hero/index.tsx @@ -0,0 +1,37 @@ +import { Col } from "react-bootstrap"; +import { useTranslation } from "react-i18next"; + +import Hero1 from "../../../assets/images/hero/hero_1.png"; +import { useActiveWeb3React } from "../../../hooks"; +import { useWalletModalToggle } from "../../../state/application/hooks"; +import * as Styled from "./styleds"; + +const Hero = () => { + const toggleConnectModal = useWalletModalToggle(); + const { t } = useTranslation(); + const { account } = useActiveWeb3React(); + + return ( + + + + + +
    + {t("app.description")} +
    + + {account ? t("wallet.viewOrChange") : t("wallet.connect")} + +
    + + + ); +}; + +export default Hero; diff --git a/src/pages/Home/Hero/styleds.tsx b/src/pages/Home/Hero/styleds.tsx new file mode 100644 index 00000000..f68a55e9 --- /dev/null +++ b/src/pages/Home/Hero/styleds.tsx @@ -0,0 +1,45 @@ +import styled from "styled-components"; +import { Button, Col } from "react-bootstrap"; + +export const Hero = styled.section` + padding: 20px 0 40px; + + @media (min-width: 768px) { + padding: 100px 0 80px; + } +`; + +export const Title = styled.h1` + margin-bottom: 3.75rem; + + @media (max-width: 767px) { + margin-top: 0; + font-size: 1.875rem; + margin-bottom: 2rem; + } +`; + +export const ConceptCol = styled(Col)` + position: relative; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +`; + +export const Image = styled.img` + position: absolute; + z-index: 2; +`; + +export const Image1 = styled(Image)` + z-index: 10; + position: relative; + width: 320px; + height: auto; +`; + +export const CustomButton = styled(Button)` + padding: 1rem 1.5rem; + font-weight: 500; +`; diff --git a/src/pages/Home/index.js b/src/pages/Home/index.tsx similarity index 56% rename from src/pages/Home/index.js rename to src/pages/Home/index.tsx index 7ca38e1a..655ee4b5 100644 --- a/src/pages/Home/index.js +++ b/src/pages/Home/index.tsx @@ -1,20 +1,22 @@ import { useEffect } from "react"; +import { useTranslation } from "react-i18next"; +import { toast } from "react-hot-toast"; import aos from "aos"; import Web3 from "web3"; -import { toast } from "react-hot-toast"; -import { useTranslation } from "react-i18next"; -import Page from "../../components/Page"; -import Hero from "./sections/Hero"; -import Features from "./sections/Features"; -import Banners from "./sections/Banners"; -import Currencies from "./sections/Currencies"; -import useParsedQueryString from "../../hooks/useParsedQueryString"; + import { useActiveWeb3React } from "../../hooks"; +import useParsedQueryString from "../../hooks/useParsedQueryString"; +import Page from "../../components/Page"; +import Hero from "./Hero"; +import Features from "./Features"; +import Banners from "./Banners"; +import Currencies from "./Currencies"; -const HomePage = (props) => { +const HomePage = () => { const { account } = useActiveWeb3React(); const queryString = useParsedQueryString(); const { t } = useTranslation(); + const message = account ? t("errors.notFound") : t("errors.walletConnect"); useEffect(() => { const web3 = new Web3(Web3.givenProvider); @@ -33,31 +35,16 @@ const HomePage = (props) => { useEffect(() => { if (queryString && queryString.error) { - if (account) { - toast.error(t("errors.notFound")); - } else { - toast.error(t("errors.walletConnect")); - } + toast.error(message); } - }, [queryString]); + }, [queryString, message]); return ( - - {/* Home-Hero: start */} + - {/* Home-Hero: end */} - - {/* Home-Currencies: start */} - {/* Home-Currencies: end */} - - {/* Home-Currencies: start */} - {/* Home-Currencies: end */} - - {/* Home-Features: start */} - {/* Home-Features: end */} ); }; diff --git a/src/pages/Home/sections/Banners.js b/src/pages/Home/sections/Banners.js deleted file mode 100644 index 5d7db834..00000000 --- a/src/pages/Home/sections/Banners.js +++ /dev/null @@ -1,69 +0,0 @@ -import Slider from "react-slick"; -import "slick-carousel/slick/slick.css"; -import "slick-carousel/slick/slick-theme.css"; - -import { banners } from "../../../constants"; -import "./banners.scss"; - -const Banners = (props) => { - const settings = { - className: "slider w-100", - dots: true, - arrows: false, - infinite: false, - centerMode: false, - slidesToShow: 3, - slidesToScroll: 3, - centerPadding: "30px", - responsive: [ - { - breakpoint: 1199, - settings: { - slidesToShow: 2, - slidesToScroll: 2, - infinite: true, - }, - }, - { - breakpoint: 767, - settings: { - slidesToShow: 1, - slidesToScroll: 1, - infinite: true, - }, - }, - ], - dotsClass: "banner__dots slick-dots", - }; - - return ( -
    -
    -
    - - {banners.map((banner, i) => { - return ( -
    - - {`banner - -
    - ); - })} -
    -
    -
    -
    - ); -}; - -export default Banners; diff --git a/src/pages/Home/sections/Features.js b/src/pages/Home/sections/Features.js deleted file mode 100644 index 8b7f1fe6..00000000 --- a/src/pages/Home/sections/Features.js +++ /dev/null @@ -1,88 +0,0 @@ -import { Col } from "react-bootstrap"; -import Slider from "react-slick"; -import "slick-carousel/slick/slick.css"; -import "slick-carousel/slick/slick-theme.css"; - -import FeatureItem from "../../../components/FeatureItem"; -import "./features.scss"; -import { useTranslation } from "react-i18next"; - -const Features = (props) => { - const { t } = useTranslation(); - - const settings = { - className: "slider w-100", - dots: true, - arrows: false, - infinite: false, - centerMode: false, - slidesToShow: 4, - slidesToScroll: 1, - centerPadding: "20px", - responsive: [ - { - breakpoint: 1199, - settings: { - slidesToShow: 2, - slidesToScroll: 2, - infinite: true, - }, - }, - { - breakpoint: 767, - settings: { - slidesToShow: 1, - slidesToScroll: 1, - infinite: true, - }, - }, - ], - dotsClass: "features__dots slick-dots d-lg-none", - }; - - return ( -
    -
    -

    {t("app.getInTouch")}

    - - -
    -
    - - - {t("app.features.support.desc")} - - - {t("app.features.blog.desc")} - - - {t("app.features.community.desc")} - - - {t("app.features.careers.desc")} - - -
    -
    - - - ); -}; - -export default Features; diff --git a/src/pages/Home/sections/Hero.js b/src/pages/Home/sections/Hero.js deleted file mode 100644 index b8bb3cac..00000000 --- a/src/pages/Home/sections/Hero.js +++ /dev/null @@ -1,48 +0,0 @@ -import { Col, Button } from "react-bootstrap"; -import styled from "styled-components"; -import { useTranslation } from "react-i18next"; - -import { useWalletModalToggle } from "../../../state/application/hooks"; -import Hero1 from "../../../assets/images/hero/hero_1.png"; -import "./hero.scss"; -import { useActiveWeb3React } from "../../../hooks"; -import { useIsDarkMode } from "../../../state/user/hooks"; - -const StyleButton = styled(Button)` - height: 56px; - font-weight: 500; - - &.btn-lg { - height: 70px; - padding: 20px 32px; - } -`; - -const Hero = (props) => { - const toggleConnectModal = useWalletModalToggle(); - const { t } = useTranslation(); - const { account } = useActiveWeb3React(); - const darkMode = useIsDarkMode(); - - return ( -
    -
    - {"OctoFi"} - - -

    {t("app.description")}

    -
    - - {account ? t("wallet.viewOrChange") : t("wallet.connect")} - -
    - - - ); -}; - -export default Hero; diff --git a/src/pages/Home/sections/banners.scss b/src/pages/Home/sections/banners.scss deleted file mode 100644 index 7d9bdb3d..00000000 --- a/src/pages/Home/sections/banners.scss +++ /dev/null @@ -1,82 +0,0 @@ -@import "../../../styles/abstracts/variables"; - -.banners { - padding: 40px 0 25px; - - @media (min-width: 576px) { - padding-top: 80px; - padding-bottom: 75px; - } - - &__container { - width: 100%; - display: block; - overflow: hidden; - margin: 0 15px; - } - - &__inner { - width: calc(100% + 53px); - margin-left: -27px; - padding-bottom: 24px; - } -} - -.banner { - &__link { - display: block; - padding: 0 27px; - width: 100%; - - &:focus { - outline: none; - } - } - - &__image { - width: 100%; - display: block; - } - - &__dots { - list-style: none; - padding-top: 0; - padding-left: 0; - margin: 0; - display: flex !important; - align-items: center; - position: relative; - justify-content: center; - - @media (min-width: 576px) { - padding-top: 10px; - } - - & li { - button { - width: 5px !important; - height: 5px !important; - border-radius: 5px; - opacity: 0.5; - background-color: $primary; - transition: opacity 0.4s ease; - margin: 0 5px; - padding: 0; - - @media (min-width: 576px) { - width: 10px !important; - height: 10px !important; - border-radius: 10px; - } - - &::before { - display: none; - } - } - - &.slick-active button { - opacity: 1; - } - } - } -} diff --git a/src/pages/Home/sections/currencies.scss b/src/pages/Home/sections/currencies.scss deleted file mode 100644 index cd07a8ea..00000000 --- a/src/pages/Home/sections/currencies.scss +++ /dev/null @@ -1,136 +0,0 @@ -@import "../../../styles/abstracts/variables"; - -.currency { - padding: 25px 0 20px; - - @media (min-width: 576px) { - padding: 55px 0 35px; - } - - h2 { - @media (max-width: 576px) { - font-size: 1.25rem; - font-weight: 700; - margin-top: 0; - margin-bottom: 20px; - } - } -} - -.currencies { - position: relative; - width: 100%; - border-collapse: collapse; - color: $light-title; - - .dark-mode & { - color: white; - } -} - -.table.currencies thead th { - border-bottom: 0; - border-top: 0; - font-weight: 400; - padding-bottom: 1.5rem; - padding-top: 1.5rem; - color: $light-title; - - .dark-mode & { - color: white; - } - - &:focus { - outline: none; - } -} - -.table.currencies tbody td { - vertical-align: middle; - border: 1px solid #caccd2; - border-left-width: 0; - border-right-width: 0; - color: white; - cursor: pointer; - padding: 1rem 0.75rem; - - .dark-mode & { - border-color: rgba(255, 255, 255, 0.2); - } -} - -.coin__icon { - margin-right: 10px; - width: 24px; - height: 24px; - display: flex; - align-items: center; - justify-content: center; - background-color: white; - border-radius: 6px; - flex-basis: 24px; - - @media (min-width: 992px) { - border-radius: 12px; - margin-right: 24px; - width: 48px; - height: 48px; - flex-basis: 48px; - } - - img { - width: 18px; - height: 18px; - object-fit: contain; - - @media (min-width: 992px) { - width: 36px; - height: 36px; - } - } -} - -.coin__symbol { - margin-right: 0.625rem; - font-size: 1rem; - font-weight: 700; - color: $light-title; - - .dark-mode & { - color: white; - } - - @media (min-width: 992px) { - margin-right: 2.25rem; - font-size: 1.125rem; - } -} - -.coin__name { - font-weight: 500; - font-size: 0.75rem; - color: $light-title; - - .dark-mode & { - color: white; - } - - @media (min-width: 992px) { - font-size: 1rem; - } -} - -.coin__price { - font-size: 1rem; - font-weight: 700; - color: $light-title; - - .dark-mode & { - color: white; - } - - @media (min-width: 992px) { - font-size: 1.25rem; - font-weight: 500; - } -} diff --git a/src/pages/Home/sections/hero.scss b/src/pages/Home/sections/hero.scss deleted file mode 100644 index d583e434..00000000 --- a/src/pages/Home/sections/hero.scss +++ /dev/null @@ -1,48 +0,0 @@ -.hero { - padding: 22.5px 0 40px; - - @media (min-width: 576px) { - padding-top: 120px; - padding-bottom: 80px; - } -} - -.hero__title.h1 { - margin-bottom: 3.75rem; - - @media (max-width: 576px) { - margin-top: 0; - font-size: 1.875rem; - margin-bottom: 2rem; - } -} - -.hero__desc { - margin-bottom: 96px; - - @media (max-width: 576px) { - font-weight: 400; - font-size: 0.875rem; - margin-bottom: 30px; - } -} - -.hero__concept { - position: relative; - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; -} - -.hero__image { - position: absolute; - z-index: 2; - - &--one { - z-index: 10; - position: relative; - width: 320px; - height: auto; - } -} diff --git a/src/pages/Launchpad/index.js b/src/pages/Launchpad/index.js index 9ade05c6..aeaabf84 100644 --- a/src/pages/Launchpad/index.js +++ b/src/pages/Launchpad/index.js @@ -182,7 +182,7 @@ const Launchpad = props => { const presales = usePresales() return ( - + @@ -192,7 +192,7 @@ const Launchpad = props => {
    - {t("launchpad.createNew")} + {t("launchpad.createNew")}
    { useEffect(() => { if (presale?.hasOwnProperty("error")) { - props.history.push("/invest/launchpad"); + props.history.push("/launchpad"); } }, [presale]); @@ -695,8 +694,8 @@ const LaunchpadItem = (props) => { }; return ( - - + + @@ -704,7 +703,7 @@ const LaunchpadItem = (props) => { - + { - + + + {liquidityPercent ? `${liquidityPercent}%` : "-"} Lock {lockDurationMap?.[lockDuration] || "-"} + + {presale?.status?.buyersCount?.toString() || "-"} Participants + + {filledPercent && !isNaN(filledPercent) ? `${filledPercent}%` : "-"} @@ -843,7 +848,7 @@ const LaunchpadItem = (props) => { @@ -1281,7 +1286,7 @@ const LaunchpadItem = (props) => { - + ); }; diff --git a/src/pages/Margin/components/OrderHistory/index.js b/src/pages/Margin/components/OrderHistory/index.js index 7d6090df..2f94b37d 100644 --- a/src/pages/Margin/components/OrderHistory/index.js +++ b/src/pages/Margin/components/OrderHistory/index.js @@ -50,7 +50,7 @@ const CardTitle = styled.h4` `; const Content = styled.div` - background-color: ${({ theme }) => theme.bg3}; + background-color: ${({ theme }) => theme.bg1}; border-radius: 18px; display: flex; flex-direction: column; diff --git a/src/pages/Margin/index.js b/src/pages/Margin/index.js index 63d1342d..a6fc61d3 100644 --- a/src/pages/Margin/index.js +++ b/src/pages/Margin/index.js @@ -70,7 +70,7 @@ const Margin = (props) => { const selectedMarket = useSelector((state) => state.margin.selectedMarket); return ( - + diff --git a/src/pages/MarketMaker/components/MakerContainer/MarketMakerDetails.js b/src/pages/MarketMaker/components/MakerContainer/MarketMakerDetails.js index 45b04082..4ddf31ad 100644 --- a/src/pages/MarketMaker/components/MakerContainer/MarketMakerDetails.js +++ b/src/pages/MarketMaker/components/MakerContainer/MarketMakerDetails.js @@ -36,7 +36,7 @@ const Price = styled(Title)` const Content = styled.div` border-radius: 18px; - background-color: ${({ theme }) => theme.bg3}; + background-color: ${({ theme }) => theme.bg1}; padding: 20px; display: flex; flex-direction: column; diff --git a/src/pages/MarketMaker/components/MarketStats/index.js b/src/pages/MarketMaker/components/MarketStats/index.js index 0b88d863..cfac4bb5 100644 --- a/src/pages/MarketMaker/components/MarketStats/index.js +++ b/src/pages/MarketMaker/components/MarketStats/index.js @@ -39,7 +39,7 @@ const CardTitle = styled.h4` `; const Content = styled.div` - background-color: ${({ theme }) => theme.bg3}; + background-color: ${({ theme }) => theme.bg1}; border-radius: 18px; display: flex; flex-direction: column; diff --git a/src/pages/MarketMaker/components/OrderHistory/index.js b/src/pages/MarketMaker/components/OrderHistory/index.js index 0bde5215..3da750e0 100644 --- a/src/pages/MarketMaker/components/OrderHistory/index.js +++ b/src/pages/MarketMaker/components/OrderHistory/index.js @@ -47,7 +47,7 @@ const CardTitle = styled.h4` `; const Content = styled.div` - background-color: ${({ theme }) => theme.bg3}; + background-color: ${({ theme }) => theme.bg1}; border-radius: 18px; display: flex; flex-direction: column; diff --git a/src/pages/MarketMaker/index.js b/src/pages/MarketMaker/index.js index 272b6433..4e9b4024 100644 --- a/src/pages/MarketMaker/index.js +++ b/src/pages/MarketMaker/index.js @@ -50,7 +50,7 @@ const ContainerCard = styled(ResponsiveCard)` const MarketMaker = (props) => { return ( - + diff --git a/src/pages/Markets/MarketCard.js b/src/pages/Markets/MarketCard.js index cf46e365..fb289962 100644 --- a/src/pages/Markets/MarketCard.js +++ b/src/pages/Markets/MarketCard.js @@ -102,7 +102,7 @@ const CustomNavLink = styled(Nav.Link)` `; const PoolsButton = styled.button` border-radius: 12px; - background-color: ${({ theme }) => theme.bg2}; + background-color: ${({ theme }) => theme.bg1}; padding: 6px 20px; max-height: 40px; min-height: 40px; @@ -131,7 +131,7 @@ const TradeButton = styled(PoolsButton)` width: 100%; &:hover { - color: ${({ theme }) => theme.bg2}; + color: ${({ theme }) => theme.bg1}; background-color: ${({ theme }) => theme.primary}; } `; diff --git a/src/pages/Markets/index.js b/src/pages/Markets/index.js index 89e1b335..fd3179cc 100644 --- a/src/pages/Markets/index.js +++ b/src/pages/Markets/index.js @@ -7,17 +7,8 @@ import MarketOverview from "../../components/MarketOverview"; import MarketCard from "./MarketCard"; import Provider from "./Provider"; import { ServerState } from "../../constants"; -import styled from "styled-components"; import { useMemo } from "react"; -const StyledRow = styled(Row)` - margin-top: 86px; - - @media (max-width: 767px) { - margin-top: 40px; - } -`; - const Markets = (props) => { const { markets, marketsStats, marketsStatsState } = props; @@ -37,9 +28,9 @@ const Markets = (props) => { }, [marketsStats]); return ( - + - + {[...Array(4)].map((item, index) => { const row = marketsStats[index] || {}; @@ -59,7 +50,7 @@ const Markets = (props) => { ); })} - + { + return ( + + + + + + ); +}; + +export default MarketsExplore; diff --git a/src/pages/NFT/Orders/index.tsx b/src/pages/NFT/Orders/index.tsx new file mode 100644 index 00000000..fffd683f --- /dev/null +++ b/src/pages/NFT/Orders/index.tsx @@ -0,0 +1,131 @@ +import { useRef, useEffect } from "react"; +import { Row, Col } from "react-bootstrap"; +import NFTCard from "../../../components/NFTCard"; +import Dropdown from "../../../components/UI/Dropdown"; +// import { OrderSide } from "opensea-js/lib/types"; +import { useTranslation } from "react-i18next"; +import * as Styled from "./styleds"; + +export type OrdersProps = { + selectedCollection: any; + seaport: any; + fetchOrders: any; + page: any; + increasePage: any; + setSort: any; + orders: any; + hasMore: any; +}; + +const Orders = ({ + selectedCollection, + seaport, + fetchOrders, + page, + increasePage, + setSort, + orders, + hasMore, +}: OrdersProps) => { + const loader = useRef(null); + const { t } = useTranslation(); + + const changeSort = (value: any) => { + setSort(value); + }; + + useEffect(() => { + fetchOrders(); + }, [selectedCollection, fetchOrders]); + + useEffect(() => { + fetchOrders(); + }, [page, seaport, fetchOrders]); + + useEffect(() => { + const handleObserver = (entities: any) => { + const target = entities[0]; + if (target.isIntersecting) { + increasePage(); + } + }; + + const options = { + root: null, + rootMargin: "20px", + threshold: 0, + }; + + const observer = new IntersectionObserver(handleObserver, options); + if (loader.current) { + // @ts-ignore + observer.observe(loader.current); + } + }, [increasePage]); + + const sortItems = [ + { + title: t(`sort.recently_created`), + value: "recently_created", + }, + { + title: t(`sort.lowest_price`), + value: "lowest_price", + }, + { + title: t(`sort.highest_price`), + value: "highest_price", + }, + { + title: t(`sort.oldest`), + value: "oldest", + }, + ]; + + return ( + <> +
    + +
    + + + {orders.map((order: any, i: number) => { + return ( +
    + + + ); + })} + + + + {hasMore && + [...Array(orders.length > 0 ? 4 : 8)].map((item, i) => { + return ( + + + + ); + })} + + + {!hasMore && orders.length === 0 && ( +
    + {t("errors.noAssets")} + + {selectedCollection + ? "Please change selected collection or try later." + : "Please try later to see new offers"} + +
    + )} + + ); +}; + +export default Orders; diff --git a/src/pages/NFT/Orders/styleds.tsx b/src/pages/NFT/Orders/styleds.tsx new file mode 100644 index 00000000..28620808 --- /dev/null +++ b/src/pages/NFT/Orders/styleds.tsx @@ -0,0 +1,16 @@ +import styled from "styled-components"; + +export const NoResultText = styled.span` + color: ${({ theme }) => theme.text1}; + font-size: 1rem; + font-weight: 700; + text-align: center; + margin-bottom: 14px; +`; + +export const NoResultDescription = styled.span` + color: ${({ theme }) => theme.text2}; + font-size: 0.875rem; + font-weight: 400; + text-align: center; +`; diff --git a/src/pages/NFT/components/Orders.js b/src/pages/NFT/components/Orders.js deleted file mode 100644 index 2d984412..00000000 --- a/src/pages/NFT/components/Orders.js +++ /dev/null @@ -1,131 +0,0 @@ -import styled from "styled-components"; -import { Row, Col } from "react-bootstrap"; -import NFTCard from "../../../components/NFTCard"; -import Dropdown from "../../../components/UI/Dropdown"; -import { useRef, useEffect } from "react"; -// import { OrderSide } from "opensea-js/lib/types"; -import { useTranslation } from "react-i18next"; - -const HeaderCol = styled(Col)` - margin-bottom: 20px; -`; - -const NoResultText = styled.span` - color: ${({ theme }) => theme.text1}; - font-size: 1rem; - font-weight: 700; - text-align: center; - margin-bottom: 14px; -`; - -const NoResultDescription = styled.span` - color: ${({ theme }) => theme.text2}; - font-size: 0.875rem; - font-weight: 400; - text-align: center; -`; - -const Orders = (props) => { - const loader = useRef(null); - const { t } = useTranslation(); - const { fetchOrders, page, increasePage, setSort, orders, hasMore } = props; - - const changeSort = (value) => { - setSort(value); - }; - - const observeAction = () => { - increasePage(); - }; - - const handleObserver = (entities) => { - const target = entities[0]; - if (target.isIntersecting) { - observeAction(); - } - }; - - useEffect(() => { - fetchOrders(); - }, [props.selectedCollection]); - - useEffect(() => { - fetchOrders(); - }, [page, props.seaport]); - - useEffect(() => { - const options = { - root: null, - rootMargin: "20px", - threshold: 0, - }; - - const observer = new IntersectionObserver(handleObserver, options); - if (loader.current) { - observer.observe(loader.current); - } - }, []); - - const sortItems = [ - { - title: t(`sort.recently_created`), - value: "recently_created", - }, - { - title: t(`sort.lowest_price`), - value: "lowest_price", - }, - { - title: t(`sort.highest_price`), - value: "highest_price", - }, - { - title: t(`sort.oldest`), - value: "oldest", - }, - ]; - return ( - - - - - - {orders.map((order, i) => { - return ( -
    - - - ); - })} - - - {hasMore && - [...Array(orders.length > 0 ? 4 : 8)].map((item, i) => { - return ( - - - - ); - })} - {!hasMore && orders.length === 0 && ( - - {t("errors.noAssets")} - - {props.selectedCollection - ? "Please change selected collection or try later." - : "Please try later to see new offers"} - - - )} - - - - ); -}; - -export default Orders; diff --git a/src/pages/NFT/index.js b/src/pages/NFT/index.js index 45c0edef..0368e349 100644 --- a/src/pages/NFT/index.js +++ b/src/pages/NFT/index.js @@ -1,85 +1,15 @@ -import styled from "styled-components"; -import { OpenSeaPort, Network } from "opensea-js"; import { Component } from "react"; -import { Button } from "react-bootstrap"; +import { withTranslation } from "react-i18next"; +import { Row, Col, Button } from "react-bootstrap"; import Web3 from "web3"; +import { OpenSeaPort, Network } from "opensea-js"; +import { OrderSide } from "opensea-js/lib/types"; import Page from "../../components/Page"; -import Card, { ResponsiveCard } from "../../components/Card"; import Collections from "../../components/Collections"; -import Orders from "./components/Orders"; -import { OrderSide } from "opensea-js/lib/types"; import withWeb3Account from "../../components/hoc/withWeb3Account"; -import { withTranslation } from "react-i18next"; - -const Row = styled.div` - margin-top: -30px; - margin-left: -10px; - margin-right: -10px; -`; - -const StyledRow = styled(Row)` - margin-top: 40px; - - @media (max-width: 767px) { - margin-top: 20px; - } -`; - -const Col = styled.div` - padding: 0 10px 20px; -`; - -const Sidebar = styled(Col)` - width: 340px; - - @media (max-width: 991px) { - width: 100%; - } -`; - -const Content = styled(Col)` - flex: 1; -`; - -const StyledCard = styled(Card)` - .card-body { - padding: 0; - } -`; - -const StyledResponsiveCard = styled(ResponsiveCard)` - .card-body { - padding: 0; - - @media (max-width: 991px) { - padding: 0 10px; - } - } - - @media (max-width: 767px) { - background-color: transparent; - border-top: 1px solid ${({ theme }) => theme.text3}; - } -`; - -const CardSection = styled.div` - padding: ${({ paddingTop, paddingBottom }) => `${paddingTop || "20px"} 20px ${paddingBottom || "20px"}`}; - border-bottom: ${({ hasBorder, theme }) => (hasBorder ? `1px solid ${theme.text3}` : "none")}; - display: flex; - flex-direction: column; -`; - -const Title = styled.h4` - font-weight: bold; - font-size: 1.25rem; - color: ${({ theme }) => theme.text1}; - margin: ${({ marginBottom }) => `0 0 ${marginBottom || "20px"}`}; - - @media (max-width: 991px) { - font-size: 1.125rem; - } -`; +import Orders from "./Orders"; +import * as Styled from "./styleds"; const PAGE_SIZE = 24; @@ -111,88 +41,82 @@ class NFT extends Component { const { t } = this.props; return ( - - - - - - {t("orderbookSide")} -
    - - -
    -
    - - {t("accountFilter")} -
    - - -
    -
    - - {t("bundles")} -
    - -
    -
    - - {t("collections")} - - -
    -
    - - - - - - - -
    + + + + + {t("orderbookSide")} +
    + + +
    +
    + + {t("accountFilter")} +
    + + +
    +
    + + {t("bundles")} +
    + +
    +
    + + {t("collections")} + + +
    +
    + + + ); } @@ -330,7 +254,6 @@ class NFT extends Component { if (this.seaport) { const { page, selectedCollection, onlyByMe, onlyForMe, side, onlyBundles } = this.state; const { account } = this.props.web3; - const { t } = this.props; if (page === 1) { this.setState({ orders: [], @@ -365,9 +288,7 @@ class NFT extends Component { }; }); } - } catch (e) { - - } + } catch (e) {} } }; } diff --git a/src/pages/NFT/styleds.tsx b/src/pages/NFT/styleds.tsx new file mode 100644 index 00000000..7920c70d --- /dev/null +++ b/src/pages/NFT/styleds.tsx @@ -0,0 +1,26 @@ +import styled from "styled-components"; +import { Col } from "react-bootstrap"; + +export const FiltersCol = styled(Col)` + min-width: 340px; + + @media (min-width: 767px) { + max-width: 480px; + } +`; + +export const CardSection = styled.div` + padding: 1rem 1rem 1.5rem; + border-bottom: 1px solid ${({ theme }) => theme.borderColor}; +`; + +export const Title = styled.h4<{ marginBottom?: string }>` + font-weight: bold; + font-size: 1.25rem; + color: ${({ theme }) => theme.text1}; + margin: ${({ marginBottom }) => `0 0 ${marginBottom || "20px"}`}; + + @media (max-width: 991px) { + font-size: 1.125rem; + } +`; diff --git a/src/pages/NewLaunchpad/index.js b/src/pages/NewLaunchpad/index.js index be7c358b..6ef6e53b 100644 --- a/src/pages/NewLaunchpad/index.js +++ b/src/pages/NewLaunchpad/index.js @@ -24,8 +24,8 @@ import { import PresaleGeneratorABI from '../../constants/abis/Presale/PresaleGenerator.json' import {useActiveWeb3React} from "../../hooks"; import {useContract} from "../../hooks/useContract"; -import {AccountState, AccountStateContent, AccountStateTitle} from "../../components/AddLiquidityModal/uniswap"; -import SVG from "react-inlinesvg"; +import WalletConnectStatus from '../../components/WalletConnectStatus'; +import { AccountState, AccountStateContent, AccountStateTitle } from "../../components/WalletConnectStatus/styles"; import {useTranslation} from "react-i18next"; import GradientButton from "../../components/UI/Button"; import {useWalletModalToggle} from "../../state/application/hooks"; @@ -57,14 +57,6 @@ const IconButton = styled(Link)` } ` -const StyledRow = styled(Row)` - margin-top: 40px; - - @media (max-width: 767px) { - margin-top: 20px; - } -` - const Title = styled.h2` line-height: 1.5rem; font-size: 1.5rem; @@ -594,7 +586,7 @@ const NewLaunchpad = props => { .then(() => { setLoading(false); toast.success('The presale created successfully!'); - props.history.push('/invest/launchpad') + props.history.push('/launchpad') }) .catch(err => { setLoading(false); @@ -605,14 +597,16 @@ const NewLaunchpad = props => { } return ( - - + + + - + @@ -620,20 +614,6 @@ const NewLaunchpad = props => { New Initial Liquidity Offering - - - - - - {t("wallet.notConnected")} - - - - { ) : ( - + @@ -697,7 +677,7 @@ const NewLaunchpad = props => { - + {selectedToken?.symbol} / {selectedToken?.name} {shortenAddress(selectedToken?.address)} @@ -708,7 +688,7 @@ const NewLaunchpad = props => { - + {selectedToken?.symbol} / {selectedToken?.name} {shortenAddress(selectedToken?.address)} @@ -959,7 +939,6 @@ const NewLaunchpad = props => { id={'referral-address'} onChange={setReferralAddress} value={referralAddress} - withoutMargin={true} placeholder={'Referral Address'} /> @@ -1026,7 +1005,7 @@ const NewLaunchpad = props => { )} - + ) } diff --git a/src/pages/Platform/index.js b/src/pages/Platform/index.js index 6451bf26..c996021c 100644 --- a/src/pages/Platform/index.js +++ b/src/pages/Platform/index.js @@ -44,21 +44,16 @@ function Platforms(props) { }, [props.balances, props.match.params.platform]); return ( - + - + - + - + diff --git a/src/pages/Pools/PoolsCard.js b/src/pages/Pools/PoolsCard.js index 3e50bcd3..a5985dd6 100644 --- a/src/pages/Pools/PoolsCard.js +++ b/src/pages/Pools/PoolsCard.js @@ -78,16 +78,6 @@ const CustomNavItem = styled(Nav.Item)` } `; -const StyledResponsiveCard = styled(ResponsiveCard)` - margin-left: -60px; - margin-right: -60px; - - @media (max-width: 991px) { - margin-left: -30px; - margin-right: -30px; - } -`; - const CustomNavLink = styled(Nav.Link)` border-radius: 18px !important; color: ${({ theme }) => theme.primary}; @@ -129,10 +119,9 @@ class PoolsCard extends Component { }); }; render() { - const theme = this.context; const { t } = this.props; return ( - +
    {t("pools.title")} @@ -224,7 +213,7 @@ class PoolsCard extends Component { {/*end::body*/} - + ); } } diff --git a/src/pages/Pools/PoolsTab.js b/src/pages/Pools/PoolsTab.js index 41bf3313..7698ac4d 100644 --- a/src/pages/Pools/PoolsTab.js +++ b/src/pages/Pools/PoolsTab.js @@ -42,7 +42,7 @@ const PlatformName = styled.span` const PoolsButton = styled.button` border-radius: 12px; - background-color: ${({ theme }) => theme.bg2}; + background-color: ${({ theme }) => theme.bg1}; padding: 6px 18px; max-height: 40px; min-height: 40px; @@ -69,7 +69,7 @@ const AddLiquidityButton = styled(PoolsButton)` transition: 0.4s ease all; &:hover { - color: ${({ theme }) => theme.bg2}; + color: ${({ theme }) => theme.bg1}; background-color: ${({ theme }) => theme.primary}; } `; @@ -79,7 +79,7 @@ const WithdrawButton = styled(PoolsButton)` transition: 0.4s ease all; &:hover { - color: ${({ theme }) => theme.bg2}; + color: ${({ theme }) => theme.bg1}; background-color: ${({ theme }) => theme.secondary}; } `; diff --git a/src/pages/Pools/index.js b/src/pages/Pools/index.js index c822e278..12a781fe 100644 --- a/src/pages/Pools/index.js +++ b/src/pages/Pools/index.js @@ -1,6 +1,5 @@ import React, { Component } from "react"; import { connect } from "react-redux"; -import { Row, Col } from "react-bootstrap"; import { Route, Switch } from "react-router-dom"; import PoolsCard from "./PoolsCard"; @@ -10,21 +9,12 @@ import UniswapLiquidityModal from "../../components/AddLiquidityModal/uniswap"; import * as actions from "../../state/pools/actions"; import Page from "../../components/Page"; import { emitter } from "../../lib/helper"; -import styled from "styled-components"; - -const StyledRow = styled(Row)` - margin-top: 40px; - - @media (max-width: 767px) { - margin-top: 20px; - } -`; class Pools extends Component { investButtonClick = () => { emitter.emit("open-modal", { action: () => { - this.props.history.push(`/invest/pools`); + this.props.history.push("/invest/pools"); emitter.emit("close-modal"); }, }); @@ -58,16 +48,12 @@ class Pools extends Component { }; render() { return ( - - -
    - - - + + diff --git a/src/pages/Proposals/index.js b/src/pages/Proposals/index.js index 6a36c4b3..63c2bbb8 100644 --- a/src/pages/Proposals/index.js +++ b/src/pages/Proposals/index.js @@ -2,160 +2,19 @@ import React, { useEffect, useState } from "react"; import { Row, Col } from "react-bootstrap"; import { useDispatch, useSelector } from "react-redux"; import BootstrapTable from "react-bootstrap-table-next"; +import { useTranslation } from "react-i18next"; import moment from "moment"; -import styled from "styled-components"; -import { ResponsiveCard } from "../../components/Card"; import { fetchProposals, fetchSpaces } from "../../state/governance/actions"; import { shorten } from "../../state/governance/hooks"; -import { Link } from "react-router-dom"; import { formatProposals, getScores } from "../../lib/utils"; import { useActiveWeb3React } from "../../hooks"; +import { ResponsiveCard } from "../../components/Card"; import Loading from "../../components/Loading"; import Page from "../../components/Page"; -import "./style.scss"; -import "../../components/UI/Button/style.scss"; import ResponsiveTable from "../../components/ResponsiveTable"; -import { useTranslation } from "react-i18next"; - -const Header = styled.div` - padding: 0; - display: flex; - align-items: center; - justify-content: space-between; - margin-bottom: 20px; - border: none; - background-color: transparent; - - @media (max-width: 767px) { - flex-direction: column; - align-items: stretch; - } -`; - -const Title = styled.h2` - font-size: 1.25rem; - font-weight: 700; - color: ${({ theme }) => theme.text1}; - margin: 0; - - @media (max-width: 767px) { - margin-bottom: 30px; - } -`; - -const PoolsButton = styled.button` - border-radius: 12px; - background-color: ${({ theme }) => theme.bg2}; - padding: 6px 20px; - max-height: 40px; - min-height: 40px; - display: flex; - align-items: center; - justify-content: center; - white-space: nowrap; - font-size: 1rem; - font-family: inherit; - font-weight: 500; - border: none; - outline: none; - text-decoration: none; - - &:hover, - &:focus, - &:active { - text-decoration: none; - outline: none; - box-shadow: none; - } -`; - -const TradeButton = styled(PoolsButton)` - color: ${({ theme }) => theme.primary}; - width: 100%; - - &:hover { - color: ${({ theme }) => theme.bg2}; - background-color: ${({ theme }) => theme.primary}; - } -`; - -const StyledLink = styled(Link)` - text-decoration: none; - &:hover, - &:focus, - &:active { - text-decoration: none; - outline: none; - box-shadow: none; - } -`; - -const RowTitle = styled.span` - font-size: 1rem; - font-weight: 400; - line-height: 19px; - color: ${({ theme }) => theme.text1}; - - @media (max-width: 991px) { - font-size: 0.875rem; - } -`; - -const CellText = styled.span` - font-weight: 700; - font-size: 0.875rem; - color: ${({ theme }) => theme.text1}; - white-space: nowrap; - - @media (max-width: 991px) { - font-weight: 400; - } -`; - -const NewButton = styled(Link)` - background-color: ${({ theme }) => theme.primaryLight}; - height: 56px; - padding: 6px 20px; - border-radius: 18px; - color: ${({ theme }) => theme.primary}; - display: flex; - align-items: center; - transition: 0.3s ease all; - justify-content: center; - - :hover, - :focus, - :active { - background-color: ${({ theme }) => theme.primary}; - color: ${({ theme }) => theme.text1}; - text-decoration: none; - outline: none; - } -`; - -const GradientButton = styled(Link)` - height: 56px; - padding: 6px 20px; - border-radius: 18px; - display: flex; - align-items: center; - justify-content: center; - margin-bottom: 20px; - - :hover, - :focus, - :active { - text-decoration: none; - outline: none; - } -`; - -const StatusText = styled.span` - @media (max-width: 991px) { - font-size: 0.875rem; - } -`; +import "../../components/UI/Button/style.scss"; +import * as Styled from "./styleds"; const Proposals = (props) => { const { library } = useActiveWeb3React(); @@ -174,7 +33,7 @@ const Proposals = (props) => { if (spaces.hasOwnProperty(id)) { dispatch(fetchProposals(id)); } else { - props.history.push("/tools/governance"); + props.history.push("/governance"); } } }, [spaces, id, props.history, dispatch]); @@ -214,7 +73,7 @@ const Proposals = (props) => { const rowEvents = { onClick: (e, row) => { - props.history.push(`/tools/governance/${id}/proposal/${row[0]}`); + props.history.push(`/governance/${id}/proposal/${row[0]}`); }, }; @@ -222,7 +81,9 @@ const Proposals = (props) => { { dataField: "asset", text: t("description"), - formatter: (cellContent, row, rowIndex) => {shorten(row[1].msg.payload.name, "name")}, + formatter: (cellContent, row, rowIndex) => ( + {shorten(row[1].msg.payload.name, "name")} + ), }, { dataField: "status", @@ -241,9 +102,9 @@ const Proposals = (props) => { {state.title} - + {state.title} - + ); }, @@ -252,14 +113,20 @@ const Proposals = (props) => { dataField: "start", text: t("startDate"), formatter(cellContent, row) { - return {moment(row[1].msg.payload.start * 1e3).format("YYYY/MM/DD HH:mm")}; + return ( + + {moment(row[1].msg.payload.start * 1e3).format("YYYY/MM/DD HH:mm")} + + ); }, }, { dataField: "end", text: t("endDate"), formatter(cellContent, row) { - return {moment(row[1].msg.payload.end * 1e3).format("YYYY/MM/DD HH:mm")}; + return ( + {moment(row[1].msg.payload.end * 1e3).format("YYYY/MM/DD HH:mm")} + ); }, }, { @@ -267,9 +134,9 @@ const Proposals = (props) => { text: t("author"), formatter(cellContent, row) { return ( - + {row[1].address.slice(0, 6)}...{row[1].address.slice(-4)} - + ); }, }, @@ -281,9 +148,9 @@ const Proposals = (props) => { formatter(cellContent, row) { return (
    - - {t("viewMore")} - + + {t("viewMore")} +
    ); }, @@ -291,22 +158,25 @@ const Proposals = (props) => { }; return ( - +
    - -
    - {t("governance.proposals")} - + + + {t("governance.proposals")} + {t("createNew")} - - + {t("createNew")} - -
    + +
    {governanceLoading ? (
    @@ -314,17 +184,19 @@ const Proposals = (props) => {
    ) : ( <> - + + + theme.text1}; + margin: 0; + + @media (max-width: 767px) { + margin-bottom: 30px; + } +`; + +export const PoolsButton = styled.button` + border-radius: 12px; + background-color: ${({ theme }) => theme.bg1}; + padding: 6px 20px; + max-height: 40px; + min-height: 40px; + display: flex; + align-items: center; + justify-content: center; + white-space: nowrap; + font-size: 1rem; + font-family: inherit; + font-weight: 500; + border: none; + outline: none; + text-decoration: none; + + &:hover, + &:focus, + &:active { + text-decoration: none; + outline: none; + box-shadow: none; + } +`; + +export const TradeButton = styled(PoolsButton)` + color: ${({ theme }) => theme.primary}; + width: 100%; + + &:hover { + color: ${({ theme }) => theme.bg1}; + background-color: ${({ theme }) => theme.primary}; + } +`; + +export const StyledLink = styled(Link)` + text-decoration: none; + &:hover, + &:focus, + &:active { + text-decoration: none; + outline: none; + box-shadow: none; + } +`; + +export const RowTitle = styled.span` + font-size: 1rem; + font-weight: 400; + line-height: 19px; + color: ${({ theme }) => theme.text1}; + + @media (max-width: 991px) { + font-size: 0.875rem; + } +`; + +export const CellText = styled.span` + font-weight: 700; + font-size: 0.875rem; + color: ${({ theme }) => theme.text1}; + white-space: nowrap; + + @media (max-width: 991px) { + font-weight: 400; + } +`; + +export const NewButton = styled(Link)` + background-color: ${({ theme }) => theme.primaryLight}; + height: 56px; + padding: 6px 20px; + border-radius: 18px; + color: ${({ theme }) => theme.primary}; + display: flex; + align-items: center; + transition: 0.3s ease all; + justify-content: center; + + :hover, + :focus, + :active { + background-color: ${({ theme }) => theme.primary}; + color: ${({ theme }) => theme.text1}; + text-decoration: none; + outline: none; + } +`; + +export const GradientButton = styled(Link)` + height: 56px; + padding: 6px 20px; + border-radius: 18px; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 20px; + + :hover, + :focus, + :active { + text-decoration: none; + outline: none; + } +`; + +export const StatusText = styled.span` + @media (max-width: 991px) { + font-size: 0.875rem; + } +`; + +export const ProposalsTableWrap = styled.div` + table { + + th { + background-color: ${({ theme }) => theme.bg5}; + color: ${({ theme }) => theme.text1}; + font-size: 0.875rem; + font-weight: 500; + text-overflow: ellipsis; + white-space: nowrap; + padding: 1.25rem 0.75rem; + min-height: 56px; + border: 0; + + &:focus { + outline: none; + } + + &:first-child { + border-top-left-radius: 18px; + border-bottom-left-radius: 18px; + padding: 1.25rem 1.375rem; + } + + &:last-child { + border-top-right-radius: 18px; + border-bottom-right-radius: 18px; + padding: 1.25rem 1.375rem 1.25rem 1.25rem; + } + } + + th, + td { + border-top: 0; + vertical-align: middle !important; + + &:first-child { + padding: 1.25rem 1.375rem; + } + + &:last-child { + padding: 1.25rem 1.375rem 1.25rem 1.25rem; + } + } + + td { + border-bottom: 1px solid ${({ theme }) => theme.borderColor}; + cursor: pointer; + } + + tr:last-child td { + border-bottom: 0; + } + } +`; diff --git a/src/pages/Spot/components/OrderHistory/index.js b/src/pages/Spot/components/OrderHistory/index.js index 4333c4af..0274cae3 100644 --- a/src/pages/Spot/components/OrderHistory/index.js +++ b/src/pages/Spot/components/OrderHistory/index.js @@ -51,7 +51,7 @@ const CardTitle = styled.h4` `; const Content = styled.div` - background-color: ${({ theme }) => theme.bg3}; + background-color: ${({ theme }) => theme.bg1}; border-radius: 18px; display: flex; flex-direction: column; diff --git a/src/pages/Spot/index.js b/src/pages/Spot/index.js index 3a2b8ee4..8f185e55 100644 --- a/src/pages/Spot/index.js +++ b/src/pages/Spot/index.js @@ -3,9 +3,7 @@ import { useEffect, useState } from "react"; import styled from "styled-components"; import Page from "../../components/Page"; -import CheckBalance from "../../components/CheckBalance"; import StyledCard, { ResponsiveCard } from "../../components/Card"; -import marketTokens from "../../constants/marketTokens.json"; import MarketStats from "./components/MarketStats"; import MarketDetails from "./components/MarketDetails"; import Orderbook from "./components/Orderbook"; @@ -92,7 +90,7 @@ const Spot = (props) => { }, [dispatch]); return ( - + diff --git a/src/pages/Swap/components/SwapHeader/index.js b/src/pages/Swap/components/SwapHeader/index.js deleted file mode 100644 index cca7e525..00000000 --- a/src/pages/Swap/components/SwapHeader/index.js +++ /dev/null @@ -1,116 +0,0 @@ -import { useContext, useRef } from "react"; -import styled, { ThemeContext } from "styled-components"; -import { Settings } from "react-feather"; - -import { useModalOpen, useToggleUniswapSettingsMenu } from "../../../../state/application/hooks"; -import { ApplicationModal } from "../../../../state/application/actions"; -import { useUserSlippageTolerance, useUserTransactionTTL } from "../../../../state/user/hooks"; -import { useOnClickOutside } from "../../../../hooks/useOnClickOutside"; -import { AutoColumn } from "../../../../components/Column"; -import { Text } from "rebass"; -import TransactionSettings from "../../../../components/TransactionSettings"; - -const StyledMenuIcon = styled(Settings)` - height: 20px; - width: 20px; - - > * { - stroke: ${({ theme }) => theme.text2}; - } - - :hover { - opacity: 0.7; - } -`; - -const StyledMenu = styled.div` - margin-left: 0.5rem; - display: flex; - justify-content: center; - align-items: center; - position: relative; - border: none; - text-align: left; -`; - -const MenuFlyout = styled.span` - min-width: 20.125rem; - background-color: ${({ theme }) => theme.bg2}; - box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), - 0px 24px 32px rgba(0, 0, 0, 0.01); - border: 1px solid ${({ theme }) => theme.text4}; - border-radius: 12px; - display: flex; - flex-direction: column; - font-size: 1rem; - position: absolute; - top: 3rem; - right: 0rem; - z-index: 100; - - ${({ theme }) => theme.mediaWidth.upToMedium` - min-width: 18.125rem; - `}; -`; - -const StyledMenuButton = styled.button` - position: relative; - width: 100%; - height: 100%; - border: none; - background-color: transparent; - margin: 0; - padding: 0; - height: 35px; - - padding: 0.15rem 0.5rem; - border-radius: 0.5rem; - - :hover, - :focus { - cursor: pointer; - outline: none; - } - - svg { - margin-top: 2px; - } -`; - -const SwapHeader = (props) => { - const node = useRef(); - const open = useModalOpen(ApplicationModal.UNISWAPSETTINGS); - const toggle = useToggleUniswapSettingsMenu(); - - const theme = useContext(ThemeContext); - const [userSlippageTolerance, setUserslippageTolerance] = useUserSlippageTolerance(); - - const [ttl, setTtl] = useUserTransactionTTL(); - - useOnClickOutside(node, open ? toggle : undefined); - - return ( - - - - - {open && ( - - - - Transaction Settings - - - - - )} - - ); -}; - -export default SwapHeader; diff --git a/src/pages/Swap/index.tsx b/src/pages/Swap/index.tsx index 6293080b..2c8da0e9 100644 --- a/src/pages/Swap/index.tsx +++ b/src/pages/Swap/index.tsx @@ -1,626 +1,15 @@ -import { CurrencyAmount, JSBI, Token, Trade } from "@uniswap/sdk"; -import React, { useCallback, useEffect, useMemo, useState } from "react"; -import { Row, Col, Button } from "react-bootstrap"; -import styled from "styled-components"; -import SVG from "react-inlinesvg"; -import { ArrowDown } from "react-feather"; - +import { Row, Col } from "react-bootstrap"; import Page from "../../components/Page"; -import ConfirmSwapModal from "../../components/swap/ConfirmSwapModal"; -import CurrencyInputPanel from "../../components/CurrencyInputPanel"; -import AdvancedSwapDetailsDropdown from "../../components/swap/AdvancedSwapDetailsDropdown"; -import confirmPriceImpactWithoutFee from "../../components/swap/confirmPriceImpactWithoutFee"; -import { Wrapper, ArrowWrapper } from "../../components/swap/styleds"; -import Loader from "../../components/Loader"; -import TokenWarningModal from "../../components/TokenWarningModal"; -import { ResponsiveCard } from "../../components/Card"; - -import { useActiveWeb3React } from "../../hooks"; -import { useCurrency } from "../../hooks/Tokens"; -import { ApprovalState, useApproveCallbackFromTrade } from "../../hooks/useApproveCallback"; -import useENSAddress from "../../hooks/useENSAddress"; -import { useSwapCallback } from "../../hooks/useSwapCallback"; -import useToggledVersion, { Version } from "../../hooks/useToggledVersion"; -import useWrapCallback, { WrapType } from "../../hooks/useWrapCallback"; -import { useWalletModalToggle } from "../../state/application/hooks"; -import { Field } from "../../state/swap/actions"; -import { - useDefaultsFromURLSearch, - useDerivedSwapInfo, - useSwapActionHandlers, - useSwapState, -} from "../../state/swap/hooks"; -import { useExpertModeManager, useUserSlippageTolerance } from "../../state/user/hooks"; -import { maxAmountSpend } from "../../utils/maxAmountSpend"; -import { computeTradePriceBreakdown, warningSeverity } from "../../utils/prices"; -import { ClickableText } from "../../components/ExternalLink"; -import TradePrice from "../../components/swap/TradePrice"; -import { LinkStyledButton } from "../../theme"; -import AddressInputPanel from "../../components/AddressInputPanel"; -import SwapHeader from "./components/SwapHeader"; -import { useTranslation } from "react-i18next"; -import useTheme from "../../hooks/useTheme"; - -const StyledRow = styled(Row)` - margin-top: 20px; -`; - -const CustomCard = styled(ResponsiveCard)``; - -const CardTitle = styled.h3` - font-weight: 700; - font-size: 1.25rem; -`; - -const HeadCol = styled(Col)` - margin-bottom: 33px; -`; - -const ApproveArrow = styled.div` - align-self: center; - margin: 24px 0 20px; - - @media (min-width: 991px) { - margin: 0 43px; - } -`; - -const StyledClickableText = styled(ClickableText)` - color: ${({ theme }) => theme.text1}; -`; - -const PriceCol = styled(Col)` - margin-top: ${({ noMargin }) => noMargin ? '0' : '-1rem'}; - margin-bottom: 1rem; - - @media (min-width: 768px) { - margin-top: ${({ noMargin }) => noMargin ? '0' : '-1.75rem'}; - margin-bottom: 1.75rem; - } -`; - -const SwitchCol = styled(Col)` - margin-bottom: 1.25rem; -`; - -const Swap = (props: any) => { - const loadedUrlParams = useDefaultsFromURLSearch(); - const { t } = useTranslation(); - const theme = useTheme(); - - // token warning stuff - const [loadedInputCurrency, loadedOutputCurrency] = [ - useCurrency(loadedUrlParams?.inputCurrencyId), - useCurrency(loadedUrlParams?.outputCurrencyId), - ]; - const [dismissTokenWarning, setDismissTokenWarning] = useState(false); - const urlLoadedTokens: Token[] = useMemo( - () => [loadedInputCurrency, loadedOutputCurrency]?.filter((c): c is Token => c instanceof Token) ?? [], - [loadedInputCurrency, loadedOutputCurrency] - ); - const handleConfirmTokenWarning = useCallback(() => { - setDismissTokenWarning(true); - }, []); - - const { account } = useActiveWeb3React(); - - const [showInverted, setShowInverted] = useState(false); - - // toggle wallet when disconnected - const toggleWalletModal = useWalletModalToggle(); - - // for expert mode - const [isExpertMode] = useExpertModeManager(); - - // get custom setting values for user - const [allowedSlippage] = useUserSlippageTolerance(); - - // swap state - const { independentField, typedValue, recipient } = useSwapState(); - const { - v1Trade, - v2Trade, - currencyBalances, - parsedAmount, - currencies, - inputError: swapInputError, - } = useDerivedSwapInfo(); - const { wrapType, execute: onWrap, inputError: wrapInputError } = useWrapCallback( - currencies[Field.INPUT], - currencies[Field.OUTPUT], - typedValue - ); - const showWrap: boolean = wrapType !== WrapType.NOT_APPLICABLE; - const { address: recipientAddress } = useENSAddress(recipient); - const toggledVersion = useToggledVersion(); - const tradesByVersion = { - [Version.v1]: v1Trade, - [Version.v2]: v2Trade, - }; - const trade = showWrap ? undefined : tradesByVersion[toggledVersion]; - - const parsedAmounts = showWrap - ? { - [Field.INPUT]: parsedAmount, - [Field.OUTPUT]: parsedAmount, - } - : { - [Field.INPUT]: independentField === Field.INPUT ? parsedAmount : trade?.inputAmount, - [Field.OUTPUT]: independentField === Field.OUTPUT ? parsedAmount : trade?.outputAmount, - }; - - const { onCurrencySelection, onUserInput, onSwitchTokens, onChangeRecipient } = useSwapActionHandlers(); - const isValid = !swapInputError; - const dependentField: Field = independentField === Field.INPUT ? Field.OUTPUT : Field.INPUT; - - const handleTypeInput = useCallback( - (value: string) => { - onUserInput(Field.INPUT, value); - }, - [onUserInput] - ); - const handleTypeOutput = useCallback( - (value: string) => { - onUserInput(Field.OUTPUT, value); - }, - [onUserInput] - ); - - // modal and loading - const [{ showConfirm, tradeToConfirm, swapErrorMessage, attemptingTxn, txHash }, setSwapState] = useState<{ - showConfirm: boolean; - tradeToConfirm: Trade | undefined; - attemptingTxn: boolean; - swapErrorMessage: string | undefined; - txHash: string | undefined; - }>({ - showConfirm: false, - tradeToConfirm: undefined, - attemptingTxn: false, - swapErrorMessage: undefined, - txHash: undefined, - }); - - const formattedAmounts = { - [independentField]: typedValue, - [dependentField]: showWrap - ? parsedAmounts[independentField]?.toExact() ?? "" - : parsedAmounts[dependentField]?.toSignificant(6) ?? "", - }; - - const route = trade?.route; - const userHasSpecifiedInputOutput = Boolean( - currencies[Field.INPUT] && - currencies[Field.OUTPUT] && - parsedAmounts[independentField]?.greaterThan(JSBI.BigInt(0)) - ); - const noRoute = !route; - - // check whether the user has approved the router on the input token - const [approval, approveCallback] = useApproveCallbackFromTrade(trade, allowedSlippage); - - // check if user has gone through approval process, used to show two step buttons, reset on token change - const [approvalSubmitted, setApprovalSubmitted] = useState(false); - - // mark when a user has submitted an approval, reset onTokenSelection for input field - useEffect(() => { - if (approval === ApprovalState.PENDING) { - setApprovalSubmitted(true); - } - }, [approval, approvalSubmitted]); - - const maxAmountInput: CurrencyAmount | undefined = maxAmountSpend(currencyBalances[Field.INPUT]); - const atMaxAmountInput = Boolean(maxAmountInput && parsedAmounts[Field.INPUT]?.equalTo(maxAmountInput)); - - // the callback to execute the swap - const { callback: swapCallback, error: swapCallbackError } = useSwapCallback(trade, allowedSlippage, recipient); - - const { priceImpactWithoutFee } = computeTradePriceBreakdown(trade); - - const handleSwap = useCallback(() => { - if (priceImpactWithoutFee && !confirmPriceImpactWithoutFee(priceImpactWithoutFee)) { - return; - } - if (!swapCallback) { - return; - } - setSwapState({ - attemptingTxn: true, - tradeToConfirm, - showConfirm, - swapErrorMessage: undefined, - txHash: undefined, - }); - swapCallback() - .then((hash) => { - setSwapState({ - attemptingTxn: false, - tradeToConfirm, - showConfirm, - swapErrorMessage: undefined, - txHash: hash, - }); - }) - .catch((error) => { - setSwapState({ - attemptingTxn: false, - tradeToConfirm, - showConfirm, - swapErrorMessage: error.message, - txHash: undefined, - }); - }); - }, [tradeToConfirm, account, priceImpactWithoutFee, recipient, recipientAddress, showConfirm, swapCallback, trade]); - - // warnings on slippage - const priceImpactSeverity = warningSeverity(priceImpactWithoutFee); - - // show approve flow when: no error on inputs, not approved or pending, or approved in current session - // never show if price impact is above threshold in non expert mode - const showApproveFlow = - !swapInputError && - (approval === ApprovalState.NOT_APPROVED || - approval === ApprovalState.PENDING || - (approvalSubmitted && approval === ApprovalState.APPROVED)) && - !(priceImpactSeverity > 3 && !isExpertMode); - - const handleConfirmDismiss = useCallback(() => { - setSwapState({ showConfirm: false, tradeToConfirm, attemptingTxn, swapErrorMessage, txHash }); - // if there was a tx hash, we want to clear the input - if (txHash) { - onUserInput(Field.INPUT, ""); - } - }, [attemptingTxn, onUserInput, swapErrorMessage, tradeToConfirm, txHash]); - - const handleAcceptChanges = useCallback(() => { - setSwapState({ tradeToConfirm: trade, swapErrorMessage, txHash, attemptingTxn, showConfirm }); - }, [attemptingTxn, showConfirm, swapErrorMessage, trade, txHash]); - - const handleInputSelect = useCallback( - (inputCurrency) => { - setApprovalSubmitted(false); // reset 2 step UI for approvals - onCurrencySelection(Field.INPUT, inputCurrency); - }, - [onCurrencySelection] - ); - - const handleMaxInput = useCallback(() => { - maxAmountInput && onUserInput(Field.INPUT, maxAmountInput.toExact()); - }, [maxAmountInput, onUserInput]); - - const handleOutputSelect = useCallback((outputCurrency) => onCurrencySelection(Field.OUTPUT, outputCurrency), [ - onCurrencySelection, - ]); +import Uniswap from "../../components/Uniswap"; +const Swap = () => { return ( - - -
    - 0 && !dismissTokenWarning} - tokens={urlLoadedTokens} - onConfirm={handleConfirmTokenWarning} - /> - - - - - - {t("convert")} - - - - - - - - - { - setApprovalSubmitted(false); // reset 2 step UI for approvals - onSwitchTokens(); - }} - color={theme.text2} - /> - - {recipient === null && ( - onChangeRecipient("")} - > - + {t("addSend")} ({t("optional")}) - - )} - - - - - {recipient !== null && ( - <> - - - { - setApprovalSubmitted(false); // reset 2 step UI for approvals - onSwitchTokens(); - }} - color={theme.text2} - /> - - onChangeRecipient(null)} - > - - {t("removeSend")} - - - - - - - )} - {showWrap ? null : ( - <> - {Boolean(trade) && ( - - - {t("price")} - - - - )} - - )} - {trade && } - - {!account ? ( - - ) : showWrap ? ( - - ) : noRoute && userHasSpecifiedInputOutput ? ( - - ) : showApproveFlow ? ( - - - - - - - - - - - - - ) : ( - - )} - - - - {trade && ( - - - {t("viewPairAnalytics")} ↗ - - - )} - - - + + + + - + ); }; diff --git a/src/pages/TokenSets/index.js b/src/pages/TokenSets/index.js deleted file mode 100644 index 323ef217..00000000 --- a/src/pages/TokenSets/index.js +++ /dev/null @@ -1,140 +0,0 @@ -import styled from "styled-components"; -import { Row, Col, Tab, Nav } from "react-bootstrap"; - -import Page from "../../components/Page"; -import { ResponsiveCard } from "../../components/Card"; -import { useState } from "react"; -import TokenSetTab from "./TokenSetTab"; -import { useTranslation } from "react-i18next"; - -const Card = styled(ResponsiveCard)` - & > .card-body { - padding: 40px 30px 34px; - } -`; - -const HeaderCol = styled(Col)` - margin: -10px 0 0; -`; - -const CustomNavLink = styled(Nav.Link)` - border-radius: 18px !important; - color: ${({ theme }) => theme.primary}; - background-color: ${({ theme }) => theme.primaryLight}; - white-space: nowrap; - padding: 14px 24px; - min-height: 56px; - font-weight: 500; - display: flex; - align-items: center; - justify-content: center; - - @media (max-width: 767px) { - padding: 6px 15px; - font-size: 1rem; - min-height: 32px; - border-radius: 12px !important; - } - - &:hover { - color: ${({ theme }) => theme.primary}; - } - - &.active { - color: ${({ theme }) => theme.text1}; - background-color: ${({ theme }) => theme.primary}; - } -`; - -const CustomNav = styled(Nav)` - margin-left: -30px !important; - margin-right: -30px !important; - overflow: auto; - - @media (min-width: 768px) { - margin-left: -10px !important; - margin-right: -10px !important; - } -`; - -const CustomNavItem = styled(Nav.Item)` - flex-grow: initial !important; - - padding: 0 10px 10px; - - @media (max-width: 767px) { - padding: 0 5px 10px; - } - - &:first-child { - @media (max-width: 767px) { - padding-left: 30px; - } - } - &:last-child { - @media (max-width: 767px) { - padding-right: 30px; - } - } -`; - -const StyledCard = styled(Card)` - margin-top: 40px; - - @media (max-width: 767px) { - margin-top: 20px; - } -`; - -const TokenSets = (props) => { - const [activeKey, setActiveKey] = useState("portfolios"); - const { t } = useTranslation(); - - return ( - - - setActiveKey(k)}> - - - - - {t("tokensets.portfolios")} - - - - {t("tokensets.rebalancingSets")} - - - - - - - - - - - - - - - - - - - - ); -}; - -export default TokenSets; diff --git a/src/pages/TokenSets/index.tsx b/src/pages/TokenSets/index.tsx new file mode 100644 index 00000000..0d848b4e --- /dev/null +++ b/src/pages/TokenSets/index.tsx @@ -0,0 +1,15 @@ +import Page from "../../components/Page"; +import { ResponsiveCard } from "../../components/Card"; +import TokenSetsTabs from "../../components/TokenSetsTabs"; + +const TokenSets = () => { + return ( + + + + + + ); +}; + +export default TokenSets; diff --git a/src/pages/TokenSets/styles.scss b/src/pages/TokenSets/styles.scss deleted file mode 100644 index a40c3830..00000000 --- a/src/pages/TokenSets/styles.scss +++ /dev/null @@ -1,39 +0,0 @@ -.tokensets__table { - position: relative; - width: 100%; - border-collapse: collapse; - color: white; -} - -.table.tokensets__table thead th { - border-bottom: 0; - border-top: 0; - color: #202020; - font-weight: 400; - padding-bottom: 1.5rem; - padding-top: 1.5rem; - font-size: 0.875rem; - - .dark-mode & { - color: white; - } - - &:focus { - outline: none; - } -} - -.table.tokensets__table tbody td { - vertical-align: middle; - border: 1px solid #caccd2; - border-left-width: 0; - border-right-width: 0; - color: #202020; - cursor: pointer; - padding: 1rem 0.75rem; - - .dark-mode & { - color: white; - border-color: rgba(255, 255, 255, 0.2); - } -} diff --git a/src/pages/Vote/ProposalContent.tsx b/src/pages/Vote/ProposalContent.tsx new file mode 100644 index 00000000..ae394f19 --- /dev/null +++ b/src/pages/Vote/ProposalContent.tsx @@ -0,0 +1,43 @@ +import { useMemo } from "react"; +import { Remarkable } from "remarkable"; +import dompurify from "dompurify"; +import Card from "../../components/Card"; +import * as Styled from "./styleds"; + +const md = new Remarkable(); + +export type ProposalContentProps = { + proposal: any; + status: any; +}; + +export default function ProposalContent({ proposal, status }: ProposalContentProps) { + const voteBody = useMemo(() => { + return dompurify.sanitize(proposal?.msg?.payload?.body); + }, [proposal?.msg?.payload?.body]); + + return ( + + + {proposal.msg.payload.name} + {status && ( + + {status.title} + + )} + + + } + > + + + ); +} diff --git a/src/pages/Vote/ProposalVotes.tsx b/src/pages/Vote/ProposalVotes.tsx new file mode 100644 index 00000000..87cfc18e --- /dev/null +++ b/src/pages/Vote/ProposalVotes.tsx @@ -0,0 +1,94 @@ +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { Button } from "react-bootstrap"; +import Loading from "../../components/Loading"; +import VoteItem from "../../components/VoteItem"; +import * as Styled from "./styleds"; + +export type ProposalVotesProps = { + votes: Array; + result: any; + proposal: any; + space: any; +}; + +export default function ProposalVotes({ votes, result, proposal, space }: ProposalVotesProps) { + const { t } = useTranslation(); + const [showMore, setShowMore] = useState(false); + + const onShowMore = () => setShowMore(true); + + return ( + + + {t("governance.votes")} + {votes && ( + + {Object.keys(votes).length} + + )} + + + } + > + + + + + + + + + + {result + ? showMore || (result.hasOwnProperty("votes") && Object.keys(result.votes || {}).length < 10) + ? Object.values(result.votes).map((vote: any, index: number) => { + return ( + + ); + }) + : result.hasOwnProperty("votes") && + Object.values(result.votes) + .slice(0, 10) + .map((vote: any, index: number) => { + return ( + + ); + }) + : null} + + + {votes ? ( + <> + {!showMore && Object.keys(votes || {}).length > 10 ? ( + + + + ) : null} + + ) : ( +
    + +
    + )} + + ); +} diff --git a/src/pages/Vote/VoteCast.tsx b/src/pages/Vote/VoteCast.tsx new file mode 100644 index 00000000..0b6c02b9 --- /dev/null +++ b/src/pages/Vote/VoteCast.tsx @@ -0,0 +1,53 @@ +import { useTranslation } from "react-i18next"; +import { Button } from 'react-bootstrap'; +import { useWeb3React } from "@web3-react/core"; +import Card from "../../components/Card"; +import * as Styled from "./styleds"; + +export type VoteCastProps = { + proposal: any; + selected: any; + onSelectOption: (T: number) => void; + onVote: () => void; +}; + +export default function VoteCast({ proposal, selected, onSelectOption, onVote }: VoteCastProps) { + const { t } = useTranslation(); + const { account } = useWeb3React(); + + return ( + + + {t("governance.castVote")} + + + } + > +
    + {proposal.msg?.payload?.choices?.map((option: any, index: number) => { + return ( + + ); + })} + +
    +
    + ); +} diff --git a/src/pages/Vote/VoteInformation.tsx b/src/pages/Vote/VoteInformation.tsx new file mode 100644 index 00000000..a042fbea --- /dev/null +++ b/src/pages/Vote/VoteInformation.tsx @@ -0,0 +1,94 @@ +import { useTranslation } from "react-i18next"; +import moment from "moment"; +import Card from "../../components/Card"; +import ListItem from "../../components/ListItem"; +import { ModifiedJazzicon } from "../../components/VoteItem"; +import VoteLogo from "../../components/VoteLogo"; +import * as Styled from "./styleds"; + +export type VoteInformationProps = { + proposal: any; + space: any; + id: any; +}; + +export default function VoteInformation({ proposal, space, id }: VoteInformationProps) { + const { t } = useTranslation(); + + return ( + + + {t("information")} + + + } + > +
    + +
    + {space.strategies.map((s: any, index: number) => { + return ( +
    + + {s.params.symbol} +
    + ); + })} +
    +
    + + +
    + + + {proposal.address.slice(0, 6)}... + {proposal.address.slice(-4)} + +
    +
    + + + + #{proposal.authorIpfsHash.slice(0, 7)} + + + + + {proposal && moment(proposal.msg.payload.start * 1e3).format("YYYY/MM/DD hh:mm")} + + + + + {proposal && moment(proposal.msg.payload.end * 1e3).format("YYYY/MM/DD hh:mm")} + + + + {proposal.address.slice(0, 6)} + + + + {proposal.msg.payload.snapshot} + + +
    +
    + ); +} diff --git a/src/pages/Vote/VoteResults.tsx b/src/pages/Vote/VoteResults.tsx new file mode 100644 index 00000000..578bfa5b --- /dev/null +++ b/src/pages/Vote/VoteResults.tsx @@ -0,0 +1,69 @@ +import { useTranslation } from "react-i18next"; +import { shorten } from "../../state/governance/hooks"; +import { _numeral } from "../../lib/utils"; +import Card from "../../components/Card"; +import * as Styled from "./styleds"; + +export type VoteResultsProps = { + result: VoteResult; + choices: Array; + symbol: string; +}; + +export type VoteResult = { + totalBalances: any; + totalVotesBalances: any; +}; + +export default function VoteResults({ result, choices, symbol }: VoteResultsProps) { + const { t } = useTranslation(); + + return ( + + + {t("results")} + + + } + > +
    + {choices.map((choice, i) => { + const progress = !result.totalVotesBalances + ? 0 + : (100 / result.totalVotesBalances) * result.totalBalances?.[i]; + + return ( + +
    + {shorten(choice, "choice")} + + + {_numeral(result.totalBalances?.[i])} + {shorten(symbol, "symbol")} + + + + {!result.totalVotesBalances ? 0 : `${progress.toFixed(2)}%`} + +
    + + {/* @ts-ignore */} + + +
    + ); + })} +
    +
    + ); +} diff --git a/src/pages/Vote/index.js b/src/pages/Vote/index.js index 429bfd7b..1d842ac5 100644 --- a/src/pages/Vote/index.js +++ b/src/pages/Vote/index.js @@ -1,221 +1,29 @@ -import React, {useEffect, useState, useCallback, useMemo} from "react"; +import React, { useEffect, useState, useCallback } from "react"; import { Row, Col, Button } from "react-bootstrap"; +import { useTranslation } from "react-i18next"; import { useDispatch, useSelector } from "react-redux"; -import { _numeral, formatProposal, getScores } from "../../lib/utils"; -import styled from "styled-components"; +import { toast } from "react-hot-toast"; +import { useWeb3React } from "@web3-react/core"; -import { Modal } from "../../components/Modal/bootstrap"; -import ListItem from "../../components/ListItem"; +import { formatProposal, getScores } from "../../lib/utils"; import Governance from "../../http/governance"; -import Card from "../../components/Card"; +import { useWalletModalToggle } from "../../state/application/hooks"; import { fetchProposals, fetchSpaces } from "../../state/governance/actions"; -import VoteItem, { ModifiedJazzicon } from "../../components/VoteItem"; -import VoteLogo from "../../components/VoteLogo"; -import moment from "moment"; +import { Modal } from "../../components/Modal/bootstrap"; +import Card from "../../components/Card"; import Loading from "../../components/Loading"; -import { useWeb3React } from "@web3-react/core"; -import { shorten } from "../../state/governance/hooks"; -import { Remarkable } from "remarkable"; import Page from "../../components/Page"; -import GradientButton from "../../components/UI/Button"; -import { toast } from "react-hot-toast"; -import { useWalletModalToggle } from "../../state/application/hooks"; -import { useTranslation } from "react-i18next"; -import dompurify from "dompurify"; - -const md = new Remarkable(); - -const StyledLinkButton = styled.button` - font-weight: 500; - font-size: 1rem; - - @media (min-width: 768px) { - font-weight: 400; - } -`; - -const VoteCard = styled(Card)` - @media (max-width: 767px) { - .card-body { - padding-top: 0; - } - } -`; - -const CardHeader = styled.div` - display: flex; - flex-direction: column; - margin: -5px; - - @media (min-width: 768px) { - margin: -10px 0 -18px; - height: ${({ withoutSubtitle }) => (withoutSubtitle ? "70px" : "auto")}; - } -`; - -const BoxTitle = styled.h4` - font-weight: 700; - font-size: 0.875rem; - line-height: 1.5rem; - color: ${({ theme }) => theme.text1}; - margin: 0 auto 0 0; - padding-right: 0.75rem; - - @media (min-width: 768px) { - padding-right: 0; - font-size: 1.25rem; - margin: 0 1.25rem 0 0; - } -`; - -const BoxSubTitle = styled.span` - margin-bottom: 0.5rem; - font-size: 0.875rem; - font-weight: 400; - color: ${({ theme }) => theme.text1}; - display: none; - - @media (min-width: 768px) { - display: block; - } -`; - -const BoxHeader = styled.div` - min-height: 40px; - display: flex; - align-items: center; - - & .label { - @media (max-width: 767px) { - height: 30px; - padding: 5px 17px; - border-radius: 12px; - font-size: 0.875rem; - } - } -`; - -const VoteContent = styled.div` - line-height: 19px; - font-size: 1rem !important; - color: ${({ theme }) => theme.text1}; - font-weight: 400; - - & p { - margin: 0; - font-weight: 400; - font-size: 0.875rem; - - @media (min-width: 768px) { - font-size: 1rem; - } - } - - & h1, - & h2, - & h3, - & h4, - & h5, - & h6 { - font-size: 1.25rem; - margin-bottom: 0; - font-weight: 500; - - @media (min-width: 768px) { - font-size: 1.875rem; - } - } -`; - -const TokenValue = styled.span` - font-size: 0.875rem; - font-weight: 500; - padding-left: 11px; - color: ${({ theme }) => theme.text1}; - - @media (min-width: 768px) { - font-size: 1rem; - font-weight: 700; - } -`; - -const AuthorLink = styled.a` - color: ${({ theme }) => theme.primary}; - font-weight: 700; - font-size: 0.875rem; - - @media (min-width: 768px) { - font-size: 1rem; - } - - &:focus, - &:active { - outline: none; - } -`; -const InfoText = styled.span` - font-size: 0.875rem; - font-weight: ${({ fontWeight }) => fontWeight || 700}; - color: ${({ theme }) => theme.text1}; - - @media (min-width: 768px) { - font-size: 1rem; - font-weight: 700; - } -`; - -const ResultRow = styled.div` - display: flex; - flex-direction: column; - - &:not(:last-child) { - margin-bottom: 40px; - } -`; - -const ResultTitle = styled.span` - font-size: 0.875rem; - font-weight: 400; - margin: 0; - color: ${({ theme }) => theme.text1}; - - @media (min-width: 768px) { - font-weight: 500; - } -`; - -const ResultProgress = styled.div` - background-color: ${({ theme }) => theme.primaryLight}; - height: 5px; - border: none; - width: 100%; - border-radius: 20px; -`; - -const ResultProgressBar = styled.div` - background-color: ${({ theme }) => theme.primary}; - border-radius: 20px; -`; - -const Table = styled.table` - border-spacing: 0 10px; - border-collapse: separate; -`; - -const Thead = styled.thead` - height: 1px; - opacity: 0; - pointer-events: none; - visibility: hidden; - display: none; -`; +import VoteCast from "./VoteCast"; +import ProposalContent from "./ProposalContent"; +import ProposalVotes from "./ProposalVotes"; +import VoteInformation from "./VoteInformation"; +import VoteResults from "./VoteResults"; const Vote = (props) => { const { account, library } = useWeb3React(); const [selectedProposal, setSelectedProposal] = useState(false); const [selectedVote, setSelectedVote] = useState(null); const [showModal, setShowModal] = useState(false); - const [showMore, setShowMore] = useState(false); const [votes, setVotes] = useState({}); const [result, setResult] = useState({}); const [status, setStatus] = useState({}); @@ -366,355 +174,65 @@ const Vote = (props) => { } }, [selectedProposal, selectedVote, account, address, api, id, library, spaces]); - const voteBody = useMemo(() => { - return dompurify.sanitize(selectedProposal.msg.payload.body); - }, [selectedProposal?.msg?.payload?.body]) - return ( - - -
    - {governanceLoading && !selectedProposal ? ( - -
    - -
    -
    - ) : ( - selectedProposal && ( - - - - - - {t("governance.proposal")} - - {selectedProposal.msg.payload.name} - {status && ( - - {status.title} - - )} - - - } - > - - - - {status.title === "Active" && ( - - - {t("governance.vote")} - - {t("governance.castVote")} - - - } - > -
    - {selectedProposal.msg.payload.choices.map((option, index) => { - return ( - - ); - })} - { - !account ? toggleWalletModal() : setShowModal(true); - }} - > - {account ? t("governance.vote") : t("wallet.connect")} - -
    -
    - - )} - - - {t("governance.proposal")} - - {t("governance.votes")} - {votes && ( - - {Object.keys(votes).length} - - )} - - - } - > -
    -
    UserVotePower
    - - - - - - - - - {result - ? showMore || - (result.hasOwnProperty("votes") && - Object.keys(result.votes || {}).length < 10) - ? Object.values(result.votes).map((vote, index) => { - return ( - - ); - }) - : result.hasOwnProperty("votes") && - Object.values(result.votes) - .slice(0, 10) - .map((vote, index) => { - return ( - - ); - }) - : null} - -
    uservotepower
    - {votes ? ( - <> - {Object.keys(votes || {}).length > 10 ? ( - setShowMore(true)} - > - {t("showMore")} - - ) : null} - - ) : ( -
    - -
    - )} -
    - - -
    + + {governanceLoading && !selectedProposal ? ( + +
    + +
    +
    + ) : ( + selectedProposal && ( + + + + + - - - - {t("information")} - - - } - > -
    - -
    - {spaces[id].strategies.map((s, index) => { - return ( -
    - - {s.params.symbol} -
    - ); - })} -
    -
    - - -
    - - - {selectedProposal && selectedProposal.address.slice(0, 6)}... - {selectedProposal && selectedProposal.address.slice(-4)} - -
    -
    - - - - #{selectedProposal && selectedProposal.authorIpfsHash.slice(0, 7)} - - - - - {selectedProposal && - moment(selectedProposal.msg.payload.start * 1e3).format( - "YYYY/MM/DD hh:mm" - )} - - - - - {selectedProposal && - moment(selectedProposal.msg.payload.end * 1e3).format( - "YYYY/MM/DD hh:mm" - )} - - - - - {selectedProposal && selectedProposal.address.slice(0, 6)} - - - - - {selectedProposal && selectedProposal.msg.payload.snapshot} - - -
    -
    - {result.results?.hasOwnProperty("totalBalances") && ( - - - {t("results")} - - - } - > -
    - {selectedProposal.msg.payload.choices.map((choice, i) => { - return ( - -
    - {shorten(choice, "choice")} - - - {_numeral(result.results.totalBalances?.[i])} - {shorten(space.symbol, "symbol")} - - - - {!result.results.totalVotesBalances - ? 0 - : `${( - (100 / - result.results.totalVotesBalances) * - result.results.totalBalances?.[i] - ).toFixed(2)}%`} - -
    - - - -
    - ); - })} -
    -
    + {status.title === "Active" && ( + + {selectedProposal && ( + { + !account ? toggleWalletModal() : setShowModal(true); + }} + /> + )} + + )} + + {votes && result && selectedProposal && ( + )}
    - ) - )} - -
    + + + {selectedProposal && ( + + )} + + {result?.results && ( + + )} + + + ) + )} + {selectedProposal && ( setShowModal(false)}> diff --git a/src/pages/Vote/styleds.tsx b/src/pages/Vote/styleds.tsx new file mode 100644 index 00000000..092be2fc --- /dev/null +++ b/src/pages/Vote/styleds.tsx @@ -0,0 +1,166 @@ +import styled from "styled-components"; +import { Table } from "react-bootstrap"; +import Card from "../../components/Card"; + +export const CustomCard = styled(Card)` + .card-body { + padding: 0 0 0.5rem 0; + } +`; + +export const BoxHeader = styled.div` + display: flex; + align-items: center; +`; + +export const BoxTitle = styled.h4` + color: ${({ theme }) => theme.text1}; + font-weight: 600; + font-size: 1rem; + line-height: 1.5; + margin: 0; + + @media (min-width: 768px) { + font-size: 1.25rem; + } +`; + +export const VoteContent = styled.div` + line-height: 1.5; + + p { + font-weight: 400; + } + + h1, + h2, + h3, + h4, + h5, + h6 { + font-size: 1.25rem; + font-weight: 600; + margin-top: 1.5rem; + + @media (min-width: 768px) { + font-size: 1.35rem; + } + } +`; + +export const TokenValue = styled.span` + font-size: 0.875rem; + font-weight: 500; + padding-left: 11px; + color: ${({ theme }) => theme.text1}; + + @media (min-width: 768px) { + font-size: 1rem; + font-weight: 700; + } +`; + +export const AuthorLink = styled.a` + color: ${({ theme }) => theme.primary}; + font-weight: 700; + font-size: 0.875rem; + + @media (min-width: 768px) { + font-size: 1rem; + } + + &:focus, + &:active { + outline: none; + } +`; + +export const InfoText = styled.span<{ fontWeight?: number }>` + font-size: 0.875rem; + font-weight: ${({ fontWeight }) => fontWeight || 700}; + color: ${({ theme }) => theme.text1}; + + @media (min-width: 768px) { + font-size: 1rem; + font-weight: 700; + } +`; + +export const ResultRow = styled.div` + display: flex; + flex-direction: column; + + &:not(:last-child) { + margin-bottom: 40px; + } +`; + +export const ResultTitle = styled.span` + font-size: 0.875rem; + font-weight: 400; + margin: 0; + color: ${({ theme }) => theme.text1}; + + @media (min-width: 768px) { + font-weight: 500; + } +`; + +export const ResultProgress = styled.div` + background-color: ${({ theme }) => theme.primaryLight}; + height: 5px; + border: none; + width: 100%; + border-radius: 20px; +`; + +export const ResultProgressBar = styled.div` + background-color: ${({ theme }) => theme.primary}; + border-radius: 20px; +`; + +export const CustomTable = styled(Table)` + margin-bottom: 0; + + td { + border: 0; + border-bottom: 1px solid ${({ theme }) => theme.borderColor}; + border-radius: 0 !important; + vertical-align: middle; + background: transparent !important; + + &:first-child, + &:last-child { + border-left: 0; + border-right: 0; + } + + &:last-child { + width: 200px; + text-align: right; + } + } + + tr:last-child td { + border-bottom: 0; + + &:first-child { + border-bottom-left-radius: 18px !important; + } + &:last-child { + border-bottom-right-radius: 18px !important; + } + } +`; + +export const Thead = styled.thead` + height: 1px; + opacity: 0; + pointer-events: none; + visibility: hidden; + display: none; +`; + +export const ShowMoreWrap = styled.div` + border-top: 1px solid ${({ theme }) => theme.borderColor}; +`; diff --git a/src/pages/Wallet/index.js b/src/pages/Wallet/index.js deleted file mode 100644 index 9d62bc3d..00000000 --- a/src/pages/Wallet/index.js +++ /dev/null @@ -1,493 +0,0 @@ -import { useDispatch, useSelector } from "react-redux"; -import { Row, Col, Tab, Nav } from "react-bootstrap"; -import Skeleton from "react-loading-skeleton"; - -import Page from "../../components/Page"; -import { ResponsiveCard } from "../../components/Card"; -import SearchIcon from "../../assets/images/search.svg"; -import React, { useEffect, useMemo, useState } from "react"; -import styled from "styled-components"; -import { WalletPageTable } from "./WalletPageTable"; -import CurrencyLogo from "../../components/CurrencyLogo"; -import CurrencyText from "../../components/CurrencyText"; -import { Link } from "react-router-dom"; -import { useActiveWeb3React } from "../../hooks"; -import NftTab from "./NftTab"; -import Loading from "../../components/Loading"; -import Web3 from "web3"; -import { Web3Wrapper } from "@0x/web3-wrapper"; -import { getContractWrappers } from "../../utils/spot/contractWrapper"; -import { ERC20TokenContract } from "@0x/contract-wrappers"; -import { UNLIMITED_ALLOWANCE_IN_BASE_UNITS } from "../../constants"; -import UnlockModal from "./UnlockModal"; -import toast from "react-hot-toast"; -import { useMemoTokenBalances } from "../../state/balances/hooks"; -import { fetchBalances, fetchTransformedBalances } from "../../state/balances/actions"; -import { useTranslation } from "react-i18next"; -import SVG from "react-inlinesvg"; -import { - InputGroupText, - InputGroup, - InputGroupPrepend, - InputGroupFormControl as FormControl, -} from "../../components/Form"; - -const CustomNav = styled(Nav)` - margin-left: -30px !important; - margin-right: -30px !important; - overflow: auto; - - @media (min-width: 768px) { - margin-left: -10px !important; - margin-right: -10px !important; - } -`; - -const CustomNavItem = styled(Nav.Item)` - flex-grow: initial !important; - - padding: 0 10px 10px; - - @media (max-width: 767px) { - padding: 0 5px 10px; - } - - &:first-child { - @media (max-width: 767px) { - padding-left: 30px; - } - } - &:last-child { - @media (max-width: 767px) { - padding-right: 30px; - } - } -`; - -const HeaderCol = styled(Col)` - margin-bottom: 20px; - - @media (min-width: 768px) { - margin-bottom: 25px; - } -`; - -const CustomInputGroup = styled(InputGroup)` - margin-bottom: 30px; -`; - -const CustomNavLink = styled(Nav.Link)` - border-radius: 18px !important; - color: ${({ theme }) => theme.primary}; - background-color: ${({ theme }) => theme.primaryLight}; - white-space: nowrap; - padding: 14px 24px; - min-height: 56px; - font-weight: 500; - display: flex; - align-items: center; - justify-content: center; - - @media (max-width: 767px) { - padding: 6px 15px; - font-size: 1rem; - min-height: 32px; - border-radius: 12px !important; - } - - &:hover { - color: ${({ theme }) => theme.primary}; - } - - &.active { - color: ${({ theme }) => theme.text1}; - background-color: ${({ theme }) => theme.primary}; - } -`; - -export const LogoContainer = styled.div` - max-width: 32px; - max-height: 32px; - height: 32px; - width: 32px; - min-width: 32px; - margin-right: 1.5rem; - - @media (max-width: 991px) { - max-width: 24px; - max-height: 24px; - height: 24px; - width: 24px; - min-width: 24px; - margin-right: 0; - margin-left: 1rem; - } -`; - -export const Title = styled.span` - font-weight: bold; - font-size: 1rem; - color: ${({ theme }) => theme.text1}; - - @media (max-width: 991px) { - font-size: 0.875rem; - } -`; - -export const CustomText = styled.span` - color: ${({ theme }) => theme.text1}; - font-weight: 400; - font-size: 0.875rem; - - @media (max-width: 991px) { - font-size: 0.75rem; - } -`; - -export const PoolsButton = styled.button` - border-radius: 12px; - background-color: ${({ theme }) => theme.bg2}; - padding: 6px 20px; - max-height: 40px; - min-height: 40px; - display: flex; - align-items: center; - justify-content: center; - white-space: nowrap; - font-size: 1rem; - font-family: inherit; - font-weight: 500; - border: none; - outline: none; - text-decoration: none; - - &:hover, - &:focus, - &:active { - text-decoration: none; - outline: none; - box-shadow: none; - } -`; - -export const TradeButton = styled(PoolsButton)` - color: ${({ theme, variant }) => (variant ? theme[variant] : theme.primary)}; - - @media (max-width: 991px) { - width: 100%; - } - - &:not(:disabled):hover { - color: ${({ theme }) => theme.bg2}; - background-color: ${({ theme, variant }) => (variant ? theme[variant] : theme.primary)}; - } - - &:disabled { - cursor: not-allowed; - opacity: 0.5; - } -`; - -export const StyledLink = styled(Link)` - text-decoration: none; - display: inline-flex; - margin-right: 30px; - - @media (max-width: 991px) { - margin-right: 0; - &:not(:last-child) { - margin-bottom: 14px; - } - } - - &:hover, - &:focus, - &:active { - text-decoration: none; - outline: none; - box-shadow: none; - } -`; - -let web3; -let web3Wrapper; - -const Wallet = (props) => { - const { account } = useActiveWeb3React(); - const [query, setQuery] = useState(""); - const [page, setPage] = useState(1); - const [unlocking, setUnlocking] = useState(false); - const [showUnlockModal, setShowUnlockModal] = useState(false); - const [done, setDone] = useState(false); - const overview = useSelector((state) => state.balances.overview); - const loading = useSelector((state) => state.balances.loading); - const { selected, currenciesRate } = useSelector((state) => state.currency); - const balances = useSelector((state) => state.balances.data); - const { ETH } = useSelector((state) => state.currency.currenciesRate); - const dispatch = useDispatch(); - const walletBalances = useMemoTokenBalances(); - const { t } = useTranslation(); - - useEffect(() => { - if (account) { - dispatch(fetchBalances(account)); - } - }, [account, dispatch]); - - useEffect(() => { - dispatch(fetchTransformedBalances(balances, walletBalances, ETH)); - }, [balances, walletBalances, ETH, dispatch]); - - useEffect(() => { - web3 = new Web3(Web3.givenProvider || new Web3.providers.HttpProvider(process.env.REACT_APP_NETWORK_URL)); - if (web3.currentProvider) { - web3Wrapper = new Web3Wrapper(web3.currentProvider); - } - }, []); - - const unlockHandler = async (token) => { - setShowUnlockModal(true); - try { - if (token.address) { - const contractWrappers = await getContractWrappers(web3.currentProvider || window.ethereum); - const approveAddress = token.address ? token.address : contractWrappers.contractAddresses.erc20Proxy; - - const erc20Token = new ERC20TokenContract(token.address, contractWrappers.getProvider()); - const amount = UNLIMITED_ALLOWANCE_IN_BASE_UNITS; - - const tx = await erc20Token.approve(approveAddress, amount).sendTransactionAsync({ - from: account, - }); - setUnlocking(true); - await web3Wrapper.awaitTransactionSuccessAsync(tx); - - if (tx) { - setUnlocking(false); - setDone(true); - } - } else if (token.symbol === "ETH") { - throw new Error("Unnecessary Approve for ethereum"); - } else { - throw new Error("Token is invalid"); - } - } catch (e) { - toast.error("Unnecessary Approve for ethereum or token is invalid"); - setUnlocking(false); - setShowUnlockModal(false); - setDone(false); - } - }; - - let tokensData = overview.wallet.balances || []; - - let filteredTokensData = useMemo(() => { - if (query === "") { - return tokensData; - } else { - const lowerQuery = query.toLowerCase(); - return tokensData.filter((token) => JSON.stringify(token.metadata).toLowerCase().includes(lowerQuery)); - } - }, [tokensData, query]); - - const TokensColumns = [ - { - dataField: "token", - text: t("token"), - formatter: (cellContent, row) => { - const isLoading = row.loading || false; - return ( -
    - - {isLoading ? ( - - ) : ( - - )} - - {isLoading ? <Skeleton width={48} height={24} /> : row.metadata.symbol} -
    - ); - }, - }, - { - dataField: "balance", - text: t("balanceTitle"), - formatter: (cellContent, row) => { - const isLoading = row.loading || false; - return ( -
    - {isLoading ? ( - - ) : ( - - {row.balance ? row.balance.toSignificant(6) : 0} - - )} -
    - ); - }, - }, - { - dataField: "value", - text: t("totalValue"), - formatter: (cellContent, row) => { - const isLoading = row.loading || false; - return ( -
    - {isLoading ? ( -
    - - -
    - ) : ( - - {row.balanceUSD} - - )} -
    - ); - }, - }, - { - dataField: "action", - text: t("table.actions"), - formatter: (cellContent, row, rowIndex, { unlockHandler }) => { - const isLoading = row.loading || false; - const value = row.balanceUSD * (currenciesRate["BTC"] || 1); - return ( -
    - {isLoading ? ( -
    - -
    - ) : ( - <> - - {t("buttons.buy")} - - - - - {t("buttons.unlock")} - - - - - {t("buttons.sell")} - - {value <= 0.001 ? ( - - - {t("buttons.convertTo", { symbol: "OCTO" })} - - - ) : ( - - {t("buttons.convertTo", { symbol: "OCTO" })} - - )} - - )} -
    - ); - }, - isAction: true, - formatExtraData: { - unlockHandler, - }, - }, - ]; - - return ( - - { - setDone(false); - setUnlocking(false); - setShowUnlockModal(false); - }} - /> - - - - - - - - {t("importList.tokens")} - - - {t("NFT")} - - - - - - - - - - setQuery(e.target.value)} - /> - - - - - - {loading ? ( -
    - -
    - ) : ( - - )} -
    - - - -
    - -
    -
    -
    -
    - ); -}; - -export default Wallet; diff --git a/src/state/balances/hooks.js b/src/state/balances/hooks.js index d9c9ed88..e629f905 100644 --- a/src/state/balances/hooks.js +++ b/src/state/balances/hooks.js @@ -49,14 +49,14 @@ export const getBalances = (balances, wallet, ethRate) => { deposits: { balances: [], total: 0, - title: "Deposits", + title: "Total Deposits", slug: "deposits", variant: "success", }, debts: { balances: [], total: 0, - title: "Debts", + title: "Total Debts", slug: "debts", variant: "danger", }, diff --git a/src/state/lists/updater.test.tsx b/src/state/lists/updater.test.tsx new file mode 100644 index 00000000..b491adfd --- /dev/null +++ b/src/state/lists/updater.test.tsx @@ -0,0 +1,107 @@ +import { render } from "../../testing/customRender"; +import { reducer } from "../../testing/testReducer"; +import { asFn } from "../../testing/utils"; +import Updater from "./updater"; + +import { useAllLists } from "./hooks"; +import { useFetchListCallback } from "../../hooks/useFetchListCallback"; +import * as uni from "@uniswap/token-lists"; + +jest.mock("../../hooks", () => ({ + useActiveWeb3React: () => ({ library: jest.fn() }), +})); +jest.mock("../../hooks/useFetchListCallback", () => ({ + useFetchListCallback: jest.fn(() => jest.fn(() => Promise.resolve())), +})); +jest.mock("../../hooks/useInterval", () => jest.fn()); +jest.mock("../../hooks/useIsWindowVisible", () => jest.fn()); +jest.mock("./hooks", () => ({ + useActiveListUrls: jest.fn(), + useAllLists: jest.fn(() => ({})), +})); + +// forward declaration for typing +let fetchList = jest.fn(() => Promise.resolve()); + +test("only fetches list for each list url", () => { + const emptyUrlData = {}; + setup(emptyUrlData); + + render(); + + expect(fetchList).not.toBeCalled(); +}); + +test("fetches list for each list url", () => { + setup({ url1: {} }); + + render(); + + expect(fetchList).toBeCalled(); +}); + +test("does not fetch with current url data", () => { + setup({ url1: { current: {} } }); + + render(); + + expect(fetchList).not.toBeCalled(); +}); + +test("does not fetch with loading url data", () => { + setup({ url1: { loadingRequestId: true } }); + + render(); + + expect(fetchList).not.toBeCalled(); +}); + +test("does not fetch with error on url data", () => { + setup({ url1: { error: true } }); + + render(); + + expect(fetchList).not.toBeCalled(); +}); + +test("throws if fetching NONE upgrade type", () => { + setup({ url1: { current: {}, pendingUpdate: {} } }); + jest.spyOn(uni, "getVersionUpgrade").mockImplementation(() => uni.VersionUpgrade.NONE); + + expect(() => render()).toThrowError(); +}); + +test("fetches upgrade if version equal to or greater", () => { + setup({ url1: { current: {}, pendingUpdate: {} } }); + jest.spyOn(uni, "getVersionUpgrade").mockImplementation(() => uni.VersionUpgrade.PATCH); + jest.spyOn(uni, "minVersionBump").mockImplementation(() => uni.VersionUpgrade.PATCH); + + render(); + + expect(reducer.mock.calls[reducer.mock.calls.length - 1][1].payload).toBe("url1"); +}); + +test("fetches upgrade if version is major", () => { + setup({ url1: { current: {}, pendingUpdate: {} } }); + jest.spyOn(uni, "getVersionUpgrade").mockImplementation(() => uni.VersionUpgrade.MAJOR); + + render(); + + expect(reducer.mock.calls[reducer.mock.calls.length - 1][1].payload).toBe("url1"); +}); + +test("does not fetch upgrade if version less than", () => { + setup({ url1: { current: {}, pendingUpdate: {} } }); + jest.spyOn(uni, "getVersionUpgrade").mockImplementation(() => uni.VersionUpgrade.PATCH); + jest.spyOn(uni, "minVersionBump").mockImplementation(() => uni.VersionUpgrade.MINOR); + + render(); + + expect(reducer.mock.calls[reducer.mock.calls.length - 1][1].payload).not.toBe("url1"); +}); + +const setup = (urlData: any) => { + asFn(useAllLists).mockImplementation(() => urlData); + asFn(useFetchListCallback).mockImplementation(jest.fn(() => fetchList)); + fetchList = jest.fn(() => Promise.resolve()); +}; diff --git a/src/styles/abstracts/variables.scss b/src/styles/abstracts/variables.scss index 4fcae422..15c34443 100644 --- a/src/styles/abstracts/variables.scss +++ b/src/styles/abstracts/variables.scss @@ -13,7 +13,7 @@ $modal-bg: #272a31; $light-background: #fff; $light-title: #000; $light-modal-bg: #f3f5fd; -$light-second-bg: rgb(212, 218, 242); +$light-second-bg: #d4daf2; // Status Colors $danger: #eb6b6b; diff --git a/src/styles/components/modal.scss b/src/styles/components/modal.scss index 0aabc7eb..7909638a 100644 --- a/src/styles/components/modal.scss +++ b/src/styles/components/modal.scss @@ -1,14 +1,12 @@ .modal-body, .modal-header { - padding-left: 86px; - padding-right: 86px; + padding: 1.25rem 2rem; } .modal-header { display: flex; align-items: center; justify-content: space-between; - min-height: 85px; .close { margin-left: 0; @@ -41,6 +39,7 @@ .custom-modal { max-width: 674px; + .modal-content { max-width: 674px; } diff --git a/src/testing/customRender.js b/src/testing/customRender.js new file mode 100644 index 00000000..7f631982 --- /dev/null +++ b/src/testing/customRender.js @@ -0,0 +1,48 @@ +import React from "react"; +import { Provider } from "react-redux"; +import { render } from "@testing-library/react"; +import "inter-ui"; +import { createWeb3ReactRoot, Web3ReactProvider } from "@web3-react/core"; +import getLibrary from "../utils/getLibrary"; +import { NetworkContextName } from "../constants"; +import "../i18n"; +import { createStore } from "redux"; +import { reducer } from "./testReducer"; + +const Web3ProviderNetwork = createWeb3ReactRoot(NetworkContextName); + +jest.mock("@web3-react/ledger-connector", () => ({ + LedgerConnector: jest.fn(() => ({})), +})); + +jest.mock("@web3-react/trezor-connector", () => ({ + TrezorConnector: jest.fn(() => ({})), +})); + +jest.mock("@web3-react/torus-connector", () => ({ + TorusConnector: jest.fn(() => ({})), +})); + +jest.mock("@web3-react/portis-connector", () => ({ + PortisConnector: jest.fn(() => ({})), +})); + +const customRender = (ui, { initialState, store = createStore(reducer, initialState), ...renderOptions } = {}) => { + const Wrapper = ({ children }) => { + return ( + + + + {children} + + + + ); + }; + + return render(ui, { wrapper: Wrapper, ...renderOptions }); +}; + +export * from "@testing-library/react"; + +export { customRender as render }; diff --git a/src/testing/testReducer.js b/src/testing/testReducer.js new file mode 100644 index 00000000..f102203d --- /dev/null +++ b/src/testing/testReducer.js @@ -0,0 +1,3 @@ +export const reducer = jest.fn((state, action) => { + return { ...state }; +}); diff --git a/src/testing/utils.ts b/src/testing/utils.ts new file mode 100644 index 00000000..d54b0e44 --- /dev/null +++ b/src/testing/utils.ts @@ -0,0 +1 @@ +export const asFn = (something: any) => something as ReturnType; diff --git a/src/theme/components.tsx b/src/theme/components.tsx index 86b82c86..649dc2e3 100644 --- a/src/theme/components.tsx +++ b/src/theme/components.tsx @@ -186,23 +186,17 @@ export const StyledInternalLink = styled(Link)` `; const StyledLink = styled.a` - text-decoration: none; - cursor: pointer; color: ${({ theme }) => theme.primary}; font-weight: 500; + text-decoration: none; - :hover { - text-decoration: underline; - } - - :focus { + &:hover, + &:focus, + &:active { + color: ${({ theme }) => theme.primary}; outline: none; text-decoration: underline; } - - :active { - text-decoration: none; - } `; const rotateImg = keyframes` diff --git a/src/theme/index.tsx b/src/theme/index.tsx index 6c5f3d6b..a8a096b9 100644 --- a/src/theme/index.tsx +++ b/src/theme/index.tsx @@ -12,10 +12,11 @@ import { Colors } from "./styled"; export * from "./components"; const MEDIA_WIDTHS = { - upToExtraSmall: 500, - upToSmall: 720, - upToMedium: 960, - upToLarge: 1280, + upToExtraSmall: 575, + upToSmall: 767, + upToMedium: 991, + upToLarge: 1199, + upToExtraLarge: 1399, }; const mediaWidthTemplates: { [width in keyof typeof MEDIA_WIDTHS]: typeof css } = Object.keys(MEDIA_WIDTHS).reduce( @@ -48,12 +49,13 @@ export function colors(darkMode?: boolean): Colors { // Backgrounds bg1: darkMode ? "#232429" : white, bg2: darkMode ? "#232429" : white, - bg3: darkMode ? "rgba(33, 36, 41, 1)" : "#FFF", - bg4: darkMode ? "#232429" : "rgb(212, 218, 242)", + bg3: darkMode ? "#3d4046" : white, + bg4: darkMode ? "#232429" : "#d4daf2", bg5: darkMode ? `rgba(255, 255, 255, 0.1) ` : `rgba(0, 0, 0, 0.05)`, // UI - borderColor: darkMode ? `rgba(255, 255, 255, 0.15) ` : `rgba(0, 0, 0, 0.15)`, + borderColor: darkMode ? `rgba(255, 255, 255, 0.05) ` : `rgba(0, 0, 0, 0.05)`, + borderColor2: darkMode ? `rgba(255, 255, 255, 0.15) ` : `rgba(0, 0, 0, 0.15)`, bodyBg: darkMode ? "linear-gradient(201.32deg, #222429 -48.82%, #232429 51.35%)" : "linear-gradient(201.32deg, #fff -48.82%, #fff 51.35%)", @@ -161,7 +163,7 @@ export const TYPE = { return ; }, Gray(props: TextProps) { - return ; + return ; }, Italic(props: TextProps) { return ; diff --git a/src/theme/styled.d.ts b/src/theme/styled.d.ts index 661181b3..0ece6463 100644 --- a/src/theme/styled.d.ts +++ b/src/theme/styled.d.ts @@ -21,6 +21,7 @@ export interface Colors { // UI borderColor: Color; + borderColor2: Color; bodyBg: Color; splashBG: Color; backdrop: Color; @@ -64,6 +65,7 @@ declare module "styled-components" { upToSmall: ThemedCssFunction; upToMedium: ThemedCssFunction; upToLarge: ThemedCssFunction; + upToExtraLarge: ThemedCssFunction; }; // css snippets diff --git a/src/typings/index.ts b/src/typings/index.ts new file mode 100644 index 00000000..f31245ea --- /dev/null +++ b/src/typings/index.ts @@ -0,0 +1,37 @@ +// Global types (types not confined to one component) go here + +export type Banner = { + image: string; + url: string; +} + +export type Feature = { + href: string; + iconName: string; + title: string; + desc: string; +} + +export type FiatOffItem = { + thumbnail: string; + title: string; + url: string; + traits: Array; +} + +export type FiatOffTraitItem = { + title: string; + icon: string; +} + +export type SnapshotSpaceProps = { + name: string; + symbol: string; + network: string; +}; + +export type SocialLink = { + name: string; + image: string; + url: string; +}; diff --git a/src/utils/promisify.js b/src/utils/promisify.js new file mode 100644 index 00000000..3df03e11 --- /dev/null +++ b/src/utils/promisify.js @@ -0,0 +1,10 @@ +export const promisify = (inner) => + new Promise((resolve, reject) => + inner((err, res) => { + if (err) { + reject(err); + } else { + resolve(res); + } + }) + ); diff --git a/yarn.lock b/yarn.lock index 618433c3..92a94102 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3950,6 +3950,11 @@ resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a" integrity sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA== +"@types/aos@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/aos/-/aos-3.0.4.tgz#fd0dce430f18d118081aaced2c79c30617b15818" + integrity sha512-mna6Jd6bdK1NpwarLopGvXOgUoCfj0470IwLxuVOFDElTGI0JTd7xSGQ0AjbAEnHErC/b3fA9t2uB3IXVKmckA== + "@types/aria-query@^4.2.0": version "4.2.1" resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.1.tgz#78b5433344e2f92e8b306c06a5622c50c245bf6b" @@ -4021,6 +4026,13 @@ resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.2.11.tgz#2521cc86f69d15c5b90664e4829d84566052c1cf" integrity sha512-2koNhpWm3DgWRp5tpkiJ8JGc1xTn2q0l+jUNUE7oMKXUf5NpI9AIdC4kbjGNFBdHtcxBD18LAksoudAVhFKCjw== +"@types/dompurify@^2.2.2": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@types/dompurify/-/dompurify-2.2.2.tgz#2c6692580eb7c653785ca3b2c1348847ea8b995d" + integrity sha512-8nNWfAa8/oZjH3OLY5Wsxu9ueo0NwVUotIi353g0P2+N5BuTLJyAVOnF4xBUY0NyFUGJHY05o1pO2bqLto+lmA== + dependencies: + "@types/trusted-types" "*" + "@types/eslint@^7.2.4": version "7.2.6" resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.6.tgz#5e9aff555a975596c03a98b59ecd103decc70c3c" @@ -4075,6 +4087,11 @@ dependencies: "@types/node" "*" +"@types/highlight.js@^9.7.0": + version "9.12.4" + resolved "https://registry.yarnpkg.com/@types/highlight.js/-/highlight.js-9.12.4.tgz#8c3496bd1b50cc04aeefd691140aa571d4dbfa34" + integrity sha512-t2szdkwmg2JJyuCM20e8kR2X59WCE5Zkl4bzm1u1Oukjm79zpbiAv+QjnwLnuuV0WHEcX2NgUItu0pAMKuOPww== + "@types/history@*": version "4.7.8" resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934" @@ -4264,6 +4281,20 @@ resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.5.tgz#434711bdd49eb5ee69d90c1d67c354a9a8ecb18b" integrity sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ== +"@types/react-bootstrap-table-next@^4.0.14": + version "4.0.14" + resolved "https://registry.yarnpkg.com/@types/react-bootstrap-table-next/-/react-bootstrap-table-next-4.0.14.tgz#e2fa2c8ba1a50c8c6cf4a7f5c55a17b19b8e0c46" + integrity sha512-Sdv+fFSeUZenEQU1bdlGfN1exKmrl/EJ49GL5d9i130xmvPjYRnEIb9NQLZE/WyJmkLs38oZUEb81efBhJuRlg== + dependencies: + "@types/react" "*" + +"@types/react-csv@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@types/react-csv/-/react-csv-1.1.2.tgz#a5694d7e5cbf4bc1d4baa178a3fa7ac3466ea8c5" + integrity sha512-hCtZyXAubxBtn3Oi3I9kNAx2liRTaMtl1eWpO2M98aYkHuoSTbYO8OcZEjyr9aJJ30Xnoxj+uES3G6L6O1qgtg== + dependencies: + "@types/react" "*" + "@types/react-dom@^16.9.7": version "16.9.10" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.10.tgz#4485b0bec3d41f856181b717f45fd7831101156f" @@ -4298,6 +4329,13 @@ "@types/history" "*" "@types/react" "*" +"@types/react-slick@^0.23.5": + version "0.23.5" + resolved "https://registry.yarnpkg.com/@types/react-slick/-/react-slick-0.23.5.tgz#e55fdc79bf19022ef77a6f22e9d64fd0f7463cc4" + integrity sha512-dQ/HwsLpnWXD5d+52WwXIQTfNCRpgd+0CKb+aA8g2CaIpA9T9COdjVYb9KI40Osb4rIgqR7u2AqtL2/HGbBMpg== + dependencies: + "@types/react" "*" + "@types/react-transition-group@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.0.tgz#882839db465df1320e4753e6e9f70ca7e9b4d46d" @@ -4345,6 +4383,14 @@ "@types/styled-system" "*" "@types/styled-system__css" "*" +"@types/remarkable@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@types/remarkable/-/remarkable-2.0.2.tgz#4bb5c6094212e736dab905b4e47106403015451f" + integrity sha512-z5JSTrZfEluIlTLWC9jiDojfWReaKpRDRj0pHSgNPlmhe5H0V/hJZIb8zeGxq0Zc0lDMKU1Rhr6cjkeabP7a6Q== + dependencies: + "@types/highlight.js" "^9.7.0" + highlight.js "^9.7.0" + "@types/resolve@0.0.8": version "0.0.8" resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" @@ -4422,6 +4468,11 @@ dependencies: "@types/jest" "*" +"@types/trusted-types@*": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.1.tgz#47a7d34b82b3042a902b101311084f7ee900bb78" + integrity sha512-TmhE+/eI8PP7EwT9EbK8i74F1ryNn0LToCyEaLpN+X+A3LS1j4CpsCk9Jwq6Y2Uu7w9wdrKl6bujdj5LSsDKKA== + "@types/uglify-js@*": version "3.11.1" resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.11.1.tgz#97ff30e61a0aa6876c270b5f538737e2d6ab8ceb" @@ -11992,6 +12043,11 @@ hex-color-regex@^1.1.0: resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== +highlight.js@^9.7.0: + version "9.18.5" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.18.5.tgz#d18a359867f378c138d6819edfc2a8acd5f29825" + integrity sha512-a5bFyofd/BHCX52/8i8uJkjr9DYwXIPnM/plwI6W7ezItLGqzt7X2G2nXuYSfsIJdkwwj/g9DG1LkcGJI/dDoA== + history@^4.9.0: version "4.10.1" resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3"