Skip to content

Commit

Permalink
Added onLoad and onLoadFailed callbacks to config
Browse files Browse the repository at this point in the history
  • Loading branch information
fluidsonic committed Jun 11, 2024
1 parent a10dcaa commit 32e3ad4
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 1 deletion.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ Place the `IntercomProvider` as high as possible in your application. This will
| 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 | |
| autoBootProps | IntercomProps | Pass properties to `boot` method when `autoBoot` is `true` | false | |
| crossOrigin | string | `crossOrigin` attribute value to pass to `<script>` tag that loads the messenger | false | |
| onLoad | () => void | triggered when the Messenger script has been loaded successfully | false | |
| onLoadFailed | () => void | triggered when the Messenger script has failed to load | false | |

#### Example
```ts
Expand Down Expand Up @@ -296,3 +298,9 @@ These props are `JavaScript` 'friendly', so [camelCase](https://en.wikipedia.org
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 `<IntercomProvider />` (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

### Detect a broken Messenger

There can be various reasons why the Messenger script has failed to load, e.g. network issues, Intercom downtime, firewall issues, or browser extensions like tracking blockers.

By using the `onLoadFailed` callback it's possible to detect when that happens and provide the user with alternative means of customer support.
23 changes: 23 additions & 0 deletions apps/playground/cypress/e2e/loadCallbacks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/// <reference types="cypress" />

describe('onLoad/onLoadFailed', () => {
it('should call onLoad when successful', () => {
cy.visit('/useIntercomWithLoadCallbacks');

cy.get('[data-cy=call]').should(($p) =>
expect($p).to.have.text('onLoad was called!'),
);
});

it('should call onLoadFailed when not successful', () => {
cy.visit('/useIntercomWithLoadCallbacks');

cy.intercept('https://widget.intercom.io/widget/jcabc7e3', {
forceNetworkError: true,
});

cy.get('[data-cy=call]').should(($p) =>
expect($p).to.have.text('onLoadFailed was called!'),
);
});
});
8 changes: 8 additions & 0 deletions apps/playground/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
UseIntercomTourPage,
UseIntercomWithCrossOrigin,
UseIntercomWithDelay,
UseIntercomWithLoadCallbacks,
} from './modules';
import { Page, Style } from './modules/common';

Expand Down Expand Up @@ -57,6 +58,10 @@ const App = () => {
path="/useIntercomWithCrossOrigin"
component={UseIntercomWithCrossOrigin}
/>
<Route
path="/useIntercomWithLoadCallbacks"
component={UseIntercomWithLoadCallbacks}
/>
<Route
path="/useIntercomWithTimeout"
component={UseIntercomWithDelay}
Expand All @@ -81,6 +86,9 @@ const App = () => {
<Link to="/useIntercomWithTimeout">
<code>useIntercom with delayed boot</code>
</Link>
<Link to="/useIntercomWithLoadCallbacks">
<code>useIntercom with load callbacks</code>
</Link>
</Navigation>
</Route>
</Router>
Expand Down
1 change: 1 addition & 0 deletions apps/playground/src/modules/useIntercom/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export { default as UseIntercomPage } from './useIntercom';
export { default as UseIntercomTourPage } from './useIntercomTour';
export { default as UseIntercomWithCrossOrigin } from './useIntercomWithCrossOrigin';
export { default as UseIntercomWithDelay } from './useIntercomWithDelay';
export { default as UseIntercomWithLoadCallbacks } from './useIntercomWithLoadCallbacks';
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { useState } from 'react';
import * as React from 'react';
import { IntercomProvider } from 'react-use-intercom';
import styled from 'styled-components';

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 = ({ call }: { call: string | undefined }) => {
return (
<Grid>
<Item>
<p>
The Intercom Messenger script will be loaded and{' '}
<code>onLoad/onLoadFailed</code> be called
</p>
<p data-cy="call">{call ?? 'Waiting…'}</p>
</Item>
</Grid>
);
};

const UseIntercomWithLoadCallbacks = () => {
const [call, setCall] = useState<string>();

return (
<IntercomProvider
appId="jcabc7e3"
autoBoot
onLoad={() => setCall('onLoad was called!')}
onLoadFailed={() => setCall('onLoadFailed was called!')}
>
<RawUseIntercomPage call={call} />
</IntercomProvider>
);
};

export default UseIntercomWithLoadCallbacks;
15 changes: 15 additions & 0 deletions packages/react-use-intercom/src/initialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@
* @param appId - Intercom app id
* @param [timeout=0] - Amount of milliseconds that the initialization should be delayed, defaults to 0
* @param [crossOrigin=undefined] - `crossOrigin` attribute value to use for the `<script>` tag, defaults to `undefined`
* @param [onLoad=undefined] - Called when the Messenger script has been loaded successfully, defaults to `undefined`.
* @param [onLoadFailed=undefined] - Called when the Messenger script has failed to load, defaults to `undefined`.
*
* @see {@link https://developers.intercom.com/installing-intercom/docs/basic-javascript}
*/
const initialize = (
appId: string,
timeout = 0,
crossOrigin: string | undefined = undefined,
onLoad: () => void = undefined,
onLoadFailed: () => void = undefined,
) => {
var w = window;
var ic = w.Intercom;
Expand All @@ -35,6 +39,17 @@ const initialize = (
s.type = 'text/javascript';
s.async = true;
s.src = 'https://widget.intercom.io/widget/' + appId;
if (onLoad) {
s.addEventListener('load', () => {
onLoad();
});
}
if (onLoadFailed) {
s.addEventListener('error', () => {
// No need to pass any information from the ErrorEvent because it will contain no information about the error.
onLoadFailed();
});
}
var x = d.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
}, timeout);
Expand Down
4 changes: 3 additions & 1 deletion packages/react-use-intercom/src/provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export const IntercomProvider: React.FC<
children,
crossOrigin,
onHide,
onLoad,
onLoadFailed,
onShow,
onUnreadCountChange,
onUserEmailSupplied,
Expand Down Expand Up @@ -85,7 +87,7 @@ export const IntercomProvider: React.FC<
}, [onShow, setIsOpen]);

if (!isSSR && shouldInitialize && !isInitialized.current) {
initialize(appId, initializeDelay, crossOrigin);
initialize(appId, initializeDelay, crossOrigin, onLoad, onLoadFailed);

// attach listeners
IntercomAPI('onHide', onHideWrapper);
Expand Down
15 changes: 15 additions & 0 deletions packages/react-use-intercom/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -476,12 +476,27 @@ export type IntercomProviderProps = {
autoBoot?: boolean;
/**
* The optional `crossOrigin` attribute value to use for the `<script>` tag that loads the messenger.
*
* Use `crossOrigin: "anonymous"` to have errors thrown by the Messenger contain all information (file name, line, message, etc.).
* This is useful for error logging and following up with Intercom's own tech support.
*
* Note that this doesn't work for errors thrown because the Messenger script file has failed to load.
*/
crossOrigin?: 'anonymous' | 'use-credentials' | '' | undefined;
/**
* When we hide the messenger, you can hook into the event. This requires a function argument.
*/
onHide?: () => void;
/**
* Called when the Messenger script file has been loaded successfully.
*/
onLoad?: () => void;
/**
* Called when the Messenger script file has failed to load.
*
* You can use this to let the customer know the support chat is unavailable and provide an alternative communication method.
*/
onLoadFailed?: () => void;
/**
* When we show the messenger, you can hook into the event. This requires a function argument.
*/
Expand Down

0 comments on commit 32e3ad4

Please sign in to comment.