From bb29b5ea757234fa560f7d15c76dc56ab7c7d9f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sigur=C3=B0ur=20Gunnar=20Nj=C3=A1lsson?= Date: Fri, 30 Apr 2021 12:11:26 +0000 Subject: [PATCH 01/14] Adding optional prop for lazy load the intercom script Fixing timeout prop --- src/initialize.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/initialize.ts b/src/initialize.ts index dd39b883..8b321bc9 100644 --- a/src/initialize.ts +++ b/src/initialize.ts @@ -6,7 +6,7 @@ * * @see {@link https://developers.intercom.com/installing-intercom/docs/basic-javascript} */ -const initialize = (appId: string) => { +const initialize = (appId: string, timeout = 0) => { var w = window; var ic = w.Intercom; if (typeof ic === 'function') { @@ -23,12 +23,14 @@ const initialize = (appId: string) => { }; w.Intercom = i; var l = function() { - var s = d.createElement('script'); - s.type = 'text/javascript'; - s.async = true; - s.src = 'https://widget.intercom.io/widget/' + appId; - var x = d.getElementsByTagName('script')[0]; - x.parentNode.insertBefore(s, x); + setTimeout(function () { + var s = d.createElement('script'); + s.type = 'text/javascript'; + s.async = true; + s.src = 'https://widget.intercom.io/widget/' + appId; + var x = d.getElementsByTagName('script')[0]; + x.parentNode.insertBefore(s, x); + }, timeout) }; if (document.readyState === 'complete') { l(); From 97186f9e161ee78daf96873e59c517c8b86078e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sigur=C3=B0ur=20Gunnar=20Nj=C3=A1lsson?= Date: Fri, 30 Apr 2021 12:19:05 +0000 Subject: [PATCH 02/14] Adding types and passing the delay to the initialize function Fix spelling --- src/provider.tsx | 3 ++- src/types.ts | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/provider.tsx b/src/provider.tsx index 221f8105..35c2227f 100644 --- a/src/provider.tsx +++ b/src/provider.tsx @@ -22,6 +22,7 @@ export const IntercomProvider: React.FC = ({ onUnreadCountChange, shouldInitialize = !isSSR, apiBase, + initializeDelayInMs, ...rest }) => { const isBooted = React.useRef(autoBoot); @@ -36,7 +37,7 @@ export const IntercomProvider: React.FC = ({ ); if (!isSSR && !window.Intercom && shouldInitialize) { - initialize(appId); + initialize(appId, initializeDelayInMs); // Only add listeners on initialization if (onHide) IntercomAPI('onHide', onHide); if (onShow) IntercomAPI('onShow', onShow); diff --git a/src/types.ts b/src/types.ts index d07b8003..dcbdc08c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -419,4 +419,10 @@ export type IntercomProviderProps = { * Format https://${INTERCOM_APP_ID}.intercom-messenger.com */ apiBase?: string; + /** + * Indicates if the intercom initialization should be delayed + * + * @remarks If not set delay is set to 0ms + * */ + initializeDelayInMs?: number; }; From 4b353ec64c10760f246d0c4a47dfd627b4544417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sigur=C3=B0ur=20Gunnar=20Nj=C3=A1lsson?= Date: Fri, 30 Apr 2021 12:19:05 +0000 Subject: [PATCH 03/14] Adding types and passing the delay to the initialize function Fix spelling Fix linting --- src/initialize.ts | 16 ++++++++-------- src/provider.tsx | 3 ++- src/types.ts | 6 ++++++ 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/initialize.ts b/src/initialize.ts index 8b321bc9..14b3a661 100644 --- a/src/initialize.ts +++ b/src/initialize.ts @@ -23,14 +23,14 @@ const initialize = (appId: string, timeout = 0) => { }; w.Intercom = i; var l = function() { - setTimeout(function () { - var s = d.createElement('script'); - s.type = 'text/javascript'; - s.async = true; - s.src = 'https://widget.intercom.io/widget/' + appId; - var x = d.getElementsByTagName('script')[0]; - x.parentNode.insertBefore(s, x); - }, timeout) + setTimeout(function() { + var s = d.createElement('script'); + s.type = 'text/javascript'; + s.async = true; + s.src = 'https://widget.intercom.io/widget/' + appId; + var x = d.getElementsByTagName('script')[0]; + x.parentNode.insertBefore(s, x); + }, timeout); }; if (document.readyState === 'complete') { l(); diff --git a/src/provider.tsx b/src/provider.tsx index 221f8105..35c2227f 100644 --- a/src/provider.tsx +++ b/src/provider.tsx @@ -22,6 +22,7 @@ export const IntercomProvider: React.FC = ({ onUnreadCountChange, shouldInitialize = !isSSR, apiBase, + initializeDelayInMs, ...rest }) => { const isBooted = React.useRef(autoBoot); @@ -36,7 +37,7 @@ export const IntercomProvider: React.FC = ({ ); if (!isSSR && !window.Intercom && shouldInitialize) { - initialize(appId); + initialize(appId, initializeDelayInMs); // Only add listeners on initialization if (onHide) IntercomAPI('onHide', onHide); if (onShow) IntercomAPI('onShow', onShow); diff --git a/src/types.ts b/src/types.ts index d07b8003..dcbdc08c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -419,4 +419,10 @@ export type IntercomProviderProps = { * Format https://${INTERCOM_APP_ID}.intercom-messenger.com */ apiBase?: string; + /** + * Indicates if the intercom initialization should be delayed + * + * @remarks If not set delay is set to 0ms + * */ + initializeDelayInMs?: number; }; From c1c3357a65a458f09f0c80b4643a24da9cb98434 Mon Sep 17 00:00:00 2001 From: devrnt Date: Mon, 3 May 2021 18:20:58 +0200 Subject: [PATCH 04/14] chore: update initialize jsdoc --- src/initialize.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/initialize.ts b/src/initialize.ts index 14b3a661..afbc69c0 100644 --- a/src/initialize.ts +++ b/src/initialize.ts @@ -3,6 +3,7 @@ * Snippet to initialize the Intercom instance * * @param appId - Intercom app id + * @param [timeout=0] - Amount of milliseconds that the initialization should be delayed, defaults to 0 * * @see {@link https://developers.intercom.com/installing-intercom/docs/basic-javascript} */ From 38bbd848b60ef582bb09fd648fc19df1400bafbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sigur=C3=B0ur=20Gunnar=20Nj=C3=A1lsson?= Date: Fri, 30 Apr 2021 12:19:05 +0000 Subject: [PATCH 05/14] Adding types and passing the delay to the initialize function Fix spelling Fix linting --- src/initialize.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/initialize.ts b/src/initialize.ts index 8b321bc9..14b3a661 100644 --- a/src/initialize.ts +++ b/src/initialize.ts @@ -23,14 +23,14 @@ const initialize = (appId: string, timeout = 0) => { }; w.Intercom = i; var l = function() { - setTimeout(function () { - var s = d.createElement('script'); - s.type = 'text/javascript'; - s.async = true; - s.src = 'https://widget.intercom.io/widget/' + appId; - var x = d.getElementsByTagName('script')[0]; - x.parentNode.insertBefore(s, x); - }, timeout) + setTimeout(function() { + var s = d.createElement('script'); + s.type = 'text/javascript'; + s.async = true; + s.src = 'https://widget.intercom.io/widget/' + appId; + var x = d.getElementsByTagName('script')[0]; + x.parentNode.insertBefore(s, x); + }, timeout); }; if (document.readyState === 'complete') { l(); From 25454c1d72b9a3d29ee88d3ef531d58f4f021eb4 Mon Sep 17 00:00:00 2001 From: devrnt Date: Mon, 3 May 2021 18:20:58 +0200 Subject: [PATCH 06/14] chore: update initialize jsdoc --- src/initialize.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/initialize.ts b/src/initialize.ts index 14b3a661..afbc69c0 100644 --- a/src/initialize.ts +++ b/src/initialize.ts @@ -3,6 +3,7 @@ * Snippet to initialize the Intercom instance * * @param appId - Intercom app id + * @param [timeout=0] - Amount of milliseconds that the initialization should be delayed, defaults to 0 * * @see {@link https://developers.intercom.com/installing-intercom/docs/basic-javascript} */ From ef59f9d4a7433ce26be6ee8e086bf3df41a098a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sigur=C3=B0ur=20Gunnar=20Nj=C3=A1lsson?= Date: Tue, 4 May 2021 21:56:40 +0000 Subject: [PATCH 07/14] Updating variable names and jsdoc --- src/provider.tsx | 4 ++-- src/types.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/provider.tsx b/src/provider.tsx index 35c2227f..6c346f66 100644 --- a/src/provider.tsx +++ b/src/provider.tsx @@ -22,7 +22,7 @@ export const IntercomProvider: React.FC = ({ onUnreadCountChange, shouldInitialize = !isSSR, apiBase, - initializeDelayInMs, + initializeDelay, ...rest }) => { const isBooted = React.useRef(autoBoot); @@ -37,7 +37,7 @@ export const IntercomProvider: React.FC = ({ ); if (!isSSR && !window.Intercom && shouldInitialize) { - initialize(appId, initializeDelayInMs); + initialize(appId, initializeDelay); // Only add listeners on initialization if (onHide) IntercomAPI('onHide', onHide); if (onShow) IntercomAPI('onShow', onShow); diff --git a/src/types.ts b/src/types.ts index dcbdc08c..928bfbb6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -420,9 +420,9 @@ export type IntercomProviderProps = { */ apiBase?: string; /** - * Indicates if the intercom initialization should be delayed + * Indicates if the intercom initialization should be delayed, dealy is in ms * * @remarks If not set delay is set to 0ms * */ - initializeDelayInMs?: number; + initializeDelay?: number; }; From d9c382d80a38e9acb72534a2edea19c683530e84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sigur=C3=B0ur=20Gunnar=20Nj=C3=A1lsson?= Date: Tue, 4 May 2021 22:40:49 +0000 Subject: [PATCH 08/14] Adding delay intercom test Fix spelling --- playground/app.tsx | 5 ++ playground/modules/useIntercom/index.ts | 1 + .../useIntercom/useIntercomWithDelay.tsx | 51 +++++++++++++++++++ src/types.ts | 2 +- test/useIntercom.test.tsx | 25 +++++++++ 5 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 playground/modules/useIntercom/useIntercomWithDelay.tsx diff --git a/playground/app.tsx b/playground/app.tsx index 62f51864..06281e1f 100644 --- a/playground/app.tsx +++ b/playground/app.tsx @@ -8,6 +8,7 @@ import { ProviderEventsPage, ProviderApiPage, UseIntercomTourPage, + UseIntercomWithDelay } from './modules'; import { Page, Style } from './modules/common'; @@ -47,6 +48,7 @@ const App = () => { + @@ -61,6 +63,9 @@ const App = () => { useIntercom with tour + + useIntercom with delayed boot + diff --git a/playground/modules/useIntercom/index.ts b/playground/modules/useIntercom/index.ts index f51c1959..3653d9ac 100644 --- a/playground/modules/useIntercom/index.ts +++ b/playground/modules/useIntercom/index.ts @@ -1,2 +1,3 @@ export { default as UseIntercomPage } from './useIntercom'; export { default as UseIntercomTourPage } from './useIntercomTour'; +export { default as UseIntercomWithDelay } from './useIntercomWithDelay' diff --git a/playground/modules/useIntercom/useIntercomWithDelay.tsx b/playground/modules/useIntercom/useIntercomWithDelay.tsx new file mode 100644 index 00000000..b45a27b7 --- /dev/null +++ b/playground/modules/useIntercom/useIntercomWithDelay.tsx @@ -0,0 +1,51 @@ +import * as React from 'react'; +import styled from 'styled-components'; + +import { IntercomProvider, useIntercom } from '../../../.'; + +import { Button } from '../common'; + +const Grid = styled.div` + display: grid; + grid-template-columns: repeat(1, 1fr); + width: 100%; +`; + +const Item = styled.div` + display: grid; + grid-template-rows: min-content; + + &::after { + content: ''; + margin: 2rem 0 1.5rem; + border-bottom: 2px solid var(--grey); + width: 100%; + } +`; + +const RawUseIntercomPage = () => { + const { + boot, + } = useIntercom(); + const handleBoot = React.useCallback(() => boot(), [boot]); + + return ( + + +

+ Intercom will be autobooted after 5000ms +

+
+
+ ); +}; + +const UseIntercomWithDelayPage = () => { + return ( + + + + ); +}; + +export default UseIntercomWithDelayPage; diff --git a/src/types.ts b/src/types.ts index 928bfbb6..543fa57f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -420,7 +420,7 @@ export type IntercomProviderProps = { */ apiBase?: string; /** - * Indicates if the intercom initialization should be delayed, dealy is in ms + * Indicates if the intercom initialization should be delayed, delay is in ms * * @remarks If not set delay is set to 0ms * */ diff --git a/test/useIntercom.test.tsx b/test/useIntercom.test.tsx index cdd477fb..b7276b6e 100644 --- a/test/useIntercom.test.tsx +++ b/test/useIntercom.test.tsx @@ -44,4 +44,29 @@ describe('useIntercom', () => { expect(window.intercomSettings).toEqual({ app_id: INTERCOM_APP_ID }); }); + + test('should set wait for a certain amount of ms until booting, booting before that will do nothing', async () => { + const { result, waitFor } = renderHook(() => useIntercom(), { + wrapper: ({ children }) => ( + {children} + ), + }); + + const { boot } = result.current; + + act(() => { + boot(); + }); + + expect(window.intercomSettings).toEqual({app_id: undefined}); + + await waitFor(() => {}, {timeout: 5000}); + + act(() => { + boot(); + }); + + expect(window.intercomSettings).toEqual({ app_id: INTERCOM_APP_ID }); + + }); }); From 8a6b7405ef7cdb19b76a642b23434dcff45fa8fc Mon Sep 17 00:00:00 2001 From: devrnt Date: Wed, 5 May 2021 08:21:43 +0200 Subject: [PATCH 09/14] chore: run linter in test --- test/useIntercom.test.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/test/useIntercom.test.tsx b/test/useIntercom.test.tsx index b7276b6e..0c6e783f 100644 --- a/test/useIntercom.test.tsx +++ b/test/useIntercom.test.tsx @@ -46,9 +46,11 @@ describe('useIntercom', () => { }); test('should set wait for a certain amount of ms until booting, booting before that will do nothing', async () => { - const { result, waitFor } = renderHook(() => useIntercom(), { + const { result, waitFor } = renderHook(() => useIntercom(), { wrapper: ({ children }) => ( - {children} + + {children} + ), }); @@ -58,15 +60,14 @@ describe('useIntercom', () => { boot(); }); - expect(window.intercomSettings).toEqual({app_id: undefined}); + expect(window.intercomSettings).toEqual({ app_id: undefined }); - await waitFor(() => {}, {timeout: 5000}); + await waitFor(() => {}, { timeout: 5000 }); act(() => { boot(); }); expect(window.intercomSettings).toEqual({ app_id: INTERCOM_APP_ID }); - }); }); From b3e17604e7734d075b602f34380acf8482851973 Mon Sep 17 00:00:00 2001 From: devrnt Date: Wed, 5 May 2021 08:25:36 +0200 Subject: [PATCH 10/14] test: rephrase test case --- test/useIntercom.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/useIntercom.test.tsx b/test/useIntercom.test.tsx index 0c6e783f..cab46cdb 100644 --- a/test/useIntercom.test.tsx +++ b/test/useIntercom.test.tsx @@ -45,7 +45,7 @@ describe('useIntercom', () => { expect(window.intercomSettings).toEqual({ app_id: INTERCOM_APP_ID }); }); - test('should set wait for a certain amount of ms until booting, booting before that will do nothing', async () => { + test('should await a certain amount on delayed initialization', async () => { const { result, waitFor } = renderHook(() => useIntercom(), { wrapper: ({ children }) => ( From 112826c6cd353b82e5388aac3d059894a5baafc5 Mon Sep 17 00:00:00 2001 From: devrnt Date: Wed, 5 May 2021 17:45:52 +0200 Subject: [PATCH 11/14] chore: use correct typescript function type --- src/provider.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/provider.tsx b/src/provider.tsx index 6c346f66..408de717 100644 --- a/src/provider.tsx +++ b/src/provider.tsx @@ -57,7 +57,10 @@ export const IntercomProvider: React.FC = ({ } const ensureIntercom = React.useCallback( - (functionName: string = 'A function', callback: Function) => { + ( + functionName: string = 'A function', + callback: (() => void) | (() => string), + ) => { if (!window.Intercom && !shouldInitialize) { logger.log( 'warn', @@ -174,8 +177,8 @@ export const IntercomProvider: React.FC = ({ const getVisitorId = React.useCallback(() => { return ensureIntercom('getVisitorId', () => { - return (IntercomAPI('getVisitorId') as unknown) as string; - }); + return IntercomAPI('getVisitorId'); + }) as string; }, [ensureIntercom]); const startTour = React.useCallback( From 7cbe92cbe2b502855ce6fba8a31c28e9aef41f56 Mon Sep 17 00:00:00 2001 From: devrnt Date: Wed, 5 May 2021 18:10:14 +0200 Subject: [PATCH 12/14] docs: add delay initialization section in readme --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 02647ea5..a9243635 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,7 @@ Place the `IntercomProvider` as high as possible in your application. This will | onUnreadCountChange | (number) => void | triggered when the current number of unread messages changes | false | | | shouldInitialize | boolean | indicates if the Intercom should be initialized. Can be used in multistaged environment | false | true | | apiBase | string | If you need to route your Messenger requests through a different endpoint than the default. Generally speaking, this is not needed.
Format: `https://${INTERCOM_APP_ID}.intercom-messenger.com` (See: [https://github.com/devrnt/react-use-intercom/pull/96](https://github.com/devrnt/react-use-intercom/pull/96)) | false | | +| initializeDelay | number | Indicates if the intercom initialization should be delayed, delay is in ms, defaults to 0. See https://github.com/devrnt/react-use-intercom/pull/236 | false | | #### Example ```javascript @@ -239,6 +240,15 @@ These props are `JavaScript` 'friendly', so [camelCase](https://en.wikipedia.org > Mind that all the properties in `react-use-intercom` are camel cased, except for the `customAttributes` property in the `boot` and `update` method from `useIntercom`. ## Advanced + +### Delay initialization + +`` uses an official intercom snippet and is directly initialized on load. In the background this snippet will load some external code that makes Intercom work. All of this magic happens on the initial load and in some use cases this can become problematic (E.g. when LCP is priority). + +Since [v1.2.0](https://github.com/devrnt/react-use-intercom/releases/tag/v1.2.0) it's possible to delay this initialisation by passing `initializeDelay` in `` (it's in milliseconds). However most of the users won't need to mess with this. + +For reference see https://github.com/devrnt/react-use-intercom/pull/236 and https://forum.intercom.com/s/question/0D52G00004WxWLs/can-i-delay-loading-intercom-on-my-site-to-reduce-the-js-load +### useCallback To reduce the amount of re-renders in your React application I suggest to make use of [`useCallback`](https://reactjs.org/docs/hooks-reference.html#usecallback) **TLDR:** `useCallback` will return a memoized version of the callback that only changes if one of the dependencies has changed. From 1ddadcd1dea0a81a7e21530db0c352f9ad407150 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sigur=C3=B0ur=20Gunnar=20Nj=C3=A1lsson?= Date: Sun, 9 May 2021 17:37:56 +0000 Subject: [PATCH 13/14] Removing unused variables and improving the description of the delay example --- playground/modules/useIntercom/useIntercomWithDelay.tsx | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/playground/modules/useIntercom/useIntercomWithDelay.tsx b/playground/modules/useIntercom/useIntercomWithDelay.tsx index b45a27b7..e7bbd780 100644 --- a/playground/modules/useIntercom/useIntercomWithDelay.tsx +++ b/playground/modules/useIntercom/useIntercomWithDelay.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import styled from 'styled-components'; -import { IntercomProvider, useIntercom } from '../../../.'; +import { IntercomProvider } from '../../../.'; import { Button } from '../common'; @@ -24,16 +24,11 @@ const Item = styled.div` `; const RawUseIntercomPage = () => { - const { - boot, - } = useIntercom(); - const handleBoot = React.useCallback(() => boot(), [boot]); - return (

- Intercom will be autobooted after 5000ms + Intercom will be initialized (and autobooted) after 5000ms

From 3a7c6c497367c0f6a24ceec2f68bf1bba397fb82 Mon Sep 17 00:00:00 2001 From: devrnt Date: Mon, 10 May 2021 18:20:56 +0200 Subject: [PATCH 14/14] chore: format playground --- playground/modules/useIntercom/useIntercomWithDelay.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/playground/modules/useIntercom/useIntercomWithDelay.tsx b/playground/modules/useIntercom/useIntercomWithDelay.tsx index e7bbd780..1aa7660a 100644 --- a/playground/modules/useIntercom/useIntercomWithDelay.tsx +++ b/playground/modules/useIntercom/useIntercomWithDelay.tsx @@ -3,8 +3,6 @@ import styled from 'styled-components'; import { IntercomProvider } from '../../../.'; -import { Button } from '../common'; - const Grid = styled.div` display: grid; grid-template-columns: repeat(1, 1fr); @@ -27,9 +25,7 @@ const RawUseIntercomPage = () => { return ( -

- Intercom will be initialized (and autobooted) after 5000ms -

+

Intercom will be initialized (and autobooted) after 5000ms

);