Skip to content

Commit

Permalink
Merge pull request #413 from Nuzhy-Deriv/nuzhy/intercom-integration
Browse files Browse the repository at this point in the history
nuzhy/CSIT-1704/intercom integration
  • Loading branch information
ameerul-deriv authored Dec 4, 2024
2 parents 2171aaf + a30fbc3 commit 19d9060
Show file tree
Hide file tree
Showing 18 changed files with 147 additions and 47 deletions.
6 changes: 6 additions & 0 deletions global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ declare global {
interface Window {
Analytics: unknown;
DD_RUM: object | undefined;
DerivInterCom: {
initialize: (config: ICConfig) => void;
};
FreshChat: {
initialize: (config: FreshChatConfig) => void;
};
GrowthbookFeatures: { [key: string]: boolean };
Intercom: ((action: IntercomAction) => void) | undefined;
LC_API: {
on_chat_ended: VoidFunction;
open_chat_window: VoidFunction;
Expand All @@ -23,6 +27,7 @@ declare global {
};
fcWidget: {
close: VoidFunction;
destroy: VoidFunction;
hide: VoidFunction;
isInitialized: () => boolean;
isLoaded: () => boolean;
Expand All @@ -31,6 +36,7 @@ declare global {
setConfig: (config: Record<string, Record<string, unknown>>) => void;
show: VoidFunction;
user: {
clear: () => Promise<void>;
setLocale(locale: string): void;
};
};
Expand Down
13 changes: 8 additions & 5 deletions src/components/AppFooter/Livechat.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useGrowthbookGetFeatureValue, useLiveChat } from '@/hooks/custom-hooks';
import useFreshChat from '@/hooks/custom-hooks/useFreshchat';
import Chat from '@/utils/chat';
import { useFreshchat, useGrowthbookGetFeatureValue, useIntercom, useLiveChat } from '@/hooks/custom-hooks';
import { Chat } from '@/utils';
import { LegacyLiveChatOutlineIcon } from '@deriv/quill-icons';
import { useTranslations } from '@deriv-com/translations';
import { Tooltip } from '@deriv-com/ui';
Expand All @@ -11,9 +10,13 @@ const Livechat = () => {
const [isFreshChatEnabled, isGBLoaded] = useGrowthbookGetFeatureValue({
featureFlag: 'enable_freshworks_live_chat_p2p',
});
const [isIntercomEnabled] = useGrowthbookGetFeatureValue({
featureFlag: 'enable_intercom_p2p',
});

const token = localStorage.getItem('authToken') || null;
const freshChat = useFreshChat(token);
const freshChat = useFreshchat(token, isFreshChatEnabled);
const icChat = useIntercom(token, isIntercomEnabled);

setInterval(() => {
/* This is for livechat last open state,
Expand All @@ -23,7 +26,7 @@ const Livechat = () => {
}
}, 10);

if (!isGBLoaded || (isFreshChatEnabled && !freshChat?.isReady)) {
if (!isGBLoaded || (isFreshChatEnabled && !freshChat?.isReady) || (isIntercomEnabled && !icChat?.isReady)) {
return null;
}

Expand Down
11 changes: 9 additions & 2 deletions src/components/AppHeader/AppHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useEffect } from 'react';
import { getOauthUrl } from '@/constants';
import { api, useGrowthbookGetFeatureValue, useOAuth } from '@/hooks';
import { getCurrentRoute } from '@/utils';
import { Chat, getCurrentRoute } from '@/utils';
import { StandaloneCircleUserRegularIcon } from '@deriv/quill-icons';
import { useAuthData } from '@deriv-com/api-hooks';
import { useTranslations } from '@deriv-com/translations';
Expand Down Expand Up @@ -55,7 +55,14 @@ const AppHeader = () => {
</Tooltip>
)}
<AccountSwitcher account={activeAccount!} />
<Button className='mr-6' onClick={oAuthLogout} size='md'>
<Button
className='mr-6'
onClick={() => {
Chat.clear();
oAuthLogout();
}}
size='md'
>
<Text size='sm' weight='bold'>
{localize('Logout')}
</Text>
Expand Down
16 changes: 13 additions & 3 deletions src/components/AppHeader/MobileMenu/MobileMenuConfig.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { ComponentProps, ReactNode } from 'react';
import { ACCOUNT_LIMITS, HELP_CENTRE, RESPONSIBLE } from '@/constants';
import { useOAuth } from '@/hooks/custom-hooks';
import { useGrowthbookGetFeatureValue, useOAuth } from '@/hooks/custom-hooks';
import useFreshChat from '@/hooks/custom-hooks/useFreshchat';
import Chat from '@/utils/chat';
import useIntercom from '@/hooks/custom-hooks/useIntercom';
import { Chat } from '@/utils';
import {
BrandDerivLogoCoralIcon,
IconTypes,
Expand Down Expand Up @@ -39,8 +40,16 @@ export const MobileMenuConfig = () => {
const { localize } = useTranslations();
const { oAuthLogout } = useOAuth();

const [isFreshChatEnabled] = useGrowthbookGetFeatureValue({
featureFlag: 'enable_freshworks_live_chat_p2p',
});
const [isIntercomEnabled] = useGrowthbookGetFeatureValue({
featureFlag: 'enable_intercom_p2p',
});

const token = localStorage.getItem('authToken') || null;
useFreshChat(token);
useFreshChat(token, isFreshChatEnabled);
useIntercom(token, isIntercomEnabled);

const menuConfig: TMenuConfig[] = [
[
Expand Down Expand Up @@ -129,6 +138,7 @@ export const MobileMenuConfig = () => {
label: localize('Log out'),
LeftComponent: LegacyLogout1pxIcon,
onClick: () => {
Chat.clear();
oAuthLogout();
},
removeBorderBottom: true,
Expand Down
2 changes: 1 addition & 1 deletion src/components/BlockedScenarios/BlockedScenarios.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Chat from '@/utils/chat';
import { Chat } from '@/utils';
import {
DerivLightIcCashierBlockedIcon,
DerivLightIcCashierUnderMaintenanceIcon,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ReactNode } from 'react';
import { ADVERT_TYPE, ERROR_CODES } from '@/constants';
import { AdRateError } from '@/pages/my-ads/components';
import Chat from '@/utils/chat';
import { Chat } from '@/utils';
import { Localize } from '@deriv-com/translations';
import { Button, Modal, Text, useDevice } from '@deriv-com/ui';
import './AdErrorTooltipModal.scss';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ComponentProps } from 'react';
import { TCurrency } from 'types';
import { ERROR_CODES } from '@/constants';
import Chat from '@/utils/chat';
import { Chat } from '@/utils';
import { Localize, useTranslations } from '@deriv-com/translations';
import { Button, Modal, Text, useDevice } from '@deriv-com/ui';
import './AdVisibilityErrorModal.scss';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { TCurrency } from 'types';
import Chat from '@/utils/chat';
import { Chat } from '@/utils';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import AdVisibilityErrorModal from '../AdVisibilityErrorModal';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react';
import { THooks } from 'types';
import { FullPageMobileWrapper } from '@/components/FullPageMobileWrapper';
import { api } from '@/hooks';
import Chat from '@/utils/chat';
import { Chat } from '@/utils';
import { Localize } from '@deriv-com/translations';
import { Button, Modal, Text, useDevice } from '@deriv-com/ui';
import { OrderDetailsComplainModalRadioGroup } from './OrderDetailsComplainModalRadioGroup';
Expand Down
2 changes: 2 additions & 0 deletions src/hooks/custom-hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ export { default as useDotButton } from './useDotButton';
export { default as useExtendedOrderDetails } from './useExtendedOrderDetails';
export { default as useFetchMore } from './useFetchMore';
export { default as useFloatingRate } from './useFloatingRate';
export { default as useFreshchat } from './useFreshchat';
export { default as useFullScreen } from './useFullScreen';
export { default as useGetBusinessHours } from './useGetBusinessHours.tsx';
export { default as useGrowthbookGetFeatureValue } from './useGrowthbookGetFeatureValue';
export { default as useHandleRouteChange } from './useHandleRouteChange';
export { default as useIntercom } from './useIntercom';
export { default as useIsAdvertiser } from './useIsAdvertiser';
export { default as useIsAdvertiserBarred } from './useIsAdvertiserBarred';
export { default as useIsP2PBlocked } from './useIsP2PBlocked';
Expand Down
49 changes: 25 additions & 24 deletions src/hooks/custom-hooks/useFreshchat.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,42 @@
import { useEffect, useState } from 'react';
import { useScript } from 'usehooks-ts';

const useFreshChat = (token: string | null) => {
const scriptStatus = useScript('https://static.deriv.com/scripts/freshchat/v1.0.2.js');
const useFreshChat = (token: string | null, flag: boolean) => {
const freshchatScript = 'https://static.deriv.com/scripts/freshchat/v1.0.2.js';
const scriptStatus = useScript(flag ? freshchatScript : null);
const [isReady, setIsReady] = useState(false);
const language = localStorage.getItem('i18n_language') || 'EN';

useEffect(() => {
const checkFcWidget = (intervalId: NodeJS.Timeout) => {
if (typeof window !== 'undefined') {
if (window.fcWidget?.isInitialized() == true && !isReady) {
if (!flag || scriptStatus !== 'ready' || !window.FreshChat || !window.fcSettings) {
return;
}

let checkInterval: NodeJS.Timeout;

const initializeFreshChat = () => {
window.FreshChat.initialize({
hideButton: true,
token,
});

checkInterval = setInterval(() => {
if (window?.fcWidget?.isInitialized()) {
setIsReady(true);
clearInterval(intervalId);
clearInterval(checkInterval);
}
}
}, 500);
};

const initFreshChat = async () => {
if (scriptStatus === 'ready' && window.FreshChat && window.fcSettings) {
window.FreshChat.initialize({
hideButton: true,
token,
});

const intervalId: NodeJS.Timeout = setInterval(() => checkFcWidget(intervalId), 500);
initializeFreshChat();

return () => clearInterval(intervalId);
return () => {
if (checkInterval) {
clearInterval(checkInterval);
}
};
}, [flag, scriptStatus, token]);

initFreshChat();
}, [isReady, language, scriptStatus, token]);

return {
isReady,
widget: window.fcWidget,
};
return { isReady };
};

export default useFreshChat;
38 changes: 38 additions & 0 deletions src/hooks/custom-hooks/useIntercom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { useEffect, useState } from 'react';
import { useScript } from 'usehooks-ts';

const useIntercom = (token: string | null, flag: boolean) => {
const intercomScript = 'https://static.deriv.com/scripts/intercom/v1.0.1.js';
const scriptStatus = useScript(flag ? intercomScript : null);
const [isReady, setIsReady] = useState(false);

useEffect(() => {
if (!flag || scriptStatus !== 'ready' || !window?.DerivInterCom) return;

let intervalId: NodeJS.Timeout;

const initIntercom = () => {
window.DerivInterCom.initialize({
hideLauncher: true,
token,
});

intervalId = setInterval(() => {
if (window?.Intercom && !isReady) {
setIsReady(true);
clearInterval(intervalId);
}
}, 500);
};

initIntercom();

return () => {
if (intervalId) clearInterval(intervalId);
};
}, [flag, isReady, scriptStatus, token]);

return { isReady };
};

export default useIntercom;
2 changes: 1 addition & 1 deletion src/pages/guide/screens/Awareness/Awareness.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Chat from '@/utils/chat';
import { Chat } from '@/utils';
import { DerivDarkScamAdvancePaymentIcon, DerivDarkScamPotIcon, DerivDarkScamSmsIcon } from '@deriv/quill-icons';
import { Localize } from '@deriv-com/translations';
import { Text, useDevice } from '@deriv-com/ui';
Expand Down
2 changes: 1 addition & 1 deletion src/pages/guide/screens/FAQs/FAQs.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useRef } from 'react';
import Chat from '@/utils/chat';
import { Chat } from '@/utils';
import { Localize, useTranslations } from '@deriv-com/translations';
import { Accordion, Text, useDevice } from '@deriv-com/ui';
import { URLConstants } from '@deriv-com/utils';
Expand Down
2 changes: 1 addition & 1 deletion src/pages/guide/screens/GettingStarted/GettingStarted.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Chat from '@/utils/chat';
import { Chat } from '@/utils/chat';
import {
DerivDarkFindAdIcon,
DerivDarkPayUserIcon,
Expand Down
2 changes: 1 addition & 1 deletion src/utils/__tests__/chat.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Chat from '../chat';
import { Chat } from '../chat';
import getFeatureFlag from '../get-featureflag';

jest.mock('../get-featureflag');
Expand Down
40 changes: 36 additions & 4 deletions src/utils/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,46 @@

import getFeatureFlag from './get-featureflag';

const Chat = {
export const Chat = {
clear: async () => {
const { isFreshChat, isIntercom } = await Chat.getFlags();
if (isFreshChat) {
window.fcWidget?.user
.clear()
.then(() => window.fcWidget.destroy())
.catch((error: Error) => {
// eslint-disable-next-line no-console
console.error('Failed to clear FreshChat:', error);
});
} else if (isIntercom && window.Intercom) {
window.Intercom('shutdown');
}
},

close: async () => {
(await Chat.isFreshChat()) ? window.fcWidget?.close() : window.LiveChatWidget?.call('hide');
},

getFlags: async () => {
try {
const [isFreshChat, isIntercom] = await Promise.all([Chat.isFreshChat(), Chat.isIntercom()]);
return { isFreshChat, isIntercom };
} catch (error) {
return { isFreshChat: false, isIntercom: false };
}
},

isFreshChat: async () => getFeatureFlag('enable_freshworks_live_chat_p2p'),
isIntercom: async () => getFeatureFlag('enable_intercom_p2p'),

open: async () => {
(await Chat.isFreshChat()) ? window.fcWidget?.open() : window.LiveChatWidget?.call('maximize');
const { isFreshChat, isIntercom } = await Chat.getFlags();
if (isFreshChat) {
window.fcWidget?.open();
} else if (isIntercom && window.Intercom) {
window.Intercom('show');
} else {
window.LiveChatWidget?.call('maximize');
}
},
};

export default Chat;
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from './account';
export * from './ad-utils';
export * from './adverts';
export * from './business-hours';
export * from './chat';
export * from './date';
export * from './error-messages';
export * from './file-dropzone';
Expand Down

0 comments on commit 19d9060

Please sign in to comment.