diff --git a/assets/_locales/en/messages.json b/assets/_locales/en/messages.json index 764d5e98..b51ba1e8 100644 --- a/assets/_locales/en/messages.json +++ b/assets/_locales/en/messages.json @@ -435,6 +435,10 @@ "message": "Currency", "description": "Reset settings title" }, + "currency": { + "message": "Currency", + "description": "Select currency" + }, "setting_dre_node": { "message": "Warp DRE node", "description": "Warp DRE node setting title" @@ -2162,6 +2166,10 @@ "message": "No transactions found", "description": "No transactions message" }, + "no_contacts" : { + "message" : "No contacts found", + "description": "No contacts found" + }, "sent": { "message": "Sent", "description": "sent" diff --git a/assets/_locales/zh_CN/messages.json b/assets/_locales/zh_CN/messages.json index b6484314..e89c4283 100644 --- a/assets/_locales/zh_CN/messages.json +++ b/assets/_locales/zh_CN/messages.json @@ -431,6 +431,10 @@ "message": "货币", "description": "Reset settings title" }, + "currency": { + "message": "货币", + "description": "Select currency" + }, "setting_dre_node": { "message": "Warp DRE 节点", "description": "Warp DRE node setting title" @@ -2148,6 +2152,10 @@ "message": "未找到交易", "description": "No transactions message" }, + "no_contacts": { + "message": "未找到联系人", + "description": "No contacts found message" + }, "sent": { "message": "已发送", "description": "sent" diff --git a/assets/ecosystem/quantum-logo.svg b/assets/ecosystem/quantum-logo.svg new file mode 100644 index 00000000..c5a68a52 --- /dev/null +++ b/assets/ecosystem/quantum-logo.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/Recipient.tsx b/src/components/Recipient.tsx index 11b970e8..7ad89fb0 100644 --- a/src/components/Recipient.tsx +++ b/src/components/Recipient.tsx @@ -153,6 +153,8 @@ export default function Recipient({ onClick, onClose }: RecipientProps) { }, {} as Record); }, [storedContacts, targetInput.state]); + const hasContacts = Object.keys(filteredAndGroupedContacts).length > 0; + return ( <> @@ -200,29 +202,35 @@ export default function Recipient({ onClick, onClose }: RecipientProps) { {browser.i18n.getMessage("your_contacts")} - {Object.keys(filteredAndGroupedContacts).map((letter) => ( - - {letter} - - {filteredAndGroupedContacts[letter].map((contact) => ( - { - onClick({ contact, address: contact.address }); - onClose(); - }} - /> + {hasContacts ? ( +
+ {Object.keys(filteredAndGroupedContacts).map((letter) => ( + + {letter} + + {filteredAndGroupedContacts[letter].map((contact) => ( + { + onClick({ contact, address: contact.address }); + onClose(); + }} + /> + ))} + ))} - - ))} +
+ ) : ( + {browser.i18n.getMessage("no_contacts")} + )}
); @@ -233,6 +241,10 @@ const SearchBarWrapper = styled.div` gap: 4px; `; +const NoContacts = styled(Text)` + padding: 11.2px 13.76px; +`; + const SubText = styled.h3` font-weight: 500; font-size: 1.25rem; @@ -244,13 +256,17 @@ const ContactAddress = styled.div` color: #aeadcd; `; -const Recents = styled.div` +const Recents = styled.button` display: flex; align-items: center; justify-content: space-between; cursor: pointer; padding: 10px 4px; border-radius: 10px; + background: transparent; + border: 0; + color: inherit; + &:hover { background-color: rgba(${(props) => props.theme.theme}, 0.12); cursor: pointer; diff --git a/src/components/SliderMenu.tsx b/src/components/SliderMenu.tsx index 6994dcac..8f30b4a5 100644 --- a/src/components/SliderMenu.tsx +++ b/src/components/SliderMenu.tsx @@ -1,48 +1,103 @@ -import { Button, type DisplayTheme } from "@arconnect/components"; +import { type DisplayTheme } from "@arconnect/components"; import { CloseIcon } from "@iconicicons/react"; +import { AnimatePresence, motion, type Variants } from "framer-motion"; +import type React from "react"; +import { useRef } from "react"; import styled, { useTheme } from "styled-components"; -import { SendInput } from "~routes/popup/send"; interface SliderMenuProps { title: string; + isOpen: boolean; onClose?: () => void; children?: React.ReactNode; } export default function SliderMenu({ - children, title, - onClose + isOpen, + onClose, + children }: SliderMenuProps) { + const wrapperElementRef = useRef(null); const theme = useTheme(); - return ( - - -
- {title} - -
- {children} - -
- ); + + const contentElement = isOpen ? ( + <> + { + e.stopPropagation(); + onClose(); + }} + /> + + + +
+ {title} + +
+ {children} + +
+ + ) : null; + + return {contentElement}; } const ExitButton = styled(CloseIcon)` cursor: pointer; `; -const Wrapper = styled.div<{ displayTheme: DisplayTheme }>` +const Wrapper = styled(motion.div)<{ displayTheme: DisplayTheme }>` position: fixed; + bottom: 0; + left: 0; + height: auto; + max-height: 93%; + border-top: ${(props) => + props.displayTheme === "light" + ? `1px solid ${props.theme.primary}` + : "none"}; display: flex; flex-direction: column; - min-height: 100%; - padding-bottom: 62px; width: 100%; + z-index: 1000; + overflow: scroll; background-color: ${(props) => props.displayTheme === "light" ? "#ffffff" : "#191919"}; + border-radius: 10px 10px 0 0; `; +export const animationSlideFromBottom: Variants = { + hidden: { + y: "100vh", + transition: { + duration: 0.2, + ease: "easeOut" + } + }, + shown: { + y: "0", + transition: { + duration: 0.2, + ease: "easeInOut" + } + } +}; + const Body = styled.div` padding: 1.0925rem; display: flex; @@ -55,6 +110,7 @@ const Header = styled.div` align-items: center; justify-content: space-between; `; + const Title = styled.h2` margin: 0; padding: 0; @@ -63,3 +119,16 @@ const Title = styled.h2` font-weight: 500; line-height: normal; `; + +export const CloseLayer = styled(motion.div)` + position: fixed; + z-index: 100; + top: 0; + left: 0; + right: 0; + bottom: 0; + width: 100vw; + height: 100vh; + cursor: default; + background-color: rgba(${(props) => props.theme.background}, 0.85); +`; diff --git a/src/components/popup/HeadV2.tsx b/src/components/popup/HeadV2.tsx index 04a955a0..fef804ee 100644 --- a/src/components/popup/HeadV2.tsx +++ b/src/components/popup/HeadV2.tsx @@ -6,7 +6,7 @@ import { } from "@arconnect/components"; import { Avatar, CloseLayer, NoAvatarIcon } from "./WalletHeader"; import { AnimatePresence, motion } from "framer-motion"; -import { hoverEffect, useTheme } from "~utils/theme"; +import { useTheme } from "~utils/theme"; import { useStorage } from "@plasmohq/storage/hook"; import { ArrowLeftIcon } from "@iconicicons/react"; import { useAnsProfile } from "~lib/ans"; @@ -22,6 +22,16 @@ import WalletSwitcher from "./WalletSwitcher"; import styled from "styled-components"; import { svgie } from "~utils/svgies"; +interface HeadV2Props { + title: string; + showOptions?: boolean; + // allow opening the wallet switcher + showBack?: boolean; + padding?: string; + allowOpen?: boolean; + back?: (...args) => any; +} + export default function HeadV2({ title, showOptions = true, @@ -29,7 +39,7 @@ export default function HeadV2({ padding, showBack = true, allowOpen = true -}: Props) { +}: HeadV2Props) { // scroll position const [scrollDirection, setScrollDirection] = useState<"up" | "down">("up"); const [scrolled, setScrolled] = useState(false); @@ -100,41 +110,45 @@ export default function HeadV2({ scrolled={scrolled} padding={padding} > - { if (back) await back(); else goBack(); }} > - - + + {title} - { - if (!allowOpen) return; - setOpen(true); - }} - > - {!ans?.avatar && !svgieAvatar && } - - {hardwareApi === "keystone" && ( - - )} - - + + { + if (!allowOpen) return; + setOpen(true); + }} + > + {!ans?.avatar && !svgieAvatar && } + + {hardwareApi === "keystone" && ( + + )} + + + + {isOpen && setOpen(false)} />} + setOpen(false)} - showOptions={showOptions} exactTop={true} + showOptions={showOptions} /> ); @@ -155,7 +169,7 @@ const HeadWrapper = styled(Section)<{ flex-direction: row; width: full; padding: ${(props) => (props.padding ? props.padding : "15px")}; - justify-content: space-between; + justify-content: center; align-items: center; background-color: rgba(${(props) => props.theme.background}, 0.75); backdrop-filter: blur(15px); @@ -167,24 +181,30 @@ const HeadWrapper = styled(Section)<{ ")" : "transparent"}; transition: border-color 0.23s ease-in-out; + user-select: none; `; -const BackWrapper = styled.div` - position: relative; +const BackButton = styled.button` + position: absolute; + top: 0; + bottom: 0; + left: 0; display: flex; width: max-content; align-items: center; justify-content: center; - padding: 8px 0; + padding: 0 15px; height: 100%; cursor: pointer; + background: transparent; + border: 0; &:active svg { transform: scale(0.92); } `; -const BackButton = styled(ArrowLeftIcon)` +const BackButtonIcon = styled(ArrowLeftIcon)` font-size: 1rem; width: 1em; height: 1em; @@ -196,25 +216,6 @@ const BackButton = styled(ArrowLeftIcon)` } `; -const PageInfo = styled(motion.div).attrs<{ - scrollDirection: "up" | "down"; - firstRender: boolean; -}>((props) => ({ - initial: !props.firstRender - ? { opacity: 0, y: props.scrollDirection === "up" ? 20 : -20 } - : undefined, - animate: { opacity: 1, y: 0 }, - exit: { opacity: 0, y: props.scrollDirection === "up" ? -20 : 20 } -}))<{ - firstRender: boolean; - scrollDirection: "up" | "down"; -}>` - display: flex; - align-items: center; - justify-content: space-between; - width: 100%; -`; - const PageTitle = styled(Text).attrs({ subtitle: true, noMargin: true @@ -223,11 +224,30 @@ const PageTitle = styled(Text).attrs({ font-weight: 500; `; -const ClickableAvatar = styled(Avatar)` +const AvatarButton = styled.button` + position: absolute; + top: 0; + bottom: 0; + right: 0; cursor: pointer; + padding: 0 15px; + height: 100%; + background: transparent; + border: 0; +`; + +const ButtonAvatar = styled(Avatar)` width: 2.1rem; height: 2.1rem; + & svg { + transition: transform 0.07s ease-in-out; + } + + &:active svg { + transform: scale(0.93); + } + ${HardwareWalletIcon} { position: absolute; right: -5px; @@ -238,13 +258,3 @@ const ClickableAvatar = styled(Avatar)` font-size: 1.4rem; } `; - -interface Props { - title: string; - showOptions?: boolean; - // allow opening the wallet switcher - showBack?: boolean; - padding?: string; - allowOpen?: boolean; - back?: (...args) => any; -} diff --git a/src/components/popup/Navigation.tsx b/src/components/popup/Navigation.tsx index 9823eb1d..8c0495a1 100644 --- a/src/components/popup/Navigation.tsx +++ b/src/components/popup/Navigation.tsx @@ -44,15 +44,14 @@ export const NavigationBar = () => { const theme = useTheme(); const [push] = useHistory(); const [location] = useLocation(); - const shouldShowNavigationBar = - location !== "/explore" && - buttons.some((button) => { - if (button.title === "Send") { - return location.startsWith(button.route); - } else { - return location === button.route; - } - }); + + const shouldShowNavigationBar = buttons.some((button) => { + if (button.title === "Send") { + return location.startsWith(button.route); + } else { + return location === button.route; + } + }); if (!shouldShowNavigationBar) { return null; @@ -60,29 +59,27 @@ export const NavigationBar = () => { return ( - - {buttons.map((button, index) => { - const active = button.route === location; - return ( - push(button.route)} - > - - {button.icon} - -
{browser.i18n.getMessage(button.title)}
-
- ); - })} -
+ {buttons.map((button, index) => { + const active = button.route === location; + return ( + push(button.route)} + > + + {button.icon} + +
{browser.i18n.getMessage(button.title)}
+
+ ); + })}
); }; -const NavigationBarWrapper = styled.div<{ displayTheme: DisplayTheme }>` +const NavigationBarWrapper = styled.nav<{ displayTheme: DisplayTheme }>` z-index: 5; border-top: 2px solid ${(props) => props.theme.primary}; position: fixed; @@ -92,9 +89,10 @@ const NavigationBarWrapper = styled.div<{ displayTheme: DisplayTheme }>` background-color: ${(props) => props.displayTheme === "light" ? "#F5F5F5" : "#191919"}; width: 378px; + display: flex; `; -const NavigationButton = styled.div<{ +const NavigationButton = styled.button<{ active?: boolean; displayTheme: DisplayTheme; }>` @@ -113,10 +111,19 @@ const NavigationButton = styled.div<{ align-items: center; justify-content: center; height: 100%; + background: transparent; + border: 0; + flex: 1 0 0; + min-width: 0; + transition: color linear 250ms; + + &:hover { + color: ${(props) => (props.displayTheme === "light" ? "#191919" : "#fff")}; + } `; const IconWrapper = styled.div<{ size: string; displayTheme: DisplayTheme }>` - color: ${(props) => (props.displayTheme === "light" ? "#191919" : "#f5f5f5")}; + color: inherit; padding: 2px; width: ${(props) => props.size}; height: ${(props) => props.size}; @@ -124,10 +131,3 @@ const IconWrapper = styled.div<{ size: string; displayTheme: DisplayTheme }>` justify-content: center; align-items: center; // Center vertically `; - -const NavigationButtons = styled.div` - display: flex; - width: 100%; - height: 100%; - justify-content: space-around; -`; diff --git a/src/components/popup/Route.tsx b/src/components/popup/Route.tsx index 0fdfbed5..c7a3c78a 100644 --- a/src/components/popup/Route.tsx +++ b/src/components/popup/Route.tsx @@ -1,5 +1,4 @@ import { AnimatePresence, motion, type Variants } from "framer-motion"; -import { type HistoryAction, useHistory } from "~utils/hash_router"; import { createElement, type PropsWithChildren } from "react"; import { useRoute, Route as BaseRoute } from "wouter"; import styled from "styled-components"; @@ -15,11 +14,9 @@ const Route: typeof BaseRoute = ({ path, component, children }) => { ? children(params) : children; - const [, _, action] = useHistory(); - return ( - {matches && {routeContent}} + {matches && {routeContent}} ); }; @@ -42,51 +39,10 @@ const PageWrapper = styled(Wrapper)` transition: background-color 0.23s ease-in-out; `; -const Page = ({ - children, - action -}: PropsWithChildren<{ action: HistoryAction }>) => { - const transition = { ease: [0.42, 0, 0.58, 1], duration: 0.27 }; - const pageAnimation: Variants = { - enter: { - x: 0, - transition, - ...(action === "push" - ? { - right: 0, - left: 0, - bottom: 0 - } - : {}) - }, - initial: { - x: action === "push" ? "100%" : "-25%", - transition, - ...(action === "push" - ? { - right: 0, - left: 0, - bottom: 0 - } - : {}) - }, - exit: { - x: action === "pop" ? "100%" : "-10%", - zIndex: action === "pop" ? 1 : -1, - transition, - ...(action === "pop" - ? { - right: 0, - left: 0, - bottom: 0 - } - : {}) - } - }; - +const Page = ({ children }: PropsWithChildren) => { const opacityAnimation: Variants = { - initial: { opacity: 0, scale: 0.8 }, - enter: { opacity: 1, scale: 1 }, + initial: { opacity: 0 }, + enter: { opacity: 1 }, exit: { opacity: 0, y: 0, transition: { duration: 0.2 } } }; @@ -95,11 +51,7 @@ const Page = ({ initial="initial" animate="enter" exit="exit" - variants={ - new URLSearchParams(window.location.search).get("expanded") - ? opacityAnimation - : pageAnimation - } + variants={opacityAnimation} > {children} diff --git a/src/components/popup/WalletHeader.tsx b/src/components/popup/WalletHeader.tsx index 22611ff1..84528852 100644 --- a/src/components/popup/WalletHeader.tsx +++ b/src/components/popup/WalletHeader.tsx @@ -296,6 +296,7 @@ export default function WalletHeader() { /> + - - {appDataOpen && ( - e.stopPropagation()} - > - - - {(!!activeAppData && ( - - - {(activeAppData?.logo && ( - {activeAppData.name - )) || } - - - - )) || ( - - - - )} -
- - {activeAppData?.name || - browser.i18n.getMessage( - activeAppData ? "appConnected" : "not_connected" - )} - - {getAppURL(activeTab.url)} -
-
- - {(!activeAppData && ( - - {browser.i18n.getMessage("not_connected_text")} - - )) || ( - <> - - - browser.tabs.create({ - url: browser.runtime.getURL( - `tabs/dashboard.html#/apps/${activeApp.url}` - ) - }) - } - > - - {browser.i18n.getMessage("settings")} - - { - await removeApp(getAppURL(activeTab.url)); - setActiveAppData(undefined); - setAppDataOpen(false); - }} - > - -
- {browser.i18n.getMessage("disconnect")} -
-
-
- - )} -
-
-
- )} -
- {(isOpen || appDataOpen) && ( + + {(isOpen || appDataOpen || menuOpen) && ( { e.stopPropagation(); setOpen(false); setAppDataOpen(false); + setMenuOpen(false); }} /> )} - setOpen(false)} /> + + setOpen(false)} + exactTop={true} + /> + setMenuOpen(false)} menuItems={menuItems} /> + + + {appDataOpen && ( + e.stopPropagation()} + > + + + {(!!activeAppData && ( + + + {(activeAppData?.logo && ( + {activeAppData.name + )) || } + + + + )) || ( + + + + )} +
+ + {activeAppData?.name || + browser.i18n.getMessage( + activeAppData ? "appConnected" : "not_connected" + )} + + {getAppURL(activeTab.url)} +
+
+ + {(!activeAppData && ( + + {browser.i18n.getMessage("not_connected_text")} + + )) || ( + <> + + + browser.tabs.create({ + url: browser.runtime.getURL( + `tabs/dashboard.html#/apps/${activeApp.url}` + ) + }) + } + > + + {browser.i18n.getMessage("settings")} + + { + await removeApp(getAppURL(activeTab.url)); + setActiveAppData(undefined); + setAppDataOpen(false); + }} + > + +
+ {browser.i18n.getMessage("disconnect")} +
+
+
+ + )} +
+
+
+ )} +
); } -const Wrapper = styled(Section)<{ +const Wrapper = styled.nav<{ displayTheme: DisplayTheme; scrolled: boolean; }>` @@ -484,7 +499,8 @@ const Wrapper = styled(Section)<{ (props.displayTheme === "light" ? "235, 235, 241" : "31, 30, 47") + ")" : "transparent"}; - transition: all 0.23s ease-in-out; + transition: border 0.23s ease-in-out; + user-select: none; `; const WalletName = styled.div` @@ -557,17 +573,12 @@ export const Avatar = styled(Squircle)` position: relative; width: ${avatarSize}; height: ${avatarSize}; - transition: all 0.07s ease-in-out; ${HardwareWalletIcon} { position: absolute; right: -5px; bottom: -5px; } - - &:active { - transform: scale(0.93); - } `; export const NoAvatarIcon = styled(UserIcon)` @@ -654,10 +665,10 @@ const AppInfoWrapper = styled(motion.div).attrs({ exit: "closed" })` position: absolute; - top: 150%; - right: 0; + top: 100%; + right: 15px; z-index: 110; - width: calc(100vw - 2 * 20px); + width: calc(100% - 30px); cursor: default; ${Card} { @@ -732,7 +743,7 @@ const AppUrl = styled(Text).attrs({ line-height: 1.1em; `; -export const CloseLayer = styled.div` +export const CloseLayer = styled(motion.div)` position: fixed; z-index: 100; top: 0; @@ -742,4 +753,5 @@ export const CloseLayer = styled.div` width: 100vw; height: 100vh; cursor: default; + background-color: rgba(${(props) => props.theme.background}, 0.85); `; diff --git a/src/components/popup/WalletSwitcher.tsx b/src/components/popup/WalletSwitcher.tsx index 81fab540..f9039b3e 100644 --- a/src/components/popup/WalletSwitcher.tsx +++ b/src/components/popup/WalletSwitcher.tsx @@ -305,16 +305,14 @@ const SwitcherPopover = styled(motion.div).attrs({ })<{ exactTop?: boolean }>` position: absolute; top: ${(props) => (props.exactTop ? "100%" : "calc(100% - 1.05rem)")}; - left: -5px; + left: 0; right: 0; z-index: 110; cursor: default; `; const Wrapper = styled(Section)<{ noPadding: boolean }>` - padding-top: 0; - padding-bottom: 0; - ${(props) => (props.noPadding ? "padding: 0;" : "")} + padding: 0 15px; `; const WalletsCard = styled(Card)` diff --git a/src/components/popup/home/Tabs.tsx b/src/components/popup/home/Tabs.tsx index 9ab78da8..fabf247c 100644 --- a/src/components/popup/home/Tabs.tsx +++ b/src/components/popup/home/Tabs.tsx @@ -70,15 +70,18 @@ const TabsWrapper = styled.div` filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.25)); `; -const StyledTab = styled.div<{ active?: boolean; tabId: number }>` +const StyledTab = styled.button<{ active?: boolean; tabId: number }>` position: relative; height: 25px; font-weight: 500; font-size: 18px; line-height: 25px; color: ${(props) => - props.active ? props.theme.primaryText : props.theme.secondaryTextv2}; + props.active ? props.theme.primaryTextv2 : props.theme.secondaryTextv2}; cursor: pointer; + background: transparent; + border: 0; + padding: 0; &::after { content: ""; diff --git a/src/popup.tsx b/src/popup.tsx index 75fa9afe..1c20dbe5 100644 --- a/src/popup.tsx +++ b/src/popup.tsx @@ -72,7 +72,7 @@ export default function Popup() { - + @@ -212,12 +212,15 @@ export default function Popup() { } const HideScrollbar = createGlobalStyle<{ expanded?: boolean }>` - body { + * { scrollbar-width: none; &::-webkit-scrollbar { display: none } + } + + body { ${(props) => props?.expanded ? `background-image: linear-gradient( to right, transparent, rgba( ${props.theme.theme},.4 ), transparent);` diff --git a/src/routes/auth/sign.tsx b/src/routes/auth/sign.tsx index df0ab618..118ce340 100644 --- a/src/routes/auth/sign.tsx +++ b/src/routes/auth/sign.tsx @@ -375,7 +375,9 @@ export default function Sign() { } else await authorize(); }} > - {browser.i18n.getMessage("sign_authorize")} + {!page + ? browser.i18n.getMessage("sign_authorize") + : browser.i18n.getMessage("keystone_scan")} diff --git a/src/routes/popup/explore.tsx b/src/routes/popup/explore.tsx index c2616c05..5f826f1d 100644 --- a/src/routes/popup/explore.tsx +++ b/src/routes/popup/explore.tsx @@ -36,7 +36,12 @@ export default function Explore() { />
{filteredApps.map((app, index) => ( - + { + browser.tabs.create({ url: app.links.website }); + }} + > { - browser.tabs.create({ url: app.links.website }); - }} > @@ -104,7 +106,7 @@ const Title = styled.div` `; const Wrapper = styled.div` - padding: 18px 1rem; + padding: 18px 1rem 64px 1rem; display: flex; flex-direction: column; gap: 18px; @@ -150,11 +152,16 @@ const Logo = styled.img` width: 25px; `; -const AppWrapper = styled.div` +const AppWrapper = styled.button` padding: 10px; gap: 12px; display: flex; justify-content: space-between; + background: none; + border: none; + cursor: pointer; + width: 100%; + text-align: left; `; const LogoDescriptionWrapper = styled.div` diff --git a/src/routes/popup/index.tsx b/src/routes/popup/index.tsx index af6fbd35..856b024c 100644 --- a/src/routes/popup/index.tsx +++ b/src/routes/popup/index.tsx @@ -131,12 +131,15 @@ export default function Home() { {loggedIn && } - {(!noBalance && ( + + {noBalance ? ( + + ) : ( <> - )) || } + )} ); } diff --git a/src/routes/popup/purchase.tsx b/src/routes/popup/purchase.tsx index e43292d2..e4ac41d3 100644 --- a/src/routes/popup/purchase.tsx +++ b/src/routes/popup/purchase.tsx @@ -13,8 +13,6 @@ import { ChevronRight, Bank, BankNote01 } from "@untitled-ui/icons-react"; import switchIcon from "url:/assets/ecosystem/switch-vertical.svg"; import styled from "styled-components"; import HeadV2 from "~components/popup/HeadV2"; -import { AnimatePresence, type Variants } from "framer-motion"; -import { SliderWrapper } from "./send"; import { useEffect, useMemo, useState } from "react"; import { PageType, trackPage } from "~utils/analytics"; import type { PaymentType, Quote } from "~lib/onramper"; @@ -22,6 +20,7 @@ import { useHistory } from "~utils/hash_router"; import { ExtensionStorage } from "~utils/storage"; import { useDebounce } from "~wallets/hooks"; import { retryWithDelay } from "~utils/retry"; +import SliderMenu from "~components/SliderMenu"; export default function Purchase() { const [push] = useHistory(); @@ -248,36 +247,34 @@ export default function Purchase() {
} /> - - {showCurrencySelector && ( - - - - )} - {showPaymentSelector && ( - - - - )} - + + { + setShowCurrencySelector(false); + }} + > + + + + { + setShowPaymentSelector(false); + }} + > + + void; payments: any[]; }) => { - const searchInput = useInput(); - return ( - {payments.map((payment, index) => { if (payment.isActive) { const isWireTransfer = payment.id === "pm_us_wire_bank_transfer"; @@ -388,11 +378,6 @@ const CurrencySelectorScreen = ({ return ( -
` const SwitchText = styled(Text)` color: ${(props) => props.theme.primaryTextv2}; `; - -const animation: Variants = { - hidden: { x: "-100%", opacity: 0 }, - shown: { x: "0%", opacity: 1 } -}; diff --git a/src/routes/popup/receive.tsx b/src/routes/popup/receive.tsx index 07f9a111..a756905c 100644 --- a/src/routes/popup/receive.tsx +++ b/src/routes/popup/receive.tsx @@ -45,9 +45,6 @@ export default function Receive({ walletName, walletAddress }: ReceiveProps) { // location const [, setLocation] = useLocation(); - const wallet = useActiveWallet(); - const keystoneWarning = wallet?.type === "hardware"; - const copyAddress: MouseEventHandler = (e) => { e.stopPropagation(); copy(effectiveAddress); @@ -77,16 +74,6 @@ export default function Receive({ walletName, walletAddress }: ReceiveProps) { />
- {keystoneWarning && ( - - - - -
- {browser.i18n.getMessage("keystone_ao_description")} -
-
- )}
diff --git a/src/routes/popup/send/index.tsx b/src/routes/popup/send/index.tsx index ea6e621e..77401098 100644 --- a/src/routes/popup/send/index.tsx +++ b/src/routes/popup/send/index.tsx @@ -241,7 +241,7 @@ export default function Send({ id }: Props) { ); setBalance( - balanceToFractioned(String(result[0]), { + balanceToFractioned(String(result[0] || 0), { id: token.id, decimals: token.decimals, divisibility: token.divisibility @@ -270,17 +270,29 @@ export default function Send({ id }: Props) { if (isAo) { return setPrice("0"); } - const res = await redstone.getPrice(token.ticker); - if (!res.value) { - return setPrice("0"); - } + const redstonePromise = redstone.getPrice(token.ticker); + const multiplierPromise = + currency === "usd" + ? 1 + : await getPrice("usd", currency).catch((err) => { + console.warn(`Error fetching price for ${currency}`, err); + + return 0; + }); + + const [redstoneResponse, multiplier] = await Promise.all([ + redstonePromise, + multiplierPromise + ]); - // get price in currency - const multiplier = - currency !== "usd" ? await getPrice("usd", currency) : 1; + const redstoneValue = redstoneResponse.value; - setPrice(BigNumber(res.value).multipliedBy(multiplier).toString()); + setPrice( + redstoneResponse && multiplier + ? BigNumber(redstoneValue).multipliedBy(multiplier).toString() + : "0" + ); })(); }, [token, currency]); @@ -321,10 +333,10 @@ export default function Send({ id }: Props) { const max = useMemo(() => { const balanceBigNum = BigNumber(balance); const networkFeeBigNum = BigNumber(networkFee); - - let maxAmountToken = balanceBigNum.minus(networkFeeBigNum); - - if (token.id !== "AR") maxAmountToken = balanceBigNum; + const maxAmountToken = + token.id === "AR" + ? BigNumber.max(0, balanceBigNum.minus(networkFeeBigNum)) + : balanceBigNum; return maxAmountToken.multipliedBy(qtyMode === "fiat" ? price : 1); }, [balance, token, networkFee, qtyMode]); @@ -339,7 +351,12 @@ export default function Send({ id }: Props) { // switch between fiat qty mode / token qty mode function switchQtyMode() { if (!+price) return; - setQty(secondaryQty.toFixed(4)); + + let formattedQuantity = secondaryQty.toFixed(4); + + if (formattedQuantity === "0.0000") formattedQuantity = "0"; + + setQty(formattedQuantity); setQtyMode((val) => (val === "fiat" ? "token" : "fiat")); } @@ -492,14 +509,20 @@ export default function Send({ id }: Props) { fullWidth icon={ - {!!+price && ( - - USD/ - - {token.ticker.toUpperCase()} - - - )} + + {!!+price && ( + <> + USD + {"/"} + + )} + + {token.ticker.toUpperCase()} + + - - {showTokenSelector && ( - - - updateSelectedToken("AR")} /> - {aoTokens.map((token, i) => ( - updateSelectedToken(token.id)} - /> - ))} - {tokens - .filter((token) => token.type === "asset") - .map((token, i) => ( - updateSelectedToken(token.id)} - key={i} - /> - ))} - - - {tokens - .filter((token) => token.type === "collectible") - .map((token, i) => ( - updateSelectedToken(token.id)} - key={i} - /> - ))} - - - - )} - {showSlider && ( - - { - setShowSlider(false); - }} - > - setShowSlider(false)} + + { + setShownTokenSelector(false); + }} + > + + updateSelectedToken("AR")} /> + + {aoTokens.map((token, i) => ( + updateSelectedToken(token.id)} + /> + ))} + + {tokens + .filter((token) => token.type === "asset") + .map((token, i) => ( + updateSelectedToken(token.id)} + key={i} /> - - - )} - + ))} + + + + {tokens + .filter((token) => token.type === "collectible") + .map((token, i) => ( + updateSelectedToken(token.id)} + key={i} + /> + ))} + + + + { + setShowSlider(false); + }} + > + setShowSlider(false)} + /> + ); @@ -692,7 +707,7 @@ const MaxButton = styled.button<{ altColor?: string }>` box-shadow: 0 0 0 0 rgba(${(props) => props.theme.theme}); `; -const CurrencyButton = styled.button` +const CurrencyButton = styled.button<{ altColor?: string }>` font-weight: 400; background-color: transparent; border-radius: 4px; @@ -706,6 +721,7 @@ const CurrencyButton = styled.button` justify-content: center; outline: none; border: 0px; + color: #b9b9b9; `; const Wrapper = styled.div<{ showOverlay: boolean }>` @@ -812,12 +828,6 @@ const InputIcons = styled.div` gap: 0.625rem; `; -const qtyTextStyle = css` - font-size: ${defaulQtytSize}rem; - font-weight: 500; - line-height: 1.1em; -`; - const BottomActions = styled(Section)` display: flex; padding: 0 15px; @@ -875,45 +885,12 @@ const TokenSelectorRightSide = styled.div` } `; -export const SliderWrapper = styled(motion.div)<{ partial?: boolean }>` - position: fixed; - top: ${(props) => (props.partial ? "50px" : 0)}; - left: 0; - bottom: 0; - right: 0; - overflow-y: auto; - background-color: rgb(${(props) => props.theme.background}); - z-index: 1000; -`; - -export const animation: Variants = { - hidden: { opacity: 0 }, - shown: { opacity: 1 } -}; - -export const animation2: Variants = { - hidden: { - y: "100vh", - transition: { - duration: 0.2, - ease: "easeOut" - } - }, - shown: { - y: "0", - transition: { - duration: 0.2, - ease: "easeInOut" - } - } -}; - -const TokensSection = styled(Section)` +const TokensList = styled.ul` display: flex; flex-direction: column; gap: 0.82rem; - padding-top: 2rem; - padding-bottom: 1.4rem; + padding: 0; + margin: 0; `; const CollectiblesList = styled(Section)` @@ -921,4 +898,8 @@ const CollectiblesList = styled(Section)` grid-template-columns: 1fr 1fr; gap: 1.2rem; padding-top: 0; + + &:empty { + display: none; + } `; diff --git a/src/utils/apps.ts b/src/utils/apps.ts index 0c9c7067..e0fc6028 100644 --- a/src/utils/apps.ts +++ b/src/utils/apps.ts @@ -15,6 +15,7 @@ import aoLinkLogo from "url:/assets/ecosystem/aolink.svg"; import llamaLogo from "url:/assets/ecosystem/llama.png"; import arswapLogo from "url:/assets/ecosystem/arswap.png"; import liquidopsLogo from "url:/assets/ecosystem/liquidops.svg"; +import quantumLogo from "url:/assets/ecosystem/quantum-logo.svg"; import betterideaLogo from "url:/assets/ecosystem/betteridea.png"; export interface App { @@ -101,6 +102,21 @@ export const apps: App[] = [ twitter: "https://x.com/Liquid_Ops" } }, + { + name: "Quantum", + category: "Defi", + description: "Bridge. Earn and Explore the AO ecosystem with Quantum.", + assets: { + logo: quantumLogo, + thumbnail: "", + lightBackground: "rgba(25, 25, 25, 1)", + darkBackground: "rgba(25, 25, 25, 1)" + }, + links: { + website: "https://bridge.astrousd.com", + twitter: "https://x.com/astrousd" + } + }, { name: "Bazar", category: "Exchange",