Skip to content

Commit e9c9537

Browse files
authored
Merge pull request #281 from jucallej/hubspot
2 parents d8b52f8 + 29ff383 commit e9c9537

8 files changed

Lines changed: 269 additions & 1 deletion

File tree

README.md

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ You can pass the following props to the `LiveChatLoaderProvider` provider:
132132

133133
## 💬 Supported Providers
134134

135-
Currently there are six supported providers:
135+
Currently there are seven supported providers:
136136

137137
<details>
138138
<summary id="help-scout">Help Scout</summary>
@@ -347,6 +347,37 @@ You can customise the Chatwoot placeholder by passing the following props to the
347347

348348
</details>
349349

350+
<details>
351+
<summary id="hubspot">Hubspot</summary>
352+
353+
To use Hubspot import the `LiveChatLoaderProvider` and set the `provider` prop
354+
as `hubSpot` and the `providerKey` prop as your Hubspot API Key.
355+
356+
Then import the `Hubspot` component.
357+
358+
```jsx
359+
import { LiveChatLoaderProvider, Hubspot } from 'react-live-chat-loader'
360+
361+
export default class App extends React.Component {
362+
render() {
363+
return (
364+
<LiveChatLoaderProvider providerKey="asdjkasl123123" provider="hubspot">
365+
/* ... */
366+
<Hubspot />
367+
</LiveChatLoaderProvider>
368+
)
369+
}
370+
}
371+
```
372+
373+
You can customise the Hubspot placeholder by passing the following props to the
374+
`Hubspot` component:
375+
376+
- `backgroundColor`: The background color of the placeholder
377+
- `loader`: A react component shown while the Hubspot libraries are loading
378+
379+
</details>
380+
350381
## ➕ Adding a Provider
351382

352383
To add a new live chat provider, follow the steps in [Contributing: Adding a Provider](CONTRIBUTING.md#-adding-a-provider).

src/components/HubSpot/index.tsx

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import React, {
2+
ReactElement,
3+
useState
4+
} from 'react'
5+
import useChat from '../../hooks/useChat'
6+
const Icon = () => (
7+
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="30" viewBox="0 0 39 37"><g fill="none"><path fill="#fff" d="M37.65 4.303c-.314-1.943-1.486-3.113-3.374-3.38C34.23.912 29.632.03 22.416.03c-7.214 0-11.812.882-11.843.889-1.477.21-2.507.967-3.042 2.192a83.103 83.103 0 0 1 9.312-.503c6.994 0 11.647.804 12.33.93 3.079.462 5.22 2.598 5.738 5.728.224 1.02.932 4.606.932 8.887 0 2.292-.206 4.395-.428 6.002 1.22-.516 1.988-1.55 2.23-3.044.008-.037.893-3.814.893-8.415 0-4.6-.885-8.377-.89-8.394"/><path fill="#fff" d="M32.077 9.76c-.314-1.944-1.487-3.114-3.374-3.382-.046-.01-4.644-.89-11.859-.89-7.214 0-11.813.88-11.843.888-1.903.27-3.075 1.44-3.384 3.363-.01.037-.894 3.814-.894 8.415 0 4.6.884 8.377.889 8.393.314 1.944 1.486 3.114 3.374 3.382.037.007 3.02.578 7.933.801l2.928 5.072a1.151 1.151 0 0 0 1.994 0l2.929-5.071c4.913-.224 7.893-.794 7.918-.8 1.902-.27 3.075-1.44 3.384-3.363.01-.037.893-3.814.893-8.414 0-4.601-.884-8.378-.888-8.394"/></g></svg>
8+
)
9+
10+
const HubSpot = ({
11+
backgroundColor,
12+
loader: Loader
13+
}: {
14+
backgroundColor?: string
15+
loader?: ReactElement
16+
}): JSX.Element | null => {
17+
const [state, loadChat] = useChat({ loadWhenIdle: true })
18+
const [isLoading, setIsLoading] = useState(false)
19+
if (state === 'complete') {
20+
return null
21+
}
22+
return (
23+
<div
24+
style={{
25+
zIndex: 1051, // 1 more than the actual widget
26+
paddingBottom: '16px',
27+
position: 'fixed',
28+
bottom: 0,
29+
paddingLeft: '0px',
30+
paddingRight: '16px',
31+
left: 'inherit',
32+
right: '0px'
33+
}}
34+
>
35+
<span
36+
style={{
37+
display: 'flex !important',
38+
paddingLeft: '24px !important',
39+
paddingTop: '20px !important',
40+
float: 'right'
41+
}}
42+
>
43+
<div
44+
style={{
45+
position: 'relative',
46+
display: 'inline-flex',
47+
alignItems: 'baseline',
48+
lineHeight: 1
49+
}}
50+
>
51+
<button
52+
onClick={() => {
53+
setIsLoading(true);
54+
loadChat({ open: true });
55+
}}
56+
aria-label="Open live chat"
57+
aria-haspopup="false"
58+
style={{
59+
fontWeight: 400,
60+
fontSize: '14px',
61+
color: 'rgb(51, 71, 91)',
62+
backgroundColor: backgroundColor || 'rgb(66, 91, 118)',
63+
boxShadow:
64+
'rgba(0, 0, 0, 0.1) 0px 1px 6px, rgba(0, 0, 0, 0.2) 0px 2px 24px',
65+
border: 'medium none',
66+
transition: 'box-shadow 150ms ease-in-out 0s',
67+
position: 'relative',
68+
borderRadius: '50%',
69+
height: '60px',
70+
width: '60px',
71+
cursor: 'pointer'
72+
}}
73+
>
74+
<div
75+
style={{
76+
position: 'absolute',
77+
top: '50%',
78+
left: '50%',
79+
transform: 'translate(-50%, -50%)',
80+
display: 'flex',
81+
width: '32px',
82+
height: '30px',
83+
lineHeight: 1
84+
}}
85+
>
86+
{isLoading && Loader ? Loader : <Icon />}
87+
</div>
88+
</button>
89+
</div>
90+
</span>
91+
</div>
92+
)
93+
}
94+
95+
export default HubSpot

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ export { default as Intercom } from './components/Intercom'
66
export { default as Messenger } from './components/Messenger'
77
export { default as Userlike } from './components/Userlike'
88
export { default as Chatwoot } from './components/Chatwoot'
9+
export { default as HubSpot } from './components/HubSpot'
910
export { default as LiveChatLoaderProvider } from './components/LiveChatLoaderProvider'

src/providers/hubSpot.ts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { State } from '../types'
2+
import waitForLoad from '../utils/waitForLoad'
3+
4+
export const domain = 'js.hs-scripts.com'
5+
declare global {
6+
interface Window {
7+
//eslint-disable-next-line @typescript-eslint/no-explicit-any
8+
HubSpotConversations: any
9+
//eslint-disable-next-line @typescript-eslint/no-explicit-any
10+
hsConversationsSettings: any
11+
//eslint-disable-next-line @typescript-eslint/no-explicit-any
12+
hsConversationsOnReady: any
13+
}
14+
}
15+
16+
const isHubspotWidgetDefined = () => window.HubSpotConversations && window.HubSpotConversations.widget;
17+
18+
const loadScript = (hsId: string) => {
19+
// Detect the provider is already loaded and return early
20+
if (window.HubSpotConversations) {
21+
return false
22+
}
23+
(function loadHubSpotSDK(d, s, id) {
24+
// fetch customerchat.js
25+
const fjs = d.getElementsByTagName(s)[0]
26+
if (d.getElementById(id)) {
27+
return
28+
}
29+
//eslint-disable-next-line @typescript-eslint/no-explicit-any
30+
const js = d.createElement(s) as any
31+
js.id = id
32+
js.src = `https://${domain}/${hsId}.js`
33+
js.type = 'text/javascript'
34+
js.async = 1
35+
js.defer = 1
36+
if (fjs) {
37+
fjs.parentNode?.insertBefore(js, fjs)
38+
} else {
39+
d.body.appendChild(js)
40+
}
41+
})(window.document, 'script', 'hs-script-loader')
42+
43+
return true
44+
}
45+
46+
const load = ({
47+
providerKey,
48+
setState,
49+
beforeInit = () => undefined,
50+
onReady = () => undefined,
51+
}: {
52+
providerKey: string
53+
setState: (state: State) => void
54+
beforeInit?: () => void
55+
onReady?: () => void
56+
}): boolean => {
57+
window.hsConversationsOnReady = [
58+
() => {
59+
isHubspotWidgetDefined() && window.HubSpotConversations.widget.load()
60+
}
61+
]
62+
const loaded = loadScript(providerKey)
63+
if (loaded) {
64+
beforeInit()
65+
66+
waitForLoad(
67+
() => {
68+
return Boolean(
69+
isHubspotWidgetDefined() &&
70+
window.HubSpotConversations.widget.status().loaded
71+
)
72+
},
73+
// Allow hubspot to complete loading before removing fake widget
74+
() => {
75+
isHubspotWidgetDefined() && window.HubSpotConversations.widget.open()
76+
setState('complete')
77+
onReady()
78+
}
79+
)
80+
}
81+
return loaded
82+
}
83+
84+
const open = (): unknown => {
85+
isHubspotWidgetDefined() &&
86+
!window.HubSpotConversations.widget.status().loaded &&
87+
window.HubSpotConversations.widget.load()
88+
89+
return (
90+
isHubspotWidgetDefined() &&
91+
window.HubSpotConversations.widget.open()
92+
)
93+
} // Open provider
94+
const close = (): unknown => isHubspotWidgetDefined() && window.HubSpotConversations.widget.close() // Close provider
95+
96+
export default {
97+
load,
98+
open,
99+
close,
100+
domain
101+
}

src/providers/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ export { default as intercom } from './intercom'
44
export { default as messenger } from './messenger'
55
export { default as userlike } from './userlike'
66
export { default as chatwoot} from './chatwoot'
7+
export { default as hubSpot } from './hubSpot'

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ export type Provider =
1414
| 'drift'
1515
| 'userlike'
1616
| 'chatwoot'
17+
| 'hubSpot'

website/components/exampleLinks.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ const ExampleLinks = () => (
3333
<a>Chatwoot</a>
3434
</Link>
3535
</li>
36+
<li>
37+
<Link href="/hubspot">
38+
<a>HubSpot</a>
39+
</Link>
40+
</li>
3641
</ul>
3742
)
3843

website/pages/hubspot.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import React from 'react'
2+
import { LiveChatLoaderProvider, HubSpot } from 'react-live-chat-loader'
3+
import type { NextPage } from 'next'
4+
5+
import Layout from '../layouts/main'
6+
import ExampleLinks from '../components/exampleLinks'
7+
8+
const Page: NextPage = () => (
9+
<LiveChatLoaderProvider
10+
provider="hubSpot"
11+
providerKey=""
12+
>
13+
<Layout title="React Live Chat Loader: Hubspot">
14+
<div className="wrapper">
15+
<div className="inner">
16+
<h1>React Live Chat Loader: Hubspot</h1>
17+
<p>
18+
This is an example implementation of the Hubspot widget using{' '}
19+
<a href="https://github.com/calibreapp/react-live-chat-loader">
20+
react-live-chat-loader
21+
</a>
22+
.
23+
</p>
24+
<p>View other demos:</p>
25+
<ExampleLinks />
26+
</div>
27+
</div>
28+
<HubSpot backgroundColor='#017848'/>
29+
</Layout>
30+
</LiveChatLoaderProvider>
31+
)
32+
33+
export default Page

0 commit comments

Comments
 (0)