Skip to content

Commit 45e99b3

Browse files
authored
Merge pull request #201 from farrah-deriv/FEQ-2456/livechat-integration
farrah/FEQ-2456/LiveChat Integration
2 parents b554a4a + f73f027 commit 45e99b3

File tree

18 files changed

+178
-32
lines changed

18 files changed

+178
-32
lines changed

global.d.ts

+6
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
declare global {
22
interface Window {
33
LC_API: {
4+
on_chat_ended: VoidFunction;
45
open_chat_window: VoidFunction;
56
};
7+
LiveChatWidget: {
8+
call: (key: string, value?: object | string) => void;
9+
init: () => void;
10+
on: (key: string, callback: VoidFunction) => void;
11+
};
612
dataLayer: {
713
push: (event: { [key: string]: boolean | number | string; event: string }) => void;
814
};

index.html

+41
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,46 @@
4242
<!-- End Google Tag Manager (noscript) -->
4343
<div id="root"></div>
4444
<script type="module" src="/src/main.tsx"></script>
45+
<!-- LiveChat script -->
46+
<script type="text/javascript">
47+
window.__lc = window.__lc || {};
48+
window.__lc.license = 12049137;
49+
window.__lc.asyncInit = true;
50+
(function (n, t, c) {
51+
function i(n) {
52+
return e._h ? e._h.apply(null, n) : e._q.push(n);
53+
}
54+
var e = {
55+
_q: [],
56+
_h: null,
57+
_v: '2.0',
58+
on: function () {
59+
i(['on', c.call(arguments)]);
60+
},
61+
once: function () {
62+
i(['once', c.call(arguments)]);
63+
},
64+
off: function () {
65+
i(['off', c.call(arguments)]);
66+
},
67+
get: function () {
68+
if (!e._h) throw new Error('[LiveChatWidget] You can’t use getters before load.');
69+
return i(['get', c.call(arguments)]);
70+
},
71+
call: function () {
72+
i(['call', c.call(arguments)]);
73+
},
74+
init: function () {
75+
var n = t.createElement('script');
76+
(n.async = !0),
77+
(n.type = 'text/javascript'),
78+
(n.src = 'https://cdn.livechatinc.com/tracking.js'),
79+
t.head.appendChild(n);
80+
},
81+
};
82+
!n.__lc.asyncInit && e.init(), (n.LiveChatWidget = n.LiveChatWidget || e);
83+
})(window, document, [].slice);
84+
</script>
85+
<!-- End LiveChat script -->
4586
</body>
4687
</html>

src/components/AppFooter/Livechat.tsx

+10-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
1+
import { useLiveChat } from '@/hooks/custom-hooks';
12
import { LegacyLiveChatOutlineIcon } from '@deriv/quill-icons';
23
import { useTranslations } from '@deriv-com/translations';
34
import { TooltipMenuIcon } from '../TooltipMenuIcon';
45

56
const Livechat = () => {
7+
const { LiveChatWidget } = useLiveChat();
68
const { localize } = useTranslations();
7-
// TODO add the logic of this
8-
// TODO add the test cases for this after adding the logics
99

1010
return (
11-
<TooltipMenuIcon as='button' className='app-footer__icon' tooltipContent={localize('Live chat')}>
11+
<TooltipMenuIcon
12+
as='button'
13+
className='app-footer__icon'
14+
onClick={() => {
15+
LiveChatWidget.call('maximize');
16+
}}
17+
tooltipContent={localize('Live chat')}
18+
>
1219
<LegacyLiveChatOutlineIcon iconSize='xs' />
1320
</TooltipMenuIcon>
1421
);

src/components/AppFooter/__tests__/AppFooter.spec.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import { render, screen } from '@testing-library/react';
77
import userEvent from '@testing-library/user-event';
88
import AppFooter from '../AppFooter';
99

10+
jest.mock('@deriv-com/api-hooks', () => ({
11+
useAuthData: jest.fn(() => ({ activeLoginid: null })),
12+
}));
1013
jest.mock('@deriv-com/translations');
1114
jest.mock('@/hooks');
1215

src/components/AppHeader/MobileMenu/MobileMenuConfig.tsx

+11-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { ComponentProps, ReactNode } from 'react';
22
import { ACCOUNT_LIMITS, HELP_CENTRE, RESPONSIBLE } from '@/constants';
3+
import { useLiveChat } from '@/hooks/custom-hooks';
34
import {
45
BrandDerivLogoCoralIcon,
56
IconTypes,
@@ -8,6 +9,7 @@ import {
89
LegacyChartsIcon,
910
LegacyHelpCentreIcon,
1011
LegacyHomeOldIcon,
12+
LegacyLiveChatOutlineIcon,
1113
LegacyLogout1pxIcon,
1214
LegacyProfileSmIcon,
1315
LegacyResponsibleTradingIcon,
@@ -34,6 +36,7 @@ type TMenuConfig = {
3436
export const MobileMenuConfig = () => {
3537
const { localize } = useTranslations();
3638
const { logout } = useAuthData();
39+
const { LiveChatWidget } = useLiveChat();
3740

3841
const menuConfig: TMenuConfig[] = [
3942
[
@@ -101,12 +104,14 @@ export const MobileMenuConfig = () => {
101104
LeftComponent: LegacyWhatsappIcon,
102105
target: '_blank',
103106
},
104-
// TODO add livechat logic
105-
// {
106-
// as: 'button',
107-
// label: localize('Live chat'),
108-
// LeftComponent: LegacyLiveChatOutlineIcon,
109-
// },
107+
{
108+
as: 'button',
109+
label: localize('Live chat'),
110+
LeftComponent: LegacyLiveChatOutlineIcon,
111+
onClick: () => {
112+
LiveChatWidget.call('maximize');
113+
},
114+
},
110115
],
111116
[
112117
{

src/components/BlockedScenarios/BlockedScenarios.tsx

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ReactComponent as P2pUnavailable } from '@/assets/p2p-unavailable.svg';
2+
import { useLiveChat } from '@/hooks';
23
import {
34
DerivLightIcCashierBlockedIcon,
45
DerivLightIcCashierLockedIcon,
@@ -19,6 +20,7 @@ type TBlockedScenariosObject = {
1920

2021
const BlockedScenarios = ({ type }: { type: string }) => {
2122
const { isMobile } = useDevice();
23+
const { LiveChatWidget } = useLiveChat();
2224

2325
const buttonTextSize = isMobile ? 'md' : 'sm';
2426
const iconSize = isMobile ? 96 : 128;
@@ -28,8 +30,9 @@ const BlockedScenarios = ({ type }: { type: string }) => {
2830
window.open(URLConstants.derivAppProduction, '_blank')?.focus();
2931
};
3032

31-
// TODO: implement this function to open live chat
32-
const openLiveChat = () => {};
33+
const openLiveChat = () => {
34+
LiveChatWidget.call('maximize');
35+
};
3336

3437
const blockedScenarios: TBlockedScenariosObject = {
3538
cashierLocked: {

src/components/BlockedScenarios/__tests__/BlockedScenarios.spec.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import { render, screen } from '@testing-library/react';
22
import userEvent from '@testing-library/user-event';
33
import BlockedScenarios from '../BlockedScenarios';
44

5+
jest.mock('@deriv-com/api-hooks', () => ({
6+
useAuthData: jest.fn(() => ({ activeLoginid: null })),
7+
}));
58
jest.mock('@deriv-com/ui', () => ({
69
...jest.requireActual('@deriv-com/ui'),
710
useDevice: () => ({ isDesktop: true }),

src/components/Modals/AdErrorTooltipModal/AdErrorTooltipModal.tsx

+15-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { ReactNode } from 'react';
22
import { ADVERT_TYPE, ERROR_CODES } from '@/constants';
3+
import { useLiveChat } from '@/hooks/custom-hooks';
34
import { AdRateError } from '@/pages/my-ads/components';
45
import { Localize } from '@deriv-com/translations';
56
import { Button, Modal, Text, useDevice } from '@deriv-com/ui';
@@ -24,7 +25,8 @@ const getAdErrorMessage = (
2425
balanceAvailable: number,
2526
advertType: string,
2627
dailyBuyLimit: string,
27-
dailySellLimit: string
28+
dailySellLimit: string,
29+
onLiveChatClick: () => void
2830
): string => {
2931
const errorMessages: { [key: string]: ReactNode | string } = {
3032
[ERROR_CODES.ADVERT_INACTIVE]: <AdRateError />,
@@ -64,7 +66,10 @@ const getAdErrorMessage = (
6466
/>
6567
),
6668
[ERROR_CODES.ADVERTISER_TEMP_BAN]: (
67-
<Localize i18n_default_text='You’re not allowed to use Deriv P2P to advertise. Please contact us via live chat for more information.' />
69+
<Localize
70+
components={[<a key={0} onClick={onLiveChatClick} />]}
71+
i18n_default_text='You’re not allowed to use Deriv P2P to advertise. Please contact us via <0>live chat</0> for more information.'
72+
/>
6873
),
6974
};
7075

@@ -82,6 +87,10 @@ const AdErrorTooltipModal = ({
8287
remainingAmount,
8388
visibilityStatus = [],
8489
}: TAdErrorTooltipModal) => {
90+
const { LiveChatWidget } = useLiveChat();
91+
const onLiveChatClick = () => {
92+
LiveChatWidget.call('maximize');
93+
};
8594
const { isMobile } = useDevice();
8695
const textSize = isMobile ? 'md' : 'sm';
8796
const getMultipleErrorMessages = (errorStatuses: string[]) =>
@@ -95,7 +104,8 @@ const AdErrorTooltipModal = ({
95104
balanceAvailable,
96105
advertType,
97106
dailyBuyLimit,
98-
dailySellLimit
107+
dailySellLimit,
108+
onLiveChatClick
99109
)}
100110
</div>
101111
));
@@ -118,7 +128,8 @@ const AdErrorTooltipModal = ({
118128
balanceAvailable,
119129
advertType,
120130
dailyBuyLimit,
121-
dailySellLimit
131+
dailySellLimit,
132+
onLiveChatClick
122133
)
123134
) : (
124135
<>

src/components/Modals/AdErrorTooltipModal/__tests__/AdErrorTooltipModal.spec.tsx

+6-10
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ const mockProps = {
1313
visibilityStatus: [],
1414
};
1515

16+
jest.mock('@deriv-com/api-hooks', () => ({
17+
useAuthData: jest.fn(() => ({ activeLoginid: null })),
18+
}));
19+
1620
jest.mock('@deriv-com/ui', () => ({
1721
...jest.requireActual('@deriv-com/ui'),
1822
useDevice: () => ({ isMobile: false }),
@@ -106,11 +110,7 @@ describe('AdErrorTooltipModal', () => {
106110
visibilityStatus: ['advertiser_temp_ban'],
107111
};
108112
render(<AdErrorTooltipModal {...newProps} />);
109-
expect(
110-
screen.getByText(
111-
'You’re not allowed to use Deriv P2P to advertise. Please contact us via live chat for more information.'
112-
)
113-
).toBeInTheDocument();
113+
expect(screen.getByText(/Youre not allowed to use Deriv P2P to advertise./)).toBeInTheDocument();
114114
});
115115
it('should display the corresponding reason when visibilityStatus is advert_inactive', () => {
116116
const newProps = {
@@ -126,10 +126,6 @@ describe('AdErrorTooltipModal', () => {
126126
visibilityStatus: ['advertiser_temp_ban', 'advertiser_daily_limit'],
127127
};
128128
render(<AdErrorTooltipModal {...newProps} />);
129-
expect(
130-
screen.getByText(
131-
'1. You’re not allowed to use Deriv P2P to advertise. Please contact us via live chat for more information.'
132-
)
133-
).toBeInTheDocument();
129+
expect(screen.getByText(/1. Youre not allowed to use Deriv P2P to advertise./)).toBeInTheDocument();
134130
});
135131
});

src/hooks/custom-hooks/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export { default as useHandleRouteChange } from './useHandleRouteChange';
99
export { default as useIsAdvertiser } from './useIsAdvertiser';
1010
export { default as useIsAdvertiserBarred } from './useIsAdvertiserBarred';
1111
export { default as useIsP2PBlocked } from './useIsP2PBlocked';
12+
export { default as useLiveChat } from './useLiveChat';
1213
export { default as useModalManager } from './useModalManager';
1314
export { default as useNavigatorOnline } from './useNavigatorOnline';
1415
export { default as useNetworkStatus } from './useNetworkStatus';

src/hooks/custom-hooks/useLiveChat.ts

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import Cookies from 'js-cookie';
2+
import { useAuthData } from '@deriv-com/api-hooks';
3+
4+
/**
5+
* Custom hook to initialize LiveChat.
6+
* @returns {object} LiveChatWidget - LiveChat widget.
7+
*/
8+
const useLiveChat = () => {
9+
const { activeLoginid } = useAuthData();
10+
11+
const initLiveChat = () => {
12+
window.LiveChatWidget.init();
13+
window.LiveChatWidget.on('ready', () => {
14+
const clientInformation = JSON.parse(Cookies.get('client_information') ?? '{}');
15+
const {
16+
currency = ' ',
17+
email = ' ',
18+
firstName = '',
19+
landingCompanyShortcode = ' ',
20+
lastName = '',
21+
loginid = ' ',
22+
residence = ' ',
23+
} = clientInformation;
24+
const sessionVariables = {
25+
currency,
26+
email,
27+
is_logged_in: !!activeLoginid,
28+
landingCompanyShortcode,
29+
loginid,
30+
residence,
31+
};
32+
33+
window.LiveChatWidget.call('set_session_variables', sessionVariables);
34+
window.LiveChatWidget.call('set_customer_email', email);
35+
window.LiveChatWidget.call('set_customer_name', `${firstName} ${lastName}`);
36+
37+
window.LC_API.on_chat_ended = () => {
38+
window.LiveChatWidget?.call('set_customer_email', email);
39+
window.LiveChatWidget?.call('set_customer_name', `${firstName} ${lastName}`);
40+
};
41+
});
42+
};
43+
44+
return {
45+
init: initLiveChat,
46+
LiveChatWidget: window.LiveChatWidget,
47+
};
48+
};
49+
50+
export default useLiveChat;

src/pages/guide/screens/Awareness/Awareness.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22
import { ReactComponent as ScamAdvancePaymentIcon } from '@/assets/scam-advance-payment.svg';
33
import { ReactComponent as ScamPotIcon } from '@/assets/scam-pot.svg';
44
import { ReactComponent as ScamSmsIcon } from '@/assets/scam-sms.svg';
5+
import { useLiveChat } from '@/hooks/custom-hooks';
56
import { Localize } from '@deriv-com/translations';
67
import { Text, useDevice } from '@deriv-com/ui';
78
import { Carousel } from '../../components';
89

910
const Awareness = () => {
11+
const { LiveChatWidget } = useLiveChat();
1012
const { isDesktop } = useDevice();
1113

1214
return (
@@ -56,8 +58,7 @@ const Awareness = () => {
5658
className='guide__content-section--link'
5759
key={0}
5860
onClick={() => {
59-
// TODO: Uncomment once live chat is integrated.
60-
// window.LC_API.open_chat_window();
61+
LiveChatWidget.call('maximize');
6162
}}
6263
/>,
6364
]}

src/pages/guide/screens/Awareness/__tests__/Awareness.spec.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { render, screen } from '@testing-library/react';
22
import Awareness from '../Awareness';
33

4+
jest.mock('@deriv-com/api-hooks', () => ({
5+
useAuthData: jest.fn(() => ({ activeLoginid: null })),
6+
}));
7+
48
jest.mock('@deriv-com/ui', () => ({
59
...jest.requireActual('@deriv-com/ui'),
610
useDevice: jest.fn(() => ({ isMobile: false })),

src/pages/guide/screens/FAQs/FAQs.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
import { useLiveChat } from '@/hooks/custom-hooks';
12
import { Localize, useTranslations } from '@deriv-com/translations';
23
import { Accordion, Text, useDevice } from '@deriv-com/ui';
34
import { URLConstants } from '@deriv-com/utils';
45

56
const FAQs = () => {
7+
const { LiveChatWidget } = useLiveChat();
68
const { isDesktop } = useDevice();
79
const { localize } = useTranslations();
810

@@ -99,8 +101,7 @@ const FAQs = () => {
99101
className='guide__content-section--link'
100102
key={0}
101103
onClick={() => {
102-
// TODO: Uncomment once live chat is integrated.
103-
// window.LC_API.open_chat_window();
104+
LiveChatWidget.call('maximize');
104105
}}
105106
/>,
106107
]}

0 commit comments

Comments
 (0)