-
Notifications
You must be signed in to change notification settings - Fork 271
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] HelpScout enhancements #830
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,67 +1,72 @@ | ||
import React, { useEffect } from 'react' | ||
import React, { useCallback, useEffect, useState } from 'react' | ||
import PropTypes from 'prop-types' | ||
import { Helmet } from 'react-helmet' | ||
import { noop, GU } from '../../utils' | ||
import { GU } from '../../utils' | ||
import { createGlobalStyle } from 'styled-components' | ||
|
||
const BEACON_EMBED = | ||
'!function(e,t,n){function a(){var e=t.getElementsByTagName("script")[0],n=t.createElement("script");n.type="text/javascript",n.async=!0,n.src="https://beacon-v2.helpscout.net",e.parentNode.insertBefore(n,e)}if(e.Beacon=n=function(t,n,a){e.Beacon.readyQueue.push({method:t,options:n,data:a})},n.readyQueue=[],"complete"===t.readyState)return a();e.attachEvent?e.attachEvent("onload",a):e.addEventListener("load",a,!1)}(window,document,window.Beacon||function(){});' | ||
const HELPSCOUT_ID = "'163e0284-762b-4e2d-b3b3-70a73a7e6c9f'" | ||
const BEACON_INIT = "window.Beacon('init'," + HELPSCOUT_ID + ')' | ||
const HELPSCOUT_ID = '163e0284-762b-4e2d-b3b3-70a73a7e6c9f' | ||
|
||
const BeaconHeadScripts = React.memo(({ optedIn, onReady }) => { | ||
function useHelpScoutBeacon(optedIn) { | ||
const [beaconInit, setBeaconInit] = useState(false) | ||
|
||
// Load the script if it doesn’t exist yet and optedIn | ||
// is true, then set the value of Beacon. | ||
useEffect(() => { | ||
let timeout = null | ||
if (optedIn) { | ||
;(function isBeaconReady() { | ||
if (window.Beacon) { | ||
onReady() | ||
return | ||
} | ||
timeout = setTimeout(isBeaconReady, 100) | ||
})() | ||
let script | ||
|
||
return () => clearTimeout(timeout) | ||
if (optedIn && !beaconInit) { | ||
if (!window.Beacon) { | ||
script = document.createElement('script') | ||
script.innerHTML = BEACON_EMBED | ||
document.body.appendChild(script) | ||
} | ||
window.Beacon('init', HELPSCOUT_ID) | ||
setBeaconInit(true) | ||
} | ||
}, [optedIn, onReady]) | ||
|
||
if (!optedIn) { | ||
return null | ||
} | ||
return () => script.remove() | ||
}, [optedIn, beaconInit]) | ||
|
||
return ( | ||
<Helmet> | ||
<script type="text/javascript">{BEACON_EMBED}</script> | ||
<script type="text/javascript">{BEACON_INIT}</script> | ||
<style> | ||
{` | ||
.BeaconFabButtonFrame, | ||
#beacon-container .Beacon div:first-of-type { | ||
display: none !important; | ||
} | ||
@media (min-width: 768px) { | ||
#beacon-container .BeaconContainer { | ||
height: 600px !important; | ||
width: 350px !important; | ||
top: unset !important; | ||
left: unset !important; | ||
bottom: 80px !important; | ||
right: ${3 * GU}px !important; | ||
} | ||
} | ||
`} | ||
</style> | ||
</Helmet> | ||
const beacon = useCallback( | ||
(...params) => { | ||
if (window.Beacon && optedIn && beaconInit) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens when this function is called but There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I need to check again, but IIRC this The
As a note, the reason why it there is a wrapping function here (instead of exposing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This is not in synch right? So, maybe there is some delay before the |
||
window.Beacon(...params) | ||
} | ||
}, | ||
[optedIn, beaconInit] | ||
) | ||
}) | ||
|
||
return beacon | ||
} | ||
|
||
const BeaconHeadScripts = ({ optedIn }) => { | ||
return optedIn ? <HelpscoutStyle /> : null | ||
} | ||
|
||
BeaconHeadScripts.propTypes = { | ||
optedIn: PropTypes.bool, | ||
onReady: PropTypes.func, | ||
} | ||
|
||
BeaconHeadScripts.defaultProps = { | ||
optedIn: false, | ||
onReady: noop, | ||
} | ||
|
||
const HelpscoutStyle = createGlobalStyle` | ||
.BeaconFabButtonFrame, | ||
#beacon-container .Beacon div:first-of-type { | ||
display: none !important; | ||
} | ||
@media (min-width: 768px) { | ||
#beacon-container .BeaconContainer { | ||
height: 600px !important; | ||
width: 350px !important; | ||
top: unset !important; | ||
left: unset !important; | ||
bottom: 80px !important; | ||
right: ${3 * GU}px !important; | ||
} | ||
} | ||
` | ||
export { useHelpScoutBeacon } | ||
export default BeaconHeadScripts |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,12 @@ | ||
import React from 'react' | ||
|
||
const IconQuestion = props => { | ||
return ( | ||
<svg | ||
width="15" | ||
height="26" | ||
viewBox="0 0 15 26" | ||
fill="none" | ||
xmlns="http://www.w3.org/2000/svg" | ||
{...props} | ||
> | ||
<path | ||
fillRule="evenodd" | ||
clipRule="evenodd" | ||
d="M9.00619 25.2001H5.8689V22.1497H9.00619V25.2001ZM9.55511 15.516L9.28063 15.7448C9.12349 15.8596 9.00615 16.1264 9.00615 16.3553V19.1381H5.86885V16.3553C5.86885 15.173 6.41781 14.0675 7.32016 13.3429L7.59464 13.1141C10.3394 10.9791 11.672 9.87356 11.672 7.96741C11.6766 6.87409 11.2319 5.82427 10.4368 5.05118C9.6417 4.27808 8.56196 3.84573 7.4375 3.85019C5.0063 3.85019 3.20297 5.60356 3.20297 7.96741H0.0656738C0.0656738 3.96428 3.32031 0.79981 7.4375 0.79981C9.39335 0.797505 11.2698 1.55192 12.6528 2.8966C14.0358 4.24129 14.8117 6.06573 14.8093 7.96741C14.8093 11.4368 12.3781 13.3429 9.55511 15.516Z" | ||
fill="white" | ||
/> | ||
</svg> | ||
) | ||
} | ||
const IconQuestion = props => ( | ||
<svg width={15} height={26} fill="none" viewBox="0 0 15 26" {...props}> | ||
<path | ||
d="M9.006 25.2H5.87v-3.05h3.137v3.05zm.55-9.684l-.275.229c-.158.115-.275.381-.275.61v2.783H5.87v-2.783c0-1.182.549-2.287 1.451-3.012l.275-.229c2.744-2.135 4.077-3.24 4.077-5.147a4.043 4.043 0 0 0-1.235-2.916 4.28 4.28 0 0 0-3-1.2c-2.43 0-4.234 1.753-4.234 4.116H.066C.066 3.964 3.32.8 7.438.8a7.471 7.471 0 0 1 5.215 2.097 7.06 7.06 0 0 1 2.156 5.07c0 3.47-2.43 5.376-5.254 7.549z" | ||
fill="#fff" | ||
/> | ||
</svg> | ||
) | ||
|
||
export default IconQuestion |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Super how this works!
I think this 2 actions (
init
andsetBeaconInit
) should be triggered in theonload
method from script no?Maybe attach the script to the head instead to the body?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On another note, by removing the
beaconReady
prop the loader is no longer shown while the script is loading, you can test this by throttling thenetwork
speed in chrome, let's figure a way to show theLoadingRing
to avoid the modal from disappearing and reappearing.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Beacon.init()
gets queued and it’s probably a good idea to keep it first, as the script will probably refuse to execute the other methods if they are called before.beaconInit
is used to know that the script is being initialized, so that we don’t inject the script twice.