diff --git a/src/_dev/hotReloadObserver.ts b/src/_dev/hotReloadObserver.ts index 1b37ddac7..ac15c3c21 100644 --- a/src/_dev/hotReloadObserver.ts +++ b/src/_dev/hotReloadObserver.ts @@ -5,13 +5,21 @@ const HOTRELOAD_TAB_URL = browser.runtime.getURL("hotreload.html"); initHotReloadTab().catch(console.error); async function initHotReloadTab() { - const existing = await browser.tabs.query({ url: `${HOTRELOAD_TAB_URL}**` }); - - if (existing.length === 0) { - await browser.tabs.create({ - url: HOTRELOAD_TAB_URL, - active: false, - index: 0, + try { + const existing = await browser.tabs.query({ + url: `${HOTRELOAD_TAB_URL}**`, }); + + if (existing.length > 0) { + await browser.tabs.remove(existing.map((t) => t.id!)); + } + } catch (err) { + console.warn(err); } + + await browser.tabs.create({ + url: HOTRELOAD_TAB_URL, + active: false, + index: 0, + }); } diff --git a/src/app/atoms/nav.ts b/src/app/atoms/nav.ts index 86dd499b6..7b1b1e4d8 100644 --- a/src/app/atoms/nav.ts +++ b/src/app/atoms/nav.ts @@ -45,3 +45,9 @@ export const tokenSlugAtom = atomWithURLHash("token", null); export const activityModalAtom = atomWithURLHash("activityOpened", false); export const chainIdUrlAtom = atomWithURLHash("chainid", null); + +export const receiveModalAtom = atomWithURLHash("receiveOpened", false); +export const receiveTokenAtom = atomWithURLHash( + "receiveToken", + null, +); diff --git a/src/app/components/MainApp.tsx b/src/app/components/MainApp.tsx index e080bfc30..46dacc78e 100644 --- a/src/app/components/MainApp.tsx +++ b/src/app/components/MainApp.tsx @@ -11,6 +11,7 @@ import ContactsDialog from "./blocks/ContactsDialog"; import AddAccountModal from "./blocks/AddAccountModal"; import ActivityModal from "./blocks/activity/ActivityModal"; import AddFundsOnRampModal from "./blocks/AddFundsOnRampModal"; +import ReceivePopup from "./blocks/ReceiveModal"; // import AuthSignatureModal from "./blocks/AuthSignatureModal"; const MainApp: FC = () => ( @@ -36,6 +37,7 @@ const Modals: FC = () => { + {/* */} diff --git a/src/app/components/PopupApp.tsx b/src/app/components/PopupApp.tsx index 2e74af081..0d2df593c 100644 --- a/src/app/components/PopupApp.tsx +++ b/src/app/components/PopupApp.tsx @@ -12,6 +12,7 @@ import Unlock from "./screens/Unlock"; import Popup from "./screens/Popup"; import Dialog from "./blocks/Dialog"; import ActivityModal from "./blocks/activity/ActivityModal"; +import ReceivePopup from "./blocks/ReceiveModal"; const PopupApp: FC = () => ( @@ -37,7 +38,12 @@ const PopupModals: FC = () => { return ( <> - {!locked && } + {!locked && ( + <> + + + + )} ); }; diff --git a/src/app/components/blocks/ReceiveModal.tsx b/src/app/components/blocks/ReceiveModal.tsx new file mode 100644 index 000000000..d21f1ae37 --- /dev/null +++ b/src/app/components/blocks/ReceiveModal.tsx @@ -0,0 +1,71 @@ +import { FC, useCallback } from "react"; +import classNames from "clsx"; +import { useAtom, useSetAtom } from "jotai"; +import { isPopup } from "lib/ext/view"; + +import { AccountAsset } from "core/types"; + +import { receiveModalAtom, receiveTokenAtom } from "app/atoms"; +import { useAccountToken } from "app/hooks"; +import ShareAddress from "../screens/receiveTabs/ShareAddress"; +import SecondaryModal, { + SecondaryModalProps, +} from "../elements/SecondaryModal"; +import AssetLogo from "../elements/AssetLogo"; + +type ReceiveModalProps = Pick; + +const ReceiveModal: FC = (props) => { + const isPopupMode = isPopup(); + const setReceiveToken = useSetAtom(receiveTokenAtom); + const [receiveOpened, setReceiveOpened] = useAtom(receiveModalAtom); + + const handleOpenChange = useCallback( + (open: boolean) => { + setReceiveOpened([open, "replace"]); + setReceiveToken([null, "replace"]); + }, + [setReceiveOpened, setReceiveToken], + ); + + return ( + + Receive{" "} + {receiveOpened && } +

+ } + small + open={receiveOpened} + onOpenChange={handleOpenChange} + className={classNames("", !isPopupMode ? "max-w-[30rem]" : "")} + headerClassName={classNames( + "!m-0 !w-full", + isPopupMode ? "!text-lg" : "!text-[1.5rem]", + )} + > + +
+ ); +}; + +export default ReceiveModal; + +const TokenDetails = () => { + const [tokenSlug] = useAtom(receiveTokenAtom); + const currentToken = useAccountToken(tokenSlug ?? "") as AccountAsset; + + return currentToken ? ( + <> + + {currentToken.symbol} + + ) : null; +}; diff --git a/src/app/components/blocks/Sidebar.Links.tsx b/src/app/components/blocks/Sidebar.Links.tsx index 2390368c7..a7b1f9674 100644 --- a/src/app/components/blocks/Sidebar.Links.tsx +++ b/src/app/components/blocks/Sidebar.Links.tsx @@ -9,18 +9,20 @@ import { ReactComponent as SwapIcon } from "app/icons/SwapIcon.svg"; // import { ReactComponent as AppsIcon } from "app/icons/Apps.svg"; import { ReactComponent as ContactsIcon } from "app/icons/Contacts.svg"; import { ReactComponent as WalletsIcon } from "app/icons/Wallets.svg"; +import { ReactComponent as BuyIcon } from "app/icons/Buy-page.svg"; import { ReactComponent as SettingsIcon } from "app/icons/Settings.svg"; import { ReactComponent as SupportIcon } from "app/icons/Support.svg"; import { ReactComponent as ActivityIcon } from "app/icons/ActivityIcon.svg"; import { ReactComponent as RewardsIcon } from "app/icons/Rewards.svg"; import * as SupportAlert from "app/components/elements/SupportAlert"; import { useDialog } from "app/hooks/dialog"; -import { activityModalAtom } from "app/atoms"; +import { activityModalAtom, receiveModalAtom } from "app/atoms"; import { useActivityBadge, useSwapBadge, useAccounts } from "app/hooks"; const useSidebarLinks = () => { const { alert } = useDialog(); const setActivityOpened = useSetAtom(activityModalAtom); + const setReceiveOpened = useSetAtom(receiveModalAtom); const activityBadgeAmount = useActivityBadge(); const { currentAccount } = useAccounts(); @@ -45,9 +47,14 @@ const useSidebarLinks = () => { Icon: SendIcon, }, { - route: Page.Receive, - label: "Buy", + label: "Receive", Icon: ReceiveIcon, + action: () => setReceiveOpened([true, "replace"]), + }, + { + route: Page.Buy, + label: "Buy", + Icon: BuyIcon, }, { route: Page.Swap, @@ -67,7 +74,12 @@ const useSidebarLinks = () => { // soon: true, // }, ]; - }, [activityBadgeAmount, swapBadgeAmount, setActivityOpened]); + }, [ + activityBadgeAmount, + swapBadgeAmount, + setActivityOpened, + setReceiveOpened, + ]); const NavLinksSecondary = useMemo(() => { return [ diff --git a/src/app/components/blocks/overview/AssetInfo.tsx b/src/app/components/blocks/overview/AssetInfo.tsx index 29ea7c248..c4ff9ce06 100644 --- a/src/app/components/blocks/overview/AssetInfo.tsx +++ b/src/app/components/blocks/overview/AssetInfo.tsx @@ -36,7 +36,8 @@ import { ReactComponent as WalletExplorerIcon } from "app/icons/external-link.sv import { ReactComponent as CoinGeckoIcon } from "app/icons/coingecko.svg"; import { ReactComponent as SwapIcon } from "app/icons/swap.svg"; import { ReactComponent as SendIcon } from "app/icons/send-action.svg"; -import { ReactComponent as BuyIcon } from "app/icons/buy-action.svg"; +import { ReactComponent as ReceiveIcon } from "app/icons/buy-action.svg"; +import { ReactComponent as BuyIcon } from "app/icons/plus-rounded.svg"; import { ReactComponent as EyeIcon } from "app/icons/eye.svg"; import TokenActivity from "./TokenActivity"; @@ -223,16 +224,25 @@ const AssetInfo: FC = () => { /> -
+
+
diff --git a/src/app/components/blocks/popup/AssetCard.tsx b/src/app/components/blocks/popup/AssetCard.tsx index caaeb7fca..519d624f0 100644 --- a/src/app/components/blocks/popup/AssetCard.tsx +++ b/src/app/components/blocks/popup/AssetCard.tsx @@ -35,13 +35,14 @@ import { import { ReactComponent as ExpandIcon } from "app/icons/expand.svg"; import { ReactComponent as SwapIcon } from "app/icons/swap.svg"; import { ReactComponent as SendIcon } from "app/icons/send-action.svg"; -import { ReactComponent as BuyIcon } from "app/icons/buy-action.svg"; import { ReactComponent as CheckIcon } from "app/icons/check.svg"; import { ReactComponent as WalletExplorerIcon } from "app/icons/external-link.svg"; import { ReactComponent as CoinGeckoIcon } from "app/icons/coingecko.svg"; import { ReactComponent as SuccessIcon } from "app/icons/success.svg"; import { ReactComponent as CopyIcon } from "app/icons/copy.svg"; import { ReactComponent as EyeIcon } from "app/icons/eye.svg"; +import { ReactComponent as ReceiveIcon } from "app/icons/buy-action.svg"; +import { ReactComponent as BuyIcon } from "app/icons/plus-rounded.svg"; import FiatAmount from "app/components/elements/FiatAmount"; import AssetLogo from "app/components/elements/AssetLogo"; @@ -51,6 +52,7 @@ import PriceChange from "app/components/elements/PriceChange"; import IconedButton from "app/components/elements/IconedButton"; import PopupModal from "./PopupModal"; +import { navigate } from "lib/navigation"; type AssetCardProps = { asset: AccountAsset; @@ -178,6 +180,13 @@ const AssetCard = memo( { + navigate((s) => ({ + ...s, + receiveOpened: true, + receiveToken: asset.tokenSlug, + })); + }} onClose={() => setModalOpen(false)} asset={asset} /> @@ -217,11 +226,17 @@ const PopoverButton: FC = ({ Icon, children, ...rest }) => ( interface IAssetModalProps { open: boolean; + onReceive: () => void; onClose: () => void; asset: AccountAsset; } -const AssetModal: FC = ({ open, asset, onClose }) => { +const AssetModal: FC = ({ + open, + onReceive, + asset, + onClose, +}) => { const { balanceUSD, name, @@ -323,6 +338,17 @@ const AssetModal: FC = ({ open, asset, onClose }) => { } Icon={SendIcon} /> + diff --git a/src/app/components/elements/AddressField.tsx b/src/app/components/elements/AddressField.tsx index e363ab6ea..23b38f279 100644 --- a/src/app/components/elements/AddressField.tsx +++ b/src/app/components/elements/AddressField.tsx @@ -11,10 +11,22 @@ import { ReactComponent as CopyIcon } from "app/icons/copy.svg"; export type AddressFieldProps = LongTextFieldProps & { setFromClipboard?: (value: string) => void; + hideLabel?: boolean; + labelClassName?: string; }; const AddressField = forwardRef( - ({ label = "Recipient", setFromClipboard, className, ...rest }, ref) => { + ( + { + label = "Recipient", + setFromClipboard, + className, + hideLabel, + labelClassName, + ...rest + }, + ref, + ) => { const { paste, pasted } = usePasteFromClipboard(setFromClipboard); const { copy, copied } = useCopyToClipboard( rest.value ?? rest.defaultValue, @@ -23,9 +35,10 @@ const AddressField = forwardRef( return ( & { label?: string; labelActions?: ReactNode; + labelClassName?: string; actions?: ReactNode; textareaClassName?: string; error?: boolean; @@ -23,6 +24,7 @@ const LongTextField = memo( label, id, labelActions, + labelClassName, spellCheck = false, actions, error, @@ -42,7 +44,12 @@ const LongTextField = memo( return (
{(label || labelActions) && ( -
+
{label && (