diff --git a/packages/types/index.d.ts b/packages/types/index.d.ts index fc5be730..145c68a6 100644 --- a/packages/types/index.d.ts +++ b/packages/types/index.d.ts @@ -82,6 +82,7 @@ export interface IFlow { export interface IUser { id: string; + fullName: string; email: string; password: string; connections: IConnection[]; diff --git a/packages/web/src/components/LiveChat/Chatwoot.ee.tsx b/packages/web/src/components/LiveChat/Chatwoot.ee.tsx new file mode 100644 index 00000000..a45f7f3e --- /dev/null +++ b/packages/web/src/components/LiveChat/Chatwoot.ee.tsx @@ -0,0 +1,41 @@ +import * as React from 'react'; + +import appConfig from 'config/app'; +import useCurrentUser from 'hooks/useCurrentUser'; + +type ChatwootProps = { + ready: boolean; +} + +const Chatwoot = ({ ready }: ChatwootProps) => { + const currentUser = useCurrentUser(); + + React.useEffect(function initiateChatwoot() { + window.chatwootSDK.run({ + websiteToken: 'EFyq5MTsvS7XwUrwSH36VskT', + baseUrl: appConfig.chatwootBaseUrl, + }); + + return function removeChatwoot() { + window.$chatwoot.reset(); + window.$chatwoot.toggleBubbleVisibility('hide'); + }; + }, []); + + React.useEffect(function initiateUser() { + if (!currentUser?.id || !ready) return; + + window.$chatwoot.setUser(currentUser.id, { + email: currentUser.email, + name: currentUser.fullName, + }); + + window.$chatwoot.toggleBubbleVisibility("show"); + }, [currentUser, ready]); + + return ( + + ); +}; + +export default Chatwoot; diff --git a/packages/web/src/components/LiveChat/index.ee.tsx b/packages/web/src/components/LiveChat/index.ee.tsx new file mode 100644 index 00000000..6af0df30 --- /dev/null +++ b/packages/web/src/components/LiveChat/index.ee.tsx @@ -0,0 +1,62 @@ +import * as React from 'react'; + +import appConfig from 'config/app'; +import useAuthentication from 'hooks/useAuthentication'; +import useCloud from 'hooks/useCloud'; +import Chatwoot from './Chatwoot.ee'; + +declare global { + interface Window { + chatwootSDK: any; + $chatwoot: any; + chatwootSettings: Record; + } +} + +const LiveChat = () => { + const isCloud = useCloud(); + const { isAuthenticated } = useAuthentication(); + const [isLoaded, setLoaded] = React.useState(false); + const [isReady, setReady] = React.useState(false); + + const shouldShow = isCloud && isAuthenticated; + + React.useLayoutEffect(() => { + window.addEventListener("chatwoot:ready", function () { + setReady(true); + }, { once: true }); + }, []); + + React.useLayoutEffect(function addChatwootScript() { + if (!shouldShow) return; + + window.chatwootSettings = { + hideMessageBubble: true, + position: 'right', + type: 'standard', + launcherTitle: 'Give us feedback' + }; + + const g = document.createElement('script') + const s = document.getElementsByTagName('script')[0]; + g.src = appConfig.chatwootBaseUrl + '/packs/js/sdk.js'; + g.defer = true; + g.async = true; + + if (s.parentNode) { + s.parentNode.insertBefore(g, s); + } + + g.onload = function () { + setLoaded(true); + } + }, [shouldShow]); + + if (!shouldShow || !isLoaded) return (); + + return ( + + ); +}; + +export default LiveChat; diff --git a/packages/web/src/config/app.ts b/packages/web/src/config/app.ts index aefd1a8f..184dbe2b 100644 --- a/packages/web/src/config/app.ts +++ b/packages/web/src/config/app.ts @@ -6,6 +6,7 @@ const config: Config = { baseUrl: process.env.REACT_APP_BASE_URL as string, graphqlUrl: process.env.REACT_APP_GRAPHQL_URL as string, notificationsUrl: process.env.REACT_APP_NOTIFICATIONS_URL as string, + chatwootBaseUrl: 'https://app.chatwoot.com', }; export default config; diff --git a/packages/web/src/graphql/link.ts b/packages/web/src/graphql/link.ts index 2f8a874a..73a04269 100644 --- a/packages/web/src/graphql/link.ts +++ b/packages/web/src/graphql/link.ts @@ -33,13 +33,16 @@ const createErrorLink = (callback: CreateLinkOptions['onError']): ApolloLink => callback?.(message); } - console.log( + console.error( `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}` ); if (message === NOT_AUTHORISED) { setItem('token', ''); - window.location.href = URLS.LOGIN; + + if (window.location.pathname !== URLS.LOGIN) { + window.location.href = URLS.LOGIN; + } } }); @@ -47,12 +50,13 @@ const createErrorLink = (callback: CreateLinkOptions['onError']): ApolloLink => if (autoSnackbar) { callback?.(networkError.toString()); } - console.log(`[Network error]: ${networkError}`); + + console.error(`[Network error]: ${networkError}`); } }); // eslint-disable-next-line @typescript-eslint/no-empty-function -const noop = () => {}; +const noop = () => { }; const createLink = (options: CreateLinkOptions): ApolloLink => { const { uri, onError = noop, token } = options; diff --git a/packages/web/src/index.tsx b/packages/web/src/index.tsx index 946c4beb..502bd72e 100644 --- a/packages/web/src/index.tsx +++ b/packages/web/src/index.tsx @@ -7,6 +7,7 @@ import SnackbarProvider from 'components/SnackbarProvider'; import { AuthenticationProvider } from 'contexts/Authentication'; import { AutomatischInfoProvider } from 'contexts/AutomatischInfo'; import Router from 'components/Router'; +import LiveChat from 'components/LiveChat/index.ee'; import routes from 'routes'; import reportWebVitals from './reportWebVitals'; @@ -18,6 +19,8 @@ ReactDOM.render( {routes} + +