Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: use location.state to store background route to prevent unmount #4062

Closed
wants to merge 8 commits into from
4 changes: 0 additions & 4 deletions src/app/common/hooks/use-location-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,11 @@ import get from 'lodash.get';

import { isUndefined } from '@shared/utils';

pete-watters marked this conversation as resolved.
Show resolved Hide resolved
export function useLocationState(propName: string): string | undefined;
export function useLocationState(propName: string, defaultValue: string): string;
export function useLocationState(propName: string, defaultValue?: string) {
const location = useLocation();
return get(location, `state.${propName}`, defaultValue);
}

export function useLocationStateWithCache<T = string>(propName: string): T | undefined;
export function useLocationStateWithCache<T = string>(propName: string, defaultValue: T): T;
export function useLocationStateWithCache<T = string>(propName: string, defaultValue?: T) {
const location = useLocation();
const [value, setValue] = useState(defaultValue);
Expand Down
12 changes: 7 additions & 5 deletions src/app/components/receive/receive-collectible.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import toast from 'react-hot-toast';
import { useLocation, useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router-dom';

import BitcoinStampImg from '@assets/images/bitcoin-stamp.png';
import { Box, Stack, useClipboard } from '@stacks/ui';
import { HomePageSelectors } from '@tests/selectors/home.selectors';
import get from 'lodash.get';

import { RouteUrls } from '@shared/route-urls';

import { useAnalytics } from '@app/common/hooks/analytics/use-analytics';
import { useLocationState } from '@app/common/hooks/use-location-state';
import { StxAvatar } from '@app/components/crypto-assets/stacks/components/stx-avatar';
import { OrdinalIcon } from '@app/components/icons/ordinal-icon';
import { useZeroIndexTaprootAddress } from '@app/query/bitcoin/ordinals/use-zero-index-taproot-address';
Expand All @@ -19,9 +19,9 @@ import { ReceiveCollectibleItem } from './receive-collectible-item';

export function ReceiveCollectible() {
const analytics = useAnalytics();
const location = useLocation();
const backgroundLocation = useLocationState('backgroundLocation');
const accountIndex = useLocationState('accountIndex');
const navigate = useNavigate();
const accountIndex = get(location.state, 'accountIndex', undefined);
const btcAddressNativeSegwit = useCurrentAccountNativeSegwitAddressIndexZero();
const btcAddressTaproot = useZeroIndexTaprootAddress(accountIndex);

Expand Down Expand Up @@ -54,7 +54,9 @@ export function ReceiveCollectible() {
data-testid={HomePageSelectors.ReceiveBtcTaprootQrCodeBtn}
onCopyAddress={() => {
void analytics.track('select_inscription_to_add_new_collectible');
navigate(RouteUrls.ReceiveCollectibleOrdinal, { state: { btcAddressTaproot } });
navigate(RouteUrls.ReceiveCollectibleOrdinal, {
state: { btcAddressTaproot, backgroundLocation },
});
}}
title="Ordinal inscription"
/>
Expand Down
3 changes: 1 addition & 2 deletions src/app/features/asset-list/asset-list.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Outlet } from 'react-router-dom';
import { useNavigate } from 'react-router-dom';
import { Outlet, useNavigate } from 'react-router-dom';

import { Box, Stack } from '@stacks/ui';
import { HomePageSelectorsLegacy } from '@tests-legacy/page-objects/home.selectors';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useNavigate } from 'react-router-dom';
import { useLocation, useNavigate } from 'react-router-dom';

import { Inscription as InscriptionType } from '@shared/models/inscription.model';
import { RouteUrls } from '@shared/route-urls';
Expand All @@ -18,10 +18,11 @@ interface InscriptionProps {
export function Inscription({ rawInscription }: InscriptionProps) {
const inscription = convertInscriptionToSupportedInscriptionType(rawInscription);
const navigate = useNavigate();
const location = useLocation();

function openSendInscriptionModal() {
navigate(RouteUrls.SendOrdinalInscription, {
state: { inscription },
state: { inscription, backgroundLocation: location },
});
}

Expand Down
15 changes: 12 additions & 3 deletions src/app/features/settings-dropdown/settings-dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,10 @@ export function SettingsDropdown() {
data-testid={SettingsSelectors.ToggleTheme}
onClick={wrappedCloseCallback(() => {
void analytics.track('click_change_theme_menu_item');
navigate(RouteUrls.ChangeTheme, { relative: 'path' });
navigate(RouteUrls.ChangeTheme, {
relative: 'path',
state: { backgroundLocation: location },
});
})}
>
Change theme
Expand Down Expand Up @@ -147,7 +150,10 @@ export function SettingsDropdown() {
data-testid={SettingsSelectors.ChangeNetworkAction}
onClick={wrappedCloseCallback(() => {
void analytics.track('click_change_network_menu_item');
navigate(RouteUrls.SelectNetwork, { relative: 'path' });
navigate(RouteUrls.SelectNetwork, {
relative: 'path',
state: { backgroundLocation: location },
});
})}
>
<Flex width="100%" alignItems="center" justifyContent="space-between">
Expand Down Expand Up @@ -178,7 +184,10 @@ export function SettingsDropdown() {
<MenuItem
color={color('feedback-error')}
onClick={wrappedCloseCallback(() =>
navigate(RouteUrls.SignOutConfirm, { relative: 'path' })
navigate(RouteUrls.SignOutConfirm, {
relative: 'path',
state: { backgroundLocation: location },
})
)}
data-testid="settings-sign-out"
>
Expand Down
10 changes: 5 additions & 5 deletions src/app/pages/home/components/home-tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { StackProps } from '@stacks/ui';

import { RouteUrls } from '@shared/route-urls';

import { useLocationState } from '@app/common/hooks/use-location-state';
import { LoadingSpinner } from '@app/components/loading-spinner';
import { Tabs } from '@app/components/tabs';

Expand All @@ -16,6 +17,7 @@ interface HomeTabsProps extends StackProps {
export function HomeTabs({ children }: HomeTabsProps) {
const navigate = useNavigate();
const { pathname } = useLocation();
const backgroundLocation = useLocationState('backgroundLocation');

const tabs = useMemo(
() => [
Expand All @@ -25,16 +27,14 @@ export function HomeTabs({ children }: HomeTabsProps) {
[]
);

const getActiveTab = useCallback(
() => tabs.findIndex(tab => tab.slug === pathname),
[tabs, pathname]
);
const path = backgroundLocation ? backgroundLocation.pathname : pathname;

const getActiveTab = useCallback(() => tabs.findIndex(tab => tab.slug === path), [tabs, path]);

const setActiveTab = useCallback(
(index: number) => navigate(tabs[index]?.slug),
[navigate, tabs]
);

return (
<Stack flexGrow={1} mt="loose" spacing="extra-loose">
<Tabs
Expand Down
10 changes: 8 additions & 2 deletions src/app/pages/home/components/receive-button.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useNavigate } from 'react-router-dom';
import { useLocation, useNavigate } from 'react-router-dom';

import { ButtonProps } from '@stacks/ui';
import { HomePageSelectors } from '@tests/selectors/home.selectors';
Expand All @@ -13,15 +13,21 @@ import { HomeActionButton } from './tx-button';

export function ReceiveButton(props: ButtonProps) {
const navigate = useNavigate();
const location = useLocation();
const isBitcoinEnabled = useConfigBitcoinEnabled();
const receivePath = isBitcoinEnabled ? RouteUrls.Receive : RouteUrls.ReceiveStx;

return (
<HomeActionButton
buttonComponent={PrimaryButton}
data-testid={HomePageSelectors.ReceiveCryptoAssetBtn}
icon={QrCodeIcon}
label="Receive"
onClick={() => navigate(isBitcoinEnabled ? RouteUrls.Receive : RouteUrls.ReceiveStx)}
onClick={() =>
navigate(receivePath, {
state: { backgroundLocation: location },
})
}
{...props}
/>
);
Expand Down
21 changes: 17 additions & 4 deletions src/app/pages/home/home.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import { Outlet, useNavigate } from 'react-router-dom';
import { Outlet, Route, Routes, useLocation, useNavigate } from 'react-router-dom';

import { RouteUrls } from '@shared/route-urls';

import { useTrackFirstDeposit } from '@app/common/hooks/analytics/transactions-analytics.hooks';
import { useOnboardingState } from '@app/common/hooks/auth/use-onboarding-state';
import { useLocationState } from '@app/common/hooks/use-location-state';
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 { InAppMessages } from '@app/features/hiro-messages/in-app-messages';
import { SuggestedFirstSteps } from '@app/features/suggested-first-steps/suggested-first-steps';
import { HomeActions } from '@app/pages/home/components/home-actions';
import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
import { settingsModalRoutes } from '@app/routes/app-routes';

import { CurrentAccount } from './components/account-area';
import { HomeTabs } from './components/home-tabs';
Expand All @@ -19,8 +22,10 @@ import { HomeLayout } from './components/home.layout';
export function Home() {
const { decodedAuthRequest } = useOnboardingState();

const stacksAccount = useCurrentStacksAccount();
const navigate = useNavigate();

const location = useLocation();
const backgroundLocation = useLocationState('backgroundLocation');
useTrackFirstDeposit();

useRouteHeader(
Expand All @@ -41,7 +46,15 @@ export function Home() {
actions={<HomeActions />}
>
<HomeTabs>
<Outlet context={{ address: stacksAccount?.address }} />
<>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to declare Routes here so I can manipulate the location depending on whether backgroundLocation is populated or not.

backgroundLocation is the route above which the modal should appear.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it possible to also read location in app-routes to see if bg location is defined? Nw if not, just curious

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can read the location there but the issue I struggled with is how to then feed said location into how we are creating the routes.

We are using createHashRouter and createRoutesFromElements and I was unable to figure out how to pass the location there.

Eventually I came up with the idea of using <Routes nested in <Home so I could then trick the location.

We can probably improve on that structure with more time and I am open to suggestions but I didn't want to start refactoring the whole of app-routes

<Routes location={backgroundLocation || location}>
<Route path={RouteUrls.Home} element={<AssetsList />} />
<Route path={RouteUrls.Activity} element={<ActivityList />}>
{settingsModalRoutes}
</Route>
</Routes>
{backgroundLocation && <Outlet />}
</>
</HomeTabs>
</HomeLayout>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import { useNavigate } from 'react-router-dom';
import { Box, Button, Flex, Text, color } from '@stacks/ui';
import { SharedComponentsSelectors } from '@tests/selectors/shared-component.selectors';

import { RouteUrls } from '@shared/route-urls';

import { useLocationState } from '@app/common/hooks/use-location-state';
import { AddressDisplayer } from '@app/components/address-displayer/address-displayer';
import { BaseDrawer } from '@app/components/drawer/base-drawer';
import { Title } from '@app/components/typography';
Expand All @@ -23,9 +22,10 @@ interface ReceiveTokensLayoutProps {
export function ReceiveTokensLayout(props: ReceiveTokensLayoutProps) {
const { address, accountName, onCopyAddressToClipboard, title, warning, hasSubtitle } = props;
const navigate = useNavigate();
const { pathname } = useLocationState('backgroundLocation');

return (
<BaseDrawer title={title} isShowing onClose={() => navigate(RouteUrls.Home)}>
<BaseDrawer title={title} isShowing onClose={() => navigate(pathname)}>
<Flex alignItems="center" flexDirection="column" pb={['loose', '48px']} px="loose">
{hasSubtitle && (
<Text color={color('text-caption')} mb="tight" textAlign="left">
Expand Down
21 changes: 18 additions & 3 deletions src/app/pages/receive-tokens/receive-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { HomePageSelectors } from '@tests/selectors/home.selectors';
import { RouteUrls } from '@shared/route-urls';

import { useAnalytics } from '@app/common/hooks/analytics/use-analytics';
import { useLocationState } from '@app/common/hooks/use-location-state';
import { StxAvatar } from '@app/components/crypto-assets/stacks/components/stx-avatar';
import { BaseDrawer } from '@app/components/drawer/base-drawer';
import { BtcIcon } from '@app/components/icons/btc-icon';
Expand All @@ -22,6 +23,7 @@ import { useCurrentAccountStxAddressState } from '@app/store/accounts/blockchain
export function ReceiveModal() {
const analytics = useAnalytics();
const navigate = useNavigate();
const backgroundLocation = useLocationState('backgroundLocation');
const btcAddress = useCurrentAccountNativeSegwitAddressIndexZero();
const stxAddress = useCurrentAccountStxAddressState();
const { onCopy: onCopyStacks } = useClipboard(stxAddress);
Expand All @@ -31,8 +33,13 @@ export function ReceiveModal() {
toast.success('Copied to clipboard!');
copyHandler();
}

return (
<BaseDrawer title="Select asset to receive" isShowing onClose={() => navigate('../')}>
<BaseDrawer
title="Select asset to receive"
isShowing
onClose={() => navigate(backgroundLocation?.pathname)}
>
<Box mx="extra-loose">
<Caption style={{ fontSize: '14px' }}>Tokens</Caption>

Expand All @@ -49,7 +56,11 @@ export function ReceiveModal() {
borderRadius="10px"
data-testid={HomePageSelectors.ReceiveBtcNativeSegwitQrCodeBtn}
mode="tertiary"
onClick={() => navigate(RouteUrls.ReceiveBtc)}
onClick={() =>
navigate(RouteUrls.ReceiveBtc, {
state: { backgroundLocation },
})
}
>
<Box color={color('text-caption')} size="14px">
<QrCodeIcon />
Expand Down Expand Up @@ -79,7 +90,11 @@ export function ReceiveModal() {
borderRadius="10px"
data-testid={HomePageSelectors.ReceiveStxQrCodeBtn}
mode="tertiary"
onClick={() => navigate(RouteUrls.ReceiveStx)}
onClick={() =>
navigate(RouteUrls.ReceiveStx, {
state: { backgroundLocation },
})
}
>
<Box color={color('text-caption')} size="14px">
<QrCodeIcon />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Box, Flex, Stack, color, useClipboard } from '@stacks/ui';
import { truncateMiddle } from '@stacks/ui-utils';

import { useAnalytics } from '@app/common/hooks/analytics/use-analytics';
import { useLocationState } from '@app/common/hooks/use-location-state';
import { openInNewTab } from '@app/common/utils/open-in-new-tab';
import { BaseDrawer } from '@app/components/drawer/base-drawer';
import { ErrorLabel } from '@app/components/error-label';
Expand All @@ -18,6 +19,7 @@ export function ReceiveCollectibleOrdinal() {
const navigate = useNavigate();
const analytics = useAnalytics();
const { state } = useLocation();
const { pathname } = useLocationState('backgroundLocation');
const { onCopy } = useClipboard(state.btcAddressTaproot);

function copyToClipboard() {
Expand All @@ -27,7 +29,7 @@ export function ReceiveCollectibleOrdinal() {
}

return (
<BaseDrawer isShowing onClose={() => navigate('../')}>
<BaseDrawer isShowing onClose={() => navigate(pathname)}>
<Box mx="extra-loose">
<Stack alignItems="center" px={['unset', 'base']} spacing="loose" textAlign="center">
<OrdinalIcon />
Expand Down
21 changes: 11 additions & 10 deletions src/app/routes/app-routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,14 @@ export function AppRoutes() {
return <RouterProvider router={routes} />;
}

export const settingsModalRoutes = (
<Route>
<Route path={RouteUrls.SignOutConfirm} element={<SignOutConfirmDrawer />} />
<Route path={RouteUrls.ChangeTheme} element={<ThemesDrawer />} />
<Route path={RouteUrls.SelectNetwork} element={<SelectNetwork />} />
</Route>
);

function useAppRoutes() {
const userHasNotConsentedToDiagnostics = useHasUserRespondedToAnalyticsConsent();

Expand All @@ -83,14 +91,6 @@ function useAppRoutes() {
)
);

const settingsModalRoutes = (
<Route>
<Route path={RouteUrls.SignOutConfirm} element={<SignOutConfirmDrawer />} />
<Route path={RouteUrls.ChangeTheme} element={<ThemesDrawer />} />
<Route path={RouteUrls.SelectNetwork} element={<SelectNetwork />} />
</Route>
);

const legacyRequestRoutes = (
<>
<Route
Expand Down Expand Up @@ -187,8 +187,9 @@ function useAppRoutes() {
}
>
<Route index element={<AssetsList />} />
<Route path={RouteUrls.Activity} element={<ActivityList />} />

<Route path={RouteUrls.Activity} element={<ActivityList />}>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ActivityList is loaded in home.tsx but the route needs to be declared here also.

Similarly settingsModalRoutes needs to be passed in so you can use the settings modals from /activities

{settingsModalRoutes}
</Route>
{requestBitcoinKeysRoutes}
{requestStacksKeysRoutes}
<Route path={RouteUrls.RetriveTaprootFunds} element={<RetrieveTaprootToNativeSegwit />} />
Expand Down