}
diff --git a/src/app/components/app-version.tsx b/src/app/components/app-version.tsx
index bb189a2877d..617d7c93cfa 100644
--- a/src/app/components/app-version.tsx
+++ b/src/app/components/app-version.tsx
@@ -1,6 +1,6 @@
import { forwardRef, useMemo } from 'react';
-import { HTMLStyledProps, styled } from 'leather-styles/jsx';
+import { Box, HTMLStyledProps, styled } from 'leather-styles/jsx';
import { BRANCH_NAME, COMMIT_SHA } from '@shared/environment';
@@ -13,20 +13,19 @@ interface AppVersionLabelProps extends HTMLStyledProps<'span'> {
}
const AppVersionLabel = forwardRef(
({ children, isLatestVersion, ...props }: AppVersionLabelProps, ref) => (
-
- {children}
-
+
+
+ {children}
+
+
)
);
diff --git a/src/app/components/bitcoin-contract-entry-point/bitcoin-contract-entry-point-layout.tsx b/src/app/components/bitcoin-contract-entry-point/bitcoin-contract-entry-point-layout.tsx
index 08c681f7c0d..3d093e70813 100644
--- a/src/app/components/bitcoin-contract-entry-point/bitcoin-contract-entry-point-layout.tsx
+++ b/src/app/components/bitcoin-contract-entry-point/bitcoin-contract-entry-point-layout.tsx
@@ -43,9 +43,9 @@ export function BitcoinContractEntryPointLayout(props: BitcoinContractEntryPoint
- {caption}
+ {caption}
- {isLoading ? '' : {usdBalance}}
+ {isLoading ? '' : {usdBalance}}
diff --git a/src/app/components/bitcoin-custom-fee/bitcoin-custom-fee.tsx b/src/app/components/bitcoin-custom-fee/bitcoin-custom-fee.tsx
index 440fba223e0..6e52b6310db 100644
--- a/src/app/components/bitcoin-custom-fee/bitcoin-custom-fee.tsx
+++ b/src/app/components/bitcoin-custom-fee/bitcoin-custom-fee.tsx
@@ -1,5 +1,6 @@
import { Dispatch, SetStateAction, useCallback, useRef } from 'react';
+import { SendCryptoAssetSelectors } from '@tests/selectors/send.selectors';
import { Form, Formik } from 'formik';
import { Stack, styled } from 'leather-styles/jsx';
import * as yup from 'yup';
@@ -8,7 +9,7 @@ import { BtcFeeType } from '@shared/models/fees/bitcoin-fees.model';
import { createMoney } from '@shared/models/money.model';
import { openInNewTab } from '@app/common/utils/open-in-new-tab';
-import { PreviewButton } from '@app/components/preview-button';
+import { Button } from '@app/ui/components/button/button';
import { Link } from '@app/ui/components/link/link';
import { OnChooseFeeArgs } from '../bitcoin-fees-list/bitcoin-fees-list';
@@ -107,7 +108,13 @@ export function BitcoinCustomFee({
hasInsufficientBalanceError={hasInsufficientBalanceError}
/>
-
+
)}
diff --git a/src/app/components/bitcoin-fees-list/components/fees-list-item.tsx b/src/app/components/bitcoin-fees-list/components/fees-list-item.tsx
index 8d58a7dabd0..2545382af11 100644
--- a/src/app/components/bitcoin-fees-list/components/fees-list-item.tsx
+++ b/src/app/components/bitcoin-fees-list/components/fees-list-item.tsx
@@ -41,7 +41,7 @@ export function FeesListItem({
{`${feeFiatValue} | ${feeRate} sats/vB | ${feeAmount}`}
diff --git a/src/app/components/broadcast-error-dialog/broadcast-error-dialog.tsx b/src/app/components/broadcast-error-dialog/broadcast-error-dialog.tsx
new file mode 100644
index 00000000000..22a026d18b9
--- /dev/null
+++ b/src/app/components/broadcast-error-dialog/broadcast-error-dialog.tsx
@@ -0,0 +1,50 @@
+import { useLocation, useNavigate } from 'react-router-dom';
+
+import GenericError from '@assets/images/generic-error.png';
+import { Flex, styled } from 'leather-styles/jsx';
+import get from 'lodash.get';
+
+import { Button } from '@app/ui/components/button/button';
+import { Dialog } from '@app/ui/components/containers/dialog/dialog';
+import { Footer } from '@app/ui/components/containers/footers/footer';
+import { Header } from '@app/ui/components/containers/headers/header';
+
+export function BroadcastErrorDialog() {
+ const navigate = useNavigate();
+ const location = useLocation();
+ const message = get(location.state, 'message', '');
+
+ return (
+ }
+ onClose={() => navigate('..')}
+ footer={
+
+ }
+ >
+
+
+
+ Unable to broadcast transaction
+
+
+ Your transaction failed to broadcast{' '}
+ {message && <>because of the error: {message.toLowerCase()}>}
+
+
+
+ );
+}
diff --git a/src/app/components/broadcast-error-drawer/broadcast-error-drawer.layout.tsx b/src/app/components/broadcast-error-drawer/broadcast-error-drawer.layout.tsx
deleted file mode 100644
index d6b7de7d14a..00000000000
--- a/src/app/components/broadcast-error-drawer/broadcast-error-drawer.layout.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import GenericError from '@assets/images/generic-error.png';
-import { Flex, styled } from 'leather-styles/jsx';
-
-import { BaseDrawer } from '@app/components/drawer/base-drawer';
-import { Button } from '@app/ui/components/button/button';
-
-interface BroadcastErrorDrawerLayoutProps {
- message: string;
- onClose(): void;
-}
-export function BroadcastErrorDrawerLayout({ message, onClose }: BroadcastErrorDrawerLayoutProps) {
- return (
- >} isShowing onClose={onClose} textAlign="center">
-
-
-
- Unable to broadcast transaction
-
-
- Your transaction failed to broadcast{' '}
- {message && <>because of the error: {message.toLowerCase()}>}
-
-
-
-
- );
-}
diff --git a/src/app/components/broadcast-error-drawer/broadcast-error-drawer.tsx b/src/app/components/broadcast-error-drawer/broadcast-error-drawer.tsx
deleted file mode 100644
index 576ab38bec8..00000000000
--- a/src/app/components/broadcast-error-drawer/broadcast-error-drawer.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import { useLocation, useNavigate } from 'react-router-dom';
-
-import get from 'lodash.get';
-
-import { BroadcastErrorDrawerLayout } from './broadcast-error-drawer.layout';
-
-export function BroadcastErrorDrawer() {
- const navigate = useNavigate();
- const location = useLocation();
-
- return (
- navigate('..')}
- />
- );
-}
diff --git a/src/app/components/centered-page-container.tsx b/src/app/components/centered-page-container.tsx
deleted file mode 100644
index fc1d1c41d95..00000000000
--- a/src/app/components/centered-page-container.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Flex, FlexProps } from 'leather-styles/jsx';
-
-export function CenteredPageContainer(props: FlexProps) {
- return (
-
- );
-}
diff --git a/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-list.tsx b/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-list.tsx
index ee6dac95034..1dd6b2ad996 100644
--- a/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-list.tsx
+++ b/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-list.tsx
@@ -1,5 +1,8 @@
import { useNavigate } from 'react-router-dom';
+import { CryptoAssetSelectors } from '@tests/selectors/crypto-asset.selectors';
+import { Stack } from 'leather-styles/jsx';
+
import { RouteUrls } from '@shared/route-urls';
import { noop } from '@shared/utils';
@@ -8,7 +11,6 @@ import { Brc20Token } from '@app/query/bitcoin/bitcoin-client';
import { useCurrentAccountNativeSegwitAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
import { Brc20TokenAssetItemLayout } from './components/brc20-token-asset-item.layout';
-import { Brc20AssetListLayout } from './components/brc20-token-asset-list.layout';
export function Brc20TokenAssetList(props: { brc20Tokens?: Brc20Token[] }) {
const navigate = useNavigate();
@@ -28,15 +30,14 @@ export function Brc20TokenAssetList(props: { brc20Tokens?: Brc20Token[] }) {
if (!props.brc20Tokens?.length) return null;
return (
-
+
{props.brc20Tokens?.map(token => (
navigateToBrc20SendForm(token) : noop}
/>
))}
-
+
);
}
diff --git a/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/components/brc20-token-asset-item.layout.tsx b/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/components/brc20-token-asset-item.layout.tsx
index 597bdd7ac26..594349afc90 100644
--- a/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/components/brc20-token-asset-item.layout.tsx
+++ b/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/components/brc20-token-asset-item.layout.tsx
@@ -12,41 +12,33 @@ import { Pressable } from '@app/ui/pressable/pressable';
interface Brc20TokenAssetItemLayoutProps {
token: Brc20Token;
onClick?(): void;
- displayNotEnoughBalance?: boolean;
}
export function Brc20TokenAssetItemLayout({
onClick,
- displayNotEnoughBalance,
+
token,
}: Brc20TokenAssetItemLayoutProps) {
const balance = createMoney(Number(token.overall_balance), token.ticker, 0);
const formattedBalance = formatBalance(balance.amount.toString());
return (
-
-
- }
- titleLeft={token.ticker}
- captionLeft="BRC-20"
- titleRight={
-
-
- {formattedBalance.value}
-
-
- }
- />
-
-
+
+ }
+ titleLeft={token.ticker}
+ captionLeft="BRC-20"
+ titleRight={
+
+
+ {formattedBalance.value}
+
+
+ }
+ />
+
);
}
diff --git a/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/components/brc20-token-asset-list.layout.tsx b/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/components/brc20-token-asset-list.layout.tsx
deleted file mode 100644
index 41ea4db9c38..00000000000
--- a/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/components/brc20-token-asset-list.layout.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import { CryptoAssetSelectors } from '@tests/selectors/crypto-asset.selectors';
-import { Stack, StackProps } from 'leather-styles/jsx';
-
-export function Brc20AssetListLayout({ children }: StackProps) {
- return (
-
- {children}
-
- );
-}
diff --git a/src/app/components/crypto-assets/choose-crypto-asset/choose-asset-container.tsx b/src/app/components/crypto-assets/choose-crypto-asset/choose-asset-container.tsx
deleted file mode 100644
index 5eae0646903..00000000000
--- a/src/app/components/crypto-assets/choose-crypto-asset/choose-asset-container.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import { Flex } from 'leather-styles/jsx';
-
-import { HasChildren } from '@app/common/has-children';
-import { whenPageMode } from '@app/common/utils';
-
-export function ChooseAssetContainer({ children }: HasChildren) {
- return whenPageMode({
- full: (
-
- {children}
-
- ),
- popup: (
-
- {children}
-
- ),
- });
-}
diff --git a/src/app/components/crypto-assets/choose-crypto-asset/choose-crypto-asset.layout.tsx b/src/app/components/crypto-assets/choose-crypto-asset/choose-crypto-asset.layout.tsx
deleted file mode 100644
index 137ce63fe77..00000000000
--- a/src/app/components/crypto-assets/choose-crypto-asset/choose-crypto-asset.layout.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import { Box, Flex, StackProps, styled } from 'leather-styles/jsx';
-
-export function ChooseCryptoAssetLayout({ children, title }: StackProps & { title: string }) {
- return (
-
-
-
- {title}
-
-
- {children}
-
- );
-}
diff --git a/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list.layout.tsx b/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list.layout.tsx
deleted file mode 100644
index c5c9418347b..00000000000
--- a/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list.layout.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import { CryptoAssetSelectors } from '@tests/selectors/crypto-asset.selectors';
-import { Stack, StackProps } from 'leather-styles/jsx';
-
-export function CryptoAssetListLayout({ children }: StackProps) {
- return (
-
- {children}
-
- );
-}
diff --git a/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list.tsx b/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list.tsx
index b9db5559af2..df157015680 100644
--- a/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list.tsx
+++ b/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list.tsx
@@ -1,3 +1,6 @@
+import { CryptoAssetSelectors } from '@tests/selectors/crypto-asset.selectors';
+import { Stack } from 'leather-styles/jsx';
+
import type { AllTransferableCryptoAssetBalances } from '@shared/models/crypto-asset-balance.model';
import { StacksFungibleTokenAsset } from '@shared/models/crypto-asset.model';
@@ -10,17 +13,21 @@ import { BtcAvatarIcon } from '@app/ui/components/avatar/btc-avatar-icon';
import { CryptoCurrencyAssetItemLayout } from '../crypto-currency-asset/crypto-currency-asset-item.layout';
import { CryptoAssetListItem } from './crypto-asset-list-item';
-import { CryptoAssetListLayout } from './crypto-asset-list.layout';
interface CryptoAssetListProps {
cryptoAssetBalances: AllTransferableCryptoAssetBalances[];
onItemClick(cryptoAssetBalance: AllTransferableCryptoAssetBalances): void;
+ variant: 'send' | 'fund';
}
-export function CryptoAssetList({ cryptoAssetBalances, onItemClick }: CryptoAssetListProps) {
+export function CryptoAssetList({
+ cryptoAssetBalances,
+ onItemClick,
+ variant,
+}: CryptoAssetListProps) {
const { whenWallet } = useWalletType();
return (
-
+
{signer => (
@@ -44,18 +51,19 @@ export function CryptoAssetList({ cryptoAssetBalances, onItemClick }: CryptoAsse
}
/>
))}
- {whenWallet({
- software: (
-
- {() => (
-
- {brc20Tokens => }
-
- )}
-
- ),
- ledger: null,
- })}
-
+ {variant === 'send' &&
+ whenWallet({
+ software: (
+
+ {() => (
+
+ {brc20Tokens => }
+
+ )}
+
+ ),
+ ledger: null,
+ })}
+
);
}
diff --git a/src/app/components/disclaimer.tsx b/src/app/components/disclaimer.tsx
index fbd8a9e72d5..0ef74549f06 100644
--- a/src/app/components/disclaimer.tsx
+++ b/src/app/components/disclaimer.tsx
@@ -10,7 +10,7 @@ interface DisclaimerProps extends BoxProps {
export function Disclaimer({ disclaimerText, learnMoreUrl, ...props }: DisclaimerProps) {
return (
-
+
{disclaimerText}
{learnMoreUrl ? (
openInNewTab(learnMoreUrl)}>
diff --git a/src/app/components/drawer/base-drawer.tsx b/src/app/components/drawer/base-drawer.tsx
deleted file mode 100644
index 0f393401cb3..00000000000
--- a/src/app/components/drawer/base-drawer.tsx
+++ /dev/null
@@ -1,127 +0,0 @@
-import { ReactNode, Suspense, memo, useCallback, useRef } from 'react';
-import { useNavigate } from 'react-router-dom';
-
-import { Box, Flex, FlexProps } from 'leather-styles/jsx';
-
-import { noop } from '@shared/utils';
-
-import { useEventListener } from '@app/common/hooks/use-event-listener';
-import { useOnClickOutside } from '@app/common/hooks/use-onclickoutside';
-
-import { DrawerHeader } from './components/drawer-header';
-
-function useDrawer(isShowing: boolean, onClose: () => void, pause?: boolean) {
- const ref = useRef(null);
-
- const handleKeyDown = useCallback(
- (e: React.KeyboardEvent) => {
- if (isShowing && e.key === 'Escape') {
- onClose();
- }
- },
- [onClose, isShowing]
- );
-
- useOnClickOutside(ref, !pause && isShowing ? onClose : null);
- useEventListener('keydown', handleKeyDown);
-
- return ref;
-}
-
-interface BaseDrawerProps extends Omit {
- children?: ReactNode;
- enableGoBack?: boolean;
- icon?: React.JSX.Element;
- isShowing: boolean;
- isWaitingOnPerformedAction?: boolean;
- onClose?(): void;
- pauseOnClickOutside?: boolean;
- title?: string;
- waitingOnPerformedActionMessage?: string;
-}
-export const BaseDrawer = memo((props: BaseDrawerProps) => {
- const {
- children,
- enableGoBack,
- icon,
- isShowing,
- isWaitingOnPerformedAction,
- onClose,
- pauseOnClickOutside,
- title,
- waitingOnPerformedActionMessage,
- ...rest
- } = props;
- const ref = useDrawer(isShowing, onClose ? onClose : noop, pauseOnClickOutside);
- const navigate = useNavigate();
-
- const onGoBack = () => navigate(-1);
-
- return (
-
-
-
-
-
- >}>{children}
-
-
-
-
- );
-});
diff --git a/src/app/components/drawer/components/drawer-header.tsx b/src/app/components/drawer/components/drawer-header.tsx
deleted file mode 100644
index c0faa9e2e2c..00000000000
--- a/src/app/components/drawer/components/drawer-header.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import { Box, Flex, styled } from 'leather-styles/jsx';
-import { useHover } from 'use-events';
-
-import { ArrowLeftIcon } from '@app/ui/icons/arrow-left-icon';
-import { CloseIcon } from '@app/ui/icons/close-icon';
-
-import { HeaderActionButton } from './header-action-button';
-
-interface DrawerHeaderProps {
- enableGoBack?: boolean;
- icon?: React.JSX.Element;
- isWaitingOnPerformedAction?: boolean;
- onClose?(): void;
- onGoBack(): void;
- title?: string;
- waitingOnPerformedActionMessage?: string;
-}
-export function DrawerHeader({
- enableGoBack,
- icon,
- isWaitingOnPerformedAction,
- onClose,
- onGoBack,
- title,
- waitingOnPerformedActionMessage,
-}: DrawerHeaderProps) {
- const [isHovered, bind] = useHover();
-
- return (
-
- {enableGoBack ? (
- }
- isWaitingOnPerformedAction={isWaitingOnPerformedAction}
- onAction={onGoBack}
- />
- ) : (
-
- )}
- {icon && icon}
- {title && {title}}
- {isHovered && isWaitingOnPerformedAction && (
-
- {waitingOnPerformedActionMessage}
-
- )}
- {onClose && (
- }
- isWaitingOnPerformedAction={isWaitingOnPerformedAction}
- onAction={onClose}
- />
- )}
-
- );
-}
diff --git a/src/app/components/drawer/components/header-action-button.tsx b/src/app/components/drawer/components/header-action-button.tsx
deleted file mode 100644
index 3fa5c4308a0..00000000000
--- a/src/app/components/drawer/components/header-action-button.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import { HomePageSelectors } from '@tests/selectors/home.selectors';
-import { Grid } from 'leather-styles/jsx';
-
-interface HeaderActionButtonProps {
- icon?: React.JSX.Element;
- isWaitingOnPerformedAction?: boolean;
- onAction?(): void;
-}
-export function HeaderActionButton(props: HeaderActionButtonProps) {
- const { icon, isWaitingOnPerformedAction, onAction } = props;
-
- return (
-
- {icon}
-
- );
-}
diff --git a/src/app/components/drawer/controlled-drawer.tsx b/src/app/components/drawer/controlled-drawer.tsx
deleted file mode 100644
index 02819286cbe..00000000000
--- a/src/app/components/drawer/controlled-drawer.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import { ReactNode } from 'react';
-
-import { BaseDrawer } from './base-drawer';
-
-interface ControlledDrawerProps {
- children?: ReactNode;
- enableGoBack?: boolean;
- icon?: React.JSX.Element;
- isShowing: boolean;
- onClose(): void;
- pauseOnClickOutside?: boolean;
- title?: string;
-}
-// The visibility of this drawer is controlled by an atom
-export function ControlledDrawer(props: ControlledDrawerProps) {
- const { children, enableGoBack, icon, isShowing, onClose, pauseOnClickOutside, title } = props;
-
- return (
-
- {children}
-
- );
-}
diff --git a/src/app/components/generic-error/generic-error.tsx b/src/app/components/generic-error/generic-error.tsx
index 6c4e401f977..757458e2b39 100644
--- a/src/app/components/generic-error/generic-error.tsx
+++ b/src/app/components/generic-error/generic-error.tsx
@@ -4,9 +4,6 @@ import { FlexProps, styled } from 'leather-styles/jsx';
import { closeWindow } from '@shared/utils';
-import { useRouteHeader } from '@app/common/hooks/use-route-header';
-import { Header } from '@app/components/header';
-
import { GenericErrorLayout } from './generic-error.layout';
export function GenericErrorListItem({ text }: { text: ReactNode }) {
@@ -23,8 +20,6 @@ interface GenericErrorProps extends FlexProps {
export function GenericError(props: GenericErrorProps) {
const { body, helpTextList, onClose = () => closeWindow(), title, ...rest } = props;
- useRouteHeader();
-
return (
- {onClose ? (
-
-
-
- ) : null}
- {!title && (!onClose || isBreakpointSm) ? (
-
-
- navigate(RouteUrls.Home)}
- />
-
-
-
- ) : (
-
- {title}
-
- )}
-
-
- {!hideActions && (
-
- )}
- {actionButton ? actionButton : null}
-
-
- );
-}
diff --git a/src/app/components/info-card/info-card.tsx b/src/app/components/info-card/info-card.tsx
index 178ffd1a03a..6795b927d17 100644
--- a/src/app/components/info-card/info-card.tsx
+++ b/src/app/components/info-card/info-card.tsx
@@ -5,7 +5,6 @@ import { Box, BoxProps, Flex, FlexProps, HStack, Stack, styled } from 'leather-s
import { isString } from '@shared/utils';
-import { whenPageMode } from '@app/common/utils';
import { Button } from '@app/ui/components/button/button';
import { DashedHr } from '@app/ui/components/hr';
@@ -13,6 +12,7 @@ import { DashedHr } from '@app/ui/components/hr';
interface InfoCardProps extends FlexProps {
children: ReactNode;
}
+/** @deprecated - replace with ui/card */
export function InfoCard({ children, ...props }: InfoCardProps) {
return (
@@ -120,23 +120,18 @@ export function InfoCardBtn({ icon, label, onClick }: InfoCardBtnProps) {
interface InfoCardFooterProps {
children: ReactNode;
}
+/** @deprecated replace with ui/footer */
export function InfoCardFooter({ children }: InfoCardFooterProps) {
return (
{children}
diff --git a/src/app/components/inscription-preview-card/components/inscription-metadata.tsx b/src/app/components/inscription-preview-card/components/inscription-metadata.tsx
index b31368f81de..869bfe31f7b 100644
--- a/src/app/components/inscription-preview-card/components/inscription-metadata.tsx
+++ b/src/app/components/inscription-preview-card/components/inscription-metadata.tsx
@@ -20,9 +20,9 @@ export function InscriptionMetadata({
{icon && icon}
{title}
- {subtitle}
+ {subtitle}
{action ? (
- action()} textStyle="caption.02" variant="text">
+ action()} textStyle="caption.01" variant="text">
{actionLabel}
) : null}
diff --git a/src/app/components/leather-logo.tsx b/src/app/components/leather-logo.tsx
deleted file mode 100644
index 8ec8f24dc8c..00000000000
--- a/src/app/components/leather-logo.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import { memo } from 'react';
-
-import { styled } from 'leather-styles/jsx';
-
-import { LogomarkIcon } from '@app/ui/icons/logomark-icon';
-
-interface LeatherLogoProps {
- onClick?(): void;
-}
-export const LeatherLogo = memo((props: LeatherLogoProps) => {
- const { onClick } = props;
-
- return (
-
-
-
- );
-});
diff --git a/src/app/components/modal-header.tsx b/src/app/components/modal-header.tsx
deleted file mode 100644
index b1d086e90ce..00000000000
--- a/src/app/components/modal-header.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-import { useNavigate } from 'react-router-dom';
-
-import { SharedComponentsSelectors } from '@tests/selectors/shared-component.selectors';
-import { Box, Flex, styled } from 'leather-styles/jsx';
-import { token } from 'leather-styles/tokens';
-
-import { RouteUrls } from '@shared/route-urls';
-
-import { NetworkModeBadge } from '@app/components/network-mode-badge';
-import { Button } from '@app/ui/components/button/button';
-import { ArrowLeftIcon } from '@app/ui/icons/arrow-left-icon';
-import { CloseIcon } from '@app/ui/icons/close-icon';
-
-interface ModalHeaderProps {
- actionButton?: React.JSX.Element;
- closeIcon?: boolean;
- hideActions?: boolean;
- onClose?(): void;
- onGoBack?(): void;
- defaultClose?: boolean;
- defaultGoBack?: boolean;
- title: string;
-}
-
-export function ModalHeader({
- actionButton,
- hideActions,
- onClose,
- onGoBack,
- closeIcon,
- title,
- defaultGoBack,
- defaultClose,
- ...rest
-}: ModalHeaderProps) {
- const navigate = useNavigate();
-
- function defaultCloseAction() {
- navigate(RouteUrls.Home);
- }
- function defaultGoBackAction() {
- navigate(-1);
- }
-
- const hasCloseIcon = onClose || defaultClose;
-
- return (
-
- {onGoBack || defaultGoBack ? (
-
-
-
- ) : (
-
- )}
-
-
-
- {title}
-
-
-
-
-
- {hasCloseIcon && (
-
- )}
-
-
- );
-}
diff --git a/src/app/components/network-mode-badge.tsx b/src/app/components/network-mode-badge.tsx
deleted file mode 100644
index d8067de7392..00000000000
--- a/src/app/components/network-mode-badge.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import { memo, useMemo } from 'react';
-import { useNavigate } from 'react-router-dom';
-
-import { ChainID } from '@stacks/transactions';
-import { Flex, FlexProps } from 'leather-styles/jsx';
-
-import { RouteUrls } from '@shared/route-urls';
-
-import { useCurrentNetworkState } from '@app/store/networks/networks.hooks';
-import { Tag } from '@app/ui/components/tag/tag';
-
-export const NetworkModeBadge = memo((props: FlexProps) => {
- const navigate = useNavigate();
- const { chain, name } = useCurrentNetworkState();
- const isTestnetChain = useMemo(
- () => chain.stacks.chainId === ChainID.Testnet,
- [chain.stacks.chainId]
- );
-
- if (!isTestnetChain) return null;
-
- return (
- navigate(RouteUrls.SelectNetwork, { relative: 'path' })}
- position="relative"
- zIndex={999}
- {...props}
- >
-
-
- );
-});
diff --git a/src/app/components/no-fees-warning-row.tsx b/src/app/components/no-fees-warning-row.tsx
index 3ed6cfedf31..420bf92a6d5 100644
--- a/src/app/components/no-fees-warning-row.tsx
+++ b/src/app/components/no-fees-warning-row.tsx
@@ -9,8 +9,8 @@ interface NoFeesWarningRowProps {
export function NoFeesWarningRow({ chainId }: NoFeesWarningRowProps) {
return (
- No fees are incurred
-
+ No fees are incurred
+
{whenStacksChainId(chainId)({
[ChainID.Testnet]: 'Testnet',
[ChainID.Mainnet]: 'Mainnet',
diff --git a/src/app/components/preview-button.tsx b/src/app/components/preview-button.tsx
deleted file mode 100644
index cf624992db5..00000000000
--- a/src/app/components/preview-button.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { SendCryptoAssetSelectors } from '@tests/selectors/send.selectors';
-import { useFormikContext } from 'formik';
-
-import { Button } from '@app/ui/components/button/button';
-
-interface PreviewButtonProps {
- text?: string;
- isDisabled?: boolean;
-}
-export function PreviewButton({ text = 'Continue', isDisabled, ...props }: PreviewButtonProps) {
- const { handleSubmit } = useFormikContext();
-
- return (
-
- );
-}
diff --git a/src/app/components/request-password.tsx b/src/app/components/request-password.tsx
index 1e87f6c88b1..f1a3f30a70b 100644
--- a/src/app/components/request-password.tsx
+++ b/src/app/components/request-password.tsx
@@ -1,16 +1,19 @@
-import { FormEvent, ReactNode, useCallback, useState } from 'react';
+import { FormEvent, useCallback, useState } from 'react';
import { SettingsSelectors } from '@tests/selectors/settings.selectors';
-import { Stack, styled } from 'leather-styles/jsx';
+import { Box, Stack, styled } from 'leather-styles/jsx';
import { useAnalytics } from '@app/common/hooks/analytics/use-analytics';
import { useKeyActions } from '@app/common/hooks/use-key-actions';
import { buildEnterKeyEvent } from '@app/common/hooks/use-modifier-key';
-import { WaitingMessages, useWaitingMessage } from '@app/common/utils/use-waiting-message';
+import { WaitingMessages, useWaitingMessage } from '@app/common/hooks/use-waiting-message';
import { Button } from '@app/ui/components/button/button';
+import { Footer } from '@app/ui/components/containers/footers/footer';
+import { Logo } from '@app/ui/components/logo';
+import { Card } from '@app/ui/layout/card/card';
+import { Page } from '@app/ui/layout/page/page.layout';
import { ErrorLabel } from './error-label';
-import { TwoColumnLayout } from './secret-key/two-column.layout';
const waitingMessages: WaitingMessages = {
'2': 'Verifying password…',
@@ -18,12 +21,13 @@ const waitingMessages: WaitingMessages = {
'20': 'Almost there',
};
+const caption =
+ 'Your password is used to secure your Secret Key and is only used locally on your device.';
+
interface RequestPasswordProps {
onSuccess(): void;
- title?: ReactNode;
- caption?: string;
}
-export function RequestPassword({ title, caption, onSuccess }: RequestPasswordProps) {
+export function RequestPassword({ onSuccess }: RequestPasswordProps) {
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const { unlockWallet } = useKeyActions();
@@ -50,72 +54,62 @@ export function RequestPassword({ title, caption, onSuccess }: RequestPasswordPr
}, [analytics, startWaitingMessage, stopWaitingMessage, unlockWallet, password, onSuccess]);
return (
- <>
-
-
- {title}
-
-
- {(isRunning && waitingMessage) || caption}
-
- >
+
+
+
+
+
+
}
- rightColumn={
- <>
-
- Your password
-
-
- ) => {
- setError('');
- setPassword(e.currentTarget.value);
- }}
- onKeyUp={buildEnterKeyEvent(submit)}
- p="space.04"
- placeholder="Enter your password"
- ring="none"
- type="password"
- textStyle="body.02"
- value={password}
- width="100%"
- />
- {error && {error}}
-
+ footer={
+
}
- />
- >
+ >
+
+ Enter your password
+ {(isRunning && waitingMessage) || caption}
+ ) => {
+ setError('');
+ setPassword(e.currentTarget.value);
+ }}
+ onKeyUp={buildEnterKeyEvent(submit)}
+ p="space.04"
+ placeholder="Enter your password"
+ ring="none"
+ type="password"
+ textStyle="body.02"
+ value={password}
+ width="100%"
+ />
+ {error && {error}}
+
+
+ {/* TODO: #4735 implement forgot password flow */}
+ {/*
+ Forgot password?
+ */}
+
+
);
}
diff --git a/src/app/components/requester-flag.tsx b/src/app/components/requester-flag.tsx
index 676a0ae68f6..7c4b213bf05 100644
--- a/src/app/components/requester-flag.tsx
+++ b/src/app/components/requester-flag.tsx
@@ -12,8 +12,7 @@ export function RequesterFlag({ requester }: RequesterFlagProps) {
return (
}
- align="top"
- alignItems="center"
+ align="middle"
justifyContent="center"
py="space.04"
px="space.02"
diff --git a/src/app/components/secret-key/secret-key-grid.tsx b/src/app/components/secret-key/secret-key-grid.tsx
deleted file mode 100644
index c791d7acd1c..00000000000
--- a/src/app/components/secret-key/secret-key-grid.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { Grid, Stack } from 'leather-styles/jsx';
-
-interface SecretKeyGridProps {
- children: React.ReactNode;
-}
-export function SecretKeyGrid({ children }: SecretKeyGridProps) {
- return (
-
-
- {children}
-
-
- );
-}
diff --git a/src/app/components/secret-key/two-column.layout.tsx b/src/app/components/secret-key/two-column.layout.tsx
deleted file mode 100644
index 7ea7e48e29e..00000000000
--- a/src/app/components/secret-key/two-column.layout.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import { Flex, Stack, styled } from 'leather-styles/jsx';
-
-interface TwoColumnLayoutProps {
- leftColumn: React.JSX.Element;
- rightColumn: React.JSX.Element;
-}
-
-export function TwoColumnLayout({
- leftColumn,
- rightColumn,
-}: TwoColumnLayoutProps): React.JSX.Element {
- return (
-
-
-
- {leftColumn}
-
-
-
-
-
- {rightColumn}
-
-
-
- );
-}
diff --git a/src/app/components/stacks-transaction-item/increase-fee-button.tsx b/src/app/components/stacks-transaction-item/increase-fee-button.tsx
index fe309deae11..84f62c82928 100644
--- a/src/app/components/stacks-transaction-item/increase-fee-button.tsx
+++ b/src/app/components/stacks-transaction-item/increase-fee-button.tsx
@@ -1,5 +1,6 @@
import { HStack, styled } from 'leather-styles/jsx';
+import { whenPageMode } from '@app/common/utils';
import { ChevronsRightIcon } from '@app/ui/icons/chevrons-right-icon';
interface IncreaseFeeButtonProps {
@@ -15,7 +16,7 @@ export function IncreaseFeeButton(props: IncreaseFeeButtonProps) {
{
onIncreaseFee();
@@ -32,7 +33,12 @@ export function IncreaseFeeButton(props: IncreaseFeeButtonProps) {
>
- Increase fee
+
+ {whenPageMode({
+ popup: 'Fee',
+ full: 'Increase fee',
+ })}
+
);
diff --git a/src/app/components/status-ready.tsx b/src/app/components/status-ready.tsx
index 1b28e08a11c..d0aabde4d7d 100644
--- a/src/app/components/status-ready.tsx
+++ b/src/app/components/status-ready.tsx
@@ -5,7 +5,7 @@ export function StatusReady() {
width: '8px',
height: '8px',
borderRadius: '50%',
- backgroundColor: '#23A978',
+ background: '#23A978',
}}
/>
);
diff --git a/src/app/features/activity-list/components/tab-wrapper.tsx b/src/app/features/activity-list/components/tab-wrapper.tsx
index 7d82c501831..f6b5220731b 100644
--- a/src/app/features/activity-list/components/tab-wrapper.tsx
+++ b/src/app/features/activity-list/components/tab-wrapper.tsx
@@ -10,8 +10,7 @@ export function ActivityListTabWrapper({
padContent = false,
}: ActivityListTabWrapperProps) {
return (
- // Height set based on the height of the empty assets screen
-
+
{children}
);
diff --git a/src/app/features/add-network/add-network-form.tsx b/src/app/features/add-network/add-network-form.tsx
new file mode 100644
index 00000000000..88c9066961e
--- /dev/null
+++ b/src/app/features/add-network/add-network-form.tsx
@@ -0,0 +1,134 @@
+import { useCallback, useEffect } from 'react';
+
+import { SelectContent, SelectItem, SelectRoot, SelectTrigger } from '@radix-ui/themes';
+import { NetworkSelectors } from '@tests/selectors/network.selectors';
+import { useFormikContext } from 'formik';
+import { css } from 'leather-styles/css';
+
+import { Input } from '@app/ui/components/input/input';
+import { Title } from '@app/ui/components/typography/title';
+
+import { type AddNetworkFormValues, useAddNetwork } from './use-add-network';
+
+export function AddNetworkForm() {
+ const { handleChange, setFieldValue, values } = useFormikContext();
+
+ const { bitcoinApi, handleApiChange } = useAddNetwork();
+
+ const setStacksUrl = useCallback(
+ (value: string) => {
+ void setFieldValue('stacksUrl', value);
+ },
+ [setFieldValue]
+ );
+
+ const setBitcoinUrl = useCallback(
+ (value: string) => {
+ void setFieldValue('bitcoinUrl', value);
+ },
+ [setFieldValue]
+ );
+
+ useEffect(() => {
+ switch (bitcoinApi) {
+ case 'mainnet':
+ setStacksUrl('https://api.hiro.so');
+ setBitcoinUrl('https://blockstream.info/api');
+ break;
+ case 'testnet':
+ setStacksUrl('https://api.testnet.hiro.so');
+ setBitcoinUrl('https://blockstream.info/testnet/api');
+ break;
+ case 'signet':
+ setStacksUrl('https://api.testnet.hiro.so');
+ setBitcoinUrl('https://mempool.space/signet/api');
+ break;
+ case 'regtest':
+ setStacksUrl('https://api.testnet.hiro.so');
+ setBitcoinUrl('https://mempool.space/testnet/api');
+ break;
+ }
+ }, [bitcoinApi, setStacksUrl, setBitcoinUrl]);
+
+ return (
+ <>
+
+ Name
+
+
+ Bitcoin API
+ {/* TODO: Replace with new Select */}
+
+
+
+
+ Mainnet
+
+
+ Testnet
+
+
+ Signet
+
+
+ Regtest
+
+
+
+ Stacks API URL
+
+ Name
+
+
+ Bitcoin API URL
+
+ Bitcoin API URL
+
+
+
+ Network key
+
+
+ >
+ );
+}
diff --git a/src/app/features/add-network/add-network.tsx b/src/app/features/add-network/add-network.tsx
index ed2af5067b7..6c85285c705 100644
--- a/src/app/features/add-network/add-network.tsx
+++ b/src/app/features/add-network/add-network.tsx
@@ -1,213 +1,28 @@
-import { useCallback, useEffect, useState } from 'react';
-import { useNavigate } from 'react-router-dom';
-
-import { SelectContent, SelectItem, SelectRoot, SelectTrigger } from '@radix-ui/themes';
-import { ChainID } from '@stacks/transactions';
import { NetworkSelectors } from '@tests/selectors/network.selectors';
-import { Formik, useFormik } from 'formik';
-import { css } from 'leather-styles/css';
+import { Form, Formik } from 'formik';
import { Stack, styled } from 'leather-styles/jsx';
-import { BitcoinNetworkModes, DefaultNetworkConfigurations } from '@shared/constants';
-import { RouteUrls } from '@shared/route-urls';
-import { isValidUrl } from '@shared/utils/validate-url';
-
-import { useRouteHeader } from '@app/common/hooks/use-route-header';
-import { removeTrailingSlash } from '@app/common/url-join';
-import { CenteredPageContainer } from '@app/components/centered-page-container';
import { ErrorLabel } from '@app/components/error-label';
-import { Header } from '@app/components/header';
-import {
- useCurrentStacksNetworkState,
- useNetworksActions,
-} from '@app/store/networks/networks.hooks';
import { Button } from '@app/ui/components/button/button';
-import { Input } from '@app/ui/components/input/input';
-import { Title } from '@app/ui/components/typography/title';
+import { Page } from '@app/ui/layout/page/page.layout';
-/**
- * The **peer** network ID.
- * Not used in signing, but needed to determine the parent of a subnet.
- */
-enum PeerNetworkID {
- Mainnet = 0x17000000,
- Testnet = 0xff000000,
-}
-
-interface AddNetworkFormValues {
- key: string;
- name: string;
- stacksUrl: string;
- bitcoinUrl: string;
-}
-const addNetworkFormValues: AddNetworkFormValues = {
- key: '',
- name: '',
- stacksUrl: '',
- bitcoinUrl: '',
-};
+import { AddNetworkForm } from './add-network-form';
+import { useAddNetwork } from './use-add-network';
export function AddNetwork() {
- const [loading, setLoading] = useState(false);
- const [error, setError] = useState('');
- const navigate = useNavigate();
- const network = useCurrentStacksNetworkState();
- const networksActions = useNetworksActions();
- const [bitcoinApi, setBitcoinApi] = useState('mainnet');
-
- const formikProps = useFormik({
- initialValues: addNetworkFormValues,
- onSubmit: () => {},
- });
-
- const { setFieldValue } = formikProps;
-
- useRouteHeader( navigate(RouteUrls.Home)} />);
-
- const handleApiChange = (newValue: BitcoinNetworkModes) => {
- setBitcoinApi(newValue);
- };
-
- const setStacksUrl = useCallback(
- (value: string) => {
- void setFieldValue('stacksUrl', value);
- },
- [setFieldValue]
- );
-
- const setBitcoinUrl = useCallback(
- (value: string) => {
- void setFieldValue('bitcoinUrl', value);
- },
- [setFieldValue]
- );
-
- useEffect(() => {
- switch (bitcoinApi) {
- case 'mainnet':
- setStacksUrl('https://api.hiro.so');
- setBitcoinUrl('https://blockstream.info/api');
- break;
- case 'testnet':
- setStacksUrl('https://api.testnet.hiro.so');
- setBitcoinUrl('https://blockstream.info/testnet/api');
- break;
- case 'signet':
- setStacksUrl('https://api.testnet.hiro.so');
- setBitcoinUrl('https://mempool.space/signet/api');
- break;
- case 'regtest':
- setStacksUrl('https://api.testnet.hiro.so');
- setBitcoinUrl('https://mempool.space/testnet/api');
- break;
- }
- }, [bitcoinApi, setStacksUrl, setBitcoinUrl]);
+ const { error, initialFormValues, loading, onSubmit } = useAddNetwork();
return (
-
- {
- const { name, stacksUrl, bitcoinUrl, key } = formikProps.values;
-
- if (!name) {
- setError('Enter a name');
- return;
- }
-
- if (!isValidUrl(stacksUrl)) {
- setError('Enter a valid Stacks API URL');
- return;
- }
-
- if (!isValidUrl(bitcoinUrl)) {
- setError('Enter a valid Bitcoin API URL');
- return;
- }
-
- if (!key) {
- setError('Enter a unique key');
- return;
- }
-
- setLoading(true);
- setError('');
-
- const stacksPath = removeTrailingSlash(new URL(formikProps.values.stacksUrl).href);
- const bitcoinPath = removeTrailingSlash(new URL(formikProps.values.bitcoinUrl).href);
-
- try {
- const bitcoinResponse = await network.fetchFn(`${bitcoinPath}/mempool/recent`);
- if (!bitcoinResponse.ok) throw new Error('Unable to fetch mempool from bitcoin node');
- const bitcoinMempool = await bitcoinResponse.json();
- if (!Array.isArray(bitcoinMempool))
- throw new Error('Unable to fetch mempool from bitcoin node');
- } catch (error) {
- setError('Unable to fetch mempool from bitcoin node');
- setLoading(false);
- return;
- }
-
- let stacksChainInfo: any;
- try {
- const stacksResponse = await network.fetchFn(`${stacksPath}/v2/info`);
- stacksChainInfo = await stacksResponse.json();
- if (!stacksChainInfo) throw new Error('Unable to fetch info from stacks node');
- } catch (error) {
- setError('Unable to fetch info from stacks node');
- setLoading(false);
- return;
- }
-
- // Attention:
- // For mainnet/testnet the v2/info response `.network_id` refers to the chain ID
- // For subnets the v2/info response `.network_id` refers to the network ID and the chain ID (they are the same for subnets)
- // The `.parent_network_id` refers to the actual peer network ID in both cases
- const { network_id: chainId, parent_network_id: parentNetworkId } = stacksChainInfo;
-
- const isSubnet = typeof stacksChainInfo.l1_subnet_governing_contract === 'string';
- const isFirstLevelSubnet =
- isSubnet &&
- (parentNetworkId === PeerNetworkID.Mainnet ||
- parentNetworkId === PeerNetworkID.Testnet);
-
- // Currently, only subnets of mainnet and testnet are supported in the wallet
- if (isFirstLevelSubnet) {
- const parentChainId =
- parentNetworkId === PeerNetworkID.Mainnet ? ChainID.Mainnet : ChainID.Testnet;
- networksActions.addNetwork({
- id: key as DefaultNetworkConfigurations,
- name: name,
- chainId: parentChainId, // Used for differentiating control flow in the wallet
- subnetChainId: chainId, // Used for signing transactions (via the network object, not to be confused with the NetworkConfigurations)
- url: stacksPath,
- bitcoinNetwork: bitcoinApi,
- bitcoinUrl: bitcoinPath,
- });
- navigate(RouteUrls.Home);
- } else if (chainId === ChainID.Mainnet || chainId === ChainID.Testnet) {
- networksActions.addNetwork({
- id: key as DefaultNetworkConfigurations,
- name: name,
- chainId: chainId,
- url: stacksPath,
- bitcoinNetwork: bitcoinApi,
- bitcoinUrl: bitcoinPath,
- });
- navigate(RouteUrls.Home);
- } else {
- setError('Unable to determine chainID from node.');
- }
- setLoading(false);
- }}
- >
- {({ handleSubmit }) => (
-
)}
-
+
);
}
diff --git a/src/app/features/add-network/use-add-network.tsx b/src/app/features/add-network/use-add-network.tsx
new file mode 100644
index 00000000000..52b62371df9
--- /dev/null
+++ b/src/app/features/add-network/use-add-network.tsx
@@ -0,0 +1,146 @@
+import { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+
+import { ChainID } from '@stacks/transactions';
+
+import type { BitcoinNetworkModes, DefaultNetworkConfigurations } from '@shared/constants';
+import { RouteUrls } from '@shared/route-urls';
+import { isValidUrl } from '@shared/utils/validate-url';
+
+import { removeTrailingSlash } from '@app/common/url-join';
+import {
+ useCurrentStacksNetworkState,
+ useNetworksActions,
+} from '@app/store/networks/networks.hooks';
+
+/**
+ * The **peer** network ID.
+ * Not used in signing, but needed to determine the parent of a subnet.
+ */
+enum PeerNetworkID {
+ Mainnet = 0x17000000,
+ Testnet = 0xff000000,
+}
+
+export interface AddNetworkFormValues {
+ key: string;
+ name: string;
+ stacksUrl: string;
+ bitcoinUrl: string;
+}
+
+const initialFormValues: AddNetworkFormValues = {
+ key: '',
+ name: '',
+ stacksUrl: '',
+ bitcoinUrl: '',
+};
+
+export function useAddNetwork() {
+ const [bitcoinApi, setBitcoinApi] = useState('mainnet');
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState('');
+ const navigate = useNavigate();
+ const network = useCurrentStacksNetworkState();
+ const networksActions = useNetworksActions();
+
+ return {
+ bitcoinApi,
+ handleApiChange: (value: BitcoinNetworkModes) => setBitcoinApi(value),
+ error,
+ initialFormValues,
+ loading,
+ onSubmit: async (values: AddNetworkFormValues) => {
+ const { name, stacksUrl, bitcoinUrl, key } = values;
+
+ if (!name) {
+ setError('Enter a name');
+ return;
+ }
+
+ if (!isValidUrl(stacksUrl)) {
+ setError('Enter a valid Stacks API URL');
+ return;
+ }
+
+ if (!isValidUrl(bitcoinUrl)) {
+ setError('Enter a valid Bitcoin API URL');
+ return;
+ }
+
+ if (!key) {
+ setError('Enter a unique key');
+ return;
+ }
+
+ setLoading(true);
+ setError('');
+
+ const stacksPath = removeTrailingSlash(new URL(values.stacksUrl).href);
+ const bitcoinPath = removeTrailingSlash(new URL(values.bitcoinUrl).href);
+
+ try {
+ const bitcoinResponse = await network.fetchFn(`${bitcoinPath}/mempool/recent`);
+ if (!bitcoinResponse.ok) throw new Error('Unable to fetch mempool from bitcoin node');
+ const bitcoinMempool = await bitcoinResponse.json();
+ if (!Array.isArray(bitcoinMempool))
+ throw new Error('Unable to fetch mempool from bitcoin node');
+ } catch (error) {
+ setError('Unable to fetch mempool from bitcoin node');
+ setLoading(false);
+ return;
+ }
+
+ let stacksChainInfo: any;
+ try {
+ const stacksResponse = await network.fetchFn(`${stacksPath}/v2/info`);
+ stacksChainInfo = await stacksResponse.json();
+ if (!stacksChainInfo) throw new Error('Unable to fetch info from stacks node');
+ } catch (error) {
+ setError('Unable to fetch info from stacks node');
+ setLoading(false);
+ return;
+ }
+
+ // Attention:
+ // For mainnet/testnet the v2/info response `.network_id` refers to the chain ID
+ // For subnets the v2/info response `.network_id` refers to the network ID and the chain ID (they are the same for subnets)
+ // The `.parent_network_id` refers to the actual peer network ID in both cases
+ const { network_id: chainId, parent_network_id: parentNetworkId } = stacksChainInfo;
+
+ const isSubnet = typeof stacksChainInfo.l1_subnet_governing_contract === 'string';
+ const isFirstLevelSubnet =
+ isSubnet &&
+ (parentNetworkId === PeerNetworkID.Mainnet || parentNetworkId === PeerNetworkID.Testnet);
+
+ // Currently, only subnets of mainnet and testnet are supported in the wallet
+ if (isFirstLevelSubnet) {
+ const parentChainId =
+ parentNetworkId === PeerNetworkID.Mainnet ? ChainID.Mainnet : ChainID.Testnet;
+ networksActions.addNetwork({
+ id: key as DefaultNetworkConfigurations,
+ name: name,
+ chainId: parentChainId, // Used for differentiating control flow in the wallet
+ subnetChainId: chainId, // Used for signing transactions (via the network object, not to be confused with the NetworkConfigurations)
+ url: stacksPath,
+ bitcoinNetwork: bitcoinApi,
+ bitcoinUrl: bitcoinPath,
+ });
+ navigate(RouteUrls.Home);
+ } else if (chainId === ChainID.Mainnet || chainId === ChainID.Testnet) {
+ networksActions.addNetwork({
+ id: key as DefaultNetworkConfigurations,
+ name: name,
+ chainId: chainId,
+ url: stacksPath,
+ bitcoinNetwork: bitcoinApi,
+ bitcoinUrl: bitcoinPath,
+ });
+ navigate(RouteUrls.Home);
+ } else {
+ setError('Unable to determine chainID from node.');
+ }
+ setLoading(false);
+ },
+ };
+}
diff --git a/src/app/features/asset-list/asset-list.tsx b/src/app/features/asset-list/asset-list.tsx
index 78bc0ff035d..4e63f2ebde6 100644
--- a/src/app/features/asset-list/asset-list.tsx
+++ b/src/app/features/asset-list/asset-list.tsx
@@ -32,7 +32,7 @@ export function AssetsList() {
const { whenWallet } = useWalletType();
return (
-
+
{whenWallet({
software: (
-
+
diff --git a/src/app/features/bitcoin-choose-fee/components/choose-fee-subtitle.tsx b/src/app/features/bitcoin-choose-fee/components/choose-fee-subtitle.tsx
index dbec55fd8d9..95961f5c709 100644
--- a/src/app/features/bitcoin-choose-fee/components/choose-fee-subtitle.tsx
+++ b/src/app/features/bitcoin-choose-fee/components/choose-fee-subtitle.tsx
@@ -10,7 +10,7 @@ export function ChooseFeeSubtitle({ isSendingMax }: { isSendingMax: boolean }) {
);
return (
-
+
{subtitle}
);
diff --git a/src/app/features/collectibles/collectibles.tsx b/src/app/features/collectibles/collectibles.tsx
index 7f440757a5b..2cb61a05127 100644
--- a/src/app/features/collectibles/collectibles.tsx
+++ b/src/app/features/collectibles/collectibles.tsx
@@ -13,7 +13,7 @@ import { useConfigNftMetadataEnabled } from '@app/query/common/remote-config/rem
import { AddCollectible } from './components/add-collectible';
import { Ordinals } from './components/bitcoin/ordinals';
import { Stamps } from './components/bitcoin/stamps';
-import { CollectiblesLayout } from './components/collectibes.layout';
+import { CollectiblesLayout } from './components/collectible.layout';
import { StacksCryptoAssets } from './components/stacks/stacks-crypto-assets';
import { TaprootBalanceDisplayer } from './components/taproot-balance-displayer';
import { useIsFetchingCollectiblesRelatedQuery } from './hooks/use-is-fetching-collectibles';
diff --git a/src/app/features/collectibles/components/_collectible-types/collectible-other.tsx b/src/app/features/collectibles/components/_collectible-types/collectible-other.tsx
index 0f6117e9226..cd649dbb4e0 100644
--- a/src/app/features/collectibles/components/_collectible-types/collectible-other.tsx
+++ b/src/app/features/collectibles/components/_collectible-types/collectible-other.tsx
@@ -10,7 +10,7 @@ export function CollectibleOther({ children, ...props }: CollectibleOtherProps)
-
+
- {title}
+
+ {title}
+
{isLoading ? (
) : (
@@ -35,12 +37,10 @@ export function CollectiblesLayout({
{subHeader}
{children}
diff --git a/src/app/features/collectibles/components/taproot-balance-displayer.tsx b/src/app/features/collectibles/components/taproot-balance-displayer.tsx
index e82e23a3986..e94fe3f89b1 100644
--- a/src/app/features/collectibles/components/taproot-balance-displayer.tsx
+++ b/src/app/features/collectibles/components/taproot-balance-displayer.tsx
@@ -19,7 +19,7 @@ export function TaprootBalanceDisplayer({ onSelectRetrieveBalance }: TaprootBala
if (balance.amount.isLessThanOrEqualTo(0)) return null;
return (
- onSelectRetrieveBalance()} textStyle="caption.02" variant="text">
+ onSelectRetrieveBalance()} textStyle="caption.01" variant="text">
{formatMoney(balance)}
diff --git a/src/app/features/container/container.layout.tsx b/src/app/features/container/container.layout.tsx
deleted file mode 100644
index a3c5e99e9b8..00000000000
--- a/src/app/features/container/container.layout.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import { Flex } from 'leather-styles/jsx';
-
-interface ContainerLayoutProps {
- children: React.JSX.Element | React.JSX.Element[];
- header: React.JSX.Element | null;
-}
-export function ContainerLayout(props: ContainerLayoutProps) {
- const { children, header } = props;
-
- return (
-
- {header || null}
-
- {children}
-
-
- );
-}
diff --git a/src/app/features/container/container.tsx b/src/app/features/container/container.tsx
index 173960eb015..6bf108f9553 100644
--- a/src/app/features/container/container.tsx
+++ b/src/app/features/container/container.tsx
@@ -1,25 +1,62 @@
-import { useEffect } from 'react';
-import { Outlet, useLocation } from 'react-router-dom';
+import { useEffect, useState } from 'react';
+import { Outlet, useLocation, useNavigate } from 'react-router-dom';
+import { ChainID } from '@stacks/transactions';
+import { OnboardingSelectors } from '@tests/selectors/onboarding.selectors';
+import { SettingsSelectors } from '@tests/selectors/settings.selectors';
+import { Box } from 'leather-styles/jsx';
+
+import { RouteUrls } from '@shared/route-urls';
import { closeWindow } from '@shared/utils';
import { useAnalytics, useInitalizeAnalytics } from '@app/common/hooks/analytics/use-analytics';
import { LoadingSpinner } from '@app/components/loading-spinner';
+import { CurrentAccountAvatar } from '@app/features/current-account/current-account-avatar';
+import { CurrentAccountName } from '@app/features/current-account/current-account-name';
+import { SwitchAccountDialog } from '@app/features/dialogs/switch-account-dialog/switch-account-dialog';
+import { InAppMessages } from '@app/features/hiro-messages/in-app-messages';
import { useOnSignOut } from '@app/routes/hooks/use-on-sign-out';
import { useOnWalletLock } from '@app/routes/hooks/use-on-wallet-lock';
import { useHasStateRehydrated } from '@app/store';
-import { useRouteHeaderState } from '@app/store/ui/ui.hooks';
+import { useCurrentNetworkState } from '@app/store/networks/networks.hooks';
+import { ContainerLayout } from '@app/ui/components/containers/container.layout';
+import { NetworkModeBadge } from '@app/ui/components/containers/headers/components/network-mode-badge';
+import { Header } from '@app/ui/components/containers/headers/header';
+import { Flag } from '@app/ui/components/flag/flag';
+import { Logo } from '@app/ui/components/logo';
+import { HamburgerIcon } from '@app/ui/icons/';
import { useRestoreFormState } from '../popup-send-form-restoration/use-restore-form-state';
-import { SettingsDropdown } from '../settings-dropdown/settings-dropdown';
-import { SwitchAccountDrawer } from '../switch-account-drawer/switch-account-drawer';
-import { ContainerLayout } from './container.layout';
+import { Settings } from '../settings/settings';
+import { TotalBalance } from './total-balance';
+import {
+ getDisplayAddresssBalanceOf,
+ isKnownPopupRoute,
+ isRpcRoute,
+ showAccountInfo,
+ showBalanceInfo,
+} from './utils/get-popup-header';
+import { getTitleFromUrl } from './utils/get-title-from-url';
+import {
+ canGoBack,
+ getIsSessionLocked,
+ getPageVariant,
+ hideLogo,
+ hideSettingsOnSm,
+ isLandingPage,
+ isNoHeaderPopup,
+ isSummaryPage,
+} from './utils/route-helpers';
export function Container() {
- const [routeHeader] = useRouteHeaderState();
- const { pathname } = useLocation();
+ const [isShowingSwitchAccount, setIsShowingSwitchAccount] = useState(false);
+ const navigate = useNavigate();
+ const { pathname: locationPathname } = useLocation();
+ const pathname = locationPathname as RouteUrls;
+
const analytics = useAnalytics();
const hasStateRehydrated = useHasStateRehydrated();
+ const { chain, name: chainName } = useCurrentNetworkState();
useOnWalletLock(() => closeWindow());
useOnSignOut(() => closeWindow());
@@ -28,13 +65,111 @@ export function Container() {
useEffect(() => void analytics.page('view', `${pathname}`), [analytics, pathname]);
+ const variant = getPageVariant(pathname);
+
+ const displayHeader = !isLandingPage(pathname) && !isNoHeaderPopup(pathname);
+ const isSessionLocked = getIsSessionLocked(pathname);
+
+ function getOnGoBackLocation(pathname: RouteUrls) {
+ switch (pathname) {
+ case RouteUrls.Swap:
+ case RouteUrls.Fund.replace(':currency', 'STX'):
+ case RouteUrls.Fund.replace(':currency', 'BTC'):
+ case RouteUrls.SendCryptoAssetForm.replace(':symbol', 'stx'):
+ case RouteUrls.SendCryptoAssetForm.replace(':symbol', 'btc'):
+ return navigate(RouteUrls.Home);
+ case RouteUrls.SendStxConfirmation:
+ return navigate(RouteUrls.SendCryptoAssetForm.replace(':symbol', 'stx'));
+ case RouteUrls.SendBtcConfirmation:
+ return navigate(RouteUrls.SendCryptoAssetForm.replace(':symbol', 'btc'));
+ default:
+ return navigate(-1);
+ }
+ }
+
if (!hasStateRehydrated) return ;
+ const showLogoSm = variant === 'home' || isSessionLocked || isKnownPopupRoute(pathname);
+ const hideSettings =
+ isKnownPopupRoute(pathname) || isSummaryPage(pathname) || variant === 'onboarding';
+
+ const isLogoClickable = variant !== 'home' && !isRpcRoute(pathname);
return (
<>
-
-
-
+ setIsShowingSwitchAccount(false)}
+ />
+
+
+ getOnGoBackLocation(pathname) : undefined}
+ onClose={isSummaryPage(pathname) ? () => navigate(RouteUrls.Home) : undefined}
+ settingsMenu={
+ hideSettings ? null : (
+
+ }
+ toggleSwitchAccount={() => setIsShowingSwitchAccount(!isShowingSwitchAccount)}
+ />
+ )
+ }
+ networkBadge={
+
+ }
+ title={getTitleFromUrl(pathname)}
+ logo={
+ !hideLogo(pathname) && (
+
+ navigate(RouteUrls.Home) : undefined}
+ />
+
+ )
+ }
+ account={
+ showAccountInfo(pathname) && (
+
+ setIsShowingSwitchAccount(!isShowingSwitchAccount)
+ }
+ />
+ }
+ >
+
+
+ )
+ }
+ totalBalance={
+ showBalanceInfo(pathname) && (
+
+ )
+ }
+ />
+ ) : null
+ }
+ >
>
diff --git a/src/app/features/container/total-balance.tsx b/src/app/features/container/total-balance.tsx
new file mode 100644
index 00000000000..2ca38223807
--- /dev/null
+++ b/src/app/features/container/total-balance.tsx
@@ -0,0 +1,57 @@
+import { Suspense } from 'react';
+
+import { Box, HStack } from 'leather-styles/jsx';
+
+import { BtcBalance } from '@app/components/balance-btc';
+import { StxBalance } from '@app/components/balance-stx';
+import { LoadingRectangle } from '@app/components/loading-rectangle';
+import { useConfigBitcoinEnabled } from '@app/query/common/remote-config/remote-config.query';
+import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
+
+interface TotalBalanceLayoutProps {
+ children: React.ReactNode;
+}
+function TotalBalanceLayout({ children }: TotalBalanceLayoutProps) {
+ return (
+
+ {children}
+
+ );
+}
+
+interface TotalBalanceProps {
+ displayAddresssBalanceOf?: 'all' | 'stx';
+}
+
+/**
+ * #4370 This code has been ported from legacy PopupHeader to load balances
+ */
+
+function TotalBalanceSuspense({ displayAddresssBalanceOf }: TotalBalanceProps) {
+ const account = useCurrentStacksAccount();
+ const isBitcoinEnabled = useConfigBitcoinEnabled();
+ return (
+
+
+ {account && displayAddresssBalanceOf === 'stx' && }
+ {isBitcoinEnabled && displayAddresssBalanceOf === 'all' && }
+
+
+ );
+}
+
+function TotalBalanceFallback() {
+ return (
+
+
+
+ );
+}
+
+export function TotalBalance(props: TotalBalanceProps) {
+ return (
+ }>
+
+
+ );
+}
diff --git a/src/app/features/container/utils/get-popup-header.ts b/src/app/features/container/utils/get-popup-header.ts
new file mode 100644
index 00000000000..16ae97bd077
--- /dev/null
+++ b/src/app/features/container/utils/get-popup-header.ts
@@ -0,0 +1,83 @@
+/**
+ * POPUP header logic notes here -> https://github.com/leather-wallet/extension/issues/4371#issuecomment-1919114939
+ */
+import { RouteUrls } from '@shared/route-urls';
+
+export function isRpcRoute(pathname: RouteUrls) {
+ //RouteUrls.RpcReceiveBitcoinContractOffer
+ if (pathname.match('/bitcoin-contract-offer')) return true;
+ switch (pathname) {
+ case RouteUrls.PsbtRequest:
+ case RouteUrls.SignatureRequest:
+ case RouteUrls.RpcStacksSignature:
+ case RouteUrls.RpcSignBip322Message:
+ case RouteUrls.RpcStacksSignature:
+ case RouteUrls.RpcSignPsbt:
+ case RouteUrls.RpcSignPsbtSummary:
+ case RouteUrls.RpcSendTransfer:
+ case RouteUrls.RpcSendTransferChooseFee:
+ case RouteUrls.RpcSendTransferConfirmation:
+ case RouteUrls.RpcSendTransferSummary:
+ return true;
+ default:
+ return false;
+ }
+}
+
+export function showAccountInfo(pathname: RouteUrls) {
+ switch (pathname) {
+ case RouteUrls.TransactionRequest:
+ case RouteUrls.ProfileUpdateRequest:
+ case RouteUrls.RpcSendTransfer:
+ return true;
+ default:
+ return false;
+ }
+}
+
+export function showBalanceInfo(pathname: RouteUrls) {
+ switch (pathname) {
+ case RouteUrls.ProfileUpdateRequest:
+ case RouteUrls.RpcSendTransfer:
+ return true;
+ case RouteUrls.TransactionRequest:
+ default:
+ return false;
+ }
+}
+
+export function getDisplayAddresssBalanceOf(pathname: RouteUrls) {
+ // TODO it's unclear when to show ALL or STX balance here
+ switch (pathname) {
+ case RouteUrls.TransactionRequest:
+ case RouteUrls.ProfileUpdateRequest:
+ case RouteUrls.RpcSendTransfer:
+ return 'all';
+ default:
+ return 'stx';
+ }
+}
+
+export function isKnownPopupRoute(pathname: RouteUrls) {
+ if (pathname.match('/bitcoin-contract-offer')) return true;
+ switch (pathname) {
+ case RouteUrls.TransactionRequest:
+ case RouteUrls.ProfileUpdateRequest:
+ case RouteUrls.PsbtRequest:
+ case RouteUrls.SignatureRequest:
+ case RouteUrls.RpcGetAddresses:
+ case RouteUrls.RpcSendTransfer:
+ case RouteUrls.SignatureRequest:
+ case RouteUrls.RpcSignBip322Message:
+ case RouteUrls.RpcStacksSignature:
+ case RouteUrls.RpcSignPsbt:
+ case RouteUrls.RpcSignPsbtSummary:
+ case RouteUrls.RpcSendTransfer:
+ case RouteUrls.RpcSendTransferChooseFee:
+ case RouteUrls.RpcSendTransferConfirmation:
+ case RouteUrls.RpcSendTransferSummary:
+ return true;
+ default:
+ return false;
+ }
+}
diff --git a/src/app/features/container/utils/get-title-from-url.ts b/src/app/features/container/utils/get-title-from-url.ts
new file mode 100644
index 00000000000..42ed7fe7a01
--- /dev/null
+++ b/src/app/features/container/utils/get-title-from-url.ts
@@ -0,0 +1,36 @@
+import { RouteUrls } from '@shared/route-urls';
+
+export function getTitleFromUrl(pathname: RouteUrls) {
+ if (pathname.match(RouteUrls.SendCryptoAsset)) {
+ // don't show send on first step of send flow or popuop transfer
+ if (pathname === RouteUrls.SendCryptoAsset) return undefined;
+ if (pathname === RouteUrls.RpcSendTransfer) return undefined;
+ return 'Send';
+ }
+
+ switch (pathname) {
+ case RouteUrls.AddNetwork:
+ return 'Add a network';
+ case RouteUrls.BitcoinContractList:
+ return 'Bitcoin Contracts';
+ case RouteUrls.BitcoinContractLockSuccess:
+ return 'Locked Bitcoin';
+ case RouteUrls.SendBrc20ChooseFee:
+ return 'Choose fee';
+ case RouteUrls.SendBrc20Confirmation:
+ case RouteUrls.SwapReview:
+ case RouteUrls.SendBrc20Confirmation:
+ case '/send/btc/confirm':
+ return 'Review';
+ case RouteUrls.Swap:
+ return 'Swap';
+ case RouteUrls.SentStxTxSummary:
+ case RouteUrls.SentBtcTxSummary:
+ return 'Sent';
+ case RouteUrls.SentBrc20Summary:
+ return 'Creating transfer inscription';
+ case RouteUrls.SendBrc20Confirmation:
+ default:
+ return undefined;
+ }
+}
diff --git a/src/app/features/container/utils/route-helpers.ts b/src/app/features/container/utils/route-helpers.ts
new file mode 100644
index 00000000000..868bea018dd
--- /dev/null
+++ b/src/app/features/container/utils/route-helpers.ts
@@ -0,0 +1,80 @@
+import { RouteUrls } from '@shared/route-urls';
+
+import { isKnownPopupRoute } from './get-popup-header';
+
+function isHomePage(pathname: RouteUrls) {
+ return (
+ pathname === RouteUrls.Home ||
+ pathname.match(RouteUrls.Activity) ||
+ pathname.match(RouteUrls.Receive) ||
+ pathname.match(RouteUrls.SendOrdinalInscription)
+ );
+}
+
+export function isLandingPage(pathname: RouteUrls) {
+ return pathname === RouteUrls.RequestDiagnostics || pathname.match(RouteUrls.Onboarding); // need to match get-started/ledger
+}
+
+function isOnboardingPage(pathname: RouteUrls) {
+ return (
+ pathname === RouteUrls.BackUpSecretKey ||
+ pathname === RouteUrls.SetPassword ||
+ pathname === RouteUrls.SignIn ||
+ pathname === RouteUrls.ViewSecretKey
+ );
+}
+
+function isFundPage(pathname: RouteUrls) {
+ return (
+ pathname === RouteUrls.Fund.replace(':currency', 'STX') ||
+ pathname === RouteUrls.Fund.replace(':currency', 'BTC')
+ );
+}
+
+export function getPageVariant(pathname: RouteUrls) {
+ if (isFundPage(pathname)) return 'fund';
+ if (isHomePage(pathname)) return 'home';
+ if (isOnboardingPage(pathname)) return 'onboarding';
+ return 'page';
+}
+
+export function getIsSessionLocked(pathname: RouteUrls) {
+ return pathname === RouteUrls.Unlock;
+}
+
+export function isSummaryPage(pathname: RouteUrls) {
+ /* TODO refactor the summary routes to make this cleaner
+ we need to block going back from summary pages catching the dynamic routes:
+ SentBtcTxSummary = '/sent/btc/:txId',
+ SentStxTxSummary = '/sent/stx/:txId',
+ SentBrc20Summary = '/send/brc20/:ticker/summary',
+ RpcSignPsbtSummary = '/sign-psbt/summary',
+ RpcSendTransferSummary = '/send-transfer/summary',
+ */
+ return pathname.match('/sent/stx/') || pathname.match('/sent/btc/' || pathname.match('summary'));
+}
+
+export function canGoBack(pathname: RouteUrls) {
+ if (getIsSessionLocked(pathname) || isKnownPopupRoute(pathname) || isSummaryPage(pathname)) {
+ return false;
+ }
+ return true;
+}
+
+export function hideLogo(pathname: RouteUrls) {
+ return pathname === RouteUrls.RpcGetAddresses || pathname === RouteUrls.ViewSecretKey;
+}
+
+export function isNoHeaderPopup(pathname: RouteUrls) {
+ return pathname === RouteUrls.RpcGetAddresses || pathname === RouteUrls.ChooseAccount;
+}
+
+export function hideSettingsOnSm(pathname: RouteUrls) {
+ switch (pathname) {
+ case RouteUrls.SendCryptoAsset:
+ case RouteUrls.FundChooseCurrency:
+ return true;
+ default:
+ return false;
+ }
+}
diff --git a/src/app/features/current-account/current-account-avatar.tsx b/src/app/features/current-account/current-account-avatar.tsx
index 39296fac380..7e5cc23bd15 100644
--- a/src/app/features/current-account/current-account-avatar.tsx
+++ b/src/app/features/current-account/current-account-avatar.tsx
@@ -3,26 +3,27 @@ import { memo } from 'react';
import { CircleProps } from 'leather-styles/jsx';
import { useCurrentAccountDisplayName } from '@app/common/hooks/account/use-account-names';
-import { useDrawers } from '@app/common/hooks/use-drawers';
-import { AccountAvatar } from '@app/components/account/account-avatar';
import { useCurrentAccountIndex } from '@app/store/accounts/account';
import { useStacksAccounts } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
import { StacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.models';
+import { AccountAvatar } from '@app/ui/components/account/account-avatar/account-avatar';
-export const CurrentAccountAvatar = memo((props: CircleProps) => {
+interface CurrentAccountAvatar extends CircleProps {
+ toggleSwitchAccount(): void;
+}
+export const CurrentAccountAvatar = memo(({ toggleSwitchAccount }: CurrentAccountAvatar) => {
const accountIndex = useCurrentAccountIndex();
const accounts = useStacksAccounts();
const currentAccount = accounts[accountIndex] as StacksAccount | undefined;
const name = useCurrentAccountDisplayName();
- const { setIsShowingSwitchAccountsState } = useDrawers();
+
if (!currentAccount) return null;
return (
setIsShowingSwitchAccountsState(true)}
+ onClick={() => toggleSwitchAccount()}
publicKey={currentAccount.stxPublicKey}
- {...props}
/>
);
});
diff --git a/src/app/features/current-account/popup-header.tsx b/src/app/features/current-account/popup-header.tsx
deleted file mode 100644
index b0c2cfc9748..00000000000
--- a/src/app/features/current-account/popup-header.tsx
+++ /dev/null
@@ -1,69 +0,0 @@
-import { Suspense } from 'react';
-
-import { Box, HStack, styled } from 'leather-styles/jsx';
-
-import { BtcBalance } from '@app/components/balance-btc';
-import { StxBalance } from '@app/components/balance-stx';
-import { LoadingRectangle } from '@app/components/loading-rectangle';
-import { NetworkModeBadge } from '@app/components/network-mode-badge';
-import { CurrentAccountAvatar } from '@app/features/current-account/current-account-avatar';
-import { CurrentAccountName } from '@app/features/current-account/current-account-name';
-import { useConfigBitcoinEnabled } from '@app/query/common/remote-config/remote-config.query';
-import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
-import { Flag } from '@app/ui/components/flag/flag';
-
-interface PopupHeaderLayoutProps {
- children: React.ReactNode;
-}
-function PopupHeaderLayout({ children }: PopupHeaderLayoutProps) {
- return (
-
- {children}
-
- );
-}
-
-interface PopupHeaderProps {
- displayAddresssBalanceOf?: 'all' | 'stx';
-}
-function PopupHeaderSuspense({ displayAddresssBalanceOf = 'stx' }: PopupHeaderProps) {
- const account = useCurrentStacksAccount();
- const isBitcoinEnabled = useConfigBitcoinEnabled();
- return (
-
- }
- >
-
-
-
-
-
-
-
- {account && displayAddresssBalanceOf === 'stx' && (
-
- )}
- {isBitcoinEnabled && displayAddresssBalanceOf === 'all' && }
-
-
-
-
- );
-}
-
-function PopupHeaderFallback() {
- return (
-
-
-
- );
-}
-
-export function PopupHeader(props: PopupHeaderProps) {
- return (
- }>
-
-
- );
-}
diff --git a/src/app/features/edit-nonce-drawer/components/edit-nonce-field.tsx b/src/app/features/dialogs/edit-nonce-dialog/components/edit-nonce-field.tsx
similarity index 100%
rename from src/app/features/edit-nonce-drawer/components/edit-nonce-field.tsx
rename to src/app/features/dialogs/edit-nonce-dialog/components/edit-nonce-field.tsx
diff --git a/src/app/features/edit-nonce-drawer/components/edit-nonce-form.tsx b/src/app/features/dialogs/edit-nonce-dialog/components/edit-nonce-form.tsx
similarity index 100%
rename from src/app/features/edit-nonce-drawer/components/edit-nonce-form.tsx
rename to src/app/features/dialogs/edit-nonce-dialog/components/edit-nonce-form.tsx
diff --git a/src/app/features/edit-nonce-drawer/edit-nonce-drawer.tsx b/src/app/features/dialogs/edit-nonce-dialog/edit-nonce-dialog.tsx
similarity index 76%
rename from src/app/features/edit-nonce-drawer/edit-nonce-drawer.tsx
rename to src/app/features/dialogs/edit-nonce-dialog/edit-nonce-dialog.tsx
index 1d72b3382dd..3d517a7d150 100644
--- a/src/app/features/edit-nonce-drawer/edit-nonce-drawer.tsx
+++ b/src/app/features/dialogs/edit-nonce-dialog/edit-nonce-dialog.tsx
@@ -8,25 +8,15 @@ import { StacksSendFormValues, StacksTransactionFormValues } from '@shared/model
import { useOnMount } from '@app/common/hooks/use-on-mount';
import { openInNewTab } from '@app/common/utils/open-in-new-tab';
-import { BaseDrawer } from '@app/components/drawer/base-drawer';
+import { Dialog } from '@app/ui/components/containers/dialog/dialog';
+import { Header } from '@app/ui/components/containers/headers/header';
import { Link } from '@app/ui/components/link/link';
import { EditNonceForm } from './components/edit-nonce-form';
const url = 'https://leather.gitbook.io/guides/transactions/nonces';
-function CustomFeeMessaging() {
- return (
-
- If your transaction has been pending for a long time, its nonce might not be correct.
- openInNewTab(url)}>
- Learn more.
-
-
- );
-}
-
-export function EditNonceDrawer() {
+export function EditNonceDialog() {
const { errors, setFieldError, setFieldValue, validateField, values } = useFormikContext<
StacksSendFormValues | StacksTransactionFormValues
>();
@@ -36,7 +26,6 @@ export function EditNonceDrawer() {
const { search } = useLocation();
useOnMount(() => setLoadedNextNonce(values.nonce));
-
const onGoBack = useCallback(() => {
if (search) {
return navigate('..' + search, { replace: true });
@@ -59,11 +48,16 @@ export function EditNonceDrawer() {
}, [loadedNextNonce, onGoBack, setFieldError, setFieldValue, values.nonce]);
return (
-
+ }>
-
+
+ If your transaction has been pending for a long time, its nonce might not be correct.
+ openInNewTab(url)}>
+ Learn more.
+
+
-
+
);
}
diff --git a/src/app/features/dialogs/high-fee-dialog/high-fee-dialog.tsx b/src/app/features/dialogs/high-fee-dialog/high-fee-dialog.tsx
new file mode 100644
index 00000000000..36cc180da11
--- /dev/null
+++ b/src/app/features/dialogs/high-fee-dialog/high-fee-dialog.tsx
@@ -0,0 +1,77 @@
+import { useEffect, useState } from 'react';
+
+import { useFormikContext } from 'formik';
+import { HStack, Stack } from 'leather-styles/jsx';
+
+import {
+ BitcoinSendFormValues,
+ StacksSendFormValues,
+ StacksTransactionFormValues,
+} from '@shared/models/form.model';
+
+import { openInNewTab } from '@app/common/utils/open-in-new-tab';
+import { Button } from '@app/ui/components/button/button';
+import { Dialog } from '@app/ui/components/containers/dialog/dialog';
+import { Footer } from '@app/ui/components/containers/footers/footer';
+import { Header } from '@app/ui/components/containers/headers/header';
+import { Link } from '@app/ui/components/link/link';
+import { Caption } from '@app/ui/components/typography/caption';
+import { Title } from '@app/ui/components/typography/title';
+import { ErrorIcon } from '@app/ui/icons';
+
+interface HighFeeDialogProps {
+ learnMoreUrl: string;
+ isShowing?: boolean;
+}
+
+export function HighFeeDialog({ learnMoreUrl, isShowing = false }: HighFeeDialogProps) {
+ const [isShowingHighFeeConfirmation, setIsShowingHighFeeConfirmation] = useState(isShowing);
+
+ useEffect(() => {
+ return () => {
+ if (isShowingHighFeeConfirmation) setIsShowingHighFeeConfirmation(false);
+ };
+ }, [isShowingHighFeeConfirmation, setIsShowingHighFeeConfirmation]);
+
+ const { handleSubmit, values } = useFormikContext<
+ BitcoinSendFormValues | StacksSendFormValues | StacksTransactionFormValues
+ >();
+ return (
+ }
+ isShowing={isShowingHighFeeConfirmation}
+ onClose={() => setIsShowingHighFeeConfirmation(false)}
+ footer={
+
+ }
+ >
+
+
+
+
+ Are you sure you want to pay {values.fee} {values.feeCurrency} in fees for this
+ transaction?
+
+
+
+ This action cannot be undone and the fees won't be returned, even if the transaction
+ fails.
+ openInNewTab(learnMoreUrl)} size="sm">
+ Learn more
+
+
+
+
+ );
+}
diff --git a/src/app/features/increase-fee-drawer/components/fee-multiplier-button.tsx b/src/app/features/dialogs/increase-fee-dialog/components/fee-multiplier-button.tsx
similarity index 100%
rename from src/app/features/increase-fee-drawer/components/fee-multiplier-button.tsx
rename to src/app/features/dialogs/increase-fee-dialog/components/fee-multiplier-button.tsx
diff --git a/src/app/features/increase-fee-drawer/components/fee-multiplier.tsx b/src/app/features/dialogs/increase-fee-dialog/components/fee-multiplier.tsx
similarity index 100%
rename from src/app/features/increase-fee-drawer/components/fee-multiplier.tsx
rename to src/app/features/dialogs/increase-fee-dialog/components/fee-multiplier.tsx
diff --git a/src/app/features/increase-fee-drawer/components/increase-fee-actions.tsx b/src/app/features/dialogs/increase-fee-dialog/components/increase-fee-actions.tsx
similarity index 85%
rename from src/app/features/increase-fee-drawer/components/increase-fee-actions.tsx
rename to src/app/features/dialogs/increase-fee-dialog/components/increase-fee-actions.tsx
index 4d4a757f7fb..170855c8486 100644
--- a/src/app/features/increase-fee-drawer/components/increase-fee-actions.tsx
+++ b/src/app/features/dialogs/increase-fee-dialog/components/increase-fee-actions.tsx
@@ -1,5 +1,4 @@
import { useFormikContext } from 'formik';
-import { Flex } from 'leather-styles/jsx';
import { LoadingKeys, useLoading } from '@app/common/hooks/use-loading';
import { useWalletType } from '@app/common/use-wallet-type';
@@ -19,20 +18,20 @@ export function IncreaseFeeActions(props: IncreaseFeeActionsProps) {
const actionText = whenWallet({ ledger: 'Confirm on Ledger', software: 'Submit' });
return (
-
-
+ >
);
}
diff --git a/src/app/features/increase-fee-drawer/components/increase-fee-field.tsx b/src/app/features/dialogs/increase-fee-dialog/components/increase-fee-field.tsx
similarity index 98%
rename from src/app/features/increase-fee-drawer/components/increase-fee-field.tsx
rename to src/app/features/dialogs/increase-fee-dialog/components/increase-fee-field.tsx
index a12414bd881..5cc073b37c1 100644
--- a/src/app/features/increase-fee-drawer/components/increase-fee-field.tsx
+++ b/src/app/features/dialogs/increase-fee-dialog/components/increase-fee-field.tsx
@@ -53,7 +53,7 @@ export function IncreaseFeeField(props: IncreaseFeeFieldProps): React.JSX.Elemen
bg="transparent"
border="default"
borderRadius="sm"
- height="64px"
+ height="inputHeight"
display="block"
p="space.04"
placeholder="Enter a custom fee"
diff --git a/src/app/features/increase-fee-drawer/hooks/use-btc-increase-fee.ts b/src/app/features/dialogs/increase-fee-dialog/hooks/use-btc-increase-fee.ts
similarity index 100%
rename from src/app/features/increase-fee-drawer/hooks/use-btc-increase-fee.ts
rename to src/app/features/dialogs/increase-fee-dialog/hooks/use-btc-increase-fee.ts
diff --git a/src/app/features/increase-fee-drawer/hooks/use-selected-tx.ts b/src/app/features/dialogs/increase-fee-dialog/hooks/use-selected-tx.ts
similarity index 100%
rename from src/app/features/increase-fee-drawer/hooks/use-selected-tx.ts
rename to src/app/features/dialogs/increase-fee-dialog/hooks/use-selected-tx.ts
diff --git a/src/app/features/dialogs/increase-fee-dialog/increase-btc-fee-dialog.tsx b/src/app/features/dialogs/increase-fee-dialog/increase-btc-fee-dialog.tsx
new file mode 100644
index 00000000000..22b89a5c723
--- /dev/null
+++ b/src/app/features/dialogs/increase-fee-dialog/increase-btc-fee-dialog.tsx
@@ -0,0 +1,110 @@
+import { Suspense } from 'react';
+import { Outlet, useLocation, useNavigate } from 'react-router-dom';
+
+import { Formik } from 'formik';
+import { Flex, Stack } from 'leather-styles/jsx';
+
+import { BitcoinTx } from '@shared/models/transactions/bitcoin-transaction.model';
+import { RouteUrls } from '@shared/route-urls';
+
+import { useBtcAssetBalance } from '@app/common/hooks/balance/btc/use-btc-balance';
+import { useLocationStateWithCache } from '@app/common/hooks/use-location-state';
+import { formatMoney } from '@app/common/money/format-money';
+import { btcToSat } from '@app/common/money/unit-conversion';
+import { getBitcoinTxValue } from '@app/common/transactions/bitcoin/utils';
+import { BitcoinCustomFeeInput } from '@app/components/bitcoin-custom-fee/bitcoin-custom-fee-input';
+import { BitcoinTransactionItem } from '@app/components/bitcoin-transaction-item/bitcoin-transaction-item';
+import { LoadingSpinner } from '@app/components/loading-spinner';
+import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
+import { Dialog } from '@app/ui/components/containers/dialog/dialog';
+import { Footer } from '@app/ui/components/containers/footers/footer';
+import { Header } from '@app/ui/components/containers/headers/header';
+import { Spinner } from '@app/ui/components/spinner';
+import { Caption } from '@app/ui/components/typography/caption';
+
+import { IncreaseFeeActions } from './components/increase-fee-actions';
+import { useBtcIncreaseFee } from './hooks/use-btc-increase-fee';
+
+export function IncreaseBtcFeeDialog() {
+ const tx = useLocationStateWithCache('btcTx') as BitcoinTx;
+ const navigate = useNavigate();
+ const location = useLocation();
+
+ const btcTx = tx;
+ const nativeSegwitSigner = useCurrentAccountNativeSegwitIndexZeroSigner();
+ const currentBitcoinAddress = nativeSegwitSigner.address;
+ const { btcAvailableAssetBalance } = useBtcAssetBalance(currentBitcoinAddress);
+ const { isBroadcasting, sizeInfo, onSubmit, validationSchema, recipient } =
+ useBtcIncreaseFee(btcTx);
+
+ const balance = formatMoney(btcAvailableAssetBalance.balance);
+
+ const onClose = () => {
+ navigate(RouteUrls.Home);
+ };
+
+ if (!tx) return null;
+
+ const initialFeeRate = `${(tx.fee / sizeInfo.txVBytes).toFixed(0)}`;
+
+ return (
+ <>
+
+ <>
+ }
+ footer={
+
+ }
+ >
+
+
+
+
+ }
+ >
+
+ If your transaction is pending for a long time, its fee might not be high enough
+ to be included in a block. Update the fee for a higher value and try again.
+
+ If your transaction is pending for a long time, its fee might not be high enough
+ to be included in a block. Update the fee for a higher value and try again.
+
+ )}
+
+
+
+
+
+
+ >
+ )}
+
+ >
+ );
+}
diff --git a/src/app/features/leather-intro-dialog/confetti-config.ts b/src/app/features/dialogs/leather-intro-dialog/confetti-config.ts
similarity index 100%
rename from src/app/features/leather-intro-dialog/confetti-config.ts
rename to src/app/features/dialogs/leather-intro-dialog/confetti-config.ts
diff --git a/src/app/features/leather-intro-dialog/leather-intro-dialog.tsx b/src/app/features/dialogs/leather-intro-dialog/leather-intro-dialog.tsx
similarity index 90%
rename from src/app/features/leather-intro-dialog/leather-intro-dialog.tsx
rename to src/app/features/dialogs/leather-intro-dialog/leather-intro-dialog.tsx
index e6e004686fb..dc59d5f6e00 100644
--- a/src/app/features/leather-intro-dialog/leather-intro-dialog.tsx
+++ b/src/app/features/dialogs/leather-intro-dialog/leather-intro-dialog.tsx
@@ -4,8 +4,6 @@ import { Outlet, Route, useNavigate } from 'react-router-dom';
import { useAnalytics } from '@app/common/hooks/analytics/use-analytics';
import { delay } from '@app/common/utils';
import { openInNewTab } from '@app/common/utils/open-in-new-tab';
-import { useAppDispatch } from '@app/store';
-import { settingsActions } from '@app/store/settings/settings.actions';
import {
LeatherIntroDialog,
@@ -38,8 +36,6 @@ export function useLeatherIntroDialogContext() {
function LeatherIntroDialogContainer() {
const analytics = useAnalytics();
const navigate = useNavigate();
- const dispatch = useAppDispatch();
-
async function onRevealNewName() {
void analytics.track('new_brand_reveal_name');
await delay(4000);
@@ -48,7 +44,6 @@ function LeatherIntroDialogContainer() {
async function onAcceptTerms() {
void analytics.track('new_brand_accept_terms');
- dispatch(settingsActions.setHasApprovedNewBrand());
navigate('../', { replace: true });
}
diff --git a/src/app/features/leather-intro-dialog/leather-intro-steps.tsx b/src/app/features/dialogs/leather-intro-dialog/leather-intro-steps.tsx
similarity index 98%
rename from src/app/features/leather-intro-dialog/leather-intro-steps.tsx
rename to src/app/features/dialogs/leather-intro-dialog/leather-intro-steps.tsx
index 9371c107d99..53312a25d0a 100644
--- a/src/app/features/leather-intro-dialog/leather-intro-steps.tsx
+++ b/src/app/features/dialogs/leather-intro-dialog/leather-intro-steps.tsx
@@ -99,7 +99,7 @@ export function LeatherIntroDialogPart2() {
Learn more →
-
+
Leather Wallet will now be provided by Leather Wallet LLC [a subsidiary of Nassau Machines
Inc]. Please review and accept Leather Wallet{' '}
diff --git a/src/app/features/switch-account-drawer/components/account-list-unavailable.tsx b/src/app/features/dialogs/switch-account-dialog/components/account-list-unavailable.tsx
similarity index 100%
rename from src/app/features/switch-account-drawer/components/account-list-unavailable.tsx
rename to src/app/features/dialogs/switch-account-dialog/components/account-list-unavailable.tsx
diff --git a/src/app/features/switch-account-drawer/components/switch-account-list-item.tsx b/src/app/features/dialogs/switch-account-dialog/components/switch-account-list-item.tsx
similarity index 95%
rename from src/app/features/switch-account-drawer/components/switch-account-list-item.tsx
rename to src/app/features/dialogs/switch-account-dialog/components/switch-account-list-item.tsx
index 432b4a4e798..323882e01b3 100644
--- a/src/app/features/switch-account-drawer/components/switch-account-list-item.tsx
+++ b/src/app/features/dialogs/switch-account-dialog/components/switch-account-list-item.tsx
@@ -9,8 +9,7 @@ import { AccountListItemLayout } from '@app/components/account/account-list-item
import { AccountNameLayout } from '@app/components/account/account-name';
import { useNativeSegwitSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
import { useStacksAccounts } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
-
-import { AccountAvatarItem } from '../../../components/account/account-avatar-item';
+import { AccountAvatarItem } from '@app/ui/components/account/account-avatar/account-avatar-item';
interface SwitchAccountListItemProps {
handleClose(): void;
diff --git a/src/app/features/dialogs/switch-account-dialog/switch-account-dialog.tsx b/src/app/features/dialogs/switch-account-dialog/switch-account-dialog.tsx
new file mode 100644
index 00000000000..491e323d65b
--- /dev/null
+++ b/src/app/features/dialogs/switch-account-dialog/switch-account-dialog.tsx
@@ -0,0 +1,83 @@
+import { memo } from 'react';
+import { Virtuoso } from 'react-virtuoso';
+
+import { Box } from 'leather-styles/jsx';
+
+import { useCreateAccount } from '@app/common/hooks/account/use-create-account';
+import { useWalletType } from '@app/common/use-wallet-type';
+import { useCurrentAccountIndex } from '@app/store/accounts/account';
+import { useFilteredBitcoinAccounts } from '@app/store/accounts/blockchain/bitcoin/bitcoin.ledger';
+import { useStacksAccounts } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
+import { useHasLedgerKeys } from '@app/store/ledger/ledger.selectors';
+import { Button } from '@app/ui/components/button/button';
+import { Dialog, DialogProps, getHeightOffset } from '@app/ui/components/containers/dialog/dialog';
+import { Footer } from '@app/ui/components/containers/footers/footer';
+import { Header } from '@app/ui/components/containers/headers/header';
+import { virtuosoHeight, virtuosoStyles } from '@app/ui/shared/virtuoso';
+
+import { AccountListUnavailable } from './components/account-list-unavailable';
+import { SwitchAccountListItem } from './components/switch-account-list-item';
+
+export const SwitchAccountDialog = memo(({ isShowing, onClose }: DialogProps) => {
+ const currentAccountIndex = useCurrentAccountIndex();
+ const createAccount = useCreateAccount();
+ const { whenWallet } = useWalletType();
+ const isLedger = useHasLedgerKeys();
+
+ const stacksAccounts = useStacksAccounts();
+ const bitcoinAccounts = useFilteredBitcoinAccounts();
+ const btcAddressesNum = bitcoinAccounts.length / 2;
+ const stacksAddressesNum = stacksAccounts.length;
+
+ const onCreateAccount = () => {
+ createAccount();
+ onClose();
+ };
+
+ if (isShowing && stacksAddressesNum === 0 && btcAddressesNum === 0) {
+ return ;
+ }
+ // #4370 SMELL without this early return the wallet crashes on new install with
+ // : Wallet is neither of type `ledger` nor `software`
+ // FIXME remove this when adding Create Account to Ledger in #2502 #4983
+ if (!isShowing) return null;
+
+ const accountNum = stacksAddressesNum || btcAddressesNum;
+
+ return (
+ }
+ isShowing={isShowing}
+ onClose={onClose}
+ footer={whenWallet({
+ software: (
+
+ ),
+ ledger: null,
+ })}
+ >
+ (
+
+
+
+ )}
+ />
+
+ );
+});
diff --git a/src/app/features/errors/app-error-boundary.tsx b/src/app/features/errors/app-error-boundary.tsx
index f13964586c5..e0b32cee18b 100644
--- a/src/app/features/errors/app-error-boundary.tsx
+++ b/src/app/features/errors/app-error-boundary.tsx
@@ -2,8 +2,6 @@ import { Box, Stack, styled } from 'leather-styles/jsx';
import { Prism } from '@app/common/clarity-prism';
import { HasChildren } from '@app/common/has-children';
-import { useRouteHeader } from '@app/common/hooks/use-route-header';
-import { Header } from '@app/components/header';
import { ErrorBoundary, FallbackProps, useErrorHandler } from '@app/features/errors/error-boundary';
import { openGithubIssue } from '@app/features/errors/utils';
import { useErrorStackTraceState } from '@app/store/ui/ui.hooks';
@@ -13,8 +11,6 @@ import { Title } from '@app/ui/components/typography/title';
function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
const [value] = useErrorStackTraceState();
- useRouteHeader();
-
return (
Something went wrong
diff --git a/src/app/features/high-fee-drawer/components/high-fee-confirmation.tsx b/src/app/features/high-fee-drawer/components/high-fee-confirmation.tsx
deleted file mode 100644
index 51aaa6015b2..00000000000
--- a/src/app/features/high-fee-drawer/components/high-fee-confirmation.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-import { useFormikContext } from 'formik';
-import { HStack, Stack } from 'leather-styles/jsx';
-
-import {
- BitcoinSendFormValues,
- StacksSendFormValues,
- StacksTransactionFormValues,
-} from '@shared/models/form.model';
-
-import { useDrawers } from '@app/common/hooks/use-drawers';
-import { openInNewTab } from '@app/common/utils/open-in-new-tab';
-import { Button } from '@app/ui/components/button/button';
-import { Link } from '@app/ui/components/link/link';
-import { Caption } from '@app/ui/components/typography/caption';
-import { Title } from '@app/ui/components/typography/title';
-
-export function HighFeeConfirmation({ learnMoreUrl }: { learnMoreUrl: string }) {
- const { handleSubmit, values } = useFormikContext<
- BitcoinSendFormValues | StacksSendFormValues | StacksTransactionFormValues
- >();
- const { setIsShowingHighFeeConfirmation } = useDrawers();
-
- return (
-
-
- Are you sure you want to pay {values.fee} {values.feeCurrency} in fees for this transaction?
-
-
- This action cannot be undone and the fees won't be returned, even if the transaction fails.{' '}
- openInNewTab(learnMoreUrl)} size="sm">
- Learn more
-
-
- If your transaction is pending for a long time, its fee might not be high enough to be
- included in a block. Update the fee for a higher value and try again.
-
+
+
+
+
+ {
+ void analytics.track('click_change_theme_menu_item');
+ setShowChangeTheme(!showChangeTheme);
+ }}
+ >
+ }>
+
+ Change theme
+
+
+
+
+
+
+ {
+ openInNewTab('https://leather.gitbook.io/guides/installing/contact-support');
+ }}
+ >
+ } textStyle="label.02">
+
+ Get support
+
+
+
+
+ openFeedbackDialog()}>
+ } textStyle="label.02">
+ Give feedback
+
+
+
+
+
+
+ {showAdvancedMenuOptions && }
+ {hasGeneratedWallet && walletType === 'software' && (
+ {
+ void analytics.track('lock_session');
+ void lockWallet();
+ navigate(RouteUrls.Unlock);
+ }}
+ data-testid={SettingsSelectors.LockListItem}
+ >
+ } textStyle="label.02">
+ Lock
+
+
+ )}
+
+ setShowSignOut(!showSignOut)}
+ data-testid={SettingsSelectors.SignOutListItem}
+ >
+ } textStyle="label.02">
+ Sign out
+
+
+
+
+
+
+
+ setShowSignOut(!showSignOut)} />
+ setShowChangeTheme(!showChangeTheme)}
+ />
+ setShowChangeNetwork(!showChangeNetwork)}
+ />
+ >
+ );
+}
diff --git a/src/app/features/settings/sign-out/sign-out-confirm.tsx b/src/app/features/settings/sign-out/sign-out-confirm.tsx
new file mode 100644
index 00000000000..f4e15f1308a
--- /dev/null
+++ b/src/app/features/settings/sign-out/sign-out-confirm.tsx
@@ -0,0 +1,36 @@
+import { useEffect } from 'react';
+import { useNavigate } from 'react-router-dom';
+
+import { RouteUrls } from '@shared/route-urls';
+
+import { useAnalytics } from '@app/common/hooks/analytics/use-analytics';
+import { useKeyActions } from '@app/common/hooks/use-key-actions';
+
+import { SignOutDialog } from './sign-out';
+
+interface SignOutProps {
+ isShowing: boolean;
+ onClose(): void;
+}
+
+export function SignOut({ isShowing = false, onClose }: SignOutProps) {
+ const analytics = useAnalytics();
+
+ useEffect(() => void analytics.track('sign-out'), [analytics]);
+ const { signOut } = useKeyActions();
+ const navigate = useNavigate();
+ // #4370 SMELL without this early return the wallet crashes on new install with
+ // : Wallet is neither of type `ledger` nor `software`
+ if (!isShowing) return null;
+ return (
+ {
+ void signOut().finally(() => {
+ navigate(RouteUrls.Onboarding);
+ });
+ }}
+ onClose={onClose}
+ />
+ );
+}
diff --git a/src/app/features/settings/sign-out/sign-out.tsx b/src/app/features/settings/sign-out/sign-out.tsx
new file mode 100644
index 00000000000..a2112056695
--- /dev/null
+++ b/src/app/features/settings/sign-out/sign-out.tsx
@@ -0,0 +1,105 @@
+import { SettingsSelectors } from '@tests/selectors/settings.selectors';
+import { useFormik } from 'formik';
+import { Flex, HStack, styled } from 'leather-styles/jsx';
+
+import { useWalletType } from '@app/common/use-wallet-type';
+import { Button } from '@app/ui/components/button/button';
+import { Callout } from '@app/ui/components/callout/callout';
+import { Dialog, DialogProps } from '@app/ui/components/containers/dialog/dialog';
+import { Footer } from '@app/ui/components/containers/footers/footer';
+import { Header } from '@app/ui/components/containers/headers/header';
+
+interface SignOutDialogProps extends DialogProps {
+ onUserDeleteWallet(): void;
+}
+export function SignOutDialog({ isShowing, onUserDeleteWallet, onClose }: SignOutDialogProps) {
+ const { whenWallet, walletType } = useWalletType();
+ const form = useFormik({
+ initialValues: {
+ confirmBackup: whenWallet({ ledger: true, software: false }),
+ confirmPasswordDisable: whenWallet({ ledger: true, software: false }),
+ },
+ onSubmit() {
+ onUserDeleteWallet();
+ },
+ });
+
+ const canSignOut = form.values.confirmBackup && form.values.confirmPasswordDisable;
+
+ return (
+ }
+ isShowing={isShowing}
+ onClose={onClose}
+ footer={
+
+ }
+ >
+
+ {whenWallet({
+ software:
+ "Back up your Secret Key before signing out. You'll be asked for your Secret Key on your next login.",
+ ledger:
+ "When you sign out, you'll need to reconnect your Ledger to sign back into your wallet.",
+ })}
+
+
+
+
+
+
+
+
+ I have backed up my Secret Key.
+
+
+
+
+
+
+
+ I understand that my password will not give me access to my wallet after I sign out.
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/features/theme-drawer/theme-list.tsx b/src/app/features/settings/theme/theme-dialog.tsx
similarity index 71%
rename from src/app/features/theme-drawer/theme-list.tsx
rename to src/app/features/settings/theme/theme-dialog.tsx
index 4cdd87616e7..ac2217059ab 100644
--- a/src/app/features/theme-drawer/theme-list.tsx
+++ b/src/app/features/settings/theme/theme-dialog.tsx
@@ -1,13 +1,13 @@
import { useCallback } from 'react';
-import { Flex, FlexProps } from 'leather-styles/jsx';
-
import { useAnalytics } from '@app/common/hooks/analytics/use-analytics';
import { UserSelectedTheme, themeLabelMap, useThemeSwitcher } from '@app/common/theme-provider';
+import { Dialog, DialogProps } from '@app/ui/components/containers/dialog/dialog';
+import { Header } from '@app/ui/components/containers/headers/header';
import { ThemeListItem } from './theme-list-item';
-export function ThemeList(props: FlexProps) {
+export function ThemeDialog({ isShowing, onClose }: DialogProps) {
const themes = Object.keys(themeLabelMap) as UserSelectedTheme[];
const analytics = useAnalytics();
const { setUserSelectedTheme } = useThemeSwitcher();
@@ -25,7 +25,11 @@ export function ThemeList(props: FlexProps) {
const { userSelectedTheme } = useThemeSwitcher();
return (
-
+ }
+ isShowing={isShowing}
+ onClose={onClose}
+ >
{themes.map(theme => (
))}
-
+
);
}
diff --git a/src/app/features/theme-drawer/theme-list-item-layout.tsx b/src/app/features/settings/theme/theme-list-item.tsx
similarity index 54%
rename from src/app/features/theme-drawer/theme-list-item-layout.tsx
rename to src/app/features/settings/theme/theme-list-item.tsx
index 716e05ea1ee..33c72298fd9 100644
--- a/src/app/features/theme-drawer/theme-list-item-layout.tsx
+++ b/src/app/features/settings/theme/theme-list-item.tsx
@@ -1,17 +1,21 @@
+import { useCallback } from 'react';
+
import { Box, Flex, styled } from 'leather-styles/jsx';
-import { CheckmarkIcon } from '@app/ui/icons/checkmark-icon';
+import { UserSelectedTheme, getThemeLabel } from '@app/common/theme-provider';
+import { CheckmarkIcon } from '@app/ui/icons';
interface ThemeListItemProps {
- themeLabel: string;
+ theme: UserSelectedTheme;
+ onThemeSelected(theme: UserSelectedTheme): void;
isActive: boolean;
- onThemeItemSelect(): void;
}
-export function ThemeListItemLayout({
- themeLabel,
- isActive,
- onThemeItemSelect,
-}: ThemeListItemProps) {
+export function ThemeListItem({ theme, onThemeSelected, isActive }: ThemeListItemProps) {
+ const themeLabel = getThemeLabel(theme);
+ const onThemeItemSelect = useCallback(() => {
+ onThemeSelected(theme);
+ }, [onThemeSelected, theme]);
+
return (
-
+ {domainName}
-
+
{domainVersion} {domainChainName}
diff --git a/src/app/features/stacks-message-signer/stacks-message-signing.tsx b/src/app/features/stacks-message-signer/stacks-message-signing.tsx
index a4193804182..d4baf58a350 100644
--- a/src/app/features/stacks-message-signer/stacks-message-signing.tsx
+++ b/src/app/features/stacks-message-signer/stacks-message-signing.tsx
@@ -13,8 +13,6 @@ import {
} from '@shared/signature/signature-types';
import { closeWindow } from '@shared/utils';
-import { useRouteHeader } from '@app/common/hooks/use-route-header';
-import { PopupHeader } from '@app/features/current-account/popup-header';
import { MessageSigningHeader } from '@app/features/message-signer/message-signing-header';
import { useOnOriginTabClose } from '@app/routes/hooks/use-on-tab-closed';
@@ -56,7 +54,6 @@ export function StacksMessageSigning({
onCancelMessageSigning,
payload,
}: StacksMessageSigningProps) {
- useRouteHeader();
useOnOriginTabClose(() => closeWindow());
if (!tabId) return null;
diff --git a/src/app/features/stacks-transaction-request/contract-call-details/contract-call-details.tsx b/src/app/features/stacks-transaction-request/contract-call-details/contract-call-details.tsx
index cd36cc7e396..a2e6d6ed277 100644
--- a/src/app/features/stacks-transaction-request/contract-call-details/contract-call-details.tsx
+++ b/src/app/features/stacks-transaction-request/contract-call-details/contract-call-details.tsx
@@ -27,6 +27,7 @@ function ContractCallDetailsSuspense() {
py="32px"
gap="space.05"
width="100%"
+ background="ink.background-primary"
>
Function and arguments
diff --git a/src/app/features/stacks-transaction-request/hooks/use-stacks-transaction-summary.ts b/src/app/features/stacks-transaction-request/hooks/use-stacks-transaction-summary.ts
index bdcab79db67..309f3b9acca 100644
--- a/src/app/features/stacks-transaction-request/hooks/use-stacks-transaction-summary.ts
+++ b/src/app/features/stacks-transaction-request/hooks/use-stacks-transaction-summary.ts
@@ -34,7 +34,6 @@ export function useStacksTransactionSummary(token: CryptoCurrencies) {
function formSentSummaryTxState(txId: string, signedTx: StacksTransaction, decimals?: number) {
return {
state: {
- hasHeaderTitle: true,
txLink: {
blockchain: 'stacks',
txid: txId || '',
diff --git a/src/app/features/stacks-transaction-request/stacks-transaction-signer.tsx b/src/app/features/stacks-transaction-request/stacks-transaction-signer.tsx
index 96fdb31a3f1..f3383c43070 100644
--- a/src/app/features/stacks-transaction-request/stacks-transaction-signer.tsx
+++ b/src/app/features/stacks-transaction-request/stacks-transaction-signer.tsx
@@ -1,3 +1,4 @@
+import { useState } from 'react';
import { Outlet, useLocation, useNavigate } from 'react-router-dom';
import { StacksTransaction } from '@stacks/transactions';
@@ -12,14 +13,12 @@ import { RouteUrls } from '@shared/route-urls';
import { useAnalytics } from '@app/common/hooks/analytics/use-analytics';
import { useOnMount } from '@app/common/hooks/use-on-mount';
-import { useRouteHeader } from '@app/common/hooks/use-route-header';
import { stxToMicroStx } from '@app/common/money/unit-conversion';
import { stxFeeValidator } from '@app/common/validation/forms/fee-validators';
import { nonceValidator } from '@app/common/validation/nonce-validators';
import { NonceSetter } from '@app/components/nonce-setter';
-import { PopupHeader } from '@app/features/current-account/popup-header';
+import { HighFeeDialog } from '@app/features/dialogs/high-fee-dialog/high-fee-dialog';
import { RequestingTabClosedWarningMessage } from '@app/features/errors/requesting-tab-closed-error-msg';
-import { HighFeeDrawer } from '@app/features/high-fee-drawer/high-fee-drawer';
import { ContractCallDetails } from '@app/features/stacks-transaction-request/contract-call-details/contract-call-details';
import { ContractDeployDetails } from '@app/features/stacks-transaction-request/contract-deploy-details/contract-deploy-details';
import { PageTop } from '@app/features/stacks-transaction-request/page-top';
@@ -52,6 +51,7 @@ export function StacksTransactionSigner({
onSignStacksTransaction,
isMultisig,
}: StacksTransactionSignerProps) {
+ const [isShowingHighFeeConfirmation, setIsShowingHighFeeConfirmation] = useState(false);
const transactionRequest = useTransactionRequestState();
const { data: stxFees } = useCalculateStacksTxFees(stacksTransaction);
const analytics = useAnalytics();
@@ -60,8 +60,6 @@ export function StacksTransactionSigner({
const { data: nextNonce } = useNextNonce();
const { search } = useLocation();
- useRouteHeader();
-
useOnMount(() => {
void analytics.track('view_transaction_signing'), [analytics];
});
@@ -133,8 +131,13 @@ export function StacksTransactionSigner({
)}
-
-
+ setIsShowingHighFeeConfirmation(true)}
+ />
+
>
)}
diff --git a/src/app/features/stacks-transaction-request/submit-action.tsx b/src/app/features/stacks-transaction-request/submit-action.tsx
index 85c7b757881..45fc22d70e3 100644
--- a/src/app/features/stacks-transaction-request/submit-action.tsx
+++ b/src/app/features/stacks-transaction-request/submit-action.tsx
@@ -5,14 +5,16 @@ import { HIGH_FEE_AMOUNT_STX } from '@shared/constants';
import { StacksTransactionFormValues } from '@shared/models/form.model';
import { isEmpty } from '@shared/utils';
-import { useDrawers } from '@app/common/hooks/use-drawers';
import { useTransactionError } from '@app/features/stacks-transaction-request/hooks/use-transaction-error';
import { Button } from '@app/ui/components/button/button';
-export function SubmitAction() {
+interface SubmitActionProps {
+ setIsShowingHighFeeConfirmation(): void;
+}
+export function SubmitAction({ setIsShowingHighFeeConfirmation }: SubmitActionProps) {
const { handleSubmit, values, validateForm, isSubmitting } =
useFormikContext();
- const { isShowingHighFeeConfirmation, setIsShowingHighFeeConfirmation } = useDrawers();
+
const error = useTransactionError();
const isDisabled = !!error || Number(values.fee) < 0;
@@ -21,7 +23,7 @@ export function SubmitAction() {
// Check for errors before showing the high fee confirmation
const formErrors = await validateForm();
if (isEmpty(formErrors) && Number(values.fee) > HIGH_FEE_AMOUNT_STX) {
- return setIsShowingHighFeeConfirmation(!isShowingHighFeeConfirmation);
+ return setIsShowingHighFeeConfirmation();
}
handleSubmit();
};
diff --git a/src/app/features/stacks-transaction-request/transaction-error/error-messages.tsx b/src/app/features/stacks-transaction-request/transaction-error/error-messages.tsx
index 2c2d0c0b984..f88783c820f 100644
--- a/src/app/features/stacks-transaction-request/transaction-error/error-messages.tsx
+++ b/src/app/features/stacks-transaction-request/transaction-error/error-messages.tsx
@@ -1,4 +1,4 @@
-import { memo } from 'react';
+import { memo, useState } from 'react';
import { Navigate } from 'react-router-dom';
import { STXTransferPayload, TransactionTypes } from '@stacks/connect';
@@ -8,9 +8,9 @@ import { RouteUrls } from '@shared/route-urls';
import { closeWindow } from '@shared/utils';
import { useAnalytics } from '@app/common/hooks/analytics/use-analytics';
-import { useDrawers } from '@app/common/hooks/use-drawers';
import { useScrollLock } from '@app/common/hooks/use-scroll-lock';
import { stacksValue } from '@app/common/stacks-utils';
+import { SwitchAccountDialog } from '@app/features/dialogs/switch-account-dialog/switch-account-dialog';
import { ErrorMessage } from '@app/features/stacks-transaction-request/transaction-error/error-message';
import { useCurrentStacksAccountBalances } from '@app/query/stacks/balance/stx-balance.hooks';
import { useCurrentNetworkState } from '@app/store/networks/networks.hooks';
@@ -24,7 +24,7 @@ interface InsufficientFundsActionButtonsProps {
}
function InsufficientFundsActionButtons({ eventName }: InsufficientFundsActionButtonsProps) {
const analytics = useAnalytics();
- const { setIsShowingSwitchAccountsState } = useDrawers();
+ const [isShowingSwitchAccount, setIsShowingSwitchAccount] = useState(false);
const onGetStx = () => {
void analytics.track(eventName);
@@ -34,8 +34,12 @@ function InsufficientFundsActionButtons({ eventName }: InsufficientFundsActionBu
return (
<>
+ setIsShowingSwitchAccount(false)}
+ />
Get STX
- setIsShowingSwitchAccountsState(true)} variant="outline">
+ setIsShowingSwitchAccount(true)} variant="outline">
Switch account
>
diff --git a/src/app/features/switch-account-drawer/components/create-account-action.tsx b/src/app/features/switch-account-drawer/components/create-account-action.tsx
deleted file mode 100644
index c3de671b39b..00000000000
--- a/src/app/features/switch-account-drawer/components/create-account-action.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import { Flex } from 'leather-styles/jsx';
-
-import { Button } from '@app/ui/components/button/button';
-
-interface CreateAccountActionProps {
- onCreateAccount(): void;
-}
-export function CreateAccountAction({ onCreateAccount }: CreateAccountActionProps) {
- return (
-
- onCreateAccount()}>
- Create new account
-
-
- );
-}
diff --git a/src/app/features/switch-account-drawer/components/switch-account-list.tsx b/src/app/features/switch-account-drawer/components/switch-account-list.tsx
deleted file mode 100644
index 3b51ffbbda5..00000000000
--- a/src/app/features/switch-account-drawer/components/switch-account-list.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import { memo } from 'react';
-import { Virtuoso } from 'react-virtuoso';
-
-import { Box } from 'leather-styles/jsx';
-
-import { useWalletType } from '@app/common/use-wallet-type';
-
-import { SwitchAccountListItem } from './switch-account-list-item';
-
-interface SwitchAccountListProps {
- handleClose(): void;
- currentAccountIndex: number;
- addressesNum: number;
-}
-export const SwitchAccountList = memo(
- ({ currentAccountIndex, handleClose, addressesNum }: SwitchAccountListProps) => {
- const { whenWallet } = useWalletType();
-
- return (
- (
-
-
-
- )}
- />
- );
- }
-);
diff --git a/src/app/features/switch-account-drawer/switch-account-drawer.tsx b/src/app/features/switch-account-drawer/switch-account-drawer.tsx
deleted file mode 100644
index 765fdef4070..00000000000
--- a/src/app/features/switch-account-drawer/switch-account-drawer.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import { memo } from 'react';
-
-import { Box } from 'leather-styles/jsx';
-
-import { useCreateAccount } from '@app/common/hooks/account/use-create-account';
-import { useWalletType } from '@app/common/use-wallet-type';
-import { ControlledDrawer } from '@app/components/drawer/controlled-drawer';
-import { useCurrentAccountIndex } from '@app/store/accounts/account';
-import { useFilteredBitcoinAccounts } from '@app/store/accounts/blockchain/bitcoin/bitcoin.ledger';
-import { useStacksAccounts } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
-import { useShowSwitchAccountsState } from '@app/store/ui/ui.hooks';
-
-import { AccountListUnavailable } from './components/account-list-unavailable';
-import { CreateAccountAction } from './components/create-account-action';
-import { SwitchAccountList } from './components/switch-account-list';
-
-export const SwitchAccountDrawer = memo(() => {
- const [isShowing, setShowSwitchAccountsState] = useShowSwitchAccountsState();
-
- const currentAccountIndex = useCurrentAccountIndex();
- const createAccount = useCreateAccount();
- const { whenWallet } = useWalletType();
-
- const stacksAccounts = useStacksAccounts();
- const bitcoinAccounts = useFilteredBitcoinAccounts();
- const btcAddressesNum = bitcoinAccounts.length / 2;
- const stacksAddressesNum = stacksAccounts.length;
-
- const onClose = () => setShowSwitchAccountsState(false);
-
- const onCreateAccount = () => {
- createAccount();
- setShowSwitchAccountsState(false);
- };
-
- if (isShowing && stacksAddressesNum === 0 && btcAddressesNum === 0) {
- return ;
- }
-
- return isShowing ? (
-
-
-
- {whenWallet({
- software: ,
- ledger: <>>,
- })}
-
-
- ) : null;
-});
diff --git a/src/app/features/theme-drawer/theme-drawer.tsx b/src/app/features/theme-drawer/theme-drawer.tsx
deleted file mode 100644
index 52c68d3627f..00000000000
--- a/src/app/features/theme-drawer/theme-drawer.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import { useNavigate } from 'react-router-dom';
-
-import { useLocationState } from '@app/common/hooks/use-location-state';
-import { BaseDrawer } from '@app/components/drawer/base-drawer';
-import { useBackgroundLocationRedirect } from '@app/routes/hooks/use-background-location-redirect';
-
-import { ThemeList } from './theme-list';
-
-export function ThemesDrawer() {
- useBackgroundLocationRedirect();
- const navigate = useNavigate();
- const backgroundLocation = useLocationState('backgroundLocation');
- return (
- navigate(backgroundLocation ?? '..')}>
-
-
- );
-}
diff --git a/src/app/features/theme-drawer/theme-list-item.tsx b/src/app/features/theme-drawer/theme-list-item.tsx
deleted file mode 100644
index 6437bda0a9e..00000000000
--- a/src/app/features/theme-drawer/theme-list-item.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import { useCallback } from 'react';
-
-import { UserSelectedTheme, getThemeLabel } from '@app/common/theme-provider';
-
-import { ThemeListItemLayout } from './theme-list-item-layout';
-
-interface ThemeListItemProps {
- theme: UserSelectedTheme;
- onThemeSelected(theme: UserSelectedTheme): void;
- isActive: boolean;
-}
-export function ThemeListItem({ theme, onThemeSelected, isActive }: ThemeListItemProps) {
- const themeLabel = getThemeLabel(theme);
- const itemSelectHandler = useCallback(() => {
- onThemeSelected(theme);
- }, [onThemeSelected, theme]);
-
- return (
-
- );
-}
diff --git a/src/app/pages/bitcoin-contract-list/bitcoin-contract-list.tsx b/src/app/pages/bitcoin-contract-list/bitcoin-contract-list.tsx
index ba8eb71cd97..de64497e615 100644
--- a/src/app/pages/bitcoin-contract-list/bitcoin-contract-list.tsx
+++ b/src/app/pages/bitcoin-contract-list/bitcoin-contract-list.tsx
@@ -1,6 +1,6 @@
import { useState } from 'react';
-import { Flex, styled } from 'leather-styles/jsx';
+import { Flex, Stack, styled } from 'leather-styles/jsx';
import {
BitcoinContractListItem,
@@ -11,7 +11,6 @@ import { FullPageLoadingSpinner } from '@app/components/loading-spinner';
import { truncateMiddle } from '@app/ui/utils/truncate-middle';
import { BitcoinContractListItemLayout } from './components/bitcoin-contract-list-item-layout';
-import { BitcoinContractListLayout } from './components/bitcoin-contract-list-layout';
export function BitcoinContractList() {
const { getAllActiveBitcoinContracts } = useBitcoinContracts();
@@ -36,7 +35,7 @@ export function BitcoinContractList() {
if (isLoading) return ;
return (
-
+
{bitcoinContracts.length === 0 || isError ? (
@@ -58,6 +57,6 @@ export function BitcoinContractList() {
);
})
)}
-
+
);
}
diff --git a/src/app/pages/bitcoin-contract-list/components/bitcoin-contract-list-item-layout.tsx b/src/app/pages/bitcoin-contract-list/components/bitcoin-contract-list-item-layout.tsx
index 12fb05fa080..3d5d9161ed9 100644
--- a/src/app/pages/bitcoin-contract-list/components/bitcoin-contract-list-item-layout.tsx
+++ b/src/app/pages/bitcoin-contract-list/components/bitcoin-contract-list-item-layout.tsx
@@ -38,7 +38,7 @@ export function BitcoinContractListItemLayout({
return (
handleOpenTxLink({
txid,
diff --git a/src/app/pages/bitcoin-contract-list/components/bitcoin-contract-list-layout.tsx b/src/app/pages/bitcoin-contract-list/components/bitcoin-contract-list-layout.tsx
deleted file mode 100644
index 3074f22f036..00000000000
--- a/src/app/pages/bitcoin-contract-list/components/bitcoin-contract-list-layout.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import { ReactNode } from 'react';
-import { useNavigate } from 'react-router-dom';
-
-import { Stack } from 'leather-styles/jsx';
-
-import { RouteUrls } from '@shared/route-urls';
-
-import { useRouteHeader } from '@app/common/hooks/use-route-header';
-import { Header } from '@app/components/header';
-
-interface BitcoinContractListProps {
- children: ReactNode;
-}
-export function BitcoinContractListLayout({ children }: BitcoinContractListProps) {
- const navigate = useNavigate();
- useRouteHeader( navigate(RouteUrls.Home)} />);
- return (
-
- {children}
-
- );
-}
diff --git a/src/app/pages/bitcoin-contract-request/bitcoin-contract-request.tsx b/src/app/pages/bitcoin-contract-request/bitcoin-contract-request.tsx
index 46150148e84..19d7ddcdc02 100644
--- a/src/app/pages/bitcoin-contract-request/bitcoin-contract-request.tsx
+++ b/src/app/pages/bitcoin-contract-request/bitcoin-contract-request.tsx
@@ -1,6 +1,7 @@
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
+import { DlcSelectors } from '@tests/selectors/requests.selectors';
import { Stack } from 'leather-styles/jsx';
import { RouteUrls } from '@shared/route-urls';
@@ -13,11 +14,13 @@ import {
import { useOnMount } from '@app/common/hooks/use-on-mount';
import { initialSearchParams } from '@app/common/initial-search-params';
import { useCurrentAccountNativeSegwitSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
+import { Footer } from '@app/ui/components/containers/footers/footer';
+import { Card } from '@app/ui/layout/card/card';
+import { CardContent } from '@app/ui/layout/card/card-content';
import { BitcoinContractOfferDetailsSimple } from './components/bitcoin-contract-offer/bitcoin-contract-offer-details';
import { BitcoinContractRequestActions } from './components/bitcoin-contract-request-actions';
import { BitcoinContractRequestHeader } from './components/bitcoin-contract-request-header';
-import { BitcoinContractRequestLayout } from './components/bitcoin-contract-request-layout';
import { BitcoinContractRequestWarningLabel } from './components/bitcoin-contract-request-warning-label';
export function BitcoinContractRequest() {
@@ -102,35 +105,51 @@ export function BitcoinContractRequest() {
return (
<>
+ {/* TODO - need to test this properly.. Tried using steps from test.describe('Bitcoin Contract Request Test' */}
{!isLoading && bitcoinAddress && bitcoinContractOfferDetails && (
-
-
-
-
+
+
+ }
+ >
+
+
-
-
-
-
+
+
+
+ {/* TODO test this as it was placed after action buttons */}
+
+
+
)}
>
);
diff --git a/src/app/pages/bitcoin-contract-request/components/bitcoin-contract-request-actions.tsx b/src/app/pages/bitcoin-contract-request/components/bitcoin-contract-request-actions.tsx
index 10a34eafb81..9e64a0a5a90 100644
--- a/src/app/pages/bitcoin-contract-request/components/bitcoin-contract-request-actions.tsx
+++ b/src/app/pages/bitcoin-contract-request/components/bitcoin-contract-request-actions.tsx
@@ -1,5 +1,4 @@
import { BitcoinContractRequestSelectors } from '@tests/selectors/bitcoin-contract-request.selectors';
-import { Box, HStack } from 'leather-styles/jsx';
import { useBtcAssetBalance } from '@app/common/hooks/balance/btc/use-btc-balance';
import { Button } from '@app/ui/components/button/button';
@@ -22,35 +21,24 @@ export function BitcoinContractRequestActions({
const canAccept = btcAvailableAssetBalance.balance.amount.isGreaterThan(requiredAmount);
return (
-
-
-
- Reject
-
-
- Accept
-
-
-
+ <>
+
+ Reject
+
+
+ Accept
+
+ >
);
}
diff --git a/src/app/pages/bitcoin-contract-request/components/bitcoin-contract-request-layout.tsx b/src/app/pages/bitcoin-contract-request/components/bitcoin-contract-request-layout.tsx
deleted file mode 100644
index 171c52142ab..00000000000
--- a/src/app/pages/bitcoin-contract-request/components/bitcoin-contract-request-layout.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import { Stack } from 'leather-styles/jsx';
-
-interface BitcoinContractRequestLayoutProps {
- children: React.ReactNode;
-}
-export function BitcoinContractRequestLayout({ children }: BitcoinContractRequestLayoutProps) {
- return (
-
- {children}
-
- );
-}
diff --git a/src/app/pages/choose-account/choose-account.tsx b/src/app/pages/choose-account/choose-account.tsx
index 90f75fe195c..41fa8bfe5ca 100644
--- a/src/app/pages/choose-account/choose-account.tsx
+++ b/src/app/pages/choose-account/choose-account.tsx
@@ -7,7 +7,6 @@ import { closeWindow } from '@shared/utils';
import { useCancelAuthRequest } from '@app/common/authentication/use-cancel-auth-request';
import { useAppDetails } from '@app/common/hooks/auth/use-app-details';
-import { useRouteHeader } from '@app/common/hooks/use-route-header';
import { RequesterFlag } from '@app/components/requester-flag';
import { ChooseAccountsList } from '@app/pages/choose-account/components/accounts';
import { useOnOriginTabClose } from '@app/routes/hooks/use-on-tab-closed';
@@ -18,7 +17,6 @@ export const ChooseAccount = memo(() => {
const cancelAuthentication = useCancelAuthRequest();
- useRouteHeader(<>>);
useOnOriginTabClose(() => closeWindow());
const handleUnmount = async () => cancelAuthentication();
@@ -31,7 +29,7 @@ export const ChooseAccount = memo(() => {
return (
<>
-
+
{url && }
diff --git a/src/app/pages/choose-account/components/accounts.tsx b/src/app/pages/choose-account/components/accounts.tsx
index 70817f1fc87..b852b778ac2 100644
--- a/src/app/pages/choose-account/components/accounts.tsx
+++ b/src/app/pages/choose-account/components/accounts.tsx
@@ -3,6 +3,7 @@ import { useNavigate } from 'react-router-dom';
import { Virtuoso } from 'react-virtuoso';
import { Box, FlexProps, HStack, styled } from 'leather-styles/jsx';
+import { token } from 'leather-styles/tokens';
import { RouteUrls } from '@shared/route-urls';
@@ -13,13 +14,14 @@ import { useWalletType } from '@app/common/use-wallet-type';
import { slugify } from '@app/common/utils';
import { AccountTotalBalance } from '@app/components/account-total-balance';
import { AcccountAddresses } from '@app/components/account/account-addresses';
-import { AccountAvatar } from '@app/components/account/account-avatar';
import { AccountListItemLayout } from '@app/components/account/account-list-item.layout';
import { usePressable } from '@app/components/item-hover';
import { useNativeSegwitAccountIndexAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
import { useStacksAccounts } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
import { StacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.models';
+import { AccountAvatar } from '@app/ui/components/account/account-avatar/account-avatar';
import { PlusIcon } from '@app/ui/icons/plus-icon';
+import { virtuosoHeight, virtuosoStyles } from '@app/ui/shared/virtuoso';
interface AccountTitlePlaceholderProps {
account: StacksAccount;
@@ -43,33 +45,28 @@ const ChooseAccountItem = memo(
const accountSlug = useMemo(() => slugify(`Account ${account?.index + 1}`), [account?.index]);
return (
- // Padding required on outer element to prevent jumpy virtualized list
-
- }
- accountName={
- }>
- {name}
-
- }
- avatar={
-
- }
- balanceLabel={
-
- }
- data-testid={`account-${accountSlug}-${account.index}`}
- index={account.index}
- isLoading={isLoading}
- isSelected={false}
- onSelectAccount={() => onSelectAccount(account.index)}
- />
-
+ }
+ accountName={
+ }>
+ {name}
+
+ }
+ avatar={
+
+ }
+ balanceLabel={}
+ data-testid={`account-${accountSlug}-${account.index}`}
+ index={account.index}
+ isLoading={isLoading}
+ isSelected={false}
+ onSelectAccount={() => onSelectAccount(account.index)}
+ />
);
}
);
@@ -83,7 +80,7 @@ const AddAccountAction = memo(() => {
};
return (
-
+
Generate new account
@@ -113,19 +110,27 @@ export const ChooseAccountsList = memo(() => {
};
if (!accounts) return null;
+ const accountNum = accounts.length;
return (
-
+
{whenWallet({ software: , ledger: <>> })}
(
-
+
+
+
)}
/>
diff --git a/src/app/pages/fund/choose-asset-to-fund/choose-asset-to-fund.tsx b/src/app/pages/fund/choose-asset-to-fund/choose-asset-to-fund.tsx
index 3745bd094c2..6010708f8f1 100644
--- a/src/app/pages/fund/choose-asset-to-fund/choose-asset-to-fund.tsx
+++ b/src/app/pages/fund/choose-asset-to-fund/choose-asset-to-fund.tsx
@@ -1,18 +1,18 @@
import { useCallback, useMemo } from 'react';
import { Outlet, useNavigate } from 'react-router-dom';
+import { Box, styled } from 'leather-styles/jsx';
+
import { AllTransferableCryptoAssetBalances } from '@shared/models/crypto-asset-balance.model';
import { RouteUrls } from '@shared/route-urls';
import { isDefined } from '@shared/utils';
import { useStxCryptoCurrencyAssetBalance } from '@app/common/hooks/balance/stx/use-stx-crypto-currency-asset-balance';
-import { useRouteHeader } from '@app/common/hooks/use-route-header';
import { useWalletType } from '@app/common/use-wallet-type';
-import { ChooseAssetContainer } from '@app/components/crypto-assets/choose-crypto-asset/choose-asset-container';
-import { ChooseCryptoAssetLayout } from '@app/components/crypto-assets/choose-crypto-asset/choose-crypto-asset.layout';
import { CryptoAssetList } from '@app/components/crypto-assets/choose-crypto-asset/crypto-asset-list';
-import { ModalHeader } from '@app/components/modal-header';
import { useCheckLedgerBlockchainAvailable } from '@app/store/accounts/blockchain/utils';
+import { Card } from '@app/ui/layout/card/card';
+import { Page } from '@app/ui/layout/page/page.layout';
export function ChooseCryptoAssetToFund() {
const stxCryptoCurrencyAssetBalance = useStxCryptoCurrencyAssetBalance();
@@ -33,9 +33,7 @@ export function ChooseCryptoAssetToFund() {
[stxCryptoCurrencyAssetBalance, checkBlockchainAvailable, whenWallet]
);
- useRouteHeader( navigate(RouteUrls.Home)} title=" " />);
-
- const navigateToSendForm = useCallback(
+ const navigateToFund = useCallback(
(cryptoAssetBalance: AllTransferableCryptoAssetBalances) => {
const { asset } = cryptoAssetBalance;
@@ -47,14 +45,23 @@ export function ChooseCryptoAssetToFund() {
return (
<>
-
-
-
-
-
+
+
+ choose asset to fund
+
+ }
+ >
+
+
+
+
+
>
);
diff --git a/src/app/pages/fund/components/fund-account-tile.tsx b/src/app/pages/fund/components/fund-account-tile.tsx
index 2396a3a7552..5338f789f11 100644
--- a/src/app/pages/fund/components/fund-account-tile.tsx
+++ b/src/app/pages/fund/components/fund-account-tile.tsx
@@ -6,12 +6,12 @@ interface FundAccountTileProps {
description: string;
icon: string;
onClickTile(): void;
- ReceiveStxIcon?(): React.JSX.Element;
+ ReceiveIcon?(): React.JSX.Element;
testId: string;
title?: string;
}
export function FundAccountTile(props: FundAccountTileProps) {
- const { attributes, description, icon, onClickTile, ReceiveStxIcon, testId, title } = props;
+ const { attributes, description, icon, onClickTile, ReceiveIcon, testId, title } = props;
return (
-
- {ReceiveStxIcon ? : null}
+
+ {ReceiveIcon ? : null}
-
-
- Let's get funds into your wallet
-
+ Let's get {symbol}
+ into your wallet
+
-
- Choose an exchange to fund your account with {name} ({nameAbbr}) or deposit from
- elsewhere. Exchanges with “Fast checkout” make it easier to purchase {nameAbbr} for direct
- deposit into your wallet with a credit card.
-
-
+
+ Choose an exchange to fund your account with {name} ({nameAbbr}) or deposit from elsewhere.
+ Exchanges with “Fast checkout” make it easier to purchase {nameAbbr} for direct deposit into
+ your wallet with a credit card.
+
{children}
-
+
);
}
diff --git a/src/app/pages/fund/components/icon-components.tsx b/src/app/pages/fund/components/icon-components.tsx
index b6ab10ba3a4..c8cf0505a03 100644
--- a/src/app/pages/fund/components/icon-components.tsx
+++ b/src/app/pages/fund/components/icon-components.tsx
@@ -8,7 +8,7 @@ export function StacksIconComponent() {
return (
<>
-
+
@@ -20,7 +20,7 @@ export function StacksIconComponent() {
export function BitcoinIconComponent() {
return (
<>
-
+
diff --git a/src/app/pages/fund/components/receive-funds-item.tsx b/src/app/pages/fund/components/receive-funds-item.tsx
index 3a5854963b8..587a14e5d8b 100644
--- a/src/app/pages/fund/components/receive-funds-item.tsx
+++ b/src/app/pages/fund/components/receive-funds-item.tsx
@@ -33,7 +33,7 @@ export function ReceiveFundsItem({ onReceive, symbol }: ReceiveStxItemProps) {
description={cryptoDescriptions[symbol].title}
icon={QRCodeIcon}
onClickTile={onReceive}
- ReceiveStxIcon={cryptoDescriptions[symbol].IconComponent}
+ ReceiveIcon={cryptoDescriptions[symbol].IconComponent}
testId={FundPageSelectors.BtnReceiveStx}
/>
);
diff --git a/src/app/pages/fund/fiat-providers-list.tsx b/src/app/pages/fund/fiat-providers-list.tsx
index 67c7680a46e..446abe09cf3 100644
--- a/src/app/pages/fund/fiat-providers-list.tsx
+++ b/src/app/pages/fund/fiat-providers-list.tsx
@@ -48,22 +48,10 @@ export function FiatProvidersList(props: FiatProvidersProps) {
return (
navigate(RouteUrls.FundChooseCurrency)} title=" " />);
-
if (!address || !balance) return ;
return (
<>
diff --git a/src/app/pages/home/components/account-actions.tsx b/src/app/pages/home/components/account-actions.tsx
index 02ac19609f8..14643d65c47 100644
--- a/src/app/pages/home/components/account-actions.tsx
+++ b/src/app/pages/home/components/account-actions.tsx
@@ -2,7 +2,7 @@ import { useLocation, useNavigate } from 'react-router-dom';
import { ChainID } from '@stacks/transactions';
import { HomePageSelectors } from '@tests/selectors/home.selectors';
-import { Flex, FlexProps } from 'leather-styles/jsx';
+import { Flex } from 'leather-styles/jsx';
import { RouteUrls } from '@shared/route-urls';
@@ -11,12 +11,12 @@ import { useConfigBitcoinEnabled } from '@app/query/common/remote-config/remote-
import { useCurrentAccountNativeSegwitIndexZeroSignerNullable } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
import { useCurrentNetwork } from '@app/store/networks/networks.selectors';
+import { IconButton } from '@app/ui/components/icon-button/icon-button';
import { CreditCardIcon, InboxIcon, SwapIcon } from '@app/ui/icons';
-import { ActionButton } from './action-button';
import { SendButton } from './send-button';
-export function AccountActions(props: FlexProps) {
+export function AccountActions() {
const navigate = useNavigate();
const location = useLocation();
const isBitcoinEnabled = useConfigBitcoinEnabled();
@@ -30,9 +30,9 @@ export function AccountActions(props: FlexProps) {
: `${RouteUrls.Home}${RouteUrls.ReceiveStx}`;
return (
-
+
- }
label="Receive"
@@ -40,7 +40,7 @@ export function AccountActions(props: FlexProps) {
/>
{(!!stacksAccount || !!btcAccount) && (
- }
label="Buy"
@@ -49,7 +49,7 @@ export function AccountActions(props: FlexProps) {
)}
{whenStacksChainId(currentNetwork.chain.stacks.chainId)({
[ChainID.Mainnet]: (
- }
label="Swap"
diff --git a/src/app/pages/home/components/account-area.tsx b/src/app/pages/home/components/account-area.tsx
deleted file mode 100644
index c611ba8e090..00000000000
--- a/src/app/pages/home/components/account-area.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import { memo } from 'react';
-
-import { HStack, HstackProps, Stack } from 'leather-styles/jsx';
-
-import { CurrentAccountAvatar } from '@app/features/current-account/current-account-avatar';
-import { CurrentAccountName } from '@app/features/current-account/current-account-name';
-import { useCurrentAccountNativeSegwitAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
-import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
-
-import { AccountTotalBalance } from '../../../components/account-total-balance';
-
-export const CurrentAccount = memo((props: HstackProps) => {
- const currentAccount = useCurrentStacksAccount();
- const btcAddress = useCurrentAccountNativeSegwitAddressIndexZero();
-
- if (!currentAccount) return null;
- return (
-
-
-
-
-
-
-
-
-
- );
-});
diff --git a/src/app/pages/home/components/account-info-card.tsx b/src/app/pages/home/components/account-info-card.tsx
deleted file mode 100644
index 9a0e2ff6059..00000000000
--- a/src/app/pages/home/components/account-info-card.tsx
+++ /dev/null
@@ -1,67 +0,0 @@
-import { SettingsSelectors } from '@tests/selectors/settings.selectors';
-import { Box, Divider, Flex, styled } from 'leather-styles/jsx';
-
-import { useCurrentAccountDisplayName } from '@app/common/hooks/account/use-account-names';
-import { useTotalBalance } from '@app/common/hooks/balance/use-total-balance';
-import { useDrawers } from '@app/common/hooks/use-drawers';
-import { useCurrentAccountNativeSegwitAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
-import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
-import { Link } from '@app/ui/components/link/link';
-import { ChevronDownIcon } from '@app/ui/icons/chevron-down-icon';
-
-import { AccountActions } from './account-actions';
-
-export function AccountInfoCard() {
- const name = useCurrentAccountDisplayName();
-
- const account = useCurrentStacksAccount();
- const btcAddress = useCurrentAccountNativeSegwitAddressIndexZero();
- const totalBalance = useTotalBalance({ btcAddress, stxAddress: account?.address || '' });
-
- const { setIsShowingSwitchAccountsState } = useDrawers();
-
- return (
-
- setIsShowingSwitchAccountsState(true)}
- >
-
-
- {name}
-
-
-
-
-
-
-
-
- {totalBalance?.totalUsdBalance}
-
-
-
-
-
-
- );
-}
diff --git a/src/app/pages/home/components/action-button.tsx b/src/app/pages/home/components/action-button.tsx
deleted file mode 100644
index 0c1234c0f80..00000000000
--- a/src/app/pages/home/components/action-button.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import { Flex, styled } from 'leather-styles/jsx';
-
-import { Button } from '@app/ui/components/button/button';
-
-import AccessibleIcon from './accessible-icon';
-
-interface ActionButtonProps extends React.ComponentProps {
- icon: React.ReactNode;
- label: string;
-}
-
-export function ActionButton({ icon, label, ...rest }: ActionButtonProps) {
- return (
-
-
- {icon}
- {label}
-
-
- );
-}
diff --git a/src/app/pages/home/components/home-tabs.tsx b/src/app/pages/home/components/home-tabs.tsx
index f19f8ba83e8..ab5bca87814 100644
--- a/src/app/pages/home/components/home-tabs.tsx
+++ b/src/app/pages/home/components/home-tabs.tsx
@@ -11,25 +11,27 @@ import { Tabs } from '@app/ui/components/tabs/tabs';
interface HomeTabsProps {
children: React.ReactNode;
}
-// TODO #4013: Abstract this to generic RouteTab once choose-fee-tab updated
+
export function HomeTabs({ children }: HomeTabsProps) {
const navigate = useNavigate();
const location = useLocation();
return (
-
+ navigate(slug)} defaultValue={location.pathname}>
Assets
-
+
Activity
}>
- {children}
+
+ {children}
+
);
diff --git a/src/app/pages/home/components/home.layout.tsx b/src/app/pages/home/components/home.layout.tsx
deleted file mode 100644
index cb10533830d..00000000000
--- a/src/app/pages/home/components/home.layout.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import React from 'react';
-
-import { HomePageSelectors } from '@tests/selectors/home.selectors';
-import { Stack } from 'leather-styles/jsx';
-
-import { AccountInfoCard } from './account-info-card';
-
-type HomeLayoutProps = Record<'currentAccount' | 'children', React.ReactNode>;
-export function HomeLayout({ children }: HomeLayoutProps) {
- return (
-
-
-
- {children}
-
-
- );
-}
diff --git a/src/app/pages/home/components/send-button.tsx b/src/app/pages/home/components/send-button.tsx
index 76fc5aa68d0..58df5cc0150 100644
--- a/src/app/pages/home/components/send-button.tsx
+++ b/src/app/pages/home/components/send-button.tsx
@@ -13,10 +13,9 @@ import {
useTransferableStacksFungibleTokenAssetBalances,
} from '@app/query/stacks/balance/stacks-ft-balances.hooks';
import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
+import { IconButton } from '@app/ui/components/icon-button/icon-button';
import { SendIcon } from '@app/ui/icons';
-import { ActionButton } from './action-button';
-
function SendButtonSuspense() {
const navigate = useNavigate();
const { whenWallet } = useWalletType();
@@ -26,7 +25,7 @@ function SendButtonSuspense() {
const isDisabled = !stxAssetBalance && ftAssets?.length === 0;
return (
- }
@@ -34,10 +33,10 @@ function SendButtonSuspense() {
whenWallet({
ledger: () =>
whenPageMode({
- full: () => navigate(RouteUrls.SendCryptoAsset, { state: { hasHeaderTitle: true } }),
+ full: () => navigate(RouteUrls.SendCryptoAsset),
popup: () => openIndexPageInNewTab(RouteUrls.SendCryptoAsset),
})(),
- software: () => navigate(RouteUrls.SendCryptoAsset, { state: { hasHeaderTitle: true } }),
+ software: () => navigate(RouteUrls.SendCryptoAsset),
})()
}
disabled={isDisabled}
@@ -45,7 +44,7 @@ function SendButtonSuspense() {
);
}
-const SendButtonFallback = memo(() => } disabled />);
+const SendButtonFallback = memo(() => } disabled />);
export function SendButton() {
return (
diff --git a/src/app/pages/home/home.tsx b/src/app/pages/home/home.tsx
index d6187512d63..675fa68984b 100644
--- a/src/app/pages/home/home.tsx
+++ b/src/app/pages/home/home.tsx
@@ -1,40 +1,59 @@
+import { useState } from 'react';
import { Route, useNavigate } from 'react-router-dom';
import { RouteUrls } from '@shared/route-urls';
+import { useCurrentAccountDisplayName } from '@app/common/hooks/account/use-account-names';
import { useOnboardingState } from '@app/common/hooks/auth/use-onboarding-state';
+import { useTotalBalance } from '@app/common/hooks/balance/use-total-balance';
import { useOnMount } from '@app/common/hooks/use-on-mount';
-import { useRouteHeader } from '@app/common/hooks/use-route-header';
-import { Header } from '@app/components/header';
import { ActivityList } from '@app/features/activity-list/activity-list';
import { AssetsList } from '@app/features/asset-list/asset-list';
+import { SwitchAccountDialog } from '@app/features/dialogs/switch-account-dialog/switch-account-dialog';
import { FeedbackButton } from '@app/features/feedback-button/feedback-button';
-import { InAppMessages } from '@app/features/hiro-messages/in-app-messages';
import { homePageModalRoutes } from '@app/routes/app-routes';
import { ModalBackgroundWrapper } from '@app/routes/components/modal-background-wrapper';
+import { useCurrentAccountNativeSegwitAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
+import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
+import { AccountCard } from '@app/ui/components/account/account.card';
+import { HomeLayout } from '@app/ui/pages/home.layout';
-import { CurrentAccount } from './components/account-area';
+import { AccountActions } from './components/account-actions';
import { HomeTabs } from './components/home-tabs';
-import { HomeLayout } from './components/home.layout';
export function Home() {
+ const [isShowingSwitchAccount, setIsShowingSwitchAccount] = useState(false);
const { decodedAuthRequest } = useOnboardingState();
const navigate = useNavigate();
+ const name = useCurrentAccountDisplayName();
- useRouteHeader(
- <>
-
-
- >
- );
+ const account = useCurrentStacksAccount();
+ const btcAddress = useCurrentAccountNativeSegwitAddressIndexZero();
+ const totalBalance = useTotalBalance({ btcAddress, stxAddress: account?.address || '' });
useOnMount(() => {
if (decodedAuthRequest) navigate(RouteUrls.ChooseAccount);
});
return (
- }>
+ setIsShowingSwitchAccount(false)}
+ />
+ }
+ toggleSwitchAccount={() => setIsShowingSwitchAccount(!isShowingSwitchAccount)}
+ >
+
+
+ }
+ >
diff --git a/src/app/pages/onboarding/allow-diagnostics/allow-diagnostics-layout.tsx b/src/app/pages/onboarding/allow-diagnostics/allow-diagnostics-layout.tsx
index ccb2bc099aa..ce7e6ddf6a4 100644
--- a/src/app/pages/onboarding/allow-diagnostics/allow-diagnostics-layout.tsx
+++ b/src/app/pages/onboarding/allow-diagnostics/allow-diagnostics-layout.tsx
@@ -1,10 +1,11 @@
-import { Dialog } from '@radix-ui/themes';
import { OnboardingSelectors } from '@tests/selectors/onboarding.selectors';
-import { css } from 'leather-styles/css';
-import { Box, Flex, HStack, Stack, styled } from 'leather-styles/jsx';
+import { Box, Flex, Stack, styled } from 'leather-styles/jsx';
import { Button } from '@app/ui/components/button/button';
-import { CheckmarkIcon } from '@app/ui/icons/checkmark-icon';
+import { Dialog } from '@app/ui/components/containers/dialog/dialog';
+import { Footer } from '@app/ui/components/containers/footers/footer';
+import { Flag } from '@app/ui/components/flag/flag';
+import { CheckmarkIcon } from '@app/ui/icons/';
import { LogomarkIcon } from '@app/ui/icons/logomark-icon';
interface ReasonToAllowDiagnosticsProps {
@@ -12,12 +13,9 @@ interface ReasonToAllowDiagnosticsProps {
}
function ReasonToAllowDiagnostics({ text }: ReasonToAllowDiagnosticsProps) {
return (
-
-
-
-
- {text}
-
+ }>
+ {text}
+
);
}
@@ -25,51 +23,56 @@ interface AllowDiagnosticsLayoutProps {
onUserAllowDiagnostics(): void;
onUserDenyDiagnostics(): void;
}
-export function AllowDiagnosticsLayout(props: AllowDiagnosticsLayoutProps) {
- const { onUserAllowDiagnostics, onUserDenyDiagnostics } = props;
+export function AllowDiagnosticsLayout({
+ onUserAllowDiagnostics,
+ onUserDenyDiagnostics,
+}: AllowDiagnosticsLayoutProps) {
+ // this dialog cannot close without a footer action has no header
return (
-
-
-
-
- Help us improve
-
-
+
-
+
+
);
}
diff --git a/src/app/pages/onboarding/allow-diagnostics/allow-diagnostics.tsx b/src/app/pages/onboarding/allow-diagnostics/allow-diagnostics.tsx
index a7c44eab07f..4d1dd6e5263 100644
--- a/src/app/pages/onboarding/allow-diagnostics/allow-diagnostics.tsx
+++ b/src/app/pages/onboarding/allow-diagnostics/allow-diagnostics.tsx
@@ -5,8 +5,6 @@ import { useLocation, useNavigate } from 'react-router-dom';
import { RouteUrls } from '@shared/route-urls';
import { useAnalytics } from '@app/common/hooks/analytics/use-analytics';
-import { useRouteHeader } from '@app/common/hooks/use-route-header';
-import { Header } from '@app/components/header';
import { settingsActions } from '@app/store/settings/settings.actions';
import { AllowDiagnosticsLayout } from './allow-diagnostics-layout';
@@ -19,8 +17,6 @@ export function AllowDiagnosticsModal() {
useEffect(() => void analytics.page('view', `${pathname}`), [analytics, pathname]);
- useRouteHeader();
-
const setDiagnosticsPermissionsAndGoToOnboarding = useCallback(
(areDiagnosticsAllowed: boolean) => {
dispatch(settingsActions.setHasAllowedAnalytics(areDiagnosticsAllowed));
diff --git a/src/app/pages/onboarding/back-up-secret-key/back-up-secret-key.tsx b/src/app/pages/onboarding/back-up-secret-key/back-up-secret-key.tsx
index 210ab4f6ab0..8721ef746e3 100644
--- a/src/app/pages/onboarding/back-up-secret-key/back-up-secret-key.tsx
+++ b/src/app/pages/onboarding/back-up-secret-key/back-up-secret-key.tsx
@@ -1,22 +1,19 @@
import { memo, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
+import { HStack, Stack, styled } from 'leather-styles/jsx';
+
import { RouteUrls } from '@shared/route-urls';
-import { useRouteHeader } from '@app/common/hooks/use-route-header';
-import { Header } from '@app/components/header';
-import { TwoColumnLayout } from '@app/components/secret-key/two-column.layout';
-import { SecretKeyDisplayer } from '@app/features/secret-key-displayer/secret-key-displayer';
+import { SecretKey } from '@app/features/secret-key-displayer/secret-key-displayer';
import { useDefaultWalletSecretKey } from '@app/store/in-memory-key/in-memory-key.selectors';
-
-import { BackUpSecretKeyContent } from './components/back-up-secret-key.content';
+import { EyeSlashIcon, KeyIcon, LockIcon } from '@app/ui/icons/';
+import { TwoColumnLayout } from '@app/ui/pages/two-column.layout';
export const BackUpSecretKeyPage = memo(() => {
const secretKey = useDefaultWalletSecretKey();
const navigate = useNavigate();
- useRouteHeader();
-
useEffect(() => {
if (!secretKey) navigate(RouteUrls.Onboarding);
}, [navigate, secretKey]);
@@ -25,8 +22,37 @@ export const BackUpSecretKeyPage = memo(() => {
return (
}
- rightColumn={}
- />
+ title={<>Back up your Secret Key>}
+ content={
+ <>
+ You'll need it to access your wallet on a new device, or this one if you lose your
+ password — so back it up somewhere safe!
+ >
+ }
+ action={
+
+
+
+
+ Your Secret Key gives access to your wallet
+
+
+
+
+
+ Never share your Secret Key with anyone
+
+
+
+
+
+ Store it somewhere 100% private and secure
+
+
+
+ }
+ >
+
+
);
});
diff --git a/src/app/pages/onboarding/back-up-secret-key/components/back-up-secret-key.content.tsx b/src/app/pages/onboarding/back-up-secret-key/components/back-up-secret-key.content.tsx
deleted file mode 100644
index 050fd314f75..00000000000
--- a/src/app/pages/onboarding/back-up-secret-key/components/back-up-secret-key.content.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import { HStack, Stack, styled } from 'leather-styles/jsx';
-
-import { KeyIcon } from '@app/ui/icons';
-import { EyeSlashIcon } from '@app/ui/icons/eye-slash-icon';
-import { LockIcon } from '@app/ui/icons/lock-icon';
-
-export function BackUpSecretKeyContent(): React.JSX.Element {
- return (
- <>
-
- Back up your Secret Key
-
-
- Here's your Secret Key: 24 words that give you access to your new wallet.
-
-
- You'll need it to access your wallet on a new device, or this one if you lose your password
- — so back it up somewhere safe!
-
-
-
-
-
- Your Secret Key gives access to your wallet
-
-
-
- Never share your Secret Key with anyone
-
-
-
- Store it somewhere 100% private and secure
-
-
- >
- );
-}
diff --git a/src/app/pages/onboarding/set-password/components/password-bars.tsx b/src/app/pages/onboarding/set-password/components/password-bars.tsx
index afa5cc44e98..1aabe5f3610 100644
--- a/src/app/pages/onboarding/set-password/components/password-bars.tsx
+++ b/src/app/pages/onboarding/set-password/components/password-bars.tsx
@@ -6,21 +6,19 @@ interface PasswordStrengthBarsProps {
export function PasswordStrengthBars({ bars }: PasswordStrengthBarsProps) {
return (
- {bars.map((bar: string, index: number) => {
- return (
-
- );
- })}
+ {bars.map((bar: string, index: number) => (
+
+ ))}
);
}
diff --git a/src/app/pages/onboarding/set-password/set-password.tsx b/src/app/pages/onboarding/set-password/set-password.tsx
index b581478f9b9..dcc6e3efc09 100644
--- a/src/app/pages/onboarding/set-password/set-password.tsx
+++ b/src/app/pages/onboarding/set-password/set-password.tsx
@@ -3,7 +3,6 @@ import { useNavigate } from 'react-router-dom';
import { OnboardingSelectors } from '@tests/selectors/onboarding.selectors';
import { Form, Formik } from 'formik';
-import { styled } from 'leather-styles/jsx';
import { debounce } from 'ts-debounce';
import * as yup from 'yup';
@@ -14,16 +13,14 @@ import { useFinishAuthRequest } from '@app/common/authentication/use-finish-auth
import { useAnalytics } from '@app/common/hooks/analytics/use-analytics';
import { useOnboardingState } from '@app/common/hooks/auth/use-onboarding-state';
import { useKeyActions } from '@app/common/hooks/use-key-actions';
-import { useRouteHeader } from '@app/common/hooks/use-route-header';
import {
blankPasswordValidation,
validatePassword,
} from '@app/common/validation/validate-password';
-import { Header } from '@app/components/header';
-import { TwoColumnLayout } from '@app/components/secret-key/two-column.layout';
import { OnboardingGate } from '@app/routes/onboarding-gate';
import { useStacksAccounts } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
import { Button } from '@app/ui/components/button/button';
+import { TwoColumnLayout } from '@app/ui/pages/two-column.layout';
import { PasswordField } from './components/password-field';
@@ -53,8 +50,6 @@ function SetPasswordPage() {
const { decodedAuthRequest } = useOnboardingState();
const analytics = useAnalytics();
- useRouteHeader( navigate(-1)} />);
-
useEffect(() => {
void analytics.page('view', '/set-password');
}, [analytics]);
@@ -125,48 +120,33 @@ function SetPasswordPage() {
{({ dirty, isSubmitting, isValid }) => (
-
- Set a password
-
-
- Your password protects your Secret Key on this device only.
-
-
- You'll need just your Secret Key to access your wallet on another device, or this
- one if you lose your password.
-
+ Set a
+ password
>
}
- rightColumn={
+ content={
<>
-
- Your password
-
-
-
- Continue
-
+ Your password protects your Secret Key on this device only. To access your wallet on
+ another device, you'll need just your Secret Key.
>
}
- />
+ >
+ <>
+
+
+ Continue
+
+ >
+
)}
diff --git a/src/app/pages/onboarding/sign-in/components/sign-in.content.tsx b/src/app/pages/onboarding/sign-in/components/sign-in.content.tsx
deleted file mode 100644
index 497c0bd6290..00000000000
--- a/src/app/pages/onboarding/sign-in/components/sign-in.content.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import { styled } from 'leather-styles/jsx';
-
-import { Link } from '@app/ui/components/link/link';
-
-export function SignInContent({
- onClick,
- twentyFourWordMode,
-}: {
- onClick(): void;
- twentyFourWordMode: boolean;
-}): React.JSX.Element {
- return (
- <>
-
- Sign in with your
- Secret Key
-
-
- Speed things up by pasting your entire Secret Key in one go.
-
-
- {twentyFourWordMode ? 'Have a 12-word Secret Key?' : 'Use 24 word Secret Key'}
-
- >
- );
-}
diff --git a/src/app/pages/onboarding/sign-in/mnemonic-form.tsx b/src/app/pages/onboarding/sign-in/mnemonic-form.tsx
index e0c2f0b1060..2a9e327e497 100644
--- a/src/app/pages/onboarding/sign-in/mnemonic-form.tsx
+++ b/src/app/pages/onboarding/sign-in/mnemonic-form.tsx
@@ -1,22 +1,21 @@
import { OnboardingSelectors } from '@tests/selectors/onboarding.selectors';
import { Form, Formik } from 'formik';
-import { Flex, styled } from 'leather-styles/jsx';
+import { Stack } from 'leather-styles/jsx';
import { isEmpty } from '@shared/utils';
import { createNullArrayOfLength } from '@app/common/utils';
import { ErrorLabel } from '@app/components/error-label';
-import { SecretKeyGrid } from '@app/components/secret-key/secret-key-grid';
import { useSignIn } from '@app/pages/onboarding/sign-in/hooks/use-sign-in';
import { Button } from '@app/ui/components/button/button';
-
-import { MnemonicWordInput } from '../../../components/secret-key/mnemonic-key/mnemonic-word-input';
+import { MnemonicWordInput } from '@app/ui/components/secret-key/mnemonic-key/mnemonic-word-input';
import {
getMnemonicErrorFields,
getMnemonicErrorMessage,
hasMnemonicFormValues,
-} from '../../../components/secret-key/mnemonic-key/utils/error-handling';
-import { validationSchema } from '../../../components/secret-key/mnemonic-key/utils/validation';
+} from '@app/ui/components/secret-key/mnemonic-key/utils/error-handling';
+import { validationSchema } from '@app/ui/components/secret-key/mnemonic-key/utils/validation';
+import { SecretKeyGrid } from '@app/ui/components/secret-key/secret-key-grid';
interface MnemonicFormProps {
mnemonic: (string | null)[];
@@ -69,24 +68,21 @@ export function MnemonicForm({ mnemonic, setMnemonic, twentyFourWordMode }: Mnem
return (
>
diff --git a/src/app/pages/send/send-crypto-asset-form/form/stacks/stacks-send-form-confirmation.tsx b/src/app/pages/send/send-crypto-asset-form/form/stacks/stacks-send-form-confirmation.tsx
index 8bb9e4fb5b7..d91454baed6 100644
--- a/src/app/pages/send/send-crypto-asset-form/form/stacks/stacks-send-form-confirmation.tsx
+++ b/src/app/pages/send/send-crypto-asset-form/form/stacks/stacks-send-form-confirmation.tsx
@@ -1,4 +1,4 @@
-import { Outlet, useNavigate, useParams } from 'react-router-dom';
+import { Outlet, useParams } from 'react-router-dom';
import { deserializeTransaction } from '@stacks/transactions';
import { Box, Stack } from 'leather-styles/jsx';
@@ -6,8 +6,6 @@ import { Box, Stack } from 'leather-styles/jsx';
import { CryptoCurrencies } from '@shared/models/currencies.model';
import { useLocationStateWithCache } from '@app/common/hooks/use-location-state';
-import { useRouteHeader } from '@app/common/hooks/use-route-header';
-import { ModalHeader } from '@app/components/modal-header';
import { useStacksBroadcastTransaction } from '@app/features/stacks-transaction-request/hooks/use-stacks-broadcast-transaction';
import { useStacksTransactionSummary } from '@app/features/stacks-transaction-request/hooks/use-stacks-transaction-summary';
import { BasicTooltip } from '@app/ui/components/tooltip/basic-tooltip';
@@ -26,7 +24,6 @@ function useStacksSendFormConfirmationState() {
export function StacksSendFormConfirmation() {
const { tx, decimals, showFeeChangeWarning } = useStacksSendFormConfirmationState();
const { symbol = 'STX' } = useParams();
- const navigate = useNavigate();
const { stacksBroadcastTransaction, isBroadcasting } = useStacksBroadcastTransaction(
symbol.toUpperCase() as CryptoCurrencies,
@@ -51,15 +48,6 @@ export function StacksSendFormConfirmation() {
memoDisplayText,
} = formReviewTxSummary(stacksDeserializedTransaction, symbol, decimals);
- useRouteHeader(
- navigate('../', { relative: 'path', replace: true })}
- title="Review"
- />
- );
-
const feeWarningTooltip = showFeeChangeWarning ? (
) {
// Validate and check high fee warning first
const formErrors = formikHelpers.validateForm();
- if (
- !isShowingHighFeeConfirmation &&
- isEmpty(formErrors) &&
- new BigNumber(values.fee).isGreaterThan(HIGH_FEE_AMOUNT_STX)
- ) {
- setIsShowingHighFeeConfirmation(true);
+ if (isEmpty(formErrors) && new BigNumber(values.fee).isGreaterThan(HIGH_FEE_AMOUNT_STX)) {
return false;
}
return true;
diff --git a/src/app/pages/send/send-crypto-asset-form/form/stx/stx-send-form.tsx b/src/app/pages/send/send-crypto-asset-form/form/stx/stx-send-form.tsx
index 870350795c6..0e95742f203 100644
--- a/src/app/pages/send/send-crypto-asset-form/form/stx/stx-send-form.tsx
+++ b/src/app/pages/send/send-crypto-asset-form/form/stx/stx-send-form.tsx
@@ -22,6 +22,7 @@ export function StxSendForm() {
sendMaxBalance,
stxFees: fees,
validationSchema,
+ fee,
} = useStxSendForm();
const amountField = (
@@ -47,6 +48,9 @@ export function StxSendForm() {
amountField={amountField}
selectedAssetField={selectedAssetField}
fees={fees}
+ // FIXME 4370 - need to fix this as fee is actually NumberSchema; in FeeValidatorFactoryArgs
+ // this needs to be the STX fee so it can be validated against HIGH_FEE_AMOUNT_STX
+ fee={fee as unknown as string}
availableTokenBalance={availableStxBalance}
/>
);
diff --git a/src/app/pages/send/send-crypto-asset-form/form/stx/use-stx-send-form.tsx b/src/app/pages/send/send-crypto-asset-form/form/stx/use-stx-send-form.tsx
index 78ad2f2b77d..7da098a7a22 100644
--- a/src/app/pages/send/send-crypto-asset-form/form/stx/use-stx-send-form.tsx
+++ b/src/app/pages/send/send-crypto-asset-form/form/stx/use-stx-send-form.tsx
@@ -50,12 +50,16 @@ export function useStxSendForm() {
availableTokenBalance: availableStxBalance,
});
+ // FIXME - I don't this this is the fee, should be value.fee or something from the form
+ const fee = stxFeeValidator(availableStxBalance);
+
return {
availableStxBalance,
initialValues,
onFormStateChange,
sendMaxBalance,
stxFees,
+ fee,
validationSchema: yup.object({
amount: stxAmountValidator(availableStxBalance).concat(
diff --git a/src/app/pages/send/send-crypto-asset-form/hooks/use-send-form-navigate.ts b/src/app/pages/send/send-crypto-asset-form/hooks/use-send-form-navigate.ts
index df0de5edb5a..4243f5411d4 100644
--- a/src/app/pages/send/send-crypto-asset-form/hooks/use-send-form-navigate.ts
+++ b/src/app/pages/send/send-crypto-asset-form/hooks/use-send-form-navigate.ts
@@ -14,7 +14,6 @@ interface ConfirmationRouteState {
decimals?: number;
token?: string;
tx: string;
- hasHeaderTitle?: boolean;
}
interface ConfirmationRouteStacksSip10Args {
@@ -49,7 +48,6 @@ export function useSendFormNavigate() {
isSendingMax,
utxos,
values,
- hasHeaderTitle: true,
},
});
},
@@ -67,7 +65,6 @@ export function useSendFormNavigate() {
fee,
feeRowValue,
time,
- hasHeaderTitle: true,
} as ConfirmationRouteState,
});
},
@@ -76,7 +73,6 @@ export function useSendFormNavigate() {
replace: true,
state: {
tx: bytesToHex(tx.serialize()),
- hasHeaderTitle: true,
showFeeChangeWarning,
} as ConfirmationRouteState,
});
diff --git a/src/app/pages/send/send-crypto-asset-form/send-crypto-asset-form.routes.tsx b/src/app/pages/send/send-crypto-asset-form/send-crypto-asset-form.routes.tsx
index 8ad33c31f81..01fec10cac8 100644
--- a/src/app/pages/send/send-crypto-asset-form/send-crypto-asset-form.routes.tsx
+++ b/src/app/pages/send/send-crypto-asset-form/send-crypto-asset-form.routes.tsx
@@ -1,23 +1,23 @@
import { Suspense } from 'react';
-import { Route } from 'react-router-dom';
+import { Outlet, Route } from 'react-router-dom';
import { RouteUrls } from '@shared/route-urls';
-import { BroadcastErrorDrawer } from '@app/components/broadcast-error-drawer/broadcast-error-drawer';
+import { BroadcastErrorDialog } from '@app/components/broadcast-error-dialog/broadcast-error-dialog';
import { SendBtcDisabled } from '@app/components/crypto-assets/choose-crypto-asset/send-btc-disabled';
import { FullPageWithHeaderLoadingSpinner } from '@app/components/loading-spinner';
-import { EditNonceDrawer } from '@app/features/edit-nonce-drawer/edit-nonce-drawer';
+import { EditNonceDialog } from '@app/features/dialogs/edit-nonce-dialog/edit-nonce-dialog';
import { ledgerBitcoinTxSigningRoutes } from '@app/features/ledger/flows/bitcoin-tx-signing/ledger-bitcoin-sign-tx-container';
import { ledgerStacksTxSigningRoutes } from '@app/features/ledger/flows/stacks-tx-signing/ledger-sign-stacks-tx-container';
import { AccountGate } from '@app/routes/account-gate';
+import { Page } from '@app/ui/layout/page/page.layout';
import { BroadcastError } from '../broadcast-error/broadcast-error';
import { ChooseCryptoAsset } from '../choose-crypto-asset/choose-crypto-asset';
-import { SendContainer } from '../send-container';
import { Brc20SentSummary } from '../sent-summary/brc20-sent-summary';
import { BtcSentSummary } from '../sent-summary/btc-sent-summary';
import { StxSentSummary } from '../sent-summary/stx-sent-summary';
-import { RecipientAccountsDrawer } from './components/recipient-accounts-drawer/recipient-accounts-drawer';
+import { RecipientAccountsDialog } from './components/recipient-accounts-dialog/recipient-accounts-dialog';
import { SendBitcoinAssetContainer } from './family/bitcoin/components/send-bitcoin-asset-container';
import { Brc20SendForm } from './form/brc-20/brc20-send-form';
import { Brc20SendFormConfirmation } from './form/brc-20/brc20-send-form-confirmation';
@@ -29,20 +29,26 @@ import { Sip10TokenSendForm } from './form/stacks-sip10/sip10-token-send-form';
import { StacksSendFormConfirmation } from './form/stacks/stacks-send-form-confirmation';
import { StxSendForm } from './form/stx/stx-send-form';
-const recipientAccountsDrawerRoute = (
+const recipientAccountsDialogRoute = (
}
+ element={}
/>
);
-const editNonceDrawerRoute = } />;
-const broadcastErrorDrawerRoute = (
- } />
+const editNonceDialogRoute = } />;
+const broadcastErrorDialogRoute = (
+ } />
);
export const sendCryptoAssetFormRoutes = (
- }>
+
+
+
+ }
+ >
}
/>
-
}>
}
>
{ledgerBitcoinTxSigningRoutes}
- {recipientAccountsDrawerRoute}
+ {recipientAccountsDialogRoute}
- } />
- } />
+ } />
+ } />
- } />
+ } />
}>
{ledgerBitcoinTxSigningRoutes}
@@ -78,11 +83,10 @@ export const sendCryptoAssetFormRoutes = (
} />
} />
-
}>
- {broadcastErrorDrawerRoute}
- {editNonceDrawerRoute}
- {recipientAccountsDrawerRoute}
+ {broadcastErrorDialogRoute}
+ {editNonceDialogRoute}
+ {recipientAccountsDialogRoute}
{ledgerStacksTxSigningRoutes}
-
}>
- {broadcastErrorDrawerRoute}
- {editNonceDrawerRoute}
- {recipientAccountsDrawerRoute}
+ {broadcastErrorDialogRoute}
+ {editNonceDialogRoute}
+ {recipientAccountsDialogRoute}
}>
{ledgerStacksTxSigningRoutes}
-
} />
);
diff --git a/src/app/pages/send/sent-summary/brc20-sent-summary.tsx b/src/app/pages/send/sent-summary/brc20-sent-summary.tsx
index 6b2748160d4..d610a1318bb 100644
--- a/src/app/pages/send/sent-summary/brc20-sent-summary.tsx
+++ b/src/app/pages/send/sent-summary/brc20-sent-summary.tsx
@@ -5,7 +5,6 @@ import get from 'lodash.get';
import { createMoney } from '@shared/models/money.model';
-import { useRouteHeader } from '@app/common/hooks/use-route-header';
import { HandleOpenStacksTxLinkArgs } from '@app/common/hooks/use-stacks-explorer-link';
import { formatMoney } from '@app/common/money/format-money';
import { openInNewTab } from '@app/common/utils/open-in-new-tab';
@@ -17,7 +16,6 @@ import {
InfoCardRow,
InfoCardSeparator,
} from '@app/components/info-card/info-card';
-import { ModalHeader } from '@app/components/modal-header';
import { Callout } from '@app/ui/components/callout/callout';
import { Link } from '@app/ui/components/link/link';
import { ExternalLinkIcon } from '@app/ui/icons/external-link-icon';
@@ -47,8 +45,6 @@ export function Brc20SentSummary() {
navigate('/');
}
- useRouteHeader();
-
return (
diff --git a/src/app/pages/send/sent-summary/btc-sent-summary.tsx b/src/app/pages/send/sent-summary/btc-sent-summary.tsx
index 8dd0f56b216..0a213463094 100644
--- a/src/app/pages/send/sent-summary/btc-sent-summary.tsx
+++ b/src/app/pages/send/sent-summary/btc-sent-summary.tsx
@@ -5,7 +5,6 @@ import { HStack, Stack } from 'leather-styles/jsx';
import { useAnalytics } from '@app/common/hooks/analytics/use-analytics';
import { useBitcoinExplorerLink } from '@app/common/hooks/use-bitcoin-explorer-link';
import { useClipboard } from '@app/common/hooks/use-copy-to-clipboard';
-import { useRouteHeader } from '@app/common/hooks/use-route-header';
import { FormAddressDisplayer } from '@app/components/address-displayer/form-address-displayer';
import {
InfoCard,
@@ -15,7 +14,6 @@ import {
InfoCardRow,
InfoCardSeparator,
} from '@app/components/info-card/info-card';
-import { ModalHeader } from '@app/components/modal-header';
import { useToast } from '@app/features/toasts/use-toast';
import { CopyIcon } from '@app/ui/icons/copy-icon';
import { ExternalLinkIcon } from '@app/ui/icons/external-link-icon';
@@ -54,8 +52,6 @@ export function BtcSentSummary() {
toast.success('ID copied!');
}
- useRouteHeader();
-
return (
diff --git a/src/app/pages/send/sent-summary/stx-sent-summary.tsx b/src/app/pages/send/sent-summary/stx-sent-summary.tsx
index 8346b8c9cce..af3fcaa6422 100644
--- a/src/app/pages/send/sent-summary/stx-sent-summary.tsx
+++ b/src/app/pages/send/sent-summary/stx-sent-summary.tsx
@@ -4,9 +4,7 @@ import { HStack, Stack } from 'leather-styles/jsx';
import { useAnalytics } from '@app/common/hooks/analytics/use-analytics';
import { useClipboard } from '@app/common/hooks/use-copy-to-clipboard';
-import { useRouteHeader } from '@app/common/hooks/use-route-header';
import { useStacksExplorerLink } from '@app/common/hooks/use-stacks-explorer-link';
-import { whenPageMode } from '@app/common/utils';
import { FormAddressDisplayer } from '@app/components/address-displayer/form-address-displayer';
import {
InfoCard,
@@ -16,7 +14,6 @@ import {
InfoCardRow,
InfoCardSeparator,
} from '@app/components/info-card/info-card';
-import { ModalHeader } from '@app/components/modal-header';
import { useToast } from '@app/features/toasts/use-toast';
import { CopyIcon } from '@app/ui/icons/copy-icon';
import { ExternalLinkIcon } from '@app/ui/icons/external-link-icon';
@@ -55,15 +52,8 @@ export function StxSentSummary() {
toast.success('ID copied!');
}
- useRouteHeader();
-
return (
-
+
-
-
-
- When you sign out,
- {whenWallet({
- software: ` you'll need your Secret Key to sign back in. Only sign out if you've backed up your Secret Key.`,
- ledger: ` you'll need to reconnect your Ledger to sign back into your wallet.`,
- })}
-
-
- {whenWallet({
- software: (
- } spacing="space.02">
- If you haven't backed up your Secret Key, you will lose all your funds.
-
- ),
- ledger: <>>,
- })}
-
-
-
-
-
-
- I've backed up my Secret Key
-
-
-
-
-
-
-
- I understand my password will no longer work for accessing my wallet upon signing out
-
-
-
- onUserSafelyReturnToHomepage()}
- variant="outline"
- >
- Cancel
-
-
- Sign out
-
-
-
-
-
- );
-}
diff --git a/src/app/pages/sign-out-confirm/sign-out-confirm.tsx b/src/app/pages/sign-out-confirm/sign-out-confirm.tsx
deleted file mode 100644
index b335ec8ed8a..00000000000
--- a/src/app/pages/sign-out-confirm/sign-out-confirm.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import { useNavigate } from 'react-router-dom';
-
-import { RouteUrls } from '@shared/route-urls';
-
-import { useKeyActions } from '@app/common/hooks/use-key-actions';
-import { useLocationState } from '@app/common/hooks/use-location-state';
-import { useBackgroundLocationRedirect } from '@app/routes/hooks/use-background-location-redirect';
-
-import { SignOutConfirmLayout } from './sign-out-confirm.layout';
-
-export function SignOutConfirmDrawer() {
- useBackgroundLocationRedirect();
- const { signOut } = useKeyActions();
- const navigate = useNavigate();
- const backgroundLocation = useLocationState('backgroundLocation');
-
- return (
- {
- void signOut().finally(() => {
- navigate(RouteUrls.Onboarding);
- });
- }}
- onUserSafelyReturnToHomepage={() => navigate(backgroundLocation ?? '..')}
- />
- );
-}
diff --git a/src/app/pages/swap/alex-swap-container.tsx b/src/app/pages/swap/alex-swap-container.tsx
index 9a47b3a7d9b..a86d90648f9 100644
--- a/src/app/pages/swap/alex-swap-container.tsx
+++ b/src/app/pages/swap/alex-swap-container.tsx
@@ -21,8 +21,8 @@ import { NonceSetter } from '@app/components/nonce-setter';
import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
import { useGenerateStacksContractCallUnsignedTx } from '@app/store/transactions/contract-call.hooks';
import { useSignStacksTransaction } from '@app/store/transactions/transaction.hooks';
+import { Page } from '@app/ui/layout/page/page.layout';
-import { SwapContainerLayout } from './components/swap-container.layout';
import { SwapForm } from './components/swap-form';
import { generateSwapRoutes } from './generate-swap-routes';
import { useAlexBroadcastSwap } from './hooks/use-alex-broadcast-swap';
@@ -204,12 +204,12 @@ function AlexSwapContainer() {
return (
-
+
-
+
);
}
diff --git a/src/app/pages/swap/components/swap-amount-field.tsx b/src/app/pages/swap/components/swap-amount-field.tsx
index 6c4336c1c8f..dbd0a5e5425 100644
--- a/src/app/pages/swap/components/swap-amount-field.tsx
+++ b/src/app/pages/swap/components/swap-amount-field.tsx
@@ -79,7 +79,7 @@ export function SwapAmountField({ amountAsFiat, isDisabled, name }: SwapAmountFi
}}
/>
{amountAsFiat ? (
-
+
{amountAsFiat}
) : null}
diff --git a/src/app/pages/swap/components/swap-assets-pair/swap-asset-item.layout.tsx b/src/app/pages/swap/components/swap-assets-pair/swap-asset-item.layout.tsx
index 44d54f375bc..5a9beb435ba 100644
--- a/src/app/pages/swap/components/swap-assets-pair/swap-asset-item.layout.tsx
+++ b/src/app/pages/swap/components/swap-assets-pair/swap-asset-item.layout.tsx
@@ -16,7 +16,7 @@ export function SwapAssetItemLayout({ caption, icon, symbol, value }: SwapAssetI
spacing="space.03"
width="100%"
>
-
+
{caption}
diff --git a/src/app/pages/swap/swap-choose-asset/components/swap-asset-item.tsx b/src/app/pages/swap/components/swap-choose-asset/components/swap-asset-item.tsx
similarity index 92%
rename from src/app/pages/swap/swap-choose-asset/components/swap-asset-item.tsx
rename to src/app/pages/swap/components/swap-choose-asset/components/swap-asset-item.tsx
index ff3dedfd37d..7fabdbcd131 100644
--- a/src/app/pages/swap/swap-choose-asset/components/swap-asset-item.tsx
+++ b/src/app/pages/swap/components/swap-choose-asset/components/swap-asset-item.tsx
@@ -7,8 +7,8 @@ import { Avatar, defaultFallbackDelay, getAvatarFallback } from '@app/ui/compone
import { ItemLayout } from '@app/ui/components/item-layout/item-layout';
import { Pressable } from '@app/ui/pressable/pressable';
-import { useAlexSdkBalanceAsFiat } from '../../hooks/use-alex-sdk-fiat-price';
-import { SwapAsset } from '../../hooks/use-swap-form';
+import { useAlexSdkBalanceAsFiat } from '../../../hooks/use-alex-sdk-fiat-price';
+import { SwapAsset } from '../../../hooks/use-swap-form';
interface SwapAssetItemProps {
asset: SwapAsset;
diff --git a/src/app/pages/swap/swap-choose-asset/components/swap-asset-list.tsx b/src/app/pages/swap/components/swap-choose-asset/components/swap-asset-list.tsx
similarity index 89%
rename from src/app/pages/swap/swap-choose-asset/components/swap-asset-list.tsx
rename to src/app/pages/swap/components/swap-choose-asset/components/swap-asset-list.tsx
index 32c2028fd89..c7870c93e52 100644
--- a/src/app/pages/swap/swap-choose-asset/components/swap-asset-list.tsx
+++ b/src/app/pages/swap/components/swap-choose-asset/components/swap-asset-list.tsx
@@ -1,7 +1,9 @@
import { useNavigate } from 'react-router-dom';
+import { SwapSelectors } from '@tests/selectors/swap.selectors';
import BigNumber from 'bignumber.js';
import { useFormikContext } from 'formik';
+import { Stack } from 'leather-styles/jsx';
import { createMoney } from '@shared/models/money.model';
import { isUndefined } from '@shared/utils';
@@ -10,10 +12,9 @@ import { convertAmountToFractionalUnit } from '@app/common/money/calculate-money
import { formatMoneyWithoutSymbol } from '@app/common/money/format-money';
import { useSwapContext } from '@app/pages/swap/swap.context';
-import { SwapAsset, SwapFormValues } from '../../hooks/use-swap-form';
+import { SwapAsset, SwapFormValues } from '../../../hooks/use-swap-form';
import { useSwapChooseAssetState } from '../swap-choose-asset';
import { SwapAssetItem } from './swap-asset-item';
-import { SwapAssetListLayout } from './swap-asset-list.layout';
interface SwapAssetList {
assets: SwapAsset[];
@@ -46,6 +47,7 @@ export function SwapAssetList({ assets }: SwapAssetList) {
await setFieldValue('swapAssetTo', asset);
setFieldError('swapAssetTo', undefined);
}
+
navigate(-1);
if (from && to && values.swapAmountFrom) {
const toAmount = await fetchToAmount(from, to, values.swapAmountFrom);
@@ -64,7 +66,7 @@ export function SwapAssetList({ assets }: SwapAssetList) {
}
return (
-
+
{selectableAssets.map(asset => (
onChooseAsset(asset)}
/>
))}
-
+
);
}
diff --git a/src/app/pages/swap/components/swap-choose-asset/swap-choose-asset.tsx b/src/app/pages/swap/components/swap-choose-asset/swap-choose-asset.tsx
new file mode 100644
index 00000000000..20683678027
--- /dev/null
+++ b/src/app/pages/swap/components/swap-choose-asset/swap-choose-asset.tsx
@@ -0,0 +1,45 @@
+import { useLocation, useNavigate } from 'react-router-dom';
+
+import get from 'lodash.get';
+
+import { RouteUrls } from '@shared/route-urls';
+
+import { Dialog } from '@app/ui/components/containers/dialog/dialog';
+import { Header } from '@app/ui/components/containers/headers/header';
+
+import { useSwapContext } from '../../swap.context';
+import { SwapAssetList } from './components/swap-asset-list';
+
+export function useSwapChooseAssetState() {
+ const location = useLocation();
+ const swapListType = get(location.state, 'swap') as string;
+ return { swapListType };
+}
+
+export function SwapChooseAsset() {
+ const { swappableAssetsFrom, swappableAssetsTo } = useSwapContext();
+ const { swapListType } = useSwapChooseAssetState();
+ const navigate = useNavigate();
+
+ const isFromList = swapListType === 'from';
+
+ const title = isFromList ? (
+ <>
+ Choose asset to swap
+ >
+ ) : (
+ <>
+ Choose asset to receive
+ >
+ );
+
+ return (
+
+ );
+}
diff --git a/src/app/pages/swap/components/swap-container.layout.tsx b/src/app/pages/swap/components/swap-container.layout.tsx
deleted file mode 100644
index bdd32521f9a..00000000000
--- a/src/app/pages/swap/components/swap-container.layout.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import { Flex } from 'leather-styles/jsx';
-
-import { HasChildren } from '@app/common/has-children';
-import { whenPageMode } from '@app/common/utils';
-
-export function SwapContainerLayout({ children }: HasChildren) {
- return whenPageMode({
- full: (
-
- {children}
-
- ),
- popup: (
-
- {children}
-
- ),
- });
-}
diff --git a/src/app/pages/swap/components/swap-content.layout.tsx b/src/app/pages/swap/components/swap-content.layout.tsx
deleted file mode 100644
index 0d3d2147691..00000000000
--- a/src/app/pages/swap/components/swap-content.layout.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import { SwapSelectors } from '@tests/selectors/swap.selectors';
-import { Flex } from 'leather-styles/jsx';
-
-import { HasChildren } from '@app/common/has-children';
-
-export function SwapContentLayout({ children }: HasChildren) {
- return (
-
- {children}
-
- );
-}
diff --git a/src/app/pages/swap/swap-error/swap-error.tsx b/src/app/pages/swap/components/swap-error.tsx
similarity index 100%
rename from src/app/pages/swap/swap-error/swap-error.tsx
rename to src/app/pages/swap/components/swap-error.tsx
diff --git a/src/app/pages/swap/components/swap-footer.layout.tsx b/src/app/pages/swap/components/swap-footer.layout.tsx
deleted file mode 100644
index 61339a2c4d8..00000000000
--- a/src/app/pages/swap/components/swap-footer.layout.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import { Flex } from 'leather-styles/jsx';
-
-import { HasChildren } from '@app/common/has-children';
-import { whenPageMode } from '@app/common/utils';
-
-export function SwapFooterLayout({ children }: HasChildren) {
- return (
-
- {children}
-
- );
-}
diff --git a/src/app/pages/swap/components/swap-review.tsx b/src/app/pages/swap/components/swap-review.tsx
new file mode 100644
index 00000000000..e4e5e1ab8dd
--- /dev/null
+++ b/src/app/pages/swap/components/swap-review.tsx
@@ -0,0 +1,44 @@
+import { Outlet } from 'react-router-dom';
+
+import { SwapSelectors } from '@tests/selectors/swap.selectors';
+
+import { LoadingKeys, useLoading } from '@app/common/hooks/use-loading';
+import { Button } from '@app/ui/components/button/button';
+import { Footer } from '@app/ui/components/containers/footers/footer';
+import { Card } from '@app/ui/layout/card/card';
+import { CardContent } from '@app/ui/layout/card/card-content';
+
+import { useSwapContext } from '../swap.context';
+import { SwapAssetsPair } from './swap-assets-pair/swap-assets-pair';
+import { SwapDetails } from './swap-details/swap-details';
+
+export function SwapReview() {
+ const { onSubmitSwap } = useSwapContext();
+ const { isLoading } = useLoading(LoadingKeys.SUBMIT_SWAP_TRANSACTION);
+
+ return (
+ <>
+
+
+ Swap
+
+
+ }
+ >
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/app/pages/swap/components/swap-selected-asset.layout.tsx b/src/app/pages/swap/components/swap-selected-asset.layout.tsx
index b8cc6083ced..c5ddb5c9eae 100644
--- a/src/app/pages/swap/components/swap-selected-asset.layout.tsx
+++ b/src/app/pages/swap/components/swap-selected-asset.layout.tsx
@@ -77,7 +77,7 @@ export function SwapSelectedAssetLayout({
{showError ? error : caption}
@@ -88,7 +88,7 @@ export function SwapSelectedAssetLayout({
onClick={onClickHandler ? onClickHandler : noop}
variant={onClickHandler ? 'underlined' : 'text'}
>
- {value}
+ {value}
) : null}
diff --git a/src/app/pages/swap/generate-swap-routes.tsx b/src/app/pages/swap/generate-swap-routes.tsx
index eba6b0906db..2a3cd476a9d 100644
--- a/src/app/pages/swap/generate-swap-routes.tsx
+++ b/src/app/pages/swap/generate-swap-routes.tsx
@@ -5,10 +5,10 @@ import { RouteUrls } from '@shared/route-urls';
import { ledgerStacksTxSigningRoutes } from '@app/features/ledger/flows/stacks-tx-signing/ledger-sign-stacks-tx-container';
import { AccountGate } from '@app/routes/account-gate';
+import { SwapChooseAsset } from './components/swap-choose-asset/swap-choose-asset';
+import { SwapError } from './components/swap-error';
+import { SwapReview } from './components/swap-review';
import { Swap } from './swap';
-import { SwapChooseAsset } from './swap-choose-asset/swap-choose-asset';
-import { SwapError } from './swap-error/swap-error';
-import { SwapReview } from './swap-review/swap-review';
export function generateSwapRoutes(container: React.ReactNode) {
return (
diff --git a/src/app/pages/swap/swap-choose-asset/components/swap-asset-list.layout.tsx b/src/app/pages/swap/swap-choose-asset/components/swap-asset-list.layout.tsx
deleted file mode 100644
index eb40bfdcbf4..00000000000
--- a/src/app/pages/swap/swap-choose-asset/components/swap-asset-list.layout.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Stack, StackProps } from 'leather-styles/jsx';
-
-export function SwapAssetListLayout({ children }: StackProps) {
- return (
-
- {children}
-
- );
-}
diff --git a/src/app/pages/swap/swap-choose-asset/swap-choose-asset.tsx b/src/app/pages/swap/swap-choose-asset/swap-choose-asset.tsx
deleted file mode 100644
index d095c762849..00000000000
--- a/src/app/pages/swap/swap-choose-asset/swap-choose-asset.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import { useLocation, useNavigate } from 'react-router-dom';
-
-import { SwapSelectors } from '@tests/selectors/swap.selectors';
-import { Box, styled } from 'leather-styles/jsx';
-import get from 'lodash.get';
-
-import { BaseDrawer } from '@app/components/drawer/base-drawer';
-
-import { useSwapContext } from '../swap.context';
-import { SwapAssetList } from './components/swap-asset-list';
-
-export function useSwapChooseAssetState() {
- const location = useLocation();
- const swapListType = get(location.state, 'swap') as string;
- return { swapListType };
-}
-
-export function SwapChooseAsset() {
- const { swappableAssetsFrom, swappableAssetsTo } = useSwapContext();
- const { swapListType } = useSwapChooseAssetState();
- const navigate = useNavigate();
-
- const isFromList = swapListType === 'from';
-
- const title = isFromList ? (
- <>
- Choose asset
-
- to swap
- >
- ) : (
- <>
- Choose asset
-
- to receive
- >
- );
-
- return (
- navigate(-1)}>
-
-
- {title}
-
-
-
-
- );
-}
diff --git a/src/app/pages/swap/swap-review/swap-review.layout.tsx b/src/app/pages/swap/swap-review/swap-review.layout.tsx
deleted file mode 100644
index 64936f0b26c..00000000000
--- a/src/app/pages/swap/swap-review/swap-review.layout.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Flex } from 'leather-styles/jsx';
-
-import { HasChildren } from '@app/common/has-children';
-
-export function SwapReviewLayout({ children }: HasChildren) {
- return (
-
- {children}
-
- );
-}
diff --git a/src/app/pages/swap/swap-review/swap-review.tsx b/src/app/pages/swap/swap-review/swap-review.tsx
deleted file mode 100644
index accef3a66a9..00000000000
--- a/src/app/pages/swap/swap-review/swap-review.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import { Outlet } from 'react-router-dom';
-
-import { SwapSelectors } from '@tests/selectors/swap.selectors';
-
-import { LoadingKeys, useLoading } from '@app/common/hooks/use-loading';
-import { useRouteHeader } from '@app/common/hooks/use-route-header';
-import { ModalHeader } from '@app/components/modal-header';
-import { Button } from '@app/ui/components/button/button';
-
-import { SwapAssetsPair } from '../components/swap-assets-pair/swap-assets-pair';
-import { SwapContentLayout } from '../components/swap-content.layout';
-import { SwapDetails } from '../components/swap-details/swap-details';
-import { SwapFooterLayout } from '../components/swap-footer.layout';
-import { useSwapContext } from '../swap.context';
-import { SwapReviewLayout } from './swap-review.layout';
-
-export function SwapReview() {
- const { onSubmitSwap } = useSwapContext();
- const { isLoading } = useLoading(LoadingKeys.SUBMIT_SWAP_TRANSACTION);
-
- useRouteHeader(, true);
-
- return (
- <>
-
-
-
-
-
-
-
- Swap
-
-
-
-
- >
- );
-}
diff --git a/src/app/pages/swap/swap.tsx b/src/app/pages/swap/swap.tsx
index 64b8cb5fce4..04331c47075 100644
--- a/src/app/pages/swap/swap.tsx
+++ b/src/app/pages/swap/swap.tsx
@@ -6,13 +6,12 @@ import { useFormikContext } from 'formik';
import { isUndefined } from '@shared/utils';
-import { useRouteHeader } from '@app/common/hooks/use-route-header';
import { LoadingSpinner } from '@app/components/loading-spinner';
-import { ModalHeader } from '@app/components/modal-header';
import { Button } from '@app/ui/components/button/button';
+import { Footer } from '@app/ui/components/containers/footers/footer';
+import { Card } from '@app/ui/layout/card/card';
+import { CardContent } from '@app/ui/layout/card/card-content';
-import { SwapContentLayout } from './components/swap-content.layout';
-import { SwapFooterLayout } from './components/swap-footer.layout';
import { SwapSelectedAssets } from './components/swap-selected-assets';
import { SwapFormValues } from './hooks/use-swap-form';
import { useSwapContext } from './swap.context';
@@ -21,8 +20,6 @@ export function Swap() {
const { isFetchingExchangeRate, swappableAssetsFrom } = useSwapContext();
const { dirty, isValid, setFieldValue, values } = useFormikContext();
- useRouteHeader(, true);
-
useAsync(async () => {
if (isUndefined(values.swapAssetFrom))
return await setFieldValue('swapAssetFrom', swappableAssetsFrom[0]);
@@ -32,21 +29,24 @@ export function Swap() {
if (isUndefined(values.swapAssetFrom)) return ;
return (
- <>
-
+
+
+ Review and swap
+
+
+ }
+ >
+
-
-
-
- Review and swap
-
-
+
- >
+
);
}
diff --git a/src/app/pages/transaction-request/transaction-request.tsx b/src/app/pages/transaction-request/transaction-request.tsx
index 55aea73d0dd..049cdd81d6f 100644
--- a/src/app/pages/transaction-request/transaction-request.tsx
+++ b/src/app/pages/transaction-request/transaction-request.tsx
@@ -1,4 +1,4 @@
-import { memo } from 'react';
+import { memo, useState } from 'react';
import { Outlet, useNavigate } from 'react-router-dom';
import { Formik, FormikHelpers } from 'formik';
@@ -13,13 +13,11 @@ import { RouteUrls } from '@shared/route-urls';
import { useAnalytics } from '@app/common/hooks/analytics/use-analytics';
import { useOnMount } from '@app/common/hooks/use-on-mount';
-import { useRouteHeader } from '@app/common/hooks/use-route-header';
import { stxFeeValidator } from '@app/common/validation/forms/fee-validators';
import { nonceValidator } from '@app/common/validation/nonce-validators';
import { NonceSetter } from '@app/components/nonce-setter';
-import { PopupHeader } from '@app/features/current-account/popup-header';
+import { HighFeeDialog } from '@app/features/dialogs/high-fee-dialog/high-fee-dialog';
import { RequestingTabClosedWarningMessage } from '@app/features/errors/requesting-tab-closed-error-msg';
-import { HighFeeDrawer } from '@app/features/high-fee-drawer/high-fee-drawer';
import { ContractCallDetails } from '@app/features/stacks-transaction-request/contract-call-details/contract-call-details';
import { ContractDeployDetails } from '@app/features/stacks-transaction-request/contract-deploy-details/contract-deploy-details';
import { FeeForm } from '@app/features/stacks-transaction-request/fee-form';
@@ -42,6 +40,8 @@ import {
import { Link } from '@app/ui/components/link/link';
function TransactionRequestBase() {
+ const [isShowingHighFeeConfirmation, setIsShowingHighFeeConfirmation] = useState(false);
+
const transactionRequest = useTransactionRequestState();
const unsignedTx = useUnsignedStacksTransactionBaseState();
const { data: stxFees } = useCalculateStacksTxFees(unsignedTx.transaction);
@@ -52,8 +52,6 @@ function TransactionRequestBase() {
const navigate = useNavigate();
const { stacksBroadcastTransaction } = useStacksBroadcastTransaction('STX');
- useRouteHeader();
-
useOnMount(() => void analytics.track('view_transaction_signing'));
async function onSubmit(
@@ -93,7 +91,13 @@ function TransactionRequestBase() {
};
return (
-
+
-
-
+ setIsShowingHighFeeConfirmation(true)}
+ />
+
+
>
)}
diff --git a/src/app/pages/unlock.tsx b/src/app/pages/unlock.tsx
index 71135790040..8906f5f5b98 100644
--- a/src/app/pages/unlock.tsx
+++ b/src/app/pages/unlock.tsx
@@ -1,56 +1,17 @@
-import { useEffect } from 'react';
import { Outlet, useNavigate } from 'react-router-dom';
-import { Box } from 'leather-styles/jsx';
-
-import { WALLET_ENVIRONMENT } from '@shared/environment';
import { RouteUrls } from '@shared/route-urls';
-import { closeWindow } from '@shared/utils';
-import { useRouteHeader } from '@app/common/hooks/use-route-header';
-import { isFullPageMode, isPopupMode } from '@app/common/utils';
-import { openIndexPageInNewTab } from '@app/common/utils/open-in-new-tab';
-import { Header } from '@app/components/header';
import { RequestPassword } from '@app/components/request-password';
-import { useNewBrandApprover } from '@app/store/settings/settings.selectors';
export function Unlock() {
const navigate = useNavigate();
- useRouteHeader();
-
- const { hasApprovedNewBrand } = useNewBrandApprover();
-
- useEffect(() => {
- if (!hasApprovedNewBrand && isPopupMode() && WALLET_ENVIRONMENT !== 'testing') {
- openIndexPageInNewTab('/unlock/we-have-a-new-name');
- closeWindow();
- }
- if (!hasApprovedNewBrand && isFullPageMode()) {
- navigate('./we-have-a-new-name');
- }
- }, [hasApprovedNewBrand, navigate]);
-
const handleSuccess = () => navigate(RouteUrls.Home);
return (
<>
- {/* Hide the logo when user hasn't consented yet */}
- {!hasApprovedNewBrand && (
-
- )}
-
-
- Your
-
- session is locked
- >
- }
- caption="Enter the password you set on this device"
- onSuccess={handleSuccess}
- />
+
>
);
diff --git a/src/app/pages/update-profile-request/components/update-profile-request.layout.tsx b/src/app/pages/update-profile-request/components/update-profile-request.layout.tsx
index 13415723fa8..a8955e1f6d8 100644
--- a/src/app/pages/update-profile-request/components/update-profile-request.layout.tsx
+++ b/src/app/pages/update-profile-request/components/update-profile-request.layout.tsx
@@ -7,7 +7,12 @@ interface ProfileUpdateRequestLayoutProps {
}
export function ProfileUpdateRequestLayout({ children }: ProfileUpdateRequestLayoutProps) {
return (
-
+
{children}
diff --git a/src/app/pages/update-profile-request/update-profile-request.tsx b/src/app/pages/update-profile-request/update-profile-request.tsx
index 44602f339bf..3369a3f4440 100644
--- a/src/app/pages/update-profile-request/update-profile-request.tsx
+++ b/src/app/pages/update-profile-request/update-profile-request.tsx
@@ -2,8 +2,6 @@ import { memo } from 'react';
import { closeWindow, isUndefined } from '@shared/utils';
-import { useRouteHeader } from '@app/common/hooks/use-route-header';
-import { PopupHeader } from '@app/features/current-account/popup-header';
import { useOnOriginTabClose } from '@app/routes/hooks/use-on-tab-closed';
import {
useIsProfileUpdateRequestValid,
@@ -18,8 +16,6 @@ function ProfileUpdateRequestBase() {
const validProfileUpdateRequest = useIsProfileUpdateRequestValid();
const { requestToken } = useProfileUpdateRequestSearchParams();
- useRouteHeader();
-
useOnOriginTabClose(() => closeWindow());
if (isUndefined(validProfileUpdateRequest) || !validProfileUpdateRequest || !requestToken)
return (
diff --git a/src/app/pages/view-secret-key/components/view-secret-key.content.tsx b/src/app/pages/view-secret-key/components/view-secret-key.content.tsx
deleted file mode 100644
index ea124ff4a10..00000000000
--- a/src/app/pages/view-secret-key/components/view-secret-key.content.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { styled } from 'leather-styles/jsx';
-
-export function ViewSecretKeyContent(): React.JSX.Element {
- return (
- <>
-
- Your Secret Key
-
-
- These 24 words are your Secret Key. They create your account, and you sign in on different
- devices with them. Make sure to save these somewhere safe.
-
-
-
-
- If you lose these words, you lose your account.
-
- >
- );
-}
diff --git a/src/app/pages/view-secret-key/view-secret-key.tsx b/src/app/pages/view-secret-key/view-secret-key.tsx
index 5921326d62b..f2e0684848d 100644
--- a/src/app/pages/view-secret-key/view-secret-key.tsx
+++ b/src/app/pages/view-secret-key/view-secret-key.tsx
@@ -1,26 +1,17 @@
import { useEffect, useState } from 'react';
-import { Outlet, useNavigate } from 'react-router-dom';
-
-import { RouteUrls } from '@shared/route-urls';
+import { Outlet } from 'react-router-dom';
import { useAnalytics } from '@app/common/hooks/analytics/use-analytics';
-import { useRouteHeader } from '@app/common/hooks/use-route-header';
-import { Header } from '@app/components/header';
import { RequestPassword } from '@app/components/request-password';
-import { TwoColumnLayout } from '@app/components/secret-key/two-column.layout';
-import { SecretKeyDisplayer } from '@app/features/secret-key-displayer/secret-key-displayer';
+import { SecretKey } from '@app/features/secret-key-displayer/secret-key-displayer';
import { useDefaultWalletSecretKey } from '@app/store/in-memory-key/in-memory-key.selectors';
-
-import { ViewSecretKeyContent } from './components/view-secret-key.content';
+import { TwoColumnLayout } from '@app/ui/pages/two-column.layout';
export function ViewSecretKey() {
const analytics = useAnalytics();
- const navigate = useNavigate();
const defaultWalletSecretKey = useDefaultWalletSecretKey();
const [showSecretKey, setShowSecretKey] = useState(false);
- useRouteHeader( navigate(RouteUrls.Home)} />);
-
useEffect(() => {
void analytics.page('view', '/save-secret-key');
}, [analytics]);
@@ -28,25 +19,28 @@ export function ViewSecretKey() {
if (showSecretKey) {
return (
}
- rightColumn={}
- />
- );
- }
-
- return (
- <>
-
- View
+ Your Secret Key
+ >
+ }
+ content={
+ <>
+ These 24 words are your Secret Key. They create your account, and you sign in on
+ different devices with them. Make sure to save these somewhere safe.
- Secret Key
+ If you lose these words, you lose your account.
>
}
- caption="Enter the password you set on this device"
- onSuccess={() => setShowSecretKey(true)}
- />
+ >
+
+
+ );
+ }
+
+ return (
+ <>
+ setShowSecretKey(true)} />
>
);
diff --git a/src/app/routes/app-routes.tsx b/src/app/routes/app-routes.tsx
index 2c397351a92..efb89d692c2 100644
--- a/src/app/routes/app-routes.tsx
+++ b/src/app/routes/app-routes.tsx
@@ -14,11 +14,11 @@ import { RouteUrls } from '@shared/route-urls';
import { LoadingSpinner } from '@app/components/loading-spinner';
import { AddNetwork } from '@app/features/add-network/add-network';
import { Container } from '@app/features/container/container';
-import { EditNonceDrawer } from '@app/features/edit-nonce-drawer/edit-nonce-drawer';
-import { IncreaseBtcFeeDrawer } from '@app/features/increase-fee-drawer/increase-btc-fee-drawer';
-import { IncreaseFeeSentDrawer } from '@app/features/increase-fee-drawer/increase-fee-sent-drawer';
-import { IncreaseStxFeeDrawer } from '@app/features/increase-fee-drawer/increase-stx-fee-drawer';
-import { leatherIntroDialogRoutes } from '@app/features/leather-intro-dialog/leather-intro-dialog';
+import { EditNonceDialog } from '@app/features/dialogs/edit-nonce-dialog/edit-nonce-dialog';
+import { IncreaseBtcFeeDialog } from '@app/features/dialogs/increase-fee-dialog/increase-btc-fee-dialog';
+import { IncreaseFeeSentDialog } from '@app/features/dialogs/increase-fee-dialog/increase-fee-sent-dialog';
+import { IncreaseStxFeeDialog } from '@app/features/dialogs/increase-fee-dialog/increase-stx-fee-dialog';
+import { leatherIntroDialogRoutes } from '@app/features/dialogs/leather-intro-dialog/leather-intro-dialog';
import { ledgerBitcoinTxSigningRoutes } from '@app/features/ledger/flows/bitcoin-tx-signing/ledger-bitcoin-sign-tx-container';
import { ledgerJwtSigningRoutes } from '@app/features/ledger/flows/jwt-signing/ledger-sign-jwt.routes';
import { requestBitcoinKeysRoutes } from '@app/features/ledger/flows/request-bitcoin-keys/ledger-request-bitcoin-keys';
@@ -53,7 +53,6 @@ import { AccountGate } from '@app/routes/account-gate';
import { receiveRoutes } from '@app/routes/receive-routes';
import { legacyRequestRoutes } from '@app/routes/request-routes';
import { rpcRequestRoutes } from '@app/routes/rpc-routes';
-import { settingsRoutes } from '@app/routes/settings-routes';
import { OnboardingGate } from './onboarding-gate';
@@ -70,7 +69,6 @@ const sentryCreateBrowserRouter = Sentry.wrapCreateBrowserRouter(createHashRoute
export const homePageModalRoutes = (
<>
- {settingsRoutes}
{receiveRoutes}
{ledgerStacksTxSigningRoutes}
{ledgerBitcoinTxSigningRoutes}
@@ -96,13 +94,17 @@ function useAppRoutes() {
} />
- }>
+ }>
{ledgerStacksTxSigningRoutes}
- }>
+ }
+ />
+ }>
{ledgerBitcoinTxSigningRoutes}
- } />
+ } />
{ledgerStacksTxSigningRoutes}
@@ -187,8 +189,6 @@ function useAppRoutes() {
}
>
- {settingsRoutes}
-
} />
} />
@@ -200,7 +200,6 @@ function useAppRoutes() {
}
>
- {settingsRoutes}
} />
@@ -213,11 +212,8 @@ function useAppRoutes() {
}
- >
- {settingsRoutes}
-
+ />
}>
- {settingsRoutes}
{leatherIntroDialogRoutes}
@@ -241,7 +237,7 @@ function useAppRoutes() {
}
>
- } />
+ } />
- } />
+ } />
} />
} />
} />
- } />
+ } />
} />
);
diff --git a/src/app/routes/request-routes.tsx b/src/app/routes/request-routes.tsx
index 1b0e931009b..1cd1886af83 100644
--- a/src/app/routes/request-routes.tsx
+++ b/src/app/routes/request-routes.tsx
@@ -3,8 +3,8 @@ import { Route } from 'react-router-dom';
import { RouteUrls } from '@shared/route-urls';
-import { BroadcastErrorDrawer } from '@app/components/broadcast-error-drawer/broadcast-error-drawer';
-import { EditNonceDrawer } from '@app/features/edit-nonce-drawer/edit-nonce-drawer';
+import { BroadcastErrorDialog } from '@app/components/broadcast-error-dialog/broadcast-error-dialog';
+import { EditNonceDialog } from '@app/features/dialogs/edit-nonce-dialog/edit-nonce-dialog';
import { ledgerStacksMessageSigningRoutes } from '@app/features/ledger/flows/stacks-message-signing/ledger-stacks-sign-msg.routes';
import { ledgerStacksTxSigningRoutes } from '@app/features/ledger/flows/stacks-tx-signing/ledger-sign-stacks-tx-container';
import { PsbtRequest } from '@app/pages/psbt-request/psbt-request';
@@ -27,8 +27,8 @@ export const legacyRequestRoutes = (
}
>
{ledgerStacksTxSigningRoutes}
- } />
- } />
+ } />
+ } />
- } />
- } />
- } />
-
-);
diff --git a/src/app/store/settings/settings.selectors.ts b/src/app/store/settings/settings.selectors.ts
index 34862abfb69..4103be10dda 100644
--- a/src/app/store/settings/settings.selectors.ts
+++ b/src/app/store/settings/settings.selectors.ts
@@ -2,9 +2,7 @@ import { useSelector } from 'react-redux';
import { createSelector } from '@reduxjs/toolkit';
-import { RootState, useAppDispatch } from '@app/store';
-
-import { settingsSlice } from './settings.slice';
+import { RootState } from '@app/store';
const selectSettings = (state: RootState) => state.settings;
@@ -40,19 +38,3 @@ const selectDismissedMessageIds = createSelector(
export function useDismissedMessageIds() {
return useSelector(selectDismissedMessageIds);
}
-
-const selectHasApprovedNewBrand = createSelector(
- selectSettings,
- state => !!state.hasApprovedNewBrand
-);
-
-export function useNewBrandApprover() {
- const hasApprovedNewBrand = useSelector(selectHasApprovedNewBrand);
- const dispatch = useAppDispatch();
- return {
- hasApprovedNewBrand,
- userApprovedNewBrand() {
- dispatch(settingsSlice.actions.setHasApprovedNewBrand());
- },
- };
-}
diff --git a/src/app/store/settings/settings.slice.ts b/src/app/store/settings/settings.slice.ts
index be0960dca6a..36105a6b890 100644
--- a/src/app/store/settings/settings.slice.ts
+++ b/src/app/store/settings/settings.slice.ts
@@ -7,15 +7,12 @@ interface InitialState {
userSelectedTheme: UserSelectedTheme;
hasAllowedAnalytics: HasAcceptedAnalytics;
dismissedMessages: string[];
- hasApprovedNewBrand: boolean;
}
const initialState: InitialState = {
userSelectedTheme: 'system',
hasAllowedAnalytics: null,
dismissedMessages: [],
- // Defaults to true, as this is undefined for existing users
- hasApprovedNewBrand: true,
};
export const settingsSlice = createSlice({
@@ -35,11 +32,5 @@ export const settingsSlice = createSlice({
resetMessages(state) {
state.dismissedMessages = [];
},
- setHasApprovedNewBrand(state) {
- state.hasApprovedNewBrand = true;
- },
- resetHasApprovedNewBrand(state) {
- state.hasApprovedNewBrand = false;
- },
},
});
diff --git a/src/app/store/ui/ui.hooks.ts b/src/app/store/ui/ui.hooks.ts
index e8344c96800..bde03bc5c23 100644
--- a/src/app/store/ui/ui.hooks.ts
+++ b/src/app/store/ui/ui.hooks.ts
@@ -1,30 +1,6 @@
import { useAtom } from 'jotai';
-import {
- errorStackTraceState,
- loadingState,
- routeHeaderState,
- showHighFeeConfirmationState,
- showSettingsStore,
- showSwitchAccountsState,
- showTxSettingsCallback,
-} from './ui';
-
-export function useShowHighFeeConfirmationState() {
- return useAtom(showHighFeeConfirmationState);
-}
-
-export function useShowSwitchAccountsState() {
- return useAtom(showSwitchAccountsState);
-}
-
-export function useShowSettingsStore() {
- return useAtom(showSettingsStore);
-}
-
-export function useShowTxSettingsCallback() {
- return useAtom(showTxSettingsCallback);
-}
+import { errorStackTraceState, loadingState } from './ui';
export function useLoadingState(key: string) {
return useAtom(loadingState(key));
@@ -33,7 +9,3 @@ export function useLoadingState(key: string) {
export function useErrorStackTraceState() {
return useAtom(errorStackTraceState);
}
-
-export function useRouteHeaderState() {
- return useAtom(routeHeaderState);
-}
diff --git a/src/app/store/ui/ui.ts b/src/app/store/ui/ui.ts
index bbcd2eaa5f6..ea1ad46c9c9 100644
--- a/src/app/store/ui/ui.ts
+++ b/src/app/store/ui/ui.ts
@@ -9,15 +9,4 @@ export const loadingState = atomFamily(_param => {
return anAtom;
});
-// TODO: refactor into atom family
-export const showSwitchAccountsState = atom(false);
-
-export const showHighFeeConfirmationState = atom(false);
-
-export const showSettingsStore = atom(false);
-
-export const showTxSettingsCallback = atom<(() => Promise) | undefined>(undefined);
-
export const errorStackTraceState = atom(null);
-
-export const routeHeaderState = atom(null);
diff --git a/src/app/components/account/account-avatar-item.tsx b/src/app/ui/components/account/account-avatar/account-avatar-item.tsx
similarity index 76%
rename from src/app/components/account/account-avatar-item.tsx
rename to src/app/ui/components/account/account-avatar/account-avatar-item.tsx
index a6470aca3de..f0b4ecdfa59 100644
--- a/src/app/components/account/account-avatar-item.tsx
+++ b/src/app/ui/components/account/account-avatar/account-avatar-item.tsx
@@ -1,6 +1,6 @@
import { memo } from 'react';
-import { AccountAvatar } from '@app/components/account/account-avatar';
+import { AccountAvatar } from '@app/ui/components/account/account-avatar/account-avatar';
interface AccountAvatarItemProps {
publicKey: string;
diff --git a/src/app/components/account/account-avatar.tsx b/src/app/ui/components/account/account-avatar/account-avatar.tsx
similarity index 100%
rename from src/app/components/account/account-avatar.tsx
rename to src/app/ui/components/account/account-avatar/account-avatar.tsx
diff --git a/src/app/ui/components/account/account.card.stories.tsx b/src/app/ui/components/account/account.card.stories.tsx
new file mode 100644
index 00000000000..8a47ba3c4e8
--- /dev/null
+++ b/src/app/ui/components/account/account.card.stories.tsx
@@ -0,0 +1,33 @@
+import type { Meta } from '@storybook/react';
+import { Flex } from 'leather-styles/jsx';
+
+import { IconButton } from '@app/ui/components/icon-button/icon-button';
+import { ArrowDownIcon, ArrowUpIcon, PlusIcon, SwapIcon } from '@app/ui/icons';
+
+import { AccountCard as Component } from './account.card';
+
+const meta: Meta = {
+ component: Component,
+ tags: ['autodocs'],
+ title: 'Layout/AccountCard',
+};
+
+export default meta;
+
+export function AccountCard() {
+ return (
+ >}
+ toggleSwitchAccount={() => null}
+ >
+
+ } label="Send" />
+ } label="Receive" />
+ } label="Buy" />
+ } label="Swap" />
+
+
+ );
+}
diff --git a/src/app/ui/components/account/account.card.tsx b/src/app/ui/components/account/account.card.tsx
new file mode 100644
index 00000000000..8508f21cfb9
--- /dev/null
+++ b/src/app/ui/components/account/account.card.tsx
@@ -0,0 +1,57 @@
+import { ReactNode } from 'react';
+
+import { SettingsSelectors } from '@tests/selectors/settings.selectors';
+import { Box, Flex, styled } from 'leather-styles/jsx';
+
+import { Link } from '@app/ui/components/link/link';
+import { ChevronDownIcon } from '@app/ui/icons';
+
+interface AccountCardProps {
+ name: string;
+ balance: string;
+ children: ReactNode;
+ switchAccount: ReactNode;
+ toggleSwitchAccount(): void;
+}
+
+export function AccountCard({
+ name,
+ balance,
+ switchAccount,
+ toggleSwitchAccount,
+ children,
+}: AccountCardProps) {
+ return (
+
+
+
+
+ {name}
+
+
+
+
+
+
+
+
+ {balance}
+
+ {switchAccount}
+ {children}
+
+
+ );
+}
diff --git a/src/app/ui/components/bullet-separator/bullet-separator.tsx b/src/app/ui/components/bullet-separator/bullet-separator.tsx
index 9d6c7f1213e..a403ed1e79e 100644
--- a/src/app/ui/components/bullet-separator/bullet-separator.tsx
+++ b/src/app/ui/components/bullet-separator/bullet-separator.tsx
@@ -7,7 +7,7 @@ export function BulletOperator(props: CircleProps) {
, keyof ButtonVariantProps> &
+export type ButtonProps = Omit<
+ React.ComponentProps,
+ keyof ButtonVariantProps
+> &
ButtonVariantProps;
export function Button(props: ButtonProps) {
- const { children, fullWidth, invert, size, trigger, type = 'button', variant, ...rest } = props;
+ const { children, fullWidth, size, trigger, invert, type = 'button', variant, ...rest } = props;
return (
diff --git a/src/app/ui/components/callout/callout.tsx b/src/app/ui/components/callout/callout.tsx
index 7ba98707b97..80d5311c75a 100644
--- a/src/app/ui/components/callout/callout.tsx
+++ b/src/app/ui/components/callout/callout.tsx
@@ -60,7 +60,7 @@ export function Callout(props: CalloutProps & CalloutVariants) {
{title}
)}
- {children && {children}}
+ {children && {children}}
diff --git a/src/app/ui/components/containers/container.layout.tsx b/src/app/ui/components/containers/container.layout.tsx
new file mode 100644
index 00000000000..147233e5b8f
--- /dev/null
+++ b/src/app/ui/components/containers/container.layout.tsx
@@ -0,0 +1,25 @@
+import { radixBaseCSS } from '@radix-ui/themes/styles.css';
+import { css } from 'leather-styles/css';
+import { Flex } from 'leather-styles/jsx';
+
+interface ContainerLayoutProps {
+ children: React.JSX.Element | React.JSX.Element[];
+ header: React.JSX.Element | null;
+}
+export function ContainerLayout({ children, header }: ContainerLayoutProps) {
+ return (
+
+ {header}
+
+ {children}
+
+
+ );
+}
diff --git a/src/app/ui/components/containers/dialog/dialog.stories.tsx b/src/app/ui/components/containers/dialog/dialog.stories.tsx
new file mode 100644
index 00000000000..dbbb86e3770
--- /dev/null
+++ b/src/app/ui/components/containers/dialog/dialog.stories.tsx
@@ -0,0 +1,26 @@
+import { useState } from 'react';
+
+import type { Meta } from '@storybook/react';
+
+import { Button } from '../../button/button';
+import { Dialog as Component } from './dialog';
+
+const meta: Meta = {
+ component: Component,
+ tags: ['autodocs'],
+ title: 'Containers/Dialog',
+};
+
+export default meta;
+
+export function Dialog() {
+ const [isShowing, setIsShowing] = useState(false);
+ return (
+ <>
+ setIsShowing(!isShowing)}>Open
+ setIsShowing(false)}>
+