-
- {t('bitsurance.dashboard.title')}
+
+
+
+
+
+ {t('bitsurance.dashboard.title')}
+
+
navigate('/market/bitsurance/account')}
+ title={t('bitsurance.dashboard.button')}>
+ +
+ {t('bitsurance.dashboard.button')}
+
+
+
+
+ {accountsByKeystore?.length && insurances ? accountsByKeystore.map(({ accounts, keystore }) => (
+ anyAccountInsured({ accounts, keystore }) && (
+
+
{keystore.name}
+ { isAmbiguousName(keystore.name, accountsByKeystore) ? (
+ // Disambiguate accounts group by adding the fingerprint.
+ // The most common case where this would happen is when adding accounts from the
+ // same seed using different passphrases.
+ ({keystore.rootFingerprint})
+ ) : null }
-
navigate('/bitsurance/account')}
- title={t('account.exportTransactions')}>
- +
- {t('bitsurance.dashboard.button')}
-
-
-
-
- {accountsByKeystore?.length && insurances ? accountsByKeystore.map(({ accounts, keystore }) => (
- anyAccountInsured({ accounts, keystore }) && (
-
-
{keystore.name}
- { isAmbiguousName(keystore.name, accountsByKeystore) ? (
- // Disambiguate accounts group by adding the fingerprint.
- // The most common case where this would happen is when adding accounts from the
- // same seed using different passphrases.
- ({keystore.rootFingerprint})
- ) : null }
-
-
- {accounts?.length ? accounts.map(account => {
- const balance = balances && balances[account.code];
- const insurance = insurances[account.code];
- return insurance ? (
-
-
-
- {accounts.filter(ac => ac.code === account.code).map(ac => ac.name)}
-
-
- { balance ? (
-
- ) : }
-
-
-
-
-
- {t('bitsurance.dashboard.coverage')}
-
-
- {insurance.details.maxCoverageFormatted}
- {' '}
- {insurance.details.currency}
-
-
-
-
-
+
+ {accounts?.length ? accounts.map(account => {
+ const balance = balances && balances[account.code];
+ const insurance = insurances[account.code];
+ return insurance ? (
+
+
+
+ {accounts.filter(ac => ac.code === account.code).map(ac => ac.name)}
+
+
+ { balance ? (
+
+ ) : }
+
+
+
+
+
+ {t('bitsurance.dashboard.coverage')}
+
+
+ {insurance.details.maxCoverageFormatted}
+ {' '}
+ {insurance.details.currency}
+
+
+
+
+
-
- )
- )) :
}
+ ) : null;
+ }) :
}
+
-
-
-
-
-
-
+ )
+ )) :
}
+
+
+
);
};
diff --git a/frontends/web/src/routes/bitsurance/widget.module.css b/frontends/web/src/routes/bitsurance/widget.module.css
index d216a64345..a5dddf1c90 100644
--- a/frontends/web/src/routes/bitsurance/widget.module.css
+++ b/frontends/web/src/routes/bitsurance/widget.module.css
@@ -14,8 +14,3 @@
position: relative;
z-index: 3000;
}
-
-.header {
- position: relative;
- z-index: 2200;
-}
diff --git a/frontends/web/src/routes/bitsurance/widget.tsx b/frontends/web/src/routes/bitsurance/widget.tsx
index f16c616577..472785dc83 100644
--- a/frontends/web/src/routes/bitsurance/widget.tsx
+++ b/frontends/web/src/routes/bitsurance/widget.tsx
@@ -5,15 +5,12 @@ import { useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { RequestAddressV0Message, MessageVersion, parseMessage, serializeMessage, V0MessageType } from 'request-address';
import { getConfig } from '@/utils/config';
-import { ScriptType, signBTCMessageUnusedAddress } from '@/api/account';
-import { getInfo } from '@/api/account';
-import { Header } from '@/components/layout';
+import { getInfo, signBTCMessageUnusedAddress, type ScriptType } from '@/api/account';
import { Spinner } from '@/components/spinner/Spinner';
import { BitsuranceTerms } from '@/components/terms/bitsurance-terms';
import { useLoad } from '@/hooks/api';
import { UseDisableBackButton } from '@/hooks/backbutton';
import { alertUser } from '@/components/alert/Alert';
-import { BitsuranceGuide } from './guide';
import { getBitsuranceURL } from '@/api/bitsurance';
import { convertScriptType } from '@/utils/request-addess';
import { useVendorIframeResizeHeight, useVendorTerms } from '@/hooks/vendor-iframe';
@@ -113,7 +110,7 @@ export const BitsuranceWidget = ({ code }: TProps) => {
try {
let message = JSON.parse(m.data);
if (message?.type === 'showInsuranceDashboard') {
- navigate('/bitsurance/dashboard');
+ navigate('/market/bitsurance/dashboard');
return;
}
@@ -135,38 +132,32 @@ export const BitsuranceWidget = ({ code }: TProps) => {
};
return (
-
-
-
- {t('bitsuranceAccount.title')}} />
-
-
- { !agreedTerms ? (
-
setAgreedTerms(true)}
- />
- ) : (
-
-
- {!iframeLoaded && }
-
-
- )}
-
+ <>
+
+ { !agreedTerms ? (
+
setAgreedTerms(true)}
+ />
+ ) : (
+
+
+ {!iframeLoaded && }
+
+
+ )}
-
-
+ >
);
};
diff --git a/frontends/web/src/routes/market/components/marketplace-navigation.tsx b/frontends/web/src/routes/market/components/marketplace-navigation.tsx
new file mode 100644
index 0000000000..de02bce1c5
--- /dev/null
+++ b/frontends/web/src/routes/market/components/marketplace-navigation.tsx
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import { useEffect } from 'react';
+import type { TAccount } from '@/api/account';
+import { getSwapStatus } from '@/api/swap';
+import { useLoad } from '@/hooks/api';
+import { MarketTab } from './markettab';
+import type { TMarketplaceTab } from './markettab';
+import type { TMarketAction } from '@/api/market';
+import { useMarketContext } from '../market-context';
+import style from './markettab.module.css';
+
+type TProps = {
+ accounts?: TAccount[];
+ activeTab: TMarketplaceTab;
+ className?: string;
+ onChangeTab: (tab: TMarketplaceTab) => void;
+ showSwap?: boolean;
+};
+
+export const getMarketActionFromSearchParams = (searchParams: URLSearchParams): TMarketAction => {
+ const tab = searchParams.get('tab');
+ if (tab === 'buy' || tab === 'sell' || tab === 'spend' || tab === 'swap' || tab === 'otc') {
+ return tab;
+ }
+ return 'buy';
+};
+
+export const MarketplaceNavigation = ({
+ accounts,
+ activeTab,
+ className = '',
+ onChangeTab,
+ showSwap,
+}: TProps) => {
+ const { setShowSwap, showSwap: contextShowSwap } = useMarketContext();
+ const swapStatus = useLoad(showSwap === undefined ? getSwapStatus : null, [accounts]);
+ const showSwapTab = showSwap ?? swapStatus?.available ?? contextShowSwap ?? false;
+
+ useEffect(() => {
+ const nextShowSwap = showSwap ?? swapStatus?.available;
+ if (nextShowSwap !== undefined) {
+ setShowSwap(nextShowSwap);
+ }
+ }, [setShowSwap, showSwap, swapStatus?.available]);
+
+ return (
+
+ );
+};
diff --git a/frontends/web/src/routes/market/components/markettab.module.css b/frontends/web/src/routes/market/components/markettab.module.css
index 23588a4f8a..dc90ee90b8 100644
--- a/frontends/web/src/routes/market/components/markettab.module.css
+++ b/frontends/web/src/routes/market/components/markettab.module.css
@@ -1,3 +1,13 @@
+.navigation {
+ box-sizing: border-box;
+ flex-grow: 0;
+ justify-content: center;
+ margin: 0 auto;
+ max-width: var(--content-width);
+ padding: 0 var(--space-default);
+ width: 100%;
+}
+
.tabLabel {
display: inline-block;
position: relative;
diff --git a/frontends/web/src/routes/market/components/markettab.test.tsx b/frontends/web/src/routes/market/components/markettab.test.tsx
index 0ba07526dc..371c2cf6a5 100644
--- a/frontends/web/src/routes/market/components/markettab.test.tsx
+++ b/frontends/web/src/routes/market/components/markettab.test.tsx
@@ -1,12 +1,24 @@
// SPDX-License-Identifier: Apache-2.0
-import '../../../../__mocks__/i18n';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { MarketTab } from './markettab';
import { getConfig } from '@/utils/config';
+vi.mock('react-i18next', () => ({
+ useTranslation: () => ({
+ t: (key: string) => ({
+ 'buy.exchange.buy': 'Buy',
+ 'buy.exchange.sell': 'Sell',
+ 'buy.exchange.spend': 'Spend',
+ 'generic.insure': 'Insure',
+ 'generic.new': 'New',
+ 'generic.swap': 'Swap',
+ }[key] || key),
+ }),
+}));
+
vi.mock('@/utils/config', () => ({
getConfig: vi.fn(),
setConfig: vi.fn(),
@@ -72,13 +84,28 @@ describe('routes/market/components/markettab', () => {
/>,
);
- const swapButton = screen.getByText('generic.swap').closest('button');
- expect(swapButton).not.toBeNull();
- await user.click(swapButton as HTMLButtonElement);
+ await user.click(screen.getByRole('button', { name: 'Swap' }));
expect(onChangeTab).toHaveBeenCalledWith('swap');
});
+ it('emits insure tab selection when insure is clicked', async () => {
+ const user = userEvent.setup();
+ const onChangeTab = vi.fn();
+
+ render(
+
,
+ );
+
+ await user.click(screen.getByRole('button', { name: 'Insure' }));
+
+ expect(onChangeTab).toHaveBeenCalledWith('insure');
+ });
+
it('shows the new badge on otc when enabled', async () => {
mockedGetConfig.mockResolvedValue({
frontend: {
diff --git a/frontends/web/src/routes/market/components/markettab.tsx b/frontends/web/src/routes/market/components/markettab.tsx
index d088c67aa2..59407f8adb 100644
--- a/frontends/web/src/routes/market/components/markettab.tsx
+++ b/frontends/web/src/routes/market/components/markettab.tsx
@@ -2,14 +2,16 @@
import { useTranslation } from 'react-i18next';
import { PillButton, PillButtonGroup } from '../../../components/pillbuttongroup/pillbuttongroup';
-import { TMarketAction } from '@/api/market';
+import type { TMarketAction } from '@/api/market';
import { NewBadge } from '@/components/new-badge/new-badge';
import style from './markettab.module.css';
+export type TMarketplaceTab = TMarketAction | 'insure';
type TProps = {
- onChangeTab: (tab: TMarketAction) => void;
- activeTab: TMarketAction;
+ onChangeTab: (tab: TMarketplaceTab) => void;
+ activeTab: TMarketplaceTab;
+ className?: string;
showSwap: boolean;
};
@@ -17,11 +19,12 @@ type TProps = {
export const MarketTab = ({
onChangeTab,
activeTab,
+ className,
showSwap,
}: TProps) => {
const { t } = useTranslation();
return (
-
+
onChangeTab('buy')}
@@ -71,6 +74,12 @@ export const MarketTab = ({
/>
+ onChangeTab('insure')}
+ >
+ {t('generic.insure')}
+
);
};
diff --git a/frontends/web/src/routes/market/market-context.tsx b/frontends/web/src/routes/market/market-context.tsx
new file mode 100644
index 0000000000..d1c2db8b27
--- /dev/null
+++ b/frontends/web/src/routes/market/market-context.tsx
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import { Dispatch, ReactNode, SetStateAction, createContext, useContext, useState } from 'react';
+import type { TOption } from './components/countryselect';
+
+type TMarketContext = {
+ marketAccountCode?: string;
+ regions: TOption[];
+ selectedRegion: string;
+ setMarketAccountCode: Dispatch>;
+ setRegions: Dispatch>;
+ setSelectedRegion: Dispatch>;
+ setShowSwap: Dispatch>;
+ showSwap?: boolean;
+};
+
+const MarketContext = createContext(null);
+
+type TProps = {
+ children: ReactNode;
+ initialMarketAccountCode?: string;
+};
+
+export const MarketProvider = ({
+ children,
+ initialMarketAccountCode,
+}: TProps) => {
+ const [marketAccountCode, setMarketAccountCode] = useState(initialMarketAccountCode);
+ const [regions, setRegions] = useState([]);
+ const [selectedRegion, setSelectedRegion] = useState('');
+ const [showSwap, setShowSwap] = useState();
+
+ return (
+
+ {children}
+
+ );
+};
+
+export const useMarketContext = () => {
+ const context = useContext(MarketContext);
+ if (context === null) {
+ throw new Error('useMarketContext must be used within MarketProvider');
+ }
+ return context;
+};
diff --git a/frontends/web/src/routes/market/market.tsx b/frontends/web/src/routes/market/market.tsx
index 9153eb3647..6c198a774c 100644
--- a/frontends/web/src/routes/market/market.tsx
+++ b/frontends/web/src/routes/market/market.tsx
@@ -9,10 +9,7 @@ import { i18n } from '@/i18n/i18n';
import * as marketAPI from '@/api/market';
import { getSwapStatus } from '@/api/swap';
import { AccountCode, TAccount } from '@/api/account';
-import { GuidedContent, GuideWrapper, Header, Main } from '@/components/layout';
import { View, ViewContent } from '@/components/view/view';
-import { MarketGuide } from './guide';
-import { isBitcoinOnly } from '@/routes/account/utils';
import { useLoad } from '@/hooks/api';
import { useVendorTerms } from '@/hooks/vendor-iframe-terms';
import { getRegionNameFromLocale } from '@/i18n/utils';
@@ -21,7 +18,7 @@ import { Spinner } from '@/components/spinner/Spinner';
import { Dialog } from '@/components/dialog/dialog';
import { alertUser } from '@/components/alert/Alert';
import { InfoButton } from '@/components/infobutton/infobutton';
-import { MarketTab } from './components/markettab';
+import { getMarketActionFromSearchParams } from './components/marketplace-navigation';
import { Deals } from './components/deals';
import { getNativeLocale } from '@/api/nativelocale';
import { getConfig, setConfig } from '@/utils/config';
@@ -30,6 +27,7 @@ import { getBTCDirectOTCLink, getPocketOTCLink, InfoContent, TInfoContentProps }
import { GroupedAccountSelector } from '@/components/groupedaccountselector/groupedaccountselector';
import { connectAnyKeystore, connectKeystore } from '@/api/keystores';
import { open } from '@/api/system';
+import { useMarketContext } from './market-context';
import style from './market.module.css';
type TProps = {
@@ -43,23 +41,33 @@ export const Market = ({
}: TProps) => {
const { t } = useTranslation();
const navigate = useNavigate();
- const [searchParams, setSearchParams] = useSearchParams();
-
- const [selectedAccount, setSelectedAccount] = useState(code);
- const [selectedRegion, setSelectedRegion] = useState('');
- const [regions, setRegions] = useState([]);
+ const [searchParams] = useSearchParams();
+ const {
+ marketAccountCode,
+ regions,
+ selectedRegion,
+ setMarketAccountCode,
+ setRegions,
+ setSelectedRegion,
+ } = useMarketContext();
+ const validRouteAccountCode = accounts.some(account => account.code === code) ? code : '';
+ const validMarketAccountCode = accounts.some(account => account.code === marketAccountCode) ? marketAccountCode : '';
+
+ const [selectedAccount, setSelectedAccount] = useState(validRouteAccountCode || validMarketAccountCode || '');
const [info, setInfo] = useState();
- const [supportedAccounts, setSupportedAccounts] = useState([]);
- const [activeTab, setActiveTab] = useState('buy');
+ const [supportedAccounts, setSupportedAccounts] = useState(accounts);
+ const activeTab = getMarketActionFromSearchParams(searchParams);
+ const selectedAccountIsValid = accounts.some(account => account.code === selectedAccount);
+ const connectedAccountCode = accounts.find(account => account.keystore.connected)?.code;
+ const nextSelectedAccount = validRouteAccountCode
+ || (selectedAccountIsValid ? selectedAccount : '')
+ || connectedAccountCode
+ || accounts[0]?.code
+ || '';
const regionCodes = useLoad(marketAPI.getMarketRegionCodes);
const nativeLocale = useLoad(getNativeLocale);
const config = useLoad(getConfig);
- const swapStatus = useLoad(getSwapStatus, [accounts]);
-
- const hasOnlyBTCAccounts = accounts.every(({ coinCode }) => isBitcoinOnly(coinCode));
-
- const title = t('generic.buySell');
const {
agreedTerms: agreedBTCDirectOTCTerms,
@@ -69,14 +77,28 @@ export const Market = ({
agreedTerms: agreedPocketOTCTerms,
} = useVendorTerms(!!config?.frontend?.skipPocketOTCDisclaimer);
- // keep account list in sync and ensure a valid selected account.
+ // keep account list in sync.
useEffect(() => {
setSupportedAccounts(accounts);
- if (!selectedAccount || !accounts.some(account => account.code === selectedAccount)) {
- const accountOfConnectedKeystore = accounts.find(account => account.keystore.connected);
- setSelectedAccount(accountOfConnectedKeystore?.code || accounts[0]?.code || '');
+ }, [accounts]);
+
+ // ensure a valid selected account.
+ useEffect(() => {
+ if (nextSelectedAccount) {
+ setMarketAccountCode(nextSelectedAccount);
}
- }, [accounts, selectedAccount]);
+ if (nextSelectedAccount !== selectedAccount) {
+ setSelectedAccount(nextSelectedAccount);
+ }
+ }, [nextSelectedAccount, selectedAccount, setMarketAccountCode]);
+
+ // keep URLs normalized to include the selected account.
+ useEffect(() => {
+ if (validRouteAccountCode || !nextSelectedAccount) {
+ return;
+ }
+ navigate(`/market/select/${nextSelectedAccount}?tab=${activeTab}`, { replace: true });
+ }, [activeTab, navigate, nextSelectedAccount, validRouteAccountCode]);
// update region Select component when `regionList` or `config` gets populated.
useEffect(() => {
@@ -94,6 +116,7 @@ export const Market = ({
// if user had selected no region before, do not pre-select any.
if (config.frontend.selectedExchangeRegion === '') {
+ setSelectedRegion('');
return;
}
@@ -108,8 +131,9 @@ export const Market = ({
//Region is available in the list
const regionAvailable = !!(regionCodes.find(code => code === userRegion));
//Pre-selecting the region
- setSelectedRegion(regionAvailable ? userRegion : '');
- }, [regionCodes, config, nativeLocale]);
+ const nextRegion = regionAvailable ? userRegion : '';
+ setSelectedRegion(nextRegion);
+ }, [regionCodes, config, nativeLocale, setRegions, setSelectedRegion]);
const buyDealsResponse = useLoad(selectedAccount ? () => marketAPI.getMarketDeals('buy', selectedAccount, selectedRegion) : null, [selectedAccount, selectedRegion]);
const sellDealsResponse = useLoad(selectedAccount ? () => marketAPI.getMarketDeals('sell', selectedAccount, selectedRegion) : null, [selectedAccount, selectedRegion]);
@@ -128,7 +152,9 @@ export const Market = ({
const handleAccountChange = async (accountCode: string) => {
if (await promptConnectKeystore(accountCode)) {
+ setMarketAccountCode(accountCode);
setSelectedAccount(accountCode);
+ navigate(`/market/select/${accountCode}?tab=${activeTab}`, { replace: true });
}
};
@@ -179,16 +205,6 @@ export const Market = ({
}
};
- const handleChangeTab = (tab: marketAPI.TMarketAction) => {
- setActiveTab(tab);
- setSearchParams(`?tab=${tab}`);
- };
-
- useEffect(() => {
- const tab = searchParams.get('tab') as marketAPI.TMarketAction | null ;
- setActiveTab(tab || 'buy');
- }, [searchParams]);
-
const goToVendor = async (vendor: marketAPI.TVendorName) => {
if (!vendor) {
return;
@@ -229,102 +245,84 @@ export const Market = ({
}
};
- const translationContext = hasOnlyBTCAccounts ? 'bitcoin' : 'crypto';
-
return (
-
-
-
- setInfo(undefined)}
- open={!!info}
- >
- {info && (
-
- )}
-
-
- {title}
-
- } />
-
-
-
- {regions.length ? (
+ <>
+
setInfo(undefined)}
+ open={!!info}
+ >
+ {info && (
+
+ )}
+
+
+
+
+ {regions.length ? (
+ <>
+ {activeTab !== 'swap' && (
<>
-
- {activeTab !== 'swap' && (
+
+ {t('buy.exchange.region')}
+
+
+
+
+ setInfo({
+ action: activeTab,
+ vendorName: 'region',
+ paymentFees: {}
+ })} />
+
+
+ {activeTab !== 'otc' && (
<>
- {t('buy.exchange.region')}
+ {t('account.account')}
-
-
- setInfo({
- action: activeTab,
- vendorName: 'region',
- paymentFees: {}
- })} />
-
- {activeTab !== 'otc' && (
- <>
-
- {t('account.account')}
-
-
-
-
- >
- )}
>
)}
-
-
- {(activeTab === 'swap' || !!selectedAccount) && (
- {getServicesLabel(activeTab)}
- )}
-
-
>
- ) :
}
-
-
-
-
-
-
-
+ )}
+
+
+ {(activeTab === 'swap' || !!selectedAccount) && (
+ {getServicesLabel(activeTab)}
+ )}
+
+
+ >
+ ) :
}
+
+
+
+ >
);
};
diff --git a/frontends/web/src/routes/market/marketplace-layout.tsx b/frontends/web/src/routes/market/marketplace-layout.tsx
new file mode 100644
index 0000000000..637e0cadef
--- /dev/null
+++ b/frontends/web/src/routes/market/marketplace-layout.tsx
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import { useEffect } from 'react';
+import { Outlet, useLocation, useNavigate, useParams, useSearchParams } from 'react-router-dom';
+import { useTranslation } from 'react-i18next';
+import type { TAccount } from '@/api/account';
+import { Header, GuidedContent, GuideWrapper, Main } from '@/components/layout';
+import { HideAmountsButton } from '@/components/hideamountsbutton/hideamountsbutton';
+import { isBitcoinOnly } from '@/routes/account/utils';
+import { BitsuranceGuide } from '@/routes/bitsurance/guide';
+import { MarketGuide } from './guide';
+import {
+ getMarketActionFromSearchParams,
+ MarketplaceNavigation,
+} from './components/marketplace-navigation';
+import type { TMarketplaceTab } from './components/markettab';
+import { MarketProvider, useMarketContext } from './market-context';
+
+type TProps = {
+ accounts: TAccount[];
+};
+
+const MarketplaceLayoutContent = ({ accounts }: TProps) => {
+ const { t } = useTranslation();
+ const { pathname } = useLocation();
+ const navigate = useNavigate();
+ const { code } = useParams();
+ const [searchParams] = useSearchParams();
+ const { marketAccountCode, setMarketAccountCode } = useMarketContext();
+
+ const isBitsurance = pathname.startsWith('/market/bitsurance');
+ const isMarketSelect = pathname.startsWith('/market/select');
+ const showMarketplaceNavigation = !pathname.startsWith('/market/bitsurance/widget');
+ const activeTab: TMarketplaceTab = isBitsurance ? 'insure' : getMarketActionFromSearchParams(searchParams);
+ const hasOnlyBTCAccounts = accounts.every(({ coinCode }) => isBitcoinOnly(coinCode));
+ const translationContext = hasOnlyBTCAccounts ? 'bitcoin' : 'crypto';
+
+ useEffect(() => {
+ if (isMarketSelect && code) {
+ setMarketAccountCode(code);
+ }
+ }, [code, isMarketSelect, setMarketAccountCode]);
+
+ const handleChangeTab = (tab: TMarketplaceTab) => {
+ if (tab === 'insure') {
+ const bitsurancePath = accounts.some(({ bitsuranceStatus }) => bitsuranceStatus)
+ ? '/market/bitsurance/dashboard'
+ : '/market/bitsurance';
+ navigate(bitsurancePath);
+ return;
+ }
+ const marketSelectAccountCode = isMarketSelect ? code || marketAccountCode : marketAccountCode;
+ navigate(`/market/select${marketSelectAccountCode ? `/${marketSelectAccountCode}` : ''}?tab=${tab}`);
+ };
+
+ return (
+
+
+
+ {t('generic.buySell')}}>
+ {pathname.startsWith('/market/bitsurance/dashboard') && (
+
+ )}
+
+ {showMarketplaceNavigation && (
+
+ )}
+
+
+ {isBitsurance ? (
+
+ ) : (
+
+ )}
+
+
+ );
+};
+
+export const MarketplaceLayout = ({ accounts }: TProps) => {
+ const { pathname } = useLocation();
+ const { code } = useParams();
+ const isMarketSelect = pathname.startsWith('/market/select');
+
+ return (
+
+
+
+ );
+};
diff --git a/frontends/web/src/routes/router.tsx b/frontends/web/src/routes/router.tsx
index db58213964..851c304215 100644
--- a/frontends/web/src/routes/router.tsx
+++ b/frontends/web/src/routes/router.tsx
@@ -7,6 +7,7 @@ import { TDevices } from '@/api/devices';
import { AddAccount } from './account/add/add-account';
import { Moonpay } from './market/moonpay';
import { Market } from './market/market';
+import { MarketplaceLayout } from './market/marketplace-layout';
import { Pocket } from './market/pocket';
import { BTCDirect } from './market/btcdirect';
import { BTCDirectOTC } from './market/btcdirect-otc';
@@ -203,6 +204,10 @@ export const AppRouter = ({ devices, devicesKey, accounts, activeAccounts }: TAp
/>
);
+ const MarketplaceLayoutEl = (
+
+ );
+
const PocketBuyEl = (
} />
-
-
+
+
+
+
+ }/>
+
+
+
+
+ }/>
+
+
@@ -305,18 +320,6 @@ export const AppRouter = ({ devices, devicesKey, accounts, activeAccounts }: TAp
-
- }/>
-
-
-
-
-
-
-
-
- }/>
-
diff --git a/frontends/web/src/routes/settings/more.tsx b/frontends/web/src/routes/settings/more.tsx
index 5a427d7f41..47be5a5519 100644
--- a/frontends/web/src/routes/settings/more.tsx
+++ b/frontends/web/src/routes/settings/more.tsx
@@ -9,7 +9,7 @@ import { GlobalBanners } from '@/components/banners';
import { SettingsItem } from '@/routes/settings/components/settingsItem/settingsItem';
import { useOnlyVisitableOnMobile } from '@/hooks/onlyvisitableonmobile';
import { useDarkmode } from '@/hooks/darkmode';
-import { CogDark, CogLight, ShieldDark, ShieldLight } from '@/components/icon';
+import { CogDark, CogLight } from '@/components/icon';
import { TDevices } from '@/api/devices';
import { useLoad } from '@/hooks/api';
import { getVersion } from '@/api/bitbox02';
@@ -57,17 +57,6 @@ export const More = ({ devices }: Props) => {
onClick={() => navigate('/settings')}
canUpgrade={canUpgrade}
/>
-
- {isDarkMode
- ?
- : }
- {t('sidebar.insurance')}
-
- }
- onClick={() => navigate('/bitsurance/bitsurance')}
- />