diff --git a/src/app/common/page/page.context.tsx b/src/app/common/page/page.context.tsx new file mode 100644 index 00000000000..a884d3df294 --- /dev/null +++ b/src/app/common/page/page.context.tsx @@ -0,0 +1,62 @@ +import { ReactNode, createContext, useContext, useEffect, useReducer } from 'react'; + +import { RouteUrls } from '@shared/route-urls'; + +interface HeaderPayloadState { + title?: string; + isSummaryPage?: boolean; + isSessionLocked?: boolean; + isSettingsVisibleOnSm?: boolean; + onBackLocation?: RouteUrls; + onClose?(): void; +} + +interface UpdateAction { + type: 'update'; + payload: HeaderPayloadState; +} + +interface ResetAction { + type: 'reset'; +} +type Action = UpdateAction | ResetAction; + +const initialPageState = { isSessionLocked: false, isSettingsVisibleOnSm: true }; +const pageReducer = (state: HeaderPayloadState, action: Action): HeaderPayloadState => { + switch (action.type) { + case 'update': + return { ...state, ...action.payload }; + case 'reset': + default: + return initialPageState; + } +}; + +const PageContext = createContext< + { state: HeaderPayloadState; dispatch: React.Dispatch } | undefined +>(undefined); + +export function PageProvider({ children }: { children: ReactNode }) { + const [state, dispatch] = useReducer(pageReducer, initialPageState); + const value = { state, dispatch }; + return {children}; +} + +export const usePageContext = () => { + const context = useContext(PageContext); + if (context === undefined) { + throw new Error('usePageContext must be used within a PageProvider'); + } + return context; +}; + +export function useUpdatePageHeaderContext(payload: HeaderPayloadState) { + const { dispatch } = usePageContext(); + + useEffect(() => { + dispatch({ type: 'update', payload }); + return () => { + dispatch({ type: 'reset' }); + }; + }); +} diff --git a/src/app/components/broadcast-error-dialog/broadcast-error-dialog.tsx b/src/app/components/broadcast-error-dialog/broadcast-error-dialog.tsx index 946a40edfef..e3129736af7 100644 --- a/src/app/components/broadcast-error-dialog/broadcast-error-dialog.tsx +++ b/src/app/components/broadcast-error-dialog/broadcast-error-dialog.tsx @@ -6,8 +6,8 @@ import get from 'lodash.get'; import { Button, Dialog } from '@leather.io/ui'; -import { Footer } from '@app/ui/components/containers/footers/footer'; -import { DialogHeader } from '@app/ui/components/containers/headers/dialog-header'; +import { Footer } from '@app/ui/layout/containers/footers/footer'; +import { DialogHeader } from '@app/ui/layout/containers/headers/dialog-header'; export function BroadcastErrorDialog() { const navigate = useNavigate(); diff --git a/src/app/components/request-password.tsx b/src/app/components/request-password.tsx index 7993607fc71..d871b7c6dbf 100644 --- a/src/app/components/request-password.tsx +++ b/src/app/components/request-password.tsx @@ -10,8 +10,8 @@ import { analytics } from '@shared/utils/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/hooks/use-waiting-message'; -import { Footer } from '@app/ui/components/containers/footers/footer'; import { Card } from '@app/ui/layout/card/card'; +import { Footer } from '@app/ui/layout/containers/footers/footer'; import { Page } from '@app/ui/layout/page/page.layout'; import { ErrorLabel } from './error-label'; diff --git a/src/app/features/add-network/add-network.tsx b/src/app/features/add-network/add-network.tsx index f3a23a4e31c..7609925e3de 100644 --- a/src/app/features/add-network/add-network.tsx +++ b/src/app/features/add-network/add-network.tsx @@ -4,6 +4,7 @@ import { Stack, styled } from 'leather-styles/jsx'; import { Button } from '@leather.io/ui'; +import { useUpdatePageHeaderContext } from '@app/common/page/page.context'; import { ErrorLabel } from '@app/components/error-label'; import { Card } from '@app/ui/layout/card/card'; import { Page } from '@app/ui/layout/page/page.layout'; @@ -13,6 +14,7 @@ import { useAddNetwork } from './use-add-network'; export function AddNetwork() { const { error, initialFormValues, loading, onSubmit } = useAddNetwork(); + useUpdatePageHeaderContext({ title: 'Add Network' }); return ( diff --git a/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-list.tsx b/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-list.tsx index 3a8933f4dfd..81a3ebcf260 100644 --- a/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-list.tsx +++ b/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-list.tsx @@ -25,7 +25,7 @@ export function Sip10TokenAssetList({ {tokens.map(token => ( + {header} + + {content} + + + ); +} diff --git a/src/app/features/container/components/home/home-header.tsx b/src/app/features/container/components/home/home-header.tsx new file mode 100644 index 00000000000..4db1762fd07 --- /dev/null +++ b/src/app/features/container/components/home/home-header.tsx @@ -0,0 +1,53 @@ +import { ReactNode, useState } from 'react'; + +import { SettingsSelectors } from '@tests/selectors/settings.selectors'; +import { Flex, Grid, GridItem, HStack, styled } from 'leather-styles/jsx'; + +import { HamburgerIcon } from '@leather.io/ui'; + +import { SwitchAccountDialog } from '@app/features/dialogs/switch-account-dialog/switch-account-dialog'; + +import { Settings } from '../../../settings/settings'; + +interface HomeHeaderProps { + networkBadge?: ReactNode; + logo?: ReactNode; +} + +export function HomeHeader({ networkBadge, logo }: HomeHeaderProps) { + const [isShowingSwitchAccount, setIsShowingSwitchAccount] = useState(false); + return ( + <> + {isShowingSwitchAccount && ( + setIsShowingSwitchAccount(false)} + /> + )} + + + + {logo} + + + + + {networkBadge} + } + toggleSwitchAccount={() => setIsShowingSwitchAccount(!isShowingSwitchAccount)} + /> + + + + + + ); +} diff --git a/src/app/features/container/components/home/home.layout.tsx b/src/app/features/container/components/home/home.layout.tsx new file mode 100644 index 00000000000..a4458c8c622 --- /dev/null +++ b/src/app/features/container/components/home/home.layout.tsx @@ -0,0 +1,36 @@ +import { Outlet } from 'react-router-dom'; + +import { ChainID } from '@stacks/transactions'; +import { Box } from 'leather-styles/jsx'; + +import { Logo, NetworkModeBadge } from '@leather.io/ui'; + +import { useCurrentNetworkState } from '@app/store/networks/networks.hooks'; + +import { ContainerLayout } from '../container.layout'; +import { HomeHeader } from './home-header'; + +export function HomeLayout() { + const { chain, name: chainName } = useCurrentNetworkState(); + + return ( + + } + logo={ + + + + } + /> + } + content={} + /> + ); +} diff --git a/src/app/features/container/components/onboarding/onboarding-header.tsx b/src/app/features/container/components/onboarding/onboarding-header.tsx new file mode 100644 index 00000000000..ffc5f7e60cd --- /dev/null +++ b/src/app/features/container/components/onboarding/onboarding-header.tsx @@ -0,0 +1,56 @@ +import { ReactNode } from 'react'; + +import { SharedComponentsSelectors } from '@tests/selectors/shared-component.selectors'; +import { Flex, Grid, GridItem, HStack, styled } from 'leather-styles/jsx'; + +import { ArrowLeftIcon } from '@leather.io/ui'; + +import { HeaderActionButton } from '../../../../ui/layout/containers/headers/components/header-action-button'; + +interface HeaderProps { + onClose?(): void; + onGoBack?(): void; + title?: ReactNode; + settingsMenu?: ReactNode; + networkBadge?: ReactNode; + logo?: ReactNode; +} + +export function OnboardingHeader({ onGoBack, networkBadge, logo }: HeaderProps) { + const logoItem = onGoBack || logo; + + return ( + + + + {logoItem && ( + + {onGoBack && ( + } + onAction={onGoBack} + dataTestId={SharedComponentsSelectors.HeaderBackBtn} + /> + )} + {logo} + + )} + + + + + {networkBadge} + + + + + ); +} diff --git a/src/app/features/container/components/onboarding/onboarding.layout.tsx b/src/app/features/container/components/onboarding/onboarding.layout.tsx new file mode 100644 index 00000000000..ad2f5171b75 --- /dev/null +++ b/src/app/features/container/components/onboarding/onboarding.layout.tsx @@ -0,0 +1,54 @@ +import { Outlet, useLocation, useNavigate } from 'react-router-dom'; + +import { ChainID } from '@stacks/transactions'; +import { OnboardingSelectors } from '@tests/selectors/onboarding.selectors'; +import { Box } from 'leather-styles/jsx'; + +import { Logo, NetworkModeBadge } from '@leather.io/ui'; + +import { RouteUrls } from '@shared/route-urls'; + +import { useCurrentNetworkState } from '@app/store/networks/networks.hooks'; + +import { ContainerLayout } from '../container.layout'; +import { OnboardingHeader } from './onboarding-header'; + +export function OnboardingLayout() { + const navigate = useNavigate(); + const { pathname: locationPathname } = useLocation(); + const pathname = locationPathname as RouteUrls; + + const { chain, name: chainName } = useCurrentNetworkState(); + + const displayHeader = !pathname.match(RouteUrls.Onboarding); + + return ( + navigate(-1)} + networkBadge={ + + } + logo={ + // TODO: RouteUrls.ViewSecretKey needs show logo when viewing key but not when entering password + pathname !== RouteUrls.ViewSecretKey && ( + + navigate(RouteUrls.Home)} + /> + + ) + } + /> + ) : null + } + content={} + /> + ); +} diff --git a/src/app/features/container/components/page/page.header.tsx b/src/app/features/container/components/page/page.header.tsx new file mode 100644 index 00000000000..e7aa1266fca --- /dev/null +++ b/src/app/features/container/components/page/page.header.tsx @@ -0,0 +1,120 @@ +import { useState } from 'react'; +import { 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 { SharedComponentsSelectors } from '@tests/selectors/shared-component.selectors'; +import { Box, Flex, Grid, GridItem, HStack, styled } from 'leather-styles/jsx'; + +import { ArrowLeftIcon, CloseIcon, HamburgerIcon, Logo, NetworkModeBadge } from '@leather.io/ui'; + +import { RouteUrls } from '@shared/route-urls'; + +import { SwitchAccountDialog } from '@app/features/dialogs/switch-account-dialog/switch-account-dialog'; +import { useCurrentNetworkState } from '@app/store/networks/networks.hooks'; + +import { usePageContext } from '../../../../common/page/page.context'; +import { HeaderActionButton } from '../../../../ui/layout/containers/headers/components/header-action-button'; +import { Settings } from '../../../settings/settings'; + +function LogoBox({ isSessionLocked }: { isSessionLocked: boolean | undefined }) { + const navigate = useNavigate(); + return ( + + navigate(RouteUrls.Home)} + /> + + ); +} + +export function PageHeader() { + const [isShowingSwitchAccount, setIsShowingSwitchAccount] = useState(false); + const navigate = useNavigate(); + const { chain, name: chainName } = useCurrentNetworkState(); + + const { + state: { title, isSummaryPage, isSessionLocked, isSettingsVisibleOnSm, onBackLocation }, + } = usePageContext(); + + // pages with nested dialogs need to specify onBackLocation + // to go back and to prevent navigate(-1) re-opening the dialog + const onGoBack = onBackLocation ? () => navigate(onBackLocation) : () => navigate(-1); + const canGoBack = !isSummaryPage && !isSessionLocked; + + return ( + <> + {isShowingSwitchAccount && ( + setIsShowingSwitchAccount(false)} + /> + )} + + + + + {canGoBack && ( + } + onAction={onGoBack} + dataTestId={SharedComponentsSelectors.HeaderBackBtn} + /> + )} + + + + + {title} + + + + + + {!isSummaryPage && ( + + + } + toggleSwitchAccount={() => setIsShowingSwitchAccount(!isShowingSwitchAccount)} + /> + + )} + {isSummaryPage && ( + } + dataTestId={SharedComponentsSelectors.HeaderCloseBtn} + onAction={() => navigate(RouteUrls.Home)} + /> + )} + + + + + + ); +} diff --git a/src/app/features/container/components/page/page.layout.tsx b/src/app/features/container/components/page/page.layout.tsx new file mode 100644 index 00000000000..463c7aa08b7 --- /dev/null +++ b/src/app/features/container/components/page/page.layout.tsx @@ -0,0 +1,13 @@ +import { Outlet } from 'react-router-dom'; + +import { PageProvider } from '../../../../common/page/page.context'; +import { ContainerLayout } from '../container.layout'; +import { PageHeader } from './page.header'; + +export function PageLayout() { + return ( + + } content={} /> + + ); +} diff --git a/src/app/features/container/components/popup/popup-header.tsx b/src/app/features/container/components/popup/popup-header.tsx new file mode 100644 index 00000000000..fb9c855dd4c --- /dev/null +++ b/src/app/features/container/components/popup/popup-header.tsx @@ -0,0 +1,36 @@ +import { ReactNode } from 'react'; + +import { Flex, Grid, GridItem, HStack, styled } from 'leather-styles/jsx'; + +interface PopupHeaderProps { + account?: ReactNode; + totalBalance?: ReactNode; + networkBadge?: ReactNode; + logo?: ReactNode; +} + +export function PopupHeader({ account, totalBalance, networkBadge, logo }: PopupHeaderProps) { + const logoItem = logo || account; + return ( + + + + {logoItem && {account ? account : logo}} + + + + {networkBadge} + {totalBalance && totalBalance} + + + + + ); +} diff --git a/src/app/features/container/components/popup/popup.layout.tsx b/src/app/features/container/components/popup/popup.layout.tsx new file mode 100644 index 00000000000..053c99ecd85 --- /dev/null +++ b/src/app/features/container/components/popup/popup.layout.tsx @@ -0,0 +1,106 @@ +import { useState } from 'react'; +import { Outlet, useLocation } from 'react-router-dom'; + +import { ChainID } from '@stacks/transactions'; +import { Box } from 'leather-styles/jsx'; + +import { Flag, Logo, NetworkModeBadge } from '@leather.io/ui'; + +import { RouteUrls } from '@shared/route-urls'; + +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 { useCurrentNetworkState } from '@app/store/networks/networks.hooks'; + +import { TotalBalance } from '../../total-balance'; +import { ContainerLayout } from '../container.layout'; +import { PopupHeader } from './popup-header'; + +function showHeader(pathname: RouteUrls) { + switch (pathname) { + case RouteUrls.RpcGetAddresses: + case RouteUrls.ChooseAccount: + return false; + default: + return true; + } +} + +function showAccountInfo(pathname: RouteUrls) { + switch (pathname) { + case RouteUrls.TransactionRequest: + case RouteUrls.ProfileUpdateRequest: + case RouteUrls.RpcSendTransfer: + return true; + default: + return false; + } +} + +function showBalanceInfo(pathname: RouteUrls) { + switch (pathname) { + case RouteUrls.ProfileUpdateRequest: + case RouteUrls.RpcSendTransfer: + return true; + default: + return false; + } +} + +export function PopupLayout() { + const [isShowingSwitchAccount, setIsShowingSwitchAccount] = useState(false); + const { pathname: locationPathname } = useLocation(); + const pathname = locationPathname as RouteUrls; + + const { chain, name: chainName } = useCurrentNetworkState(); + + const isHeaderVisible = showHeader(pathname); + + return ( + <> + {isShowingSwitchAccount && ( + setIsShowingSwitchAccount(false)} + /> + )} + + } + logo={ + + + + } + account={ + showAccountInfo(pathname) && ( + } + onClick={() => setIsShowingSwitchAccount(!isShowingSwitchAccount)} + cursor="pointer" + > + + + ) + } + totalBalance={ + // We currently only show displayAddresssBalanceOf="all" in the total balance component + showBalanceInfo(pathname) && + } + /> + ) : undefined + } + content={} + /> + + ); +} diff --git a/src/app/features/container/container.tsx b/src/app/features/container/container.tsx index 6c0a578154e..9af93251be2 100644 --- a/src/app/features/container/container.tsx +++ b/src/app/features/container/container.tsx @@ -1,12 +1,5 @@ -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 { Flag, HamburgerIcon, Logo, NetworkModeBadge } from '@leather.io/ui'; +import { useEffect } from 'react'; +import { Outlet, useLocation } from 'react-router-dom'; import { RouteUrls } from '@shared/route-urls'; import { closeWindow } from '@shared/utils'; @@ -14,47 +7,18 @@ import { analytics } from '@shared/utils/analytics'; import { useInitalizeAnalytics } from '@app/common/app-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 { useCurrentNetworkState } from '@app/store/networks/networks.hooks'; -import { ContainerLayout } from '@app/ui/components/containers/container.layout'; -import { Header } from '@app/ui/components/containers/headers/header'; import { useRestoreFormState } from '../popup-send-form-restoration/use-restore-form-state'; -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 [isShowingSwitchAccount, setIsShowingSwitchAccount] = useState(false); - const navigate = useNavigate(); const { pathname: locationPathname } = useLocation(); const pathname = locationPathname as RouteUrls; const hasStateRehydrated = useHasStateRehydrated(); - const { chain, name: chainName } = useCurrentNetworkState(); useOnWalletLock(() => closeWindow()); useOnSignOut(() => closeWindow()); @@ -63,117 +27,13 @@ export function Container() { useEffect(() => void analytics.page('view', `${pathname}`), [pathname]); - const variant = getPageVariant(pathname); - - const displayHeader = !isLandingPage(pathname) && !isNoHeaderPopup(pathname); - const isSessionLocked = getIsSessionLocked(pathname); - - // TODO: Refactor? This is very hard to manage with dynamic routes. Temporarily - // added a fix to catch the swap route: '/swap/:base/:quote?' - function getOnGoBackLocation(pathname: RouteUrls) { - if (pathname.includes('/swap')) return navigate(RouteUrls.Home); - switch (pathname) { - 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 ( <> - {isShowingSwitchAccount && ( - 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/utils/get-popup-header.ts b/src/app/features/container/utils/get-popup-header.ts deleted file mode 100644 index 805ed8c0101..00000000000 --- a/src/app/features/container/utils/get-popup-header.ts +++ /dev/null @@ -1,83 +0,0 @@ -/** - * POPUP header logic notes here -> https://github.com/leather-io/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 deleted file mode 100644 index 42ed7fe7a01..00000000000 --- a/src/app/features/container/utils/get-title-from-url.ts +++ /dev/null @@ -1,36 +0,0 @@ -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 deleted file mode 100644 index 7e7f66f31fa..00000000000 --- a/src/app/features/container/utils/route-helpers.ts +++ /dev/null @@ -1,80 +0,0 @@ -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.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 3f264aad94c..91e8890eb23 100644 --- a/src/app/features/current-account/current-account-avatar.tsx +++ b/src/app/features/current-account/current-account-avatar.tsx @@ -1,5 +1,3 @@ -import { memo } from 'react'; - import { CircleProps } from 'leather-styles/jsx'; import { useCurrentAccountDisplayName } from '@app/common/hooks/account/use-account-names'; @@ -9,17 +7,12 @@ import { AccountAvatar } from '@app/ui/components/account/account-avatar/account interface CurrentAccountAvatar extends CircleProps { toggleSwitchAccount(): void; } -export const CurrentAccountAvatar = memo(({ toggleSwitchAccount }: CurrentAccountAvatar) => { +export function CurrentAccountAvatar() { const stacksAccount = useCurrentStacksAccount(); const name = useCurrentAccountDisplayName(); if (!stacksAccount) return null; return ( - toggleSwitchAccount()} - publicKey={stacksAccount.stxPublicKey} - /> + ); -}); +} diff --git a/src/app/features/dialogs/edit-nonce-dialog/edit-nonce-dialog.tsx b/src/app/features/dialogs/edit-nonce-dialog/edit-nonce-dialog.tsx index 35389fa1f3d..1e7c94e0dbd 100644 --- a/src/app/features/dialogs/edit-nonce-dialog/edit-nonce-dialog.tsx +++ b/src/app/features/dialogs/edit-nonce-dialog/edit-nonce-dialog.tsx @@ -10,7 +10,7 @@ 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 { DialogHeader } from '@app/ui/components/containers/headers/dialog-header'; +import { DialogHeader } from '@app/ui/layout/containers/headers/dialog-header'; import { EditNonceForm } from './components/edit-nonce-form'; 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 index 8a382bca309..229c3b96d6f 100644 --- 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 @@ -16,8 +16,8 @@ import { BitcoinCustomFeeInput } from '@app/components/bitcoin-custom-fee/bitcoi import { BitcoinTransactionItem } from '@app/components/bitcoin-transaction-item/bitcoin-transaction-item'; import { useBtcCryptoAssetBalanceNativeSegwit } from '@app/query/bitcoin/balance/btc-balance-native-segwit.hooks'; import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; -import { Footer } from '@app/ui/components/containers/footers/footer'; -import { DialogHeader } from '@app/ui/components/containers/headers/dialog-header'; +import { Footer } from '@app/ui/layout/containers/footers/footer'; +import { DialogHeader } from '@app/ui/layout/containers/headers/dialog-header'; import { IncreaseFeeActions } from './components/increase-fee-actions'; import { useBtcIncreaseFee } from './hooks/use-btc-increase-fee'; diff --git a/src/app/features/dialogs/increase-fee-dialog/increase-stx-fee-dialog.tsx b/src/app/features/dialogs/increase-fee-dialog/increase-stx-fee-dialog.tsx index 216d4bb1c89..d97992cc954 100644 --- a/src/app/features/dialogs/increase-fee-dialog/increase-stx-fee-dialog.tsx +++ b/src/app/features/dialogs/increase-fee-dialog/increase-stx-fee-dialog.tsx @@ -27,8 +27,8 @@ import { useStacksBroadcastTransaction } from '@app/features/stacks-transaction- import { useToast } from '@app/features/toasts/use-toast'; import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { useSubmittedTransactionsActions } from '@app/store/submitted-transactions/submitted-transactions.hooks'; -import { Footer } from '@app/ui/components/containers/footers/footer'; -import { DialogHeader } from '@app/ui/components/containers/headers/dialog-header'; +import { Footer } from '@app/ui/layout/containers/footers/footer'; +import { DialogHeader } from '@app/ui/layout/containers/headers/dialog-header'; import { IncreaseFeeActions } from './components/increase-fee-actions'; import { IncreaseFeeField } from './components/increase-fee-field'; 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 index 4f1724f28e7..a477c50eef5 100644 --- a/src/app/features/dialogs/switch-account-dialog/switch-account-dialog.tsx +++ b/src/app/features/dialogs/switch-account-dialog/switch-account-dialog.tsx @@ -10,18 +10,13 @@ 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 { Footer } from '@app/ui/components/containers/footers/footer'; -import { DialogHeader } from '@app/ui/components/containers/headers/dialog-header'; import { VirtuosoWrapper } from '@app/ui/components/virtuoso'; +import { Footer } from '@app/ui/layout/containers/footers/footer'; +import { DialogHeader } from '@app/ui/layout/containers/headers/dialog-header'; import { AccountListUnavailable } from './components/account-list-unavailable'; import { SwitchAccountListItem } from './components/switch-account-list-item'; -export interface SwitchAccountOutletContext { - isShowingSwitchAccount: boolean; - setIsShowingSwitchAccount(isShowing: boolean): void; -} - interface SwitchAccountDialogProps { isShowing: boolean; onClose(): void; diff --git a/src/app/features/ledger/flows/jwt-signing/ledger-sign-jwt-container.tsx b/src/app/features/ledger/flows/jwt-signing/ledger-sign-jwt-container.tsx index 975596b33a7..a23de81aaf5 100644 --- a/src/app/features/ledger/flows/jwt-signing/ledger-sign-jwt-container.tsx +++ b/src/app/features/ledger/flows/jwt-signing/ledger-sign-jwt-container.tsx @@ -26,7 +26,7 @@ import { useCurrentStacksAccount, useStacksAccounts, } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; -import { DialogHeader } from '@app/ui/components/containers/headers/dialog-header'; +import { DialogHeader } from '@app/ui/layout/containers/headers/dialog-header'; import { useLedgerNavigate } from '../../hooks/use-ledger-navigate'; import { checkLockedDeviceError, useLedgerResponseState } from '../../utils/generic-ledger-utils'; diff --git a/src/app/features/ledger/flows/stacks-message-signing/ledger-stacks-sign-msg-container.tsx b/src/app/features/ledger/flows/stacks-message-signing/ledger-stacks-sign-msg-container.tsx index c0436f993c7..37dc2443bc7 100644 --- a/src/app/features/ledger/flows/stacks-message-signing/ledger-stacks-sign-msg-container.tsx +++ b/src/app/features/ledger/flows/stacks-message-signing/ledger-stacks-sign-msg-container.tsx @@ -21,7 +21,7 @@ import { } from '@app/features/ledger/utils/stacks-ledger-utils'; import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { StacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.models'; -import { DialogHeader } from '@app/ui/components/containers/headers/dialog-header'; +import { DialogHeader } from '@app/ui/layout/containers/headers/dialog-header'; import { useLedgerAnalytics } from '../../hooks/use-ledger-analytics.hook'; import { useLedgerNavigate } from '../../hooks/use-ledger-navigate'; diff --git a/src/app/features/ledger/generic-flows/request-keys/request-keys-flow.tsx b/src/app/features/ledger/generic-flows/request-keys/request-keys-flow.tsx index 5be63817bbc..2b929a5d64e 100644 --- a/src/app/features/ledger/generic-flows/request-keys/request-keys-flow.tsx +++ b/src/app/features/ledger/generic-flows/request-keys/request-keys-flow.tsx @@ -3,7 +3,7 @@ import { Outlet } from 'react-router-dom'; import { Dialog } from '@leather.io/ui'; import { useScrollLock } from '@app/common/hooks/use-scroll-lock'; -import { DialogHeader } from '@app/ui/components/containers/headers/dialog-header'; +import { DialogHeader } from '@app/ui/layout/containers/headers/dialog-header'; import { useLedgerNavigate } from '../../hooks/use-ledger-navigate'; import { LedgerRequestKeysContext, LedgerRequestKeysProvider } from './ledger-request-keys.context'; diff --git a/src/app/features/ledger/generic-flows/tx-signing/tx-signing-flow.tsx b/src/app/features/ledger/generic-flows/tx-signing/tx-signing-flow.tsx index b08d07d70a7..fe6c0064f08 100644 --- a/src/app/features/ledger/generic-flows/tx-signing/tx-signing-flow.tsx +++ b/src/app/features/ledger/generic-flows/tx-signing/tx-signing-flow.tsx @@ -3,7 +3,7 @@ import { Outlet } from 'react-router-dom'; import { Dialog } from '@leather.io/ui'; import { useScrollLock } from '@app/common/hooks/use-scroll-lock'; -import { DialogHeader } from '@app/ui/components/containers/headers/dialog-header'; +import { DialogHeader } from '@app/ui/layout/containers/headers/dialog-header'; import { LedgerTxSigningContext, LedgerTxSigningProvider } from './ledger-sign-tx.context'; diff --git a/src/app/features/ledger/generic-steps/connect-device/connect-ledger-start.tsx b/src/app/features/ledger/generic-steps/connect-device/connect-ledger-start.tsx index 9db6583a8aa..1313e8527ab 100644 --- a/src/app/features/ledger/generic-steps/connect-device/connect-ledger-start.tsx +++ b/src/app/features/ledger/generic-steps/connect-device/connect-ledger-start.tsx @@ -7,7 +7,7 @@ import { closeWindow } from '@shared/utils'; import { doesBrowserSupportWebUsbApi, whenPageMode } from '@app/common/utils'; import { openIndexPageInNewTab } from '@app/common/utils/open-in-new-tab'; -import { DialogHeader } from '@app/ui/components/containers/headers/dialog-header'; +import { DialogHeader } from '@app/ui/layout/containers/headers/dialog-header'; import { immediatelyAttemptLedgerConnection } from '../../hooks/use-when-reattempt-ledger-connection'; import { ConnectLedger } from './connect-ledger'; diff --git a/src/app/features/ledger/generic-steps/unsupported-browser/unsupported-browser.layout.tsx b/src/app/features/ledger/generic-steps/unsupported-browser/unsupported-browser.layout.tsx index 4486d86e91e..efc8ab8f5ba 100644 --- a/src/app/features/ledger/generic-steps/unsupported-browser/unsupported-browser.layout.tsx +++ b/src/app/features/ledger/generic-steps/unsupported-browser/unsupported-browser.layout.tsx @@ -5,7 +5,7 @@ import { styled } from 'leather-styles/jsx'; import { Dialog, Link } from '@leather.io/ui'; import { UnsupportedBrowserImg } from '@app/features/ledger/illustrations/ledger-illu-unsupported-browser'; -import { DialogHeader } from '@app/ui/components/containers/headers/dialog-header'; +import { DialogHeader } from '@app/ui/layout/containers/headers/dialog-header'; import { LedgerTitle } from '../../components/ledger-title'; import { LedgerWrapper } from '../../components/ledger-wrapper'; diff --git a/src/app/features/psbt-signer/components/psbt-request-actions.tsx b/src/app/features/psbt-signer/components/psbt-request-actions.tsx index 6b8746b8b7f..eec5dc2bec8 100644 --- a/src/app/features/psbt-signer/components/psbt-request-actions.tsx +++ b/src/app/features/psbt-signer/components/psbt-request-actions.tsx @@ -1,6 +1,6 @@ import { Button } from '@leather.io/ui'; -import { Footer } from '@app/ui/components/containers/footers/footer'; +import { Footer } from '@app/ui/layout/containers/footers/footer'; interface PsbtRequestActionsProps { isLoading?: boolean; diff --git a/src/app/features/psbt-signer/psbt-signer.tsx b/src/app/features/psbt-signer/psbt-signer.tsx index 50073036c2d..d1c84eb4796 100644 --- a/src/app/features/psbt-signer/psbt-signer.tsx +++ b/src/app/features/psbt-signer/psbt-signer.tsx @@ -15,9 +15,9 @@ import { useBreakOnNonCompliantEntity } from '@app/query/common/compliance-check import { useOnOriginTabClose } from '@app/routes/hooks/use-on-tab-closed'; import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; import { useCurrentAccountTaprootIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/taproot-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 { Footer } from '@app/ui/layout/containers/footers/footer'; import * as Psbt from './components'; import { usePsbtDetails } from './hooks/use-psbt-details'; diff --git a/src/app/features/retrieve-taproot-to-native-segwit/components/retrieve-taproot-to-native-segwit.layout.tsx b/src/app/features/retrieve-taproot-to-native-segwit/components/retrieve-taproot-to-native-segwit.layout.tsx index d72d6a97620..b20e22b6178 100644 --- a/src/app/features/retrieve-taproot-to-native-segwit/components/retrieve-taproot-to-native-segwit.layout.tsx +++ b/src/app/features/retrieve-taproot-to-native-segwit/components/retrieve-taproot-to-native-segwit.layout.tsx @@ -2,9 +2,9 @@ import { Flex, styled } from 'leather-styles/jsx'; import { BtcAvatarIcon, Button, Callout, Dialog } from '@leather.io/ui'; -import { Footer } from '@app/ui/components/containers/footers/footer'; -import { DialogHeader } from '@app/ui/components/containers/headers/dialog-header'; import { Card } from '@app/ui/layout/card/card'; +import { Footer } from '@app/ui/layout/containers/footers/footer'; +import { DialogHeader } from '@app/ui/layout/containers/headers/dialog-header'; interface RetrieveTaprootToNativeSegwitLayoutProps { isBroadcasting: boolean; diff --git a/src/app/features/settings/network/network.tsx b/src/app/features/settings/network/network.tsx index a79ef308ab0..3b270c55fb4 100644 --- a/src/app/features/settings/network/network.tsx +++ b/src/app/features/settings/network/network.tsx @@ -11,8 +11,8 @@ import { analytics } from '@shared/utils/analytics'; import { NetworkListItem } from '@app/features/settings/network/network-list-item'; import { useCurrentNetworkState, useNetworksActions } from '@app/store/networks/networks.hooks'; import { useNetworks } from '@app/store/networks/networks.selectors'; -import { Footer } from '@app/ui/components/containers/footers/footer'; -import { DialogHeader } from '@app/ui/components/containers/headers/dialog-header'; +import { Footer } from '@app/ui/layout/containers/footers/footer'; +import { DialogHeader } from '@app/ui/layout/containers/headers/dialog-header'; const defaultNetworkIds = Object.values(WalletDefaultNetworkConfigurationIds) as string[]; diff --git a/src/app/features/settings/sign-out/sign-out.tsx b/src/app/features/settings/sign-out/sign-out.tsx index 04e83ee6e6b..8c0f8f159de 100644 --- a/src/app/features/settings/sign-out/sign-out.tsx +++ b/src/app/features/settings/sign-out/sign-out.tsx @@ -5,8 +5,8 @@ import { Flex, HStack, styled } from 'leather-styles/jsx'; import { Button, Callout, Dialog } from '@leather.io/ui'; import { useWalletType } from '@app/common/use-wallet-type'; -import { Footer } from '@app/ui/components/containers/footers/footer'; -import { DialogHeader } from '@app/ui/components/containers/headers/dialog-header'; +import { Footer } from '@app/ui/layout/containers/footers/footer'; +import { DialogHeader } from '@app/ui/layout/containers/headers/dialog-header'; interface SignOutDialogProps { isShowing: boolean; diff --git a/src/app/features/settings/theme/theme-dialog.tsx b/src/app/features/settings/theme/theme-dialog.tsx index 96cd5117739..d98a0943876 100644 --- a/src/app/features/settings/theme/theme-dialog.tsx +++ b/src/app/features/settings/theme/theme-dialog.tsx @@ -5,7 +5,7 @@ import { Dialog } from '@leather.io/ui'; import { analytics } from '@shared/utils/analytics'; import { UserSelectedTheme, themeLabelMap, useThemeSwitcher } from '@app/common/theme-provider'; -import { DialogHeader } from '@app/ui/components/containers/headers/dialog-header'; +import { DialogHeader } from '@app/ui/layout/containers/headers/dialog-header'; import { ThemeListItem } from './theme-list-item'; diff --git a/src/app/features/stacks-high-fee-warning/stacks-high-fee-dialog.tsx b/src/app/features/stacks-high-fee-warning/stacks-high-fee-dialog.tsx index 9ebcd5850e7..524dbc92ece 100644 --- a/src/app/features/stacks-high-fee-warning/stacks-high-fee-dialog.tsx +++ b/src/app/features/stacks-high-fee-warning/stacks-high-fee-dialog.tsx @@ -7,8 +7,8 @@ import { Button, Caption, Dialog, ErrorIcon, Link, Title } from '@leather.io/ui' import { StacksSendFormValues } from '@shared/models/form.model'; import { openInNewTab } from '@app/common/utils/open-in-new-tab'; -import { Footer } from '@app/ui/components/containers/footers/footer'; -import { DialogHeader } from '@app/ui/components/containers/headers/dialog-header'; +import { Footer } from '@app/ui/layout/containers/footers/footer'; +import { DialogHeader } from '@app/ui/layout/containers/headers/dialog-header'; import { useStacksHighFeeWarningContext } from './stacks-high-fee-warning-container'; 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 0c645eb5c2c..86dc6449a8d 100644 --- a/src/app/pages/bitcoin-contract-list/bitcoin-contract-list.tsx +++ b/src/app/pages/bitcoin-contract-list/bitcoin-contract-list.tsx @@ -9,6 +9,7 @@ import { useBitcoinContracts, } from '@app/common/hooks/use-bitcoin-contracts'; import { useOnMount } from '@app/common/hooks/use-on-mount'; +import { useUpdatePageHeaderContext } from '@app/common/page/page.context'; import { FullPageLoadingSpinner } from '@app/components/loading-spinner'; import { BitcoinContractListItemLayout } from './components/bitcoin-contract-list-item-layout'; @@ -19,6 +20,8 @@ export function BitcoinContractList() { const [isLoading, setLoading] = useState(true); const [isError, setError] = useState(false); + useUpdatePageHeaderContext({ title: 'Bitcoin Contracts' }); + useOnMount(() => { const fetchAndFormatBitcoinContracts = async () => { const fetchedBitcoinContracts = await getAllActiveBitcoinContracts(); 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 19d7ddcdc02..5e701d68576 100644 --- a/src/app/pages/bitcoin-contract-request/bitcoin-contract-request.tsx +++ b/src/app/pages/bitcoin-contract-request/bitcoin-contract-request.tsx @@ -14,9 +14,9 @@ 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 { Footer } from '@app/ui/layout/containers/footers/footer'; import { BitcoinContractOfferDetailsSimple } from './components/bitcoin-contract-offer/bitcoin-contract-offer-details'; import { BitcoinContractRequestActions } from './components/bitcoin-contract-request-actions'; 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 d67188eab31..11c7a16ffff 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 @@ -5,6 +5,7 @@ import { Stack, styled } from 'leather-styles/jsx'; import { RouteUrls } from '@shared/route-urls'; +import { useUpdatePageHeaderContext } from '@app/common/page/page.context'; import { BitcoinNativeSegwitAccountLoader } from '@app/components/loaders/bitcoin-account-loader'; import { BtcAssetItemBalanceLoader } from '@app/components/loaders/btc-balance-loader'; import { CurrentStacksAccountLoader } from '@app/components/loaders/stacks-account-loader'; @@ -16,6 +17,7 @@ import { Page } from '@app/ui/layout/page/page.layout'; export function ChooseCryptoAssetToFund() { const navigate = useNavigate(); + useUpdatePageHeaderContext({ isSettingsVisibleOnSm: false, onBackLocation: RouteUrls.Home }); const navigateToFund = useCallback( (symbol: string) => navigate(RouteUrls.Fund.replace(':currency', symbol)), diff --git a/src/app/pages/fund/fund.tsx b/src/app/pages/fund/fund.tsx index 506bf98b4e4..203b7223e14 100644 --- a/src/app/pages/fund/fund.tsx +++ b/src/app/pages/fund/fund.tsx @@ -9,6 +9,7 @@ import type { import { RouteUrls } from '@shared/route-urls'; +import { useUpdatePageHeaderContext } from '@app/common/page/page.context'; import { FullPageLoadingSpinner } from '@app/components/loading-spinner'; import { useCurrentAccountNativeSegwitIndexZeroSignerNullable } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; @@ -28,6 +29,9 @@ export function FundPage() { const currentStxAccount = useCurrentStacksAccount(); const bitcoinSigner = useCurrentAccountNativeSegwitIndexZeroSignerNullable(); const { currency = 'STX' } = useParams(); + useUpdatePageHeaderContext({ + onBackLocation: RouteUrls.FundChooseCurrency, + }); const fundCryptoCurrencyMap: Record = { BTC: { diff --git a/src/app/pages/home/home.tsx b/src/app/pages/home/home.tsx index 0e323f01bd3..aae20bd2617 100644 --- a/src/app/pages/home/home.tsx +++ b/src/app/pages/home/home.tsx @@ -1,4 +1,5 @@ -import { Route, useNavigate, useOutletContext } from 'react-router-dom'; +import { useState } from 'react'; +import { Route, useNavigate } from 'react-router-dom'; import { RouteUrls } from '@shared/route-urls'; @@ -7,7 +8,7 @@ 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 { ActivityList } from '@app/features/activity-list/activity-list'; -import { SwitchAccountOutletContext } from '@app/features/dialogs/switch-account-dialog/switch-account-dialog'; +import { SwitchAccountDialog } from '@app/features/dialogs/switch-account-dialog/switch-account-dialog'; import { FeedbackButton } from '@app/features/feedback-button/feedback-button'; import { Assets } from '@app/pages/home/components/assets'; import { homePageModalRoutes } from '@app/routes/app-routes'; @@ -23,8 +24,7 @@ import { HomeTabs } from './components/home-tabs'; export function Home() { const { decodedAuthRequest } = useOnboardingState(); - const { isShowingSwitchAccount, setIsShowingSwitchAccount } = - useOutletContext(); + const [isShowingSwitchAccount, setIsShowingSwitchAccount] = useState(false); const navigate = useNavigate(); const account = useCurrentStacksAccount(); const currentAccountIndex = useCurrentAccountIndex(); @@ -58,6 +58,12 @@ export function Home() { } > + {isShowingSwitchAccount && ( + setIsShowingSwitchAccount(false)} + /> + )} diff --git a/src/app/pages/receive/components/receive-tokens.layout.tsx b/src/app/pages/receive/components/receive-tokens.layout.tsx index d476f84da4a..bc5c76c6dbb 100644 --- a/src/app/pages/receive/components/receive-tokens.layout.tsx +++ b/src/app/pages/receive/components/receive-tokens.layout.tsx @@ -9,8 +9,8 @@ import { AddressDisplayer, Button, Dialog } from '@leather.io/ui'; import { useLocationState } from '@app/common/hooks/use-location-state'; import { useBackgroundLocationRedirect } from '@app/routes/hooks/use-background-location-redirect'; -import { Footer } from '@app/ui/components/containers/footers/footer'; -import { Header } from '@app/ui/components/containers/headers/header'; +import { Footer } from '@app/ui/layout/containers/footers/footer'; +import { DialogHeader } from '@app/ui/layout/containers/headers/dialog-header'; interface ReceiveTokensLayoutProps { address: string; @@ -29,14 +29,14 @@ export function ReceiveTokensLayout(props: ReceiveTokensLayoutProps) { return ( Receive
{title} } - onGoBack={() => navigate(backgroundLocation ?? '..')} + onClose={() => navigate(backgroundLocation ?? '..')} /> } isShowing diff --git a/src/app/pages/receive/receive-dialog.tsx b/src/app/pages/receive/receive-dialog.tsx index 57ef07fcd89..ffae8414f49 100644 --- a/src/app/pages/receive/receive-dialog.tsx +++ b/src/app/pages/receive/receive-dialog.tsx @@ -14,7 +14,7 @@ import { useBackgroundLocationRedirect } from '@app/routes/hooks/use-background- import { useZeroIndexTaprootAddress } from '@app/store/accounts/blockchain/bitcoin/bitcoin.hooks'; import { useCurrentAccountNativeSegwitAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; -import { Header } from '@app/ui/components/containers/headers/header'; +import { DialogHeader } from '@app/ui/layout/containers/headers/dialog-header'; import { ReceiveCollectibles } from './components/receive-collectibles'; import { ReceiveTokens } from './components/receive-tokens'; @@ -86,10 +86,10 @@ export function ReceiveDialog({ type = 'full' }: ReceiveDialogProps) { return ( navigate(backgroundLocation ?? '..')} + onClose={() => navigate(backgroundLocation ?? '..')} /> } onClose={() => navigate(backgroundLocation ?? '..')} diff --git a/src/app/pages/send/choose-crypto-asset/choose-crypto-asset.tsx b/src/app/pages/send/choose-crypto-asset/choose-crypto-asset.tsx index 3942c3452c1..24d29e998de 100644 --- a/src/app/pages/send/choose-crypto-asset/choose-crypto-asset.tsx +++ b/src/app/pages/send/choose-crypto-asset/choose-crypto-asset.tsx @@ -5,12 +5,15 @@ import { Box, styled } from 'leather-styles/jsx'; import { RouteUrls } from '@shared/route-urls'; import { AssetList } from '@app/features/asset-list/asset-list'; +// import { useUpdatePageHeaderContext } from '@app/ui/layout/containers/page/page.context'; import { useConfigBitcoinSendEnabled } from '@app/query/common/remote-config/remote-config.query'; import { Card } from '@app/ui/layout/card/card'; export function ChooseCryptoAsset() { const navigate = useNavigate(); const isBitcoinSendEnabled = useConfigBitcoinSendEnabled(); + // updating context title here causes a memory leak re-rendering PageLayout + // useUpdatePageHeaderContext({ isSettingsVisibleOnSm: false, title: 'Send' }); function navigateToSendForm(symbol: string, contractId?: string) { if (symbol === 'BTC' && !isBitcoinSendEnabled) return navigate(RouteUrls.SendBtcDisabled); diff --git a/src/app/pages/send/locked-bitcoin-summary/locked-bitcoin-summary.tsx b/src/app/pages/send/locked-bitcoin-summary/locked-bitcoin-summary.tsx index 9b47b1928c5..45ee6dbd755 100644 --- a/src/app/pages/send/locked-bitcoin-summary/locked-bitcoin-summary.tsx +++ b/src/app/pages/send/locked-bitcoin-summary/locked-bitcoin-summary.tsx @@ -9,15 +9,17 @@ import { analytics } from '@shared/utils/analytics'; import { useBitcoinExplorerLink } from '@app/common/hooks/use-bitcoin-explorer-link'; import { useClipboard } from '@app/common/hooks/use-copy-to-clipboard'; +import { useUpdatePageHeaderContext } from '@app/common/page/page.context'; import { InfoCardAssetValue, InfoCardBtn } from '@app/components/info-card/info-card'; import { useToast } from '@app/features/toasts/use-toast'; -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 { Footer } from '@app/ui/layout/containers/footers/footer'; export function LockBitcoinSummary() { const { state } = useLocation(); const toast = useToast(); + useUpdatePageHeaderContext({ title: 'Locked Bitcoin' }); const { txId, txMoney, txFiatValue, txFiatValueSymbol, symbol, txLink } = state; diff --git a/src/app/pages/send/ordinal-inscription/send-inscription-choose-fee.tsx b/src/app/pages/send/ordinal-inscription/send-inscription-choose-fee.tsx index 3f44a7b911f..6695f3b49f7 100644 --- a/src/app/pages/send/ordinal-inscription/send-inscription-choose-fee.tsx +++ b/src/app/pages/send/ordinal-inscription/send-inscription-choose-fee.tsx @@ -14,7 +14,7 @@ import { import { LoadingSpinner } from '@app/components/loading-spinner'; import { BitcoinChooseFee } from '@app/features/bitcoin-choose-fee/bitcoin-choose-fee'; import { useValidateBitcoinSpend } from '@app/features/bitcoin-choose-fee/hooks/use-validate-bitcoin-spend'; -import { DialogHeader } from '@app/ui/components/containers/headers/dialog-header'; +import { DialogHeader } from '@app/ui/layout/containers/headers/dialog-header'; import { useSendInscriptionState } from './components/send-inscription-container'; import { useSendInscriptionFeesList } from './hooks/use-send-inscription-fees-list'; diff --git a/src/app/pages/send/ordinal-inscription/send-inscription-form.tsx b/src/app/pages/send/ordinal-inscription/send-inscription-form.tsx index 339f28d5c5e..a9165932361 100644 --- a/src/app/pages/send/ordinal-inscription/send-inscription-form.tsx +++ b/src/app/pages/send/ordinal-inscription/send-inscription-form.tsx @@ -10,8 +10,8 @@ import { RouteUrls } from '@shared/route-urls'; import { ErrorLabel } from '@app/components/error-label'; import { InscriptionPreview } from '@app/components/inscription-preview-card/components/inscription-preview'; import { InscriptionPreviewCard } from '@app/components/inscription-preview-card/inscription-preview-card'; -import { Footer } from '@app/ui/components/containers/footers/footer'; -import { DialogHeader } from '@app/ui/components/containers/headers/dialog-header'; +import { Footer } from '@app/ui/layout/containers/footers/footer'; +import { DialogHeader } from '@app/ui/layout/containers/headers/dialog-header'; import { RecipientAddressTypeField } from '../send-crypto-asset-form/components/recipient-address-type-field'; import { CollectibleAsset } from './components/collectible-asset'; diff --git a/src/app/pages/send/ordinal-inscription/send-inscription-review.tsx b/src/app/pages/send/ordinal-inscription/send-inscription-review.tsx index 24e26acf4d4..bb752b3286a 100644 --- a/src/app/pages/send/ordinal-inscription/send-inscription-review.tsx +++ b/src/app/pages/send/ordinal-inscription/send-inscription-review.tsx @@ -16,9 +16,9 @@ import { InscriptionPreview } from '@app/components/inscription-preview-card/com import { useCurrentNativeSegwitUtxos } from '@app/query/bitcoin/address/utxos-by-address.hooks'; import { useAppDispatch } from '@app/store'; import { inscriptionSent } from '@app/store/ordinals/ordinals.slice'; -import { Footer } from '@app/ui/components/containers/footers/footer'; -import { DialogHeader } from '@app/ui/components/containers/headers/dialog-header'; import { Card } from '@app/ui/layout/card/card'; +import { Footer } from '@app/ui/layout/containers/footers/footer'; +import { DialogHeader } from '@app/ui/layout/containers/headers/dialog-header'; import { InscriptionPreviewCard } from '../../../components/inscription-preview-card/inscription-preview-card'; import { useSendInscriptionState } from './components/send-inscription-container'; diff --git a/src/app/pages/send/ordinal-inscription/sent-inscription-summary.tsx b/src/app/pages/send/ordinal-inscription/sent-inscription-summary.tsx index 8cbcff5c829..af798193ee6 100644 --- a/src/app/pages/send/ordinal-inscription/sent-inscription-summary.tsx +++ b/src/app/pages/send/ordinal-inscription/sent-inscription-summary.tsx @@ -15,9 +15,9 @@ import { FormAddressDisplayer } from '@app/components/address-displayer/form-add import { InfoCardBtn, InfoCardRow, InfoCardSeparator } from '@app/components/info-card/info-card'; import { InscriptionPreview } from '@app/components/inscription-preview-card/components/inscription-preview'; import { useToast } from '@app/features/toasts/use-toast'; -import { Footer } from '@app/ui/components/containers/footers/footer'; -import { DialogHeader } from '@app/ui/components/containers/headers/dialog-header'; import { Card } from '@app/ui/layout/card/card'; +import { Footer } from '@app/ui/layout/containers/footers/footer'; +import { DialogHeader } from '@app/ui/layout/containers/headers/dialog-header'; import { InscriptionPreviewCard } from '../../../components/inscription-preview-card/inscription-preview-card'; diff --git a/src/app/pages/send/send-crypto-asset-form/components/recipient-accounts-dialog/recipient-accounts-dialog.tsx b/src/app/pages/send/send-crypto-asset-form/components/recipient-accounts-dialog/recipient-accounts-dialog.tsx index 3af89fdde84..37d28459a8a 100644 --- a/src/app/pages/send/send-crypto-asset-form/components/recipient-accounts-dialog/recipient-accounts-dialog.tsx +++ b/src/app/pages/send/send-crypto-asset-form/components/recipient-accounts-dialog/recipient-accounts-dialog.tsx @@ -8,8 +8,8 @@ import { Dialog } from '@leather.io/ui'; import { useFilteredBitcoinAccounts } from '@app/store/accounts/blockchain/bitcoin/bitcoin.ledger'; import { useStacksAccounts } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; -import { DialogHeader } from '@app/ui/components/containers/headers/dialog-header'; import { VirtuosoWrapper } from '@app/ui/components/virtuoso'; +import { DialogHeader } from '@app/ui/layout/containers/headers/dialog-header'; import { AccountListItem } from './account-list-item'; diff --git a/src/app/pages/send/send-crypto-asset-form/form/brc20/brc20-choose-fee.tsx b/src/app/pages/send/send-crypto-asset-form/form/brc20/brc20-choose-fee.tsx index 3c43058f2aa..b8088d8e5b5 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/brc20/brc20-choose-fee.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/brc20/brc20-choose-fee.tsx @@ -11,6 +11,7 @@ import { createMoney } from '@leather.io/utils'; import { logger } from '@shared/logger'; import { RouteUrls } from '@shared/route-urls'; +import { useUpdatePageHeaderContext } from '@app/common/page/page.context'; import { formFeeRowValue } from '@app/common/send/utils'; import { useGenerateUnsignedNativeSegwitTx } from '@app/common/transactions/bitcoin/use-generate-bitcoin-tx'; import { @@ -40,6 +41,7 @@ function useBrc20ChooseFeeState() { export function BrcChooseFee() { const toast = useToast(); + useUpdatePageHeaderContext({ title: 'Choose fee' }); const navigate = useNavigate(); const { amount, recipient, ticker, utxos, holderAddress } = useBrc20ChooseFeeState(); const generateTx = useGenerateUnsignedNativeSegwitTx(); diff --git a/src/app/pages/send/send-crypto-asset-form/form/brc20/brc20-send-form-confirmation.tsx b/src/app/pages/send/send-crypto-asset-form/form/brc20/brc20-send-form-confirmation.tsx index 9eb2af315e1..95437d7a6d5 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/brc20/brc20-send-form-confirmation.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/brc20/brc20-send-form-confirmation.tsx @@ -19,9 +19,9 @@ import { } from '@app/components/info-card/info-card'; import { useCurrentNativeSegwitUtxos } from '@app/query/bitcoin/address/utxos-by-address.hooks'; import { useBrc20Transfers } from '@app/query/bitcoin/ordinals/brc20/brc20-tokens.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 { Footer } from '@app/ui/layout/containers/footers/footer'; import { useSendFormNavigate } from '../../hooks/use-send-form-navigate'; diff --git a/src/app/pages/send/send-crypto-asset-form/form/brc20/brc20-send-form.tsx b/src/app/pages/send/send-crypto-asset-form/form/brc20/brc20-send-form.tsx index bc3ffd79965..20d20669629 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/brc20/brc20-send-form.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/brc20/brc20-send-form.tsx @@ -10,10 +10,10 @@ import { Brc20AvatarIcon, Button, Callout, Link } from '@leather.io/ui'; import { convertAmountToBaseUnit, formatMoney } from '@leather.io/utils'; import { openInNewTab } from '@app/common/utils/open-in-new-tab'; -import { AvailableBalance } from '@app/ui/components/containers/footers/available-balance'; -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 { AvailableBalance } from '@app/ui/layout/containers/footers/available-balance'; +import { Footer } from '@app/ui/layout/containers/footers/footer'; import { AmountField } from '../../components/amount-field'; import { SelectedAssetField } from '../../components/selected-asset-field'; diff --git a/src/app/pages/send/send-crypto-asset-form/form/btc/btc-choose-fee.tsx b/src/app/pages/send/send-crypto-asset-form/form/btc/btc-choose-fee.tsx index 7a08c2c58ed..66e8ee7b0bb 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/btc/btc-choose-fee.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/btc/btc-choose-fee.tsx @@ -11,6 +11,7 @@ import { useBitcoinFeesList } from '@app/components/bitcoin-fees-list/use-bitcoi import { BitcoinChooseFee } from '@app/features/bitcoin-choose-fee/bitcoin-choose-fee'; import { useValidateBitcoinSpend } from '@app/features/bitcoin-choose-fee/hooks/use-validate-bitcoin-spend'; +// import { useUpdatePageHeaderContext } from '@app/ui/layout/containers/page/page.context'; import { useSendBitcoinAssetContextState } from '../../family/bitcoin/components/send-bitcoin-asset-container'; import { useBtcChooseFee } from './use-btc-choose-fee'; @@ -22,6 +23,7 @@ export function useBtcChooseFeeState() { } export function BtcChooseFee() { + // useUpdatePageHeaderContext({ title: 'Send' }); const { isSendingMax, txValues, utxos } = useBtcChooseFeeState(); const { selectedFeeType, setSelectedFeeType } = useSendBitcoinAssetContextState(); const { amountAsMoney, previewTransaction } = useBtcChooseFee(); diff --git a/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form-confirmation.tsx b/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form-confirmation.tsx index b54a4b4b730..43ebd1804d4 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form-confirmation.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form-confirmation.tsx @@ -34,10 +34,11 @@ import { InfoCardRow, InfoCardSeparator, } from '@app/components/info-card/info-card'; +// import { useUpdatePageHeaderContext } from '@app/ui/layout/containers/page/page.context'; import { useCurrentNativeSegwitUtxos } from '@app/query/bitcoin/address/utxos-by-address.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 { Footer } from '@app/ui/layout/containers/footers/footer'; import { useSendFormNavigate } from '../../hooks/use-send-form-navigate'; @@ -56,6 +57,7 @@ function useBtcSendFormConfirmationState() { export function BtcSendFormConfirmation() { const navigate = useNavigate(); + // useUpdatePageHeaderContext({ title: 'Review' }); const { tx, recipient, fee, arrivesIn, feeRowValue } = useBtcSendFormConfirmationState(); const transaction = useMemo(() => btc.Transaction.fromRaw(hexToBytes(tx)), [tx]); diff --git a/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form.tsx b/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form.tsx index db36e2fa060..3fe2f95c822 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form.tsx @@ -9,10 +9,10 @@ import { useCryptoCurrencyMarketDataMeanAverage } from '@leather.io/query'; import { BtcAvatarIcon, Button, Callout, Link } from '@leather.io/ui'; import { formatMoney } from '@leather.io/utils'; -import { AvailableBalance } from '@app/ui/components/containers/footers/available-balance'; -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 { AvailableBalance } from '@app/ui/layout/containers/footers/available-balance'; +import { Footer } from '@app/ui/layout/containers/footers/footer'; import { AmountField } from '../../components/amount-field'; import { SelectedAssetField } from '../../components/selected-asset-field'; @@ -28,7 +28,6 @@ const symbol: CryptoCurrencies = 'BTC'; export function BtcSendForm() { const routeState = useSendFormRouteState(); const marketData = useCryptoCurrencyMarketDataMeanAverage('BTC'); - const { balance, calcMaxSpend, @@ -42,6 +41,9 @@ export function BtcSendForm() { validationSchema, } = useBtcSendForm(); + // with this enabled PageLayout re-renders in a loop + // useUpdatePageHeaderContext({ title: 'Send' }); + return ( {token => ( diff --git a/src/app/pages/send/send-crypto-asset-form/form/stacks/stacks-common-send-form.tsx b/src/app/pages/send/send-crypto-asset-form/form/stacks/stacks-common-send-form.tsx index 94cb620296d..c27f72ce47e 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/stacks/stacks-common-send-form.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/stacks/stacks-common-send-form.tsx @@ -13,14 +13,15 @@ import { formatMoney } from '@leather.io/utils'; import { StacksSendFormValues } from '@shared/models/form.model'; import { RouteUrls } from '@shared/route-urls'; +import { useUpdatePageHeaderContext } from '@app/common/page/page.context'; import { FeesRow } from '@app/components/fees-row/fees-row'; import { NonceSetter } from '@app/components/nonce-setter'; import { useUpdatePersistedSendFormValues } from '@app/features/popup-send-form-restoration/use-update-persisted-send-form-values'; import { HighFeeDialog } from '@app/features/stacks-high-fee-warning/stacks-high-fee-dialog'; -import { AvailableBalance } from '@app/ui/components/containers/footers/available-balance'; -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 { AvailableBalance } from '@app/ui/layout/containers/footers/available-balance'; +import { Footer } from '@app/ui/layout/containers/footers/footer'; import { MemoField } from '../../components/memo-field'; import { StacksRecipientField } from '../../family/stacks/components/stacks-recipient-field'; @@ -51,6 +52,7 @@ export function StacksCommonSendForm({ }: StacksCommonSendFormProps) { const navigate = useNavigate(); const { onFormStateChange } = useUpdatePersistedSendFormValues(); + useUpdatePageHeaderContext({ title: 'Send' }); return ( } /> + }> navigate(RouteUrls.Swap)} header={ -
Choose asset
to swap } - variant="bigTitle" - onGoBack={() => navigate(RouteUrls.Swap)} + variant="large" + onClose={() => navigate(RouteUrls.Swap)} /> } > diff --git a/src/app/pages/swap/components/swap-asset-dialog/swap-asset-dialog-quote.tsx b/src/app/pages/swap/components/swap-asset-dialog/swap-asset-dialog-quote.tsx index 81ce10f70b3..fb8a3f6abea 100644 --- a/src/app/pages/swap/components/swap-asset-dialog/swap-asset-dialog-quote.tsx +++ b/src/app/pages/swap/components/swap-asset-dialog/swap-asset-dialog-quote.tsx @@ -2,7 +2,7 @@ import { Dialog } from '@leather.io/ui'; import { RouteUrls } from '@shared/route-urls'; -import { Header } from '@app/ui/components/containers/headers/header'; +import { DialogHeader } from '@app/ui/layout/containers/headers/dialog-header'; import { useSwapNavigate } from '../../hooks/use-swap-navigate'; import { useSwapContext } from '../../swap.context'; @@ -17,14 +17,14 @@ export function SwapAssetDialogQuote() { isShowing onClose={() => navigate(RouteUrls.Swap)} header={ -
Choose asset
to receive } - variant="bigTitle" - onGoBack={() => navigate(RouteUrls.Swap)} + variant="large" + onClose={() => navigate(RouteUrls.Swap)} /> } > diff --git a/src/app/pages/swap/components/swap-review.tsx b/src/app/pages/swap/components/swap-review.tsx index c7f8675f6d3..677adf6e5ae 100644 --- a/src/app/pages/swap/components/swap-review.tsx +++ b/src/app/pages/swap/components/swap-review.tsx @@ -5,9 +5,9 @@ import { SwapSelectors } from '@tests/selectors/swap.selectors'; import { Button } from '@leather.io/ui'; import { LoadingKeys, useLoading } from '@app/common/hooks/use-loading'; -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 { Footer } from '@app/ui/layout/containers/footers/footer'; import { useSwapContext } from '../swap.context'; import { SwapAssetsPair } from './swap-assets-pair/swap-assets-pair'; diff --git a/src/app/pages/swap/swap.tsx b/src/app/pages/swap/swap.tsx index 260777648e1..b4730d6f563 100644 --- a/src/app/pages/swap/swap.tsx +++ b/src/app/pages/swap/swap.tsx @@ -7,10 +7,13 @@ import { useFormikContext } from 'formik'; import { Button } from '@leather.io/ui'; import { isUndefined } from '@leather.io/utils'; +import { RouteUrls } from '@shared/route-urls'; + +import { useUpdatePageHeaderContext } from '@app/common/page/page.context'; import { LoadingSpinner } from '@app/components/loading-spinner'; -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 { Footer } from '@app/ui/layout/containers/footers/footer'; import { SwapAssetSelectBase } from './components/swap-asset-select/swap-asset-select-base'; import { SwapAssetSelectQuote } from './components/swap-asset-select/swap-asset-select-quote'; @@ -18,6 +21,8 @@ import { SwapFormValues } from './hooks/use-swap-form'; import { useSwapContext } from './swap.context'; export function Swap() { + // Swap uses routed dialogs to choose assets so needs onBackLocation to go Home + useUpdatePageHeaderContext({ title: 'Swap', onBackLocation: RouteUrls.Home }); const { isFetchingExchangeRate, swappableAssetsBase, swappableAssetsQuote } = useSwapContext(); const { dirty, isValid, setFieldValue, values, validateForm } = useFormikContext(); diff --git a/src/app/pages/unlock.tsx b/src/app/pages/unlock.tsx index da4ad908c1a..c4489518386 100644 --- a/src/app/pages/unlock.tsx +++ b/src/app/pages/unlock.tsx @@ -1,10 +1,11 @@ import { Outlet, useNavigate } from 'react-router-dom'; +import { useUpdatePageHeaderContext } from '@app/common/page/page.context'; import { RequestPassword } from '@app/components/request-password'; export function Unlock() { const navigate = useNavigate(); - + useUpdatePageHeaderContext({ isSessionLocked: true }); // Here we want to return to the previous route. The user could land on any // page when the wallet is locked, so we can't assume as single route. const returnToPreviousRoute = () => navigate(-1); diff --git a/src/app/routes/app-routes.tsx b/src/app/routes/app-routes.tsx index be665b76641..b03d6d3c8f7 100644 --- a/src/app/routes/app-routes.tsx +++ b/src/app/routes/app-routes.tsx @@ -1,4 +1,3 @@ -import { Suspense } from 'react'; import { Navigate, Route, @@ -13,8 +12,11 @@ import { RouteUrls } from '@shared/route-urls'; import { LoadingSpinner } from '@app/components/loading-spinner'; import { AddNetwork } from '@app/features/add-network/add-network'; +import { HomeLayout } from '@app/features/container/components/home/home.layout'; +import { OnboardingLayout } from '@app/features/container/components/onboarding/onboarding.layout'; +import { PageLayout } from '@app/features/container/components/page/page.layout'; +import { PopupLayout } from '@app/features/container/components/popup/popup.layout'; import { Container } from '@app/features/container/container'; -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 { IncreaseStxFeeDialog } from '@app/features/dialogs/increase-fee-dialog/increase-stx-fee-dialog'; import { leatherIntroDialogRoutes } from '@app/features/dialogs/leather-intro-dialog/leather-intro-dialog'; @@ -28,7 +30,6 @@ import { UnsupportedBrowserLayout } from '@app/features/ledger/generic-steps'; import { ConnectLedgerStart } from '@app/features/ledger/generic-steps/connect-device/connect-ledger-start'; import { RetrieveTaprootToNativeSegwit } from '@app/features/retrieve-taproot-to-native-segwit/retrieve-taproot-to-native-segwit'; import { BitcoinContractList } from '@app/pages/bitcoin-contract-list/bitcoin-contract-list'; -import { BitcoinContractRequest } from '@app/pages/bitcoin-contract-request/bitcoin-contract-request'; import { ChooseAccount } from '@app/pages/choose-account/choose-account'; import { ChooseCryptoAssetToFund } from '@app/pages/fund/choose-asset-to-fund/choose-asset-to-fund'; import { FundPage } from '@app/pages/fund/fund'; @@ -39,7 +40,6 @@ import { WelcomePage } from '@app/pages/onboarding/welcome/welcome'; import { ReceiveBtcModal } from '@app/pages/receive/receive-btc'; import { ReceiveStxModal } from '@app/pages/receive/receive-stx'; import { RequestError } from '@app/pages/request-error/request-error'; -import { RpcSignStacksTransaction } from '@app/pages/rpc-sign-stacks-transaction/rpc-sign-stacks-transaction'; import { BroadcastError } from '@app/pages/send/broadcast-error/broadcast-error'; import { LockBitcoinSummary } from '@app/pages/send/locked-bitcoin-summary/locked-bitcoin-summary'; import { sendOrdinalRoutes } from '@app/pages/send/ordinal-inscription/ordinal-routes'; @@ -81,186 +81,170 @@ function useAppRoutes() { return sentryCreateBrowserRouter( createRoutesFromElements( }> - }> - - - - } - > - {homePageModalRoutes} + }> + {/* FIXME don't duplicate RouterErrorBoundary just for layout */} + }> + + + + } + > + {homePageModalRoutes} + + + }> + }> + } + /> + }> + {ledgerStacksTxSigningRoutes} + + } + /> + }> + {ledgerBitcoinTxSigningRoutes} + - } - /> - }> {ledgerStacksTxSigningRoutes} - - } - /> - }> - {ledgerBitcoinTxSigningRoutes} - - {ledgerStacksTxSigningRoutes} + } /> + } /> + } /> - - }> - - - - } - /> - } /> - } /> - } /> - - - - } - > - } /> } + path={RouteUrls.AddNetwork} + element={ + + + + } + /> + + + + } + > + {ledgerJwtSigningRoutes} + + + + + } + > + } /> + } /> + + + + + } + > + } /> + + + {sendCryptoAssetFormRoutes} + + }> + {leatherIntroDialogRoutes} + + } /> + + + + } /> - {requestBitcoinKeysRoutes} - {requestStacksKeysRoutes} - - - - - } - /> - { - const { SetPasswordRoute } = await import( - '@app/pages/onboarding/set-password/set-password' - ); - return { Component: SetPasswordRoute }; - }} - /> - - - - - } - /> - - - - } - /> - - - - } - > - {ledgerJwtSigningRoutes} - - - - - - } - > - } /> - } /> - - - - - } - > - } /> + {alexSwapRoutes} + - {sendCryptoAssetFormRoutes} + }> + }> + + + + } + > + } /> + } + /> + + {requestBitcoinKeysRoutes} + {requestStacksKeysRoutes} + - - - - } - /> - }> - {leatherIntroDialogRoutes} - + + + + } + /> + { + const { SetPasswordRoute } = await import( + '@app/pages/onboarding/set-password/set-password' + ); + return { Component: SetPasswordRoute }; + }} + /> - {legacyRequestRoutes} - {rpcRequestRoutes} - } /> - - - - } - /> + + + + } + /> - - - - } - > - } /> + + + + } + /> + - { - const { RpcSignBip322MessageRoute } = await import( - '@app/pages/rpc-sign-bip322-message/rpc-sign-bip322-message' - ); - return { Component: RpcSignBip322MessageRoute }; - }} - > - {ledgerBitcoinTxSigningRoutes} + }> + }> + {legacyRequestRoutes} + {rpcRequestRoutes} - - {alexSwapRoutes} - - {/* Catch-all route redirects to onboarding */} - } /> + + {/* Catch-all route redirects to onboarding */} + } /> ) ); diff --git a/src/app/routes/rpc-routes.tsx b/src/app/routes/rpc-routes.tsx index 8893db05c47..0d8e5a69fab 100644 --- a/src/app/routes/rpc-routes.tsx +++ b/src/app/routes/rpc-routes.tsx @@ -3,13 +3,16 @@ import { Route } from 'react-router-dom'; import { RouteUrls } from '@shared/route-urls'; +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 { ledgerStacksMessageSigningRoutes } from '@app/features/ledger/flows/stacks-message-signing/ledger-stacks-sign-msg.routes'; +import { BitcoinContractRequest } from '@app/pages/bitcoin-contract-request/bitcoin-contract-request'; import { RpcGetAddresses } from '@app/pages/rpc-get-addresses/rpc-get-addresses'; import { rpcSendTransferRoutes } from '@app/pages/rpc-send-transfer/rpc-send-transfer.routes'; import { RpcSignPsbt } from '@app/pages/rpc-sign-psbt/rpc-sign-psbt'; import { RpcSignPsbtSummary } from '@app/pages/rpc-sign-psbt/rpc-sign-psbt-summary'; import { RpcStacksMessageSigning } from '@app/pages/rpc-sign-stacks-message/rpc-sign-stacks-message'; +import { RpcSignStacksTransaction } from '@app/pages/rpc-sign-stacks-transaction/rpc-sign-stacks-transaction'; import { AccountGate } from '@app/routes/account-gate'; import { SuspenseLoadingSpinner } from './app-routes'; @@ -66,5 +69,25 @@ export const rpcRequestRoutes = ( > {ledgerStacksMessageSigningRoutes} + + }> + + + + } + /> + + + + } + > + } /> + ); diff --git a/src/app/ui/components/containers/container.layout.tsx b/src/app/ui/components/containers/container.layout.tsx deleted file mode 100644 index 6167da4552d..00000000000 --- a/src/app/ui/components/containers/container.layout.tsx +++ /dev/null @@ -1,22 +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({ children, header }: ContainerLayoutProps) { - return ( - - {header} - - {children} - - - ); -} diff --git a/src/app/ui/components/containers/headers/components/big-title-header.tsx b/src/app/ui/components/containers/headers/components/big-title-header.tsx deleted file mode 100644 index b64910a12b9..00000000000 --- a/src/app/ui/components/containers/headers/components/big-title-header.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { ReactNode } from 'react'; - -import { SharedComponentsSelectors } from '@tests/selectors/shared-component.selectors'; -import { Flex, styled } from 'leather-styles/jsx'; - -import { CloseIcon } from '@leather.io/ui'; - -import { HeaderActionButton } from './header-action-button'; - -interface BigTitleHeaderProps { - onClose?(): void; - title?: ReactNode; -} - -export function BigTitleHeader({ onClose, title }: BigTitleHeaderProps) { - return ( - - {title} - {onClose && ( - - } - dataTestId={SharedComponentsSelectors.HeaderCloseBtn} - onAction={onClose} - /> - - )} - - ); -} diff --git a/src/app/ui/components/containers/headers/header.stories.tsx b/src/app/ui/components/containers/headers/header.stories.tsx deleted file mode 100644 index d6b48b0b883..00000000000 --- a/src/app/ui/components/containers/headers/header.stories.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import type { Meta } from '@storybook/react'; - -import { Header as Component, HeaderProps } from './header'; - -const meta: Meta = { - component: Component, - tags: ['autodocs'], - title: 'Containers/Header', - args: { - variant: 'home', - }, -}; - -export default meta; - -export function Header(args: HeaderProps) { - return null} onGoBack={() => null} />; -} - -export function PageHeader(args: HeaderProps) { - return null} />; -} diff --git a/src/app/ui/components/containers/headers/header.tsx b/src/app/ui/components/containers/headers/header.tsx deleted file mode 100644 index 285d5e58d36..00000000000 --- a/src/app/ui/components/containers/headers/header.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { ReactNode } from 'react'; - -import { SharedComponentsSelectors } from '@tests/selectors/shared-component.selectors'; -import { Flex, Grid, GridItem, HStack, styled } from 'leather-styles/jsx'; - -import { ArrowLeftIcon, CloseIcon } from '@leather.io/ui'; - -import { BigTitleHeader } from './components/big-title-header'; -import { HeaderActionButton } from './components/header-action-button'; - -type HeaderVariants = 'page' | 'home' | 'onboarding' | 'bigTitle' | 'fund'; - -export interface HeaderProps { - variant: HeaderVariants; - onClose?(): void; - onGoBack?(): void; - title?: ReactNode; - account?: ReactNode; - totalBalance?: ReactNode; - settingsMenu?: ReactNode; - networkBadge?: ReactNode; - logo?: ReactNode; -} - -export function Header({ - variant, - onClose, - onGoBack, - account, - totalBalance, - settingsMenu, - networkBadge, - title, - logo, -}: HeaderProps) { - const logoItem = onGoBack || logo || account; - - return ( - - - - {logoItem && ( - - {variant !== 'home' && onGoBack ? ( - } - onAction={onGoBack} - dataTestId={SharedComponentsSelectors.HeaderBackBtn} - /> - ) : undefined} - {account ? account : logo} - - )} - - - {title && {title}} - - - - {networkBadge} - {totalBalance && totalBalance} - {settingsMenu} - {variant !== 'bigTitle' && onClose && ( - } - dataTestId={SharedComponentsSelectors.HeaderCloseBtn} - onAction={onClose} - /> - )} - - - - {variant === 'bigTitle' && } - - ); -} diff --git a/src/app/ui/components/containers/footers/available-balance.tsx b/src/app/ui/layout/containers/footers/available-balance.tsx similarity index 100% rename from src/app/ui/components/containers/footers/available-balance.tsx rename to src/app/ui/layout/containers/footers/available-balance.tsx diff --git a/src/app/ui/components/containers/footers/footer.stories.tsx b/src/app/ui/layout/containers/footers/footer.stories.tsx similarity index 100% rename from src/app/ui/components/containers/footers/footer.stories.tsx rename to src/app/ui/layout/containers/footers/footer.stories.tsx diff --git a/src/app/ui/components/containers/footers/footer.tsx b/src/app/ui/layout/containers/footers/footer.tsx similarity index 100% rename from src/app/ui/components/containers/footers/footer.tsx rename to src/app/ui/layout/containers/footers/footer.tsx diff --git a/src/app/ui/components/containers/headers/components/header-action-button.tsx b/src/app/ui/layout/containers/headers/components/header-action-button.tsx similarity index 100% rename from src/app/ui/components/containers/headers/components/header-action-button.tsx rename to src/app/ui/layout/containers/headers/components/header-action-button.tsx diff --git a/src/app/ui/components/containers/headers/dialog-header.tsx b/src/app/ui/layout/containers/headers/dialog-header.tsx similarity index 66% rename from src/app/ui/components/containers/headers/dialog-header.tsx rename to src/app/ui/layout/containers/headers/dialog-header.tsx index 40d0f58093a..4fed3c77c90 100644 --- a/src/app/ui/components/containers/headers/dialog-header.tsx +++ b/src/app/ui/layout/containers/headers/dialog-header.tsx @@ -7,9 +7,10 @@ import { CloseIcon, IconButton } from '@leather.io/ui'; interface DialogHeaderProps { onClose?(): void; title?: ReactNode; + variant?: 'default' | 'large'; } -export function DialogHeader({ onClose, title }: DialogHeaderProps) { +export function DialogHeader({ onClose, title, variant = 'default' }: DialogHeaderProps) { return ( {title && ( - + {title} )}