Skip to content

Commit daa3a03

Browse files
authored
Merge pull request #345 from IlirEdis/FrontChat
Front support added
2 parents e9c9537 + bf1510b commit daa3a03

8 files changed

Lines changed: 315 additions & 41 deletions

File tree

README.md

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
An npm module that allows you to mitigate the negative performance and user
99
experience impact of chat tools. `react-live-chat-loader` shows a fake widget
10-
until the page has become idle or users are ready to interact with chat. Currently works with [Intercom](#intercom), [Help Scout](#help-scout), [Drift](#drift), [Messenger](#messenger), [Userlike](#userlike) and [Chatwoot](#chatwoot).
10+
until the page has become idle or users are ready to interact with chat. Currently works with [Intercom](#intercom), [Help Scout](#help-scout), [Drift](#drift), [Messenger](#messenger), [Userlike](#userlike), [Front](#front) and [Chatwoot](#chatwoot).
1111

1212
Made by the team at [♠ Calibre](https://calibreapp.com/), your performance companion.
1313

@@ -122,7 +122,7 @@ export const LoadChatButton = () => {
122122

123123
You can pass the following props to the `LiveChatLoaderProvider` provider:
124124

125-
- `provider`: Choose from `helpScout`, `intercom`, `drift` or `messenger` ([see below](#-supported-providers))
125+
- `provider`: Choose from `helpScout`, `intercom`, `drift`, `front` or `messenger` ([see below](#-supported-providers))
126126
- `providerKey`: Provider API Key ([see below](#-supported-providers))
127127
- `idlePeriod`: How long to wait in ms before loading the provider. Default is
128128
`2000`. Set to `0` to never load. This value is used in a `setTimeout` in
@@ -132,7 +132,7 @@ You can pass the following props to the `LiveChatLoaderProvider` provider:
132132

133133
## 💬 Supported Providers
134134

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

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

350350
<details>
351+
<summary id="front">Front</summary>
352+
353+
To use Front import the `LiveChatLoaderProvider` and set the `provider` prop
354+
as `front` and the `providerKey` prop as your Front Chat `chatId`.
355+
356+
Then import the `Front` component.
357+
358+
```jsx
359+
import { LiveChatLoaderProvider, Front } from 'react-live-chat-loader'
360+
361+
export default () => {
362+
return (
363+
<LiveChatLoaderProvider
364+
providerKey="your-front-chat-chatId"
365+
provider="front"
366+
>
367+
/* ... */
368+
<Front />
369+
</LiveChatLoaderProvider>
370+
)
371+
}
372+
```
373+
374+
You can customise the Front placeholder icon by passing the following props to the `Front` component:
375+
376+
- `color`: The background color of the placeholder widget.
377+
- `containerClass`: Class to be added to the placeholder element, defaults to `live-chat-loader-placeholder`
378+
379+
See the [official Front documentation](https://help.front.com/) for more details.
380+
351381
<summary id="hubspot">Hubspot</summary>
352382

353383
To use Hubspot import the `LiveChatLoaderProvider` and set the `provider` prop

src/components/Front/index.tsx

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import React, { CSSProperties } from 'react'
2+
import useChat from '../../hooks/useChat'
3+
import { ClassNames, ProviderProps } from '../../types'
4+
5+
const styles: {
6+
button: CSSProperties
7+
wrapper: CSSProperties
8+
iconWrapper: CSSProperties
9+
icon: CSSProperties
10+
} = {
11+
wrapper: {
12+
position: 'fixed',
13+
// z-index is 1 more than Front's actual launcher as when the real widget loads
14+
zIndex: 100000000,
15+
bottom: '20px',
16+
right: '20px',
17+
border: 'none',
18+
outline: 'none',
19+
width: '64px',
20+
height: '64px'
21+
},
22+
button: {
23+
maxWidth: '64px',
24+
width: '64px',
25+
maxHeight: '64px',
26+
height: '64px',
27+
borderRadius: '64px',
28+
cursor: 'pointer',
29+
border: 'none'
30+
},
31+
iconWrapper: {
32+
position: 'relative',
33+
width: '100%',
34+
height: '100%'
35+
},
36+
icon: {
37+
position: 'absolute',
38+
top: '50%',
39+
marginTop: '-21px',
40+
left: '50%',
41+
marginLeft: '-22px'
42+
}
43+
}
44+
45+
interface Props extends ProviderProps {
46+
color?: string
47+
}
48+
49+
const Front = ({
50+
color = '#5151E1',
51+
containerClass = ClassNames.container
52+
}: Props) => {
53+
const [state, loadChat] = useChat({ loadWhenIdle: true })
54+
55+
if (state === 'complete') {
56+
return null
57+
}
58+
59+
return (
60+
<div style={{ ...styles.wrapper }} className={containerClass}>
61+
<button
62+
onClick={() => loadChat({ open: true })}
63+
onMouseEnter={() => loadChat({ open: false })}
64+
style={{
65+
...styles.button,
66+
backgroundColor: color
67+
}}
68+
>
69+
<div style={{ ...styles.iconWrapper }}>
70+
<svg width="40" height="40" role="button" style={{ ...styles.icon }}>
71+
<title>Launch Front Chat</title>
72+
<defs>
73+
<filter
74+
id="chat-logo-a"
75+
data-testid="chat-logo-shadow"
76+
width="127.8%"
77+
height="127.8%"
78+
x="-13.9%"
79+
y="-11.1%"
80+
filterUnits="objectBoundingBox"
81+
>
82+
<feOffset
83+
dy="1"
84+
in="SourceAlpha"
85+
result="shadowOffsetOuter1"
86+
></feOffset>
87+
<feGaussianBlur
88+
in="shadowOffsetOuter1"
89+
result="shadowBlurOuter1"
90+
stdDeviation="1.5"
91+
></feGaussianBlur>
92+
<feColorMatrix
93+
in="shadowBlurOuter1"
94+
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"
95+
></feColorMatrix>
96+
</filter>
97+
</defs>
98+
<g fill="none" transform="translate(-11 -12)">
99+
<use
100+
data-testid="chat-logo-svg-shadow"
101+
fill="#000"
102+
filter="url(#chat-logo-a)"
103+
href="#chat-logo-b"
104+
></use>
105+
<path
106+
fill="#FFF"
107+
d="M32,18 C24.2680135,18 18,24.2680135 18,32 C18,39.7319865 24.2680135,46 32,46 L45.6,46 C45.8209139,46 46,45.8209139 46,45.6 L46,32 C46,24.2680135 39.7319865,18 32,18 Z M32,14 C41.9411255,14 50,22.0588745 50,32 L50,45.6 C50,48.0300529 48.0300529,50 45.6,50 L32,50 C22.0588745,50 14,41.9411255 14,32 C14,22.0588745 22.0588745,14 32,14 Z M25.9634146,31.5853659 L37.9634146,31.5853659 C39.0679841,31.5853659 39.9634146,30.6899354 39.9634146,29.5853659 C39.9634146,28.4807964 39.0679841,27.5853659 37.9634146,27.5853659 L25.9634146,27.5853659 C24.8588451,27.5853659 23.9634146,28.4807964 23.9634146,29.5853659 C23.9634146,30.6899354 24.8588451,31.5853659 25.9634146,31.5853659 Z M31.195122,38.8292683 L37.195122,38.8292683 C38.2996915,38.8292683 39.195122,37.9338378 39.195122,36.8292683 C39.195122,35.7246988 38.2996915,34.8292683 37.195122,34.8292683 L31.195122,34.8292683 C30.0905525,34.8292683 29.195122,35.7246988 29.195122,36.8292683 C29.195122,37.9338378 30.0905525,38.8292683 31.195122,38.8292683 Z"
108+
></path>
109+
</g>
110+
</svg>
111+
</div>
112+
</button>
113+
</div>
114+
)
115+
}
116+
117+
export default Front

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ 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 Front } from './components/Front'
910
export { default as HubSpot } from './components/HubSpot'
1011
export { default as LiveChatLoaderProvider } from './components/LiveChatLoaderProvider'

src/providers/front.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Website: https://front.com/product/live-chat
2+
// Documentation: https://help.front.com/
3+
4+
import { State } from '../types'
5+
import waitForLoad from '../utils/waitForLoad'
6+
7+
declare global {
8+
interface Window {
9+
FrontChat?: {
10+
(
11+
command: string,
12+
params?: Record<string, string | boolean | object>
13+
): void
14+
// This isn't part of the FrontChat API; we add it to track when Front is fully initialized
15+
hasInitialized: boolean
16+
}
17+
}
18+
}
19+
20+
const domain = 'https://chat-assets.frontapp.com'
21+
/* eslint-disable */
22+
const loadScript = (onload: () => void) => {
23+
if (window.FrontChat) return false
24+
25+
const script = () => {
26+
var s = document.createElement('script')
27+
s.type = 'text/javascript'
28+
s.async = true
29+
s.id = 'front-chat-script'
30+
s.src = `${domain}/v1/chat.bundle.js`
31+
var x = document.getElementsByTagName('script')[0]
32+
x.parentNode?.insertBefore(s, x)
33+
s.onload = onload
34+
}
35+
script()
36+
37+
return true
38+
}
39+
/* eslint-enable */
40+
41+
const load = ({
42+
providerKey,
43+
setState,
44+
beforeInit = () => undefined,
45+
onReady = () => undefined
46+
}: {
47+
providerKey: string
48+
setState: (state: State) => void
49+
beforeInit?: () => void
50+
onReady?: () => void
51+
}): boolean => {
52+
const loaded = loadScript(() => {
53+
beforeInit()
54+
55+
window.FrontChat?.('init', {
56+
chatId: providerKey,
57+
onInitCompleted: () => {
58+
setState('complete')
59+
onReady()
60+
if (window.FrontChat) window.FrontChat.hasInitialized = true
61+
}
62+
//Read more: https://dev.frontapp.com/docs/chat-sdk-reference
63+
})
64+
})
65+
return loaded
66+
}
67+
68+
const open = (): void => {
69+
waitForLoad(
70+
() => !!window.FrontChat?.hasInitialized,
71+
() => window.FrontChat?.('show')
72+
)
73+
}
74+
75+
export default {
76+
domain,
77+
load,
78+
open
79+
}

src/providers/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ export { default as helpScout } from './helpScout'
33
export { default as intercom } from './intercom'
44
export { default as messenger } from './messenger'
55
export { default as userlike } from './userlike'
6-
export { default as chatwoot} from './chatwoot'
6+
export { default as front } from './front'
7+
export { default as chatwoot } from './chatwoot'
78
export { default as hubSpot } from './hubSpot'

src/types.ts

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

website/components/exampleLinks.js

Lines changed: 49 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,43 +2,55 @@ import React from 'react'
22
import Link from 'next/link'
33

44
const ExampleLinks = () => (
5-
<ul>
6-
<li>
7-
<Link href="/intercom">
8-
<a>Intercom</a>
9-
</Link>
10-
</li>
11-
<li>
12-
<Link href="/helpscout">
13-
<a>Help Scout</a>
14-
</Link>
15-
</li>
16-
<li>
17-
<Link href="/drift">
18-
<a>Drift</a>
19-
</Link>
20-
</li>
21-
<li>
22-
<Link href="/messenger">
23-
<a>Facebook Messenger</a>
24-
</Link>
25-
</li>
26-
<li>
27-
<Link href="/userlike">
28-
<a>Userlike</a>
29-
</Link>
30-
</li>
31-
<li>
32-
<Link href="/chatwoot">
33-
<a>Chatwoot</a>
34-
</Link>
35-
</li>
36-
<li>
37-
<Link href="/hubspot">
38-
<a>HubSpot</a>
39-
</Link>
40-
</li>
41-
</ul>
5+
<>
6+
<ul>
7+
<li>
8+
<Link href="/intercom">
9+
<a>Intercom</a>
10+
</Link>
11+
</li>
12+
<li>
13+
<Link href="/helpscout">
14+
<a>Help Scout</a>
15+
</Link>
16+
</li>
17+
<li>
18+
<Link href="/drift">
19+
<a>Drift</a>
20+
</Link>
21+
</li>
22+
<li>
23+
<Link href="/messenger">
24+
<a>Facebook Messenger</a>
25+
</Link>
26+
</li>
27+
<li>
28+
<Link href="/userlike">
29+
<a>Userlike</a>
30+
</Link>
31+
</li>
32+
<li>
33+
<Link href="/chatwoot">
34+
<a>Chatwoot</a>
35+
</Link>
36+
</li>
37+
<li>
38+
<Link href="/front">
39+
<a>Front</a>
40+
</Link>
41+
</li>
42+
<li>
43+
<Link href="/hubspot">
44+
<a>HubSpot</a>
45+
</Link>
46+
</li>
47+
</ul>
48+
<p>
49+
Some providers may require the addition of a valid API key to their{' '}
50+
<code>providerKey</code>
51+
{''} prop in order for the demo to function correctly.
52+
</p>
53+
</>
4254
)
4355

4456
export default ExampleLinks

0 commit comments

Comments
 (0)