Skip to content

Commit

Permalink
Merge branch 'binary-com:master' into WALL-3284-Blank-Page-with-Inval…
Browse files Browse the repository at this point in the history
…id-token-when-accessing-wallets-in-desktop-(for-wallets-created-using-a-different-device/browser)
  • Loading branch information
heorhi-deriv committed May 7, 2024
2 parents 16ed790 + b9c28e0 commit 04edcc2
Show file tree
Hide file tree
Showing 218 changed files with 4,574 additions and 3,670 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

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

Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ import React from 'react';
import { Button, Text } from '@deriv/components';
import { localize } from '@deriv/translations';
import IdvDocumentRejected from '../../../../Assets/ic-idv-document-rejected.svg';
import { POIContext } from '@deriv/shared';
import { submission_status_code } from '../../../../Sections/Verification/ProofOfIdentity/proof-of-identity-utils';

type TIdvLimited = {
handleRequireSubmission: () => void;
};

const IdvLimited = ({ handleRequireSubmission }: TIdvLimited) => {
const { setSubmissionStatus } = React.useContext(POIContext);

return (
<div className='proof-of-identity__container'>
<IdvDocumentRejected className='icon' />
Expand All @@ -21,7 +25,10 @@ const IdvLimited = ({ handleRequireSubmission }: TIdvLimited) => {
<Button
type='button'
className='account-management__continue'
onClick={handleRequireSubmission}
onClick={() => {
handleRequireSubmission();
setSubmissionStatus(submission_status_code.selecting);
}}
large
text={localize('Upload identity document')}
primary
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
SelectNative,
Text,
} from '@deriv/components';
import { routes, platforms, WS, EMPLOYMENT_VALUES, shouldHideOccupationField } from '@deriv/shared';
import { routes, platforms, WS, shouldHideOccupationField } from '@deriv/shared';
import { observer, useStore } from '@deriv/stores';
import { localize, Localize } from '@deriv/translations';
import LeaveConfirm from 'Components/leave-confirm';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ jest.mock('@deriv/shared', () => ({
describe('Passkeys', () => {
let mock_store: ReturnType<typeof mockStore>, modal_root_el: HTMLElement;
const create_passkey = 'Create passkey';
const error_message = 'We’re experiencing a temporary issue in processing your request. Please try again later.';
const error_title = 'Unable to process your request';

const tracking_event = 'ce_passkey_account_settings_form';
const getAnalyticsParams = (
Expand Down Expand Up @@ -297,9 +299,8 @@ describe('Passkeys', () => {
});

it('renders passkeys registration error modal and triggers closing', async () => {
const mock_error = 'registration test error';
(useRegisterPasskey as jest.Mock).mockReturnValue({
passkey_registration_error: { message: mock_error },
passkey_registration_error: { message: 'error' },
clearPasskeyRegistrationError: mockClearPasskeyRegistrationError,
});

Expand All @@ -309,23 +310,23 @@ describe('Passkeys', () => {
</RenderWrapper>
);

const try_again_button = screen.getByRole('button', { name: /try again/i });
expect(screen.getByText(mock_error)).toBeInTheDocument();
userEvent.click(try_again_button);
const ok_button = screen.getByRole('button', { name: /ok/i });
expect(screen.getByText(error_message)).toBeInTheDocument();
expect(screen.getByText(error_title)).toBeInTheDocument();
userEvent.click(ok_button);
await waitFor(() => {
expect(mockClearPasskeyRegistrationError).toBeCalledTimes(1);
expect(mockHistoryPush).toHaveBeenCalledWith(routes.traders_hub);
});
});

it('renders passkeys list error modal and triggers closing', async () => {
const mock_error = 'list test error';

(useRegisterPasskey as jest.Mock).mockReturnValue({
passkey_registration_error: null,
});

(useGetPasskeysList as jest.Mock).mockReturnValue({
passkeys_list_error: { message: mock_error },
passkeys_list_error: { message: 'error' },
reloadPasskeysList: mockReloadPasskeysList,
});

Expand All @@ -335,11 +336,10 @@ describe('Passkeys', () => {
</RenderWrapper>
);

const try_again_button = screen.getByRole('button', { name: /try again/i });
expect(screen.getByText(mock_error)).toBeInTheDocument();
userEvent.click(try_again_button);
await waitFor(() => {
expect(mockReloadPasskeysList).toBeCalledTimes(1);
});
const ok_button = screen.getByRole('button', { name: /ok/i });
expect(screen.getByText(error_message)).toBeInTheDocument();
expect(screen.getByText(error_title)).toBeInTheDocument();
userEvent.click(ok_button);
expect(mockHistoryPush).toHaveBeenCalledWith(routes.traders_hub);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,17 @@ export const getModalContent = ({ error, is_passkey_registration_started }: TGet
const error_message = isNotSupportedError(error as TServerError) ? (
<Localize i18n_default_text="This device doesn't support passkeys." />
) : (
(error as TServerError)?.message
);
const button_text = (
<Localize i18n_default_text={isNotSupportedError(error as TServerError) ? 'OK' : 'Try again'} />
<Localize i18n_default_text='We’re experiencing a temporary issue in processing your request. Please try again later.' />
);
const button_text = <Localize i18n_default_text='OK' />;

const error_message_header = (
<Text size='xs' weight='bold'>
<Localize i18n_default_text='Passkey setup failed' />
{isNotSupportedError(error as TServerError) ? (
<Localize i18n_default_text='Passkey setup failed' />
) : (
<Localize i18n_default_text='Unable to process your request' />
)}
</Text>
);

Expand Down
7 changes: 1 addition & 6 deletions packages/account/src/Sections/Security/Passkeys/passkeys.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,19 +85,14 @@ const Passkeys = observer(() => {
};

const onCloseError = () => {
if (passkeys_list_error) {
reloadPasskeysList();
}
if (passkey_registration_error) {
clearPasskeyRegistrationError();
}
};
const onModalButtonClick = () => {
if (error) {
onCloseModal(onCloseError);
if ((error as TServerError).name === NOT_SUPPORTED_ERROR_NAME) {
history.push(routes.traders_hub);
}
history.push(routes.traders_hub);
} else {
passkeysMenuActionEventTrack('create_passkey_reminder_passed');
createPasskey();
Expand Down
46 changes: 46 additions & 0 deletions packages/account/src/hooks/__test__/useKycAuthStatus.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from 'react';
import { renderHook } from '@testing-library/react-hooks';
import { useKycAuthStatus } from '../useKycAuthStatus';
import { useQuery } from '@deriv/api';
import { StoreProvider, mockStore } from '@deriv/stores';

jest.mock('@deriv/api', () => ({
...jest.requireActual('@deriv/api'),
useQuery: jest.fn(),
}));

describe('useKycAuthStatus', () => {
afterEach(() => {
jest.clearAllMocks();
});

it('should return kyc_auth_status', () => {
const mock_response = {
data: {
kyc_auth_status: {
identity: {
status: 'none',
},
document: {
status: 'none',
},
},
},
};

const mock = mockStore({
client: {
is_authorize: true,
},
});

(useQuery as jest.Mock).mockReturnValue(mock_response);

const wrapper = ({ children }: { children: JSX.Element }) => (
<StoreProvider store={mock}>{children}</StoreProvider>
);

const { result } = renderHook(() => useKycAuthStatus({ country: 'in' }), { wrapper });
expect(result.current.kyc_auth_status).toEqual(mock_response.data.kyc_auth_status);
});
});
1 change: 1 addition & 0 deletions packages/account/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { useKycAuthStatus } from './useKycAuthStatus';
21 changes: 21 additions & 0 deletions packages/account/src/hooks/useKycAuthStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useQuery } from '@deriv/api';
import { TSocketRequestPayload } from '@deriv/api/types';
import { useStore } from '@deriv/stores';

type TKycAuthStatusPayload = TSocketRequestPayload<'kyc_auth_status'>['payload'];

/** Custom hook that returns Proof of Identity (POI) and Proof of Address (POA) authentication status details. */
export const useKycAuthStatus = (payload?: TKycAuthStatusPayload) => {
const { client } = useStore();

const { is_authorize } = client;
const { data, ...kyc_auth_status_rest } = useQuery('kyc_auth_status', {
payload,
options: { enabled: is_authorize },
});
return {
/** The KYC auth status */
kyc_auth_status: data?.kyc_auth_status,
...kyc_auth_status_rest,
};
};
2 changes: 1 addition & 1 deletion packages/api/src/remote_config.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"cs_chat_livechat":true,"cs_chat_whatsapp":true,"marketing_growthbook":true,"tracking_GTM":true,"tracking_datadog":true,"tracking_hotjar":true,"tracking_rudderstack": true}
{"cs_chat_livechat":true,"cs_chat_whatsapp":true,"marketing_growthbook":true,"passkeys":true,"tracking_GTM":true,"tracking_datadog":true,"tracking_hotjar":true,"tracking_rudderstack":true}
4 changes: 4 additions & 0 deletions packages/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,10 @@ type KycAuthStatus = {
* Reason(s) for the rejected POI attempt.
*/
rejected_reasons?: string[];
/**
* Indicate if the verification report was returned by the provider (IDV only).
*/
report_available?: 0 | 1;
};
/**
* Service used for the current POI status.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import { Localize, localize } from '@deriv/translations';
import './add-options-account.scss';
import { useStore, observer } from '@deriv/stores';
import { isMobile, ContentFlag } from '@deriv/shared';
import { Analytics } from '@deriv-com/analytics';

const AddOptions = observer(() => {
const { client, traders_hub, ui } = useStore();
const { is_real, content_flag } = traders_hub;
const { is_real, content_flag, selected_account_type } = traders_hub;
const { setShouldShowCooldownModal, openRealAccountSignup } = ui;
const { real_account_creation_unlock_date } = client;

Expand All @@ -29,6 +30,12 @@ const AddOptions = observer(() => {
type='submit'
has_effect
onClick={() => {
Analytics.trackEvent('ce_tradershub_dashboard_form', {
action: 'account_get',
form_name: 'traders_hub_default',
account_mode: selected_account_type,
account_name: 'cfd_banner',
});
if (is_real && eu_user) {
if (real_account_creation_unlock_date) {
setShouldShowCooldownModal(true);
Expand Down
14 changes: 12 additions & 2 deletions packages/appstore/src/components/app-content.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
import React from 'react';
import React, { useEffect } from 'react';
import { routes } from '@deriv/shared';
import { observer, useStore } from '@deriv/stores';
import { Analytics } from '@deriv-com/analytics';
import Routes from 'Components/routes/routes';
import classNames from 'classnames';
import './app.scss';

const AppContent: React.FC = observer(() => {
const { ui } = useStore();
const { ui, traders_hub } = useStore();
const { is_dark_mode_on } = ui;
const { selected_account_type } = traders_hub;

useEffect(() => {
Analytics.trackEvent('ce_tradershub_dashboard_form', {
action: 'open',
form_name: 'traders_hub_default',
account_mode: selected_account_type,
});
}, []);

return (
<main
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import React from 'react';
import { useStore } from '@deriv/stores';
import { Analytics } from '@deriv-com/analytics';
import { SessionStore } from '@deriv/shared';
import { getAllowedLanguages, Localize } from '@deriv/translations';
import { LabelPairedCircleChevronDownXlBoldIcon, LabelPairedXmarkLgBoldIcon } from '@deriv/quill-icons';
import { TEbooksUrl } from 'Components/banners/book-banner/book-banner';

type TBookBannerTemplate = {
e_book_show_way: string;
e_books_url: TEbooksUrl;
e_book_from_landing: keyof TEbooksUrl;
lang: ReturnType<typeof getAllowedLanguages>;
};

const BookBannerTemplate = ({ e_book_show_way, e_books_url, e_book_from_landing, lang }: TBookBannerTemplate) => {
const [is_banner_shows, setIsBannerShows] = React.useState(true);
const { traders_hub, ui } = useStore();
const { selected_account_type } = traders_hub;
const analytics_data: Parameters<typeof Analytics.trackEvent>[1] = {
banner_name: e_book_from_landing,
account_mode: selected_account_type,
};

React.useEffect(() => {
Analytics.trackEvent('ce_tradershub_banner', {
action: 'open',
...analytics_data,
});
}, []);

return (
<React.Fragment>
{is_banner_shows ? (
<div id='e-book-banner' className='book-banner-template'>
<div className='book-banner-template__left'>
<LabelPairedCircleChevronDownXlBoldIcon width='24' height='24' fill='#00822A' />
{e_book_show_way === 'banner-with-link' ? (
<div className='book-banner-template__content'>
<label>
<Localize i18n_default_text='We’ve sent your e-book to your email. You can also download it here.' />
</label>
<a
href={e_books_url[e_book_from_landing][lang] || e_books_url[e_book_from_landing].EN}
target='_blank'
rel='noopener noreferrer'
onClick={() => {
Analytics.trackEvent('ce_tradershub_banner', {
action: 'click download',
...analytics_data,
});
}}
>
<Localize i18n_default_text='Download e-book' />
</a>
</div>
) : (
<div className='book-banner-template__content'>
<label>
<Localize i18n_default_text='We’ve sent your e-book. Check your email to download it.' />
</label>
</div>
)}
</div>
<LabelPairedXmarkLgBoldIcon
className='book-banner-template__cancel'
width='24'
height='24'
fill='var(--text-prominent)'
onClick={() => {
Analytics.trackEvent('ce_tradershub_banner', {
action: 'close',
...analytics_data,
});
SessionStore.remove('show_book');
setIsBannerShows(false);
}}
/>
</div>
) : null}
</React.Fragment>
);
};

export default BookBannerTemplate;
Loading

0 comments on commit 04edcc2

Please sign in to comment.