chore: redirect to last visited market instead first from the list (#2489)
* chore: redirect to last visited market instead first from the list * chore: redirect to last visited market instead first from the list - add int tests * feat: redirect to last visited market instead first from the list - refactor solution * feat: redirect to last visited market instead first from the list - fix failing int test * feat: redirect to last visited market instead first from the list - fix failing int test * feat: redirect to last visited market instead first from the list - fix failing int test * feat: redirect to last visited market instead first from the list - use immer in globalStore * chore: redirect to last visited market - improve use of zustand
This commit is contained in:
parent
ccce5a2848
commit
6c84153cdb
@ -133,6 +133,7 @@ describe('Navbar', { tags: '@smoke' }, () => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.visit('/');
|
||||
cy.wait('@Market');
|
||||
cy.getByTestId('dialog-close').click();
|
||||
});
|
||||
|
||||
|
@ -224,4 +224,34 @@ describe('home', { tags: '@regression' }, () => {
|
||||
.should('exist');
|
||||
});
|
||||
});
|
||||
|
||||
describe('redirect should take last visited market into consideration', () => {
|
||||
beforeEach(() => {
|
||||
cy.window().then((window) => {
|
||||
window.localStorage.removeItem('marketId');
|
||||
});
|
||||
});
|
||||
it('marketId comes from existing market', () => {
|
||||
cy.window().then((window) => {
|
||||
window.localStorage.setItem('marketId', 'market-1');
|
||||
cy.visit('/');
|
||||
cy.wait('@Market');
|
||||
cy.location('hash').should('equal', '#/markets/market-1');
|
||||
cy.get('[role="dialog"]').should('not.exist');
|
||||
});
|
||||
});
|
||||
|
||||
it('marketId comes from not-existing market', () => {
|
||||
cy.window().then((window) => {
|
||||
window.localStorage.setItem('marketId', 'market-not-existing');
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'Market', null);
|
||||
});
|
||||
cy.visit('/');
|
||||
cy.wait('@Market');
|
||||
cy.location('hash').should('equal', '#/markets/market-not-existing');
|
||||
cy.get('[role="dialog"]').should('not.exist');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,14 +1,10 @@
|
||||
import { marketsWithDataProvider } from '@vegaprotocol/market-list';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
titlefy,
|
||||
useDataProvider,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import { Links, Routes } from '../../pages/client-router';
|
||||
import { useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useGlobalStore, usePageTitleStore } from '../../stores';
|
||||
import { marketsWithDataProvider } from '@vegaprotocol/market-list';
|
||||
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import { Links, Routes } from '../../pages/client-router';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
|
||||
export const Home = () => {
|
||||
const navigate = useNavigate();
|
||||
@ -17,40 +13,26 @@ export const Home = () => {
|
||||
const { data, error, loading } = useDataProvider({
|
||||
dataProvider: marketsWithDataProvider,
|
||||
});
|
||||
const { update } = useGlobalStore((store) => ({
|
||||
update: store.update,
|
||||
}));
|
||||
|
||||
const { pageTitle, updateTitle } = usePageTitleStore((store) => ({
|
||||
pageTitle: store.pageTitle,
|
||||
updateTitle: store.updateTitle,
|
||||
}));
|
||||
const update = useGlobalStore((store) => store.update);
|
||||
const marketId = useGlobalStore((store) => store.marketId);
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
const marketId = data[0]?.id;
|
||||
const marketName = data[0]?.tradableInstrument.instrument.name;
|
||||
const marketPrice = data[0]?.data?.markPrice
|
||||
? addDecimalsFormatNumber(
|
||||
data[0]?.data?.markPrice,
|
||||
data[0]?.decimalPlaces
|
||||
)
|
||||
: null;
|
||||
const newPageTitle = titlefy([marketName, marketPrice]);
|
||||
|
||||
if (marketId) {
|
||||
navigate(Links[Routes.MARKET](marketId), {
|
||||
if (marketId) {
|
||||
navigate(Links[Routes.MARKET](marketId), {
|
||||
replace: true,
|
||||
});
|
||||
} else if (data) {
|
||||
const marketDataId = data[0]?.id;
|
||||
if (marketDataId) {
|
||||
navigate(Links[Routes.MARKET](marketDataId), {
|
||||
replace: true,
|
||||
});
|
||||
update({ marketId });
|
||||
if (pageTitle !== newPageTitle) {
|
||||
updateTitle(newPageTitle);
|
||||
}
|
||||
} else {
|
||||
navigate(Links[Routes.MARKET]());
|
||||
}
|
||||
update({ shouldDisplayWelcomeDialog: true });
|
||||
}
|
||||
}, [data, navigate, update, pageTitle, updateTitle]);
|
||||
}, [marketId, data, navigate, update]);
|
||||
|
||||
return (
|
||||
<AsyncRenderer data={data} loading={loading} error={error}>
|
||||
|
@ -32,29 +32,23 @@ export interface SingleMarketData extends SingleMarketFieldsFragment {
|
||||
}
|
||||
|
||||
export const Market = () => {
|
||||
const params = useParams();
|
||||
const { marketId } = useParams();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const marketId = params.marketId;
|
||||
|
||||
const { w } = useWindowSize();
|
||||
const { update } = useGlobalStore((store) => ({
|
||||
update: store.update,
|
||||
}));
|
||||
const update = useGlobalStore((store) => store.update);
|
||||
const lastMarketId = useGlobalStore((store) => store.marketId);
|
||||
|
||||
const { pageTitle, updateTitle } = usePageTitleStore((store) => ({
|
||||
pageTitle: store.pageTitle,
|
||||
updateTitle: store.updateTitle,
|
||||
}));
|
||||
const pageTitle = usePageTitleStore((store) => store.pageTitle);
|
||||
const updateTitle = usePageTitleStore((store) => store.updateTitle);
|
||||
|
||||
const onSelect = useCallback(
|
||||
(id: string) => {
|
||||
if (id && id !== marketId) {
|
||||
update({ marketId: id });
|
||||
navigate(Links[Routes.MARKET](id));
|
||||
}
|
||||
},
|
||||
[marketId, update, navigate]
|
||||
[marketId, navigate]
|
||||
);
|
||||
|
||||
const variables = useMemo(
|
||||
@ -64,12 +58,22 @@ export const Market = () => {
|
||||
[marketId]
|
||||
);
|
||||
|
||||
const updateMarketId = useCallback(
|
||||
({ data }: { data: { id?: string } | null }) => {
|
||||
if (data?.id && data.id !== lastMarketId) {
|
||||
update({ marketId: data.id });
|
||||
}
|
||||
return true;
|
||||
},
|
||||
[update, lastMarketId]
|
||||
);
|
||||
const { data, error, loading } = useDataProvider<
|
||||
SingleMarketFieldsFragment,
|
||||
never
|
||||
>({
|
||||
dataProvider: marketProvider,
|
||||
variables,
|
||||
update: updateMarketId,
|
||||
skip: !marketId,
|
||||
});
|
||||
|
||||
@ -104,11 +108,10 @@ export const Market = () => {
|
||||
}
|
||||
return <TradePanels market={data} onSelect={onSelect} />;
|
||||
}, [w, data, onSelect]);
|
||||
|
||||
if (!data && marketId) {
|
||||
return (
|
||||
<Splash>
|
||||
<p>{t('Not found')}</p>
|
||||
<p>{t('Market not found')}</p>
|
||||
</Splash>
|
||||
);
|
||||
}
|
||||
@ -119,13 +122,9 @@ export const Market = () => {
|
||||
error={error}
|
||||
data={data || undefined}
|
||||
noDataCondition={(data) => false}
|
||||
render={(data) => {
|
||||
if (!data && marketId) {
|
||||
return <Splash>{t('Market not found')}</Splash>;
|
||||
}
|
||||
return <>{tradeView}</>;
|
||||
}}
|
||||
/>
|
||||
>
|
||||
{tradeView}
|
||||
</AsyncRenderer>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,18 +1,15 @@
|
||||
import { useCallback } from 'react';
|
||||
import { MarketsContainer } from '@vegaprotocol/market-list';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Links, Routes } from '../../pages/client-router';
|
||||
|
||||
export const Markets = () => {
|
||||
const navigate = useNavigate();
|
||||
const { update } = useGlobalStore((store) => ({ update: store.update }));
|
||||
const handleOnSelect = useCallback(
|
||||
(marketId: string) => {
|
||||
update({ marketId });
|
||||
navigate(Links[Routes.MARKET](marketId));
|
||||
},
|
||||
[update, navigate]
|
||||
[navigate]
|
||||
);
|
||||
|
||||
return <MarketsContainer onSelect={handleOnSelect} />;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useMemo, useState, useCallback } from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { Dialog } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
@ -12,59 +12,50 @@ import * as constants from '../constants';
|
||||
import { RiskNoticeDialog } from './risk-notice-dialog';
|
||||
import { WelcomeNoticeDialog } from './welcome-notice-dialog';
|
||||
import { WelcomeLandingDialog } from './welcome-landing-dialog';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
|
||||
interface DialogConfig {
|
||||
open?: boolean;
|
||||
content: React.ReactNode;
|
||||
title?: string;
|
||||
size?: 'small' | 'medium';
|
||||
onClose: () => void;
|
||||
}
|
||||
export const WelcomeDialog = () => {
|
||||
const { pathname } = useLocation();
|
||||
const { VEGA_ENV } = useEnvironment();
|
||||
const [dialog, setDialog] = useState<DialogConfig | null>(null);
|
||||
const onClose = useCallback(() => {
|
||||
setDialog(null);
|
||||
}, [setDialog]);
|
||||
let dialogContent: React.ReactNode = null;
|
||||
let title = '';
|
||||
let size: 'small' | 'medium' = 'small';
|
||||
const [riskAccepted] = useLocalStorage(constants.RISK_ACCEPTED_KEY);
|
||||
|
||||
const { data } = useDataProvider({
|
||||
dataProvider: activeMarketsProvider,
|
||||
});
|
||||
|
||||
useMemo(() => {
|
||||
switch (true) {
|
||||
case riskAccepted !== 'true' && VEGA_ENV === Networks.MAINNET:
|
||||
setDialog({
|
||||
content: <RiskNoticeDialog onClose={onClose} />,
|
||||
title: t('WARNING'),
|
||||
size: 'medium',
|
||||
onClose,
|
||||
});
|
||||
break;
|
||||
case pathname === '/' && data?.length === 0:
|
||||
setDialog({
|
||||
content: <WelcomeNoticeDialog />,
|
||||
onClose,
|
||||
});
|
||||
break;
|
||||
case pathname === '/' && (data?.length || 0) > 0:
|
||||
setDialog({
|
||||
content: <WelcomeLandingDialog onClose={onClose} />,
|
||||
onClose,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}, [onClose, data?.length, riskAccepted, pathname, VEGA_ENV, setDialog]);
|
||||
return dialog ? (
|
||||
const { update, shouldDisplayWelcomeDialog } = useGlobalStore((store) => ({
|
||||
update: store.update,
|
||||
shouldDisplayWelcomeDialog: store.shouldDisplayWelcomeDialog,
|
||||
}));
|
||||
const isRiskDialogNeeded =
|
||||
riskAccepted !== 'true' && VEGA_ENV === Networks.MAINNET;
|
||||
const isWelcomeDialogNeeded = pathname === '/' || shouldDisplayWelcomeDialog;
|
||||
const onClose = useCallback(() => {
|
||||
update({ shouldDisplayWelcomeDialog: isRiskDialogNeeded });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
dialogContent = null;
|
||||
}, [update, isRiskDialogNeeded]);
|
||||
|
||||
if (isRiskDialogNeeded) {
|
||||
dialogContent = <RiskNoticeDialog onClose={onClose} />;
|
||||
title = t('WARNING');
|
||||
size = 'medium';
|
||||
} else if (isWelcomeDialogNeeded && data?.length === 0) {
|
||||
dialogContent = <WelcomeNoticeDialog />;
|
||||
} else if (isWelcomeDialogNeeded && (data?.length || 0) > 0) {
|
||||
dialogContent = <WelcomeLandingDialog onClose={onClose} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={Boolean(dialog.content)}
|
||||
title={dialog.title}
|
||||
size={dialog.size}
|
||||
onChange={dialog.onClose}
|
||||
open={Boolean(dialogContent)}
|
||||
title={title}
|
||||
size={size}
|
||||
onChange={onClose}
|
||||
>
|
||||
{dialog.content}
|
||||
{dialogContent}
|
||||
</Dialog>
|
||||
) : null;
|
||||
);
|
||||
};
|
||||
|
@ -12,7 +12,6 @@ import {
|
||||
} from '../select-market';
|
||||
import { WelcomeDialogHeader } from './welcome-dialog-header';
|
||||
import { Link, useNavigate, useParams } from 'react-router-dom';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
import { ProposedMarkets } from './proposed-markets';
|
||||
import { Links, Routes } from '../../pages/client-router';
|
||||
|
||||
@ -27,18 +26,13 @@ export const SelectMarketLandingTable = ({
|
||||
const navigate = useNavigate();
|
||||
const marketId = params.marketId;
|
||||
|
||||
const { update } = useGlobalStore((store) => ({
|
||||
update: store.update,
|
||||
}));
|
||||
|
||||
const onSelect = useCallback(
|
||||
(id: string) => {
|
||||
if (id && id !== marketId) {
|
||||
update({ marketId: id });
|
||||
navigate(Links[Routes.MARKET](id));
|
||||
}
|
||||
},
|
||||
[marketId, update, navigate]
|
||||
[marketId, navigate]
|
||||
);
|
||||
|
||||
const onSelectMarket = useCallback(
|
||||
|
@ -3,7 +3,7 @@ import type { RouteObject } from 'react-router-dom';
|
||||
import { useRoutes } from 'react-router-dom';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import trimEnd from 'lodash/trimEnd';
|
||||
|
||||
const LazyHome = dynamic(() => import('../client-pages/home'), {
|
||||
@ -91,7 +91,7 @@ export const ClientRouter = () => {
|
||||
<Suspense
|
||||
fallback={
|
||||
<div className="w-full h-full flex justify-center items-center">
|
||||
{t('Loading...')}
|
||||
<Loader />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
|
@ -1,10 +1,12 @@
|
||||
import { LocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import create from 'zustand';
|
||||
import produce from 'immer';
|
||||
|
||||
interface GlobalStore {
|
||||
networkSwitcherDialog: boolean;
|
||||
marketId: string | null;
|
||||
update: (store: Partial<Omit<GlobalStore, 'update'>>) => void;
|
||||
shouldDisplayWelcomeDialog: boolean;
|
||||
}
|
||||
|
||||
interface PageTitleStore {
|
||||
@ -15,10 +17,15 @@ interface PageTitleStore {
|
||||
export const useGlobalStore = create<GlobalStore>((set) => ({
|
||||
networkSwitcherDialog: false,
|
||||
marketId: LocalStorage.getItem('marketId') || null,
|
||||
update: (state) => {
|
||||
set(state);
|
||||
if (state.marketId) {
|
||||
LocalStorage.setItem('marketId', state.marketId);
|
||||
shouldDisplayWelcomeDialog: false,
|
||||
update: (newState) => {
|
||||
set(
|
||||
produce((state: GlobalStore) => {
|
||||
Object.assign(state, newState);
|
||||
})
|
||||
);
|
||||
if (newState.marketId) {
|
||||
LocalStorage.setItem('marketId', newState.marketId);
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
Loading…
Reference in New Issue
Block a user