Skip to content

Commit

Permalink
[DTRA] Ahmad/DTRA-1705/Insufficient Balance And Not Logged in User Ac…
Browse files Browse the repository at this point in the history
…tion Sheet (#16787)

* chore: done

* fix: fix

* fix: test case

* fix: adding in mockStore

* chore: icon

* chore: fix

* fix: test case

* fix: changing snakcase

* chore: review comments

* fix: review fix for signup

* chore: fix

* chore: fix
  • Loading branch information
ahmadtaimoor-deriv authored Sep 13, 2024
1 parent dbe0633 commit d761e7f
Show file tree
Hide file tree
Showing 16 changed files with 358 additions and 57 deletions.
10 changes: 6 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions packages/core/src/Stores/common-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export default class CommonStore extends BaseStore {
setSelectedContractType: action.bound,
setServerTime: action.bound,
setServicesError: action.bound,
resetServicesError: action.bound,
setWithdrawURL: action.bound,
showError: action.bound,
was_socket_opened: observable,
Expand Down Expand Up @@ -280,13 +281,15 @@ export default class CommonStore extends BaseStore {
setWithdrawURL(withdraw_url) {
this.withdraw_url = withdraw_url;
}

setServicesError(error) {
resetServicesError() {
this.services_error = {};
}
setServicesError(error, hide_toast = false) {
this.services_error = error;
if (isMobile()) {
if (error.code === 'CompanyWideLimitExceeded' || error.code === 'PleaseAuthenticate') {
this.root_store.ui.toggleServicesErrorModal(true);
} else {
} else if (!hide_toast) {
this.root_store.ui.addToast({
content: error.message,
type: 'error',
Expand Down
1 change: 1 addition & 0 deletions packages/stores/src/mockStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ const mock = (): TStores & { is_mock: boolean } => {
routeTo: jest.fn(),
changeCurrentLanguage: jest.fn(),
changeSelectedLanguage: jest.fn(),
resetServicesError: jest.fn(),
is_network_online: false,
network_status: {},
services_error: {},
Expand Down
3 changes: 2 additions & 1 deletion packages/stores/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,8 @@ type TCommonStore = {
setAppstorePlatform: (value?: string) => void;
setError?: (has_error: boolean, error: TCommonStoreError) => void;
setSelectedContractType: (contract_type: string) => void;
setServicesError: (error: TCommonStoreServicesError) => void;
setServicesError: (error: TCommonStoreServicesError, hide_toast: boolean) => void;
resetServicesError: () => void;
showError: (error: TCommonStoreError) => void;
app_routing_history: TAppRoutingHistory[];
getExchangeRate: (from_currency: string, to_currency: string) => Promise<number>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ jest.mock('@deriv-com/quill-ui', () => ({
}),
}));
describe('BottomNav', () => {
const default_mock_store = mockStore({});
const mockedTradeContainer = <div>MockedTrade</div>;
const mockedPositionsContainer = <div>MockedPositions</div>;
const renderedBottomNav = (
<StoreProvider store={mockStore({})}>
<StoreProvider store={default_mock_store}>
<BrowserRouter>
<BottomNav>
<div>{mockedTradeContainer}</div>
Expand All @@ -29,15 +30,18 @@ describe('BottomNav', () => {
expect(container).toBeInTheDocument();
});
it('should render the correct number of BottomNavItem components', () => {
default_mock_store.client.is_logged_in = true;
render(renderedBottomNav);
expect(screen.getByText('Positions')).toBeInTheDocument();
expect(screen.getByText('Trade')).toBeInTheDocument();
});
it('should render MockedTrade by default since selected index is 0', () => {
default_mock_store.client.is_logged_in = true;
render(renderedBottomNav);
expect(screen.getByText('MockedTrade')).toBeInTheDocument();
});
it('should render MockedPositions if 2nd MockedBottomNavItem is selected', () => {
default_mock_store.client.is_logged_in = true;
render(renderedBottomNav);
userEvent.click(screen.getByText('Positions'));
expect(screen.getByText('MockedPositions')).toBeInTheDocument();
Expand Down
44 changes: 25 additions & 19 deletions packages/trader/src/AppV2/Components/BottomNav/bottom-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ type BottomNavProps = {
const BottomNav = observer(({ children, className, onScroll }: BottomNavProps) => {
const history = useHistory();
const location = useLocation();
const { active_positions_count } = useStore().portfolio;
const { client, portfolio } = useStore();
const { active_positions_count } = portfolio;
const { is_logged_in } = client;

const bottomNavItems = [
{
Expand Down Expand Up @@ -75,24 +77,28 @@ const BottomNav = observer(({ children, className, onScroll }: BottomNavProps) =
<div className='bottom-nav-selection' onScroll={onScroll}>
{children}
</div>
<Navigation.Bottom className='bottom-nav-container' onChange={(_, index) => handleSelect(index)}>
{bottomNavItems.map((item, index) => (
<Navigation.BottomAction
key={index}
index={index}
activeIcon={item.icon}
icon={item.icon}
label={item.label}
selected={index === selectedIndex}
showLabel
className={clsx(
'bottom-nav-item',
index === selectedIndex && 'bottom-nav-item--active',
item.path === routes.trader_positions && 'bottom-nav-item--positions'
)}
/>
))}
</Navigation.Bottom>
{is_logged_in ? (
<Navigation.Bottom className='bottom-nav-container' onChange={(_, index) => handleSelect(index)}>
{bottomNavItems.map((item, index) => (
<Navigation.BottomAction
key={index}
index={index}
activeIcon={item.icon}
icon={item.icon}
label={item.label}
selected={index === selectedIndex}
showLabel
className={clsx(
'bottom-nav-item',
index === selectedIndex && 'bottom-nav-item--active',
item.path === routes.trader_positions && 'bottom-nav-item--positions'
)}
/>
))}
</Navigation.Bottom>
) : (
<></>
)}
</div>
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@
width: 100%;
z-index: 2; // chart has z-index: 1, it should not push purchase buttons down
bottom: var(--core-spacing-2800);

&__un-auth {
bottom: var(--core-spacing-50);
}
&.slide {
&-enter {
transform: translateY(100%);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const PurchaseButton = observer(() => {
const {
contract_replay: { is_market_closed },
portfolio: { all_positions, onClickSell },
client: { is_logged_in },
} = useStore();
const {
contract_type,
Expand Down Expand Up @@ -126,7 +127,11 @@ const PurchaseButton = observer(() => {
unmountOnExit
mountOnEnter
>
<div className='purchase-button__wrapper'>
<div
className={clsx('purchase-button__wrapper', {
'purchase-button__wrapper__un-auth': !is_logged_in,
})}
>
{contract_types.map((trade_type, index) => {
const info = proposal_info?.[trade_type] || {};
const is_single_button = contract_types.length === 1;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import './service-error-sheet.scss';
import ServiceErrorSheet from './service-error-sheet';

export default ServiceErrorSheet;
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react';
import { Localize } from '@deriv/translations';
import { Text } from '@deriv-com/quill-ui';

type ServiceErrorProps = {
is_insufficient_balance: boolean;
is_authorization_required: boolean;
services_error: { message?: string };
};

const ServiceErrorDescription: React.FC<ServiceErrorProps> = ({
is_insufficient_balance,
is_authorization_required,
services_error,
}) => {
if (is_insufficient_balance) {
return (
<>
<Text size='lg' bold className='service-error-sheet__body__heading'>
<Localize i18n_default_text='Insufficient balance' />
</Text>
<Text>{services_error?.message || <Localize i18n_default_text='An error occurred.' />}</Text>
</>
);
}

if (is_authorization_required) {
return (
<>
<Text size='lg' bold className='service-error-sheet__body__heading'>
<Localize i18n_default_text='Start trading with us' />
</Text>
<Text>
<Localize i18n_default_text='Log in or create a free account to place a trade.' />
</Text>
</>
);
}

return null;
};

export default ServiceErrorDescription;
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.service-error-sheet {
&__body {
margin: var(--core-size-1200);
&__heading {
margin-bottom: var(--core-size-1200);
}
}
&__footer {
margin: var(--core-size-400);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import React, { useEffect, useState } from 'react';
import { observer, useStore } from '@deriv/stores';
import { ActionSheet, Text } from '@deriv-com/quill-ui';
import { getLanguage, Localize } from '@deriv/translations';
import { redirectToLogin } from '@deriv/shared';
import { useHistory } from 'react-router';
import { useSignupTrigger } from 'AppV2/Hooks/useSignupTrigger';
import ServiceErrorDescription from './service-error-description';

const ServiceErrorSheet = observer(() => {
const [is_open, setIsOpen] = useState(false);
const { common } = useStore();
const { services_error, resetServicesError } = common;
const { handleSignup } = useSignupTrigger();
const history = useHistory();

const is_insufficient_balance =
services_error.code === 'InsufficientBalance' || services_error.code === 'InvalidContractProposal';
const is_authorization_required = services_error.code === 'AuthorizationRequired' && services_error.type === 'buy';

useEffect(() => {
if (is_insufficient_balance || is_authorization_required) {
setIsOpen(true);
}
}, [services_error]);

useEffect(() => {
if (!is_open && services_error.code) {
resetServicesError();
}
}, [resetServicesError, is_open]);

return (
<ActionSheet.Root
className='service-error-sheet'
isOpen={is_open}
onClose={() => {
if (services_error.code) {
setIsOpen(false);
}
}}
expandable={false}
position='left'
>
<ActionSheet.Portal showHandlebar shouldCloseOnDrag>
<div className='service-error-sheet__body'>
<ServiceErrorDescription
is_authorization_required={is_authorization_required}
is_insufficient_balance={is_insufficient_balance}
services_error={services_error}
/>
</div>
<ActionSheet.Footer
className='service-error-sheet__footer'
alignment='vertical'
{...(is_insufficient_balance
? {
primaryAction: {
content: <Localize i18n_default_text='Deposit now' />,
onAction: () => {
resetServicesError();
history.push('/cashier/deposit');
},
},
primaryButtonColor: 'coral',
}
: {})}
{...(is_authorization_required
? {
primaryAction: {
content: <Localize i18n_default_text='Create free account' />,
onAction: () => {
resetServicesError();
handleSignup();
},
},
primaryButtonColor: 'coral',
secondaryAction: {
content: <Localize i18n_default_text='Login' />,
onAction: () => {
resetServicesError();
redirectToLogin(false, getLanguage());
},
},
}
: {})}
/>
</ActionSheet.Portal>
</ActionSheet.Root>
);
});

export default ServiceErrorSheet;
Loading

0 comments on commit d761e7f

Please sign in to comment.