@kiwicom/smart-faq
Version:
Smart FAQ
249 lines (226 loc) • 7.84 kB
JavaScript
// @flow
/* eslint-disable jsx-a11y/no-static-element-interactions */
import * as React from 'react';
import css from 'styled-jsx/css';
import classNames from 'classnames';
import 'url-search-params-polyfill';
import EventListener, { withOptions } from 'react-event-listener';
import InitIntl from '@kiwicom/nitro/lib/components/InitIntl';
import * as intlContext from '@kiwicom/nitro/lib/services/intl/context';
import Routes from './SmartFAQ/Routes';
import { CloseContext } from './SmartFAQ/context/Close';
import { LanguageContext } from './SmartFAQ/context/Language';
import { UserContext } from './SmartFAQ/context/User';
import PageVariantContext from './SmartFAQ/context/PageVariant';
import BookingStateProvider from './SmartFAQ/context/BookingState';
import type { UserContextType } from './SmartFAQ/context/User';
import SearchStateProvider from './SmartFAQ/context/SearchState';
import SelectedBookingProvider from './SmartFAQ/context/SelectedBooking';
import ExtraInfoStateProvider from './SmartFAQ/context/ExtraInfoState';
import Emergencies from './SmartFAQ/context/Emergencies';
import ErrorBoundary from './SmartFAQ/common/ErrorBoundary';
import {
LogladyTracker,
LogladyTimeTracker,
EnterTracker,
TimeTracker,
} from './shared/helpers/analytics/trackers';
import type { SmartFAQProps } from './types';
import MobileSafariScroll from './SmartFAQ/helpers/MobileSafariScroll';
import GuaranteeChatInfo from './shared/context/GuaranteeChatInfo';
import { langInfos } from './translations/langInfos';
import fallbackTranslations from './translations/enKeys.json';
import { setTracker } from './shared/cuckoo/tracker';
const style = css`
.smartFAQ {
display: none;
position: fixed;
min-width: 480px;
top: 0;
bottom: 0;
right: 0;
background-color: #fff;
font-family: 'Roboto', sans-serif;
}
.smartFAQ.open {
display: block;
}
.smartFAQ * {
box-sizing: border-box;
font-family: 'Roboto', sans-serif;
}
@media only screen and (max-width: 901px) {
.smartFAQ {
min-width: 100%;
}
}
`;
type State = {|
userContext: UserContextType,
|};
class SmartFAQApp extends React.PureComponent<SmartFAQProps, State> {
static getDerivedStateFromProps(nextProps: SmartFAQProps) {
if (nextProps.cuckoo) {
setTracker(nextProps.cuckoo);
}
return {
userContext: {
user: nextProps.user,
onLogin: nextProps.onLogin,
onLogout: nextProps.onLogout,
loginToken: nextProps.loginToken,
simpleToken: nextProps.simpleToken,
kwAuthToken: nextProps.kwAuthToken,
brand: nextProps.brand,
},
};
}
constructor(props: SmartFAQProps) {
super(props);
this.state = {
userContext: {
user: props.user,
onLogin: props.onLogin,
onLogout: props.onLogout,
loginToken: props.loginToken,
simpleToken: props.simpleToken,
kwAuthToken: props.kwAuthToken,
brand: props.brand,
},
};
if (props.cuckoo) {
setTracker(props.cuckoo);
}
}
onKeyDown = (e: KeyboardEvent) => {
// $FlowExpectedError: I know "noInputFocus" property doesn't exist...
e.noInputFocus = true;
};
renderApp() {
const { route, emergencies, language } = this.props;
const isOpen = Boolean(route);
const langInfo = langInfos[language];
const translations = this.props.translations
? this.props.translations
: fallbackTranslations;
const intl = { language: langInfo, translations };
return (
<div
className={classNames('smartFAQ', { open: isOpen })}
data-test="SmartFAQHelp"
>
<meta
name="viewport"
content="width=device-width, initial-scale=1 shrink-to-fit=no"
/>
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
<link
href="https://fonts.googleapis.com/css?family=Roboto"
rel="stylesheet"
/>
<ErrorBoundary>
<InitIntl raw={intl}>
{intl => (
<intlContext.Provider value={intl}>
<LanguageContext.Provider value={language}>
<CloseContext.Provider value={this.props.onClose}>
<UserContext.Provider value={this.state.userContext}>
<SearchStateProvider>
<Emergencies.Provider value={emergencies}>
<SelectedBookingProvider bid={this.props.bid}>
<BookingStateProvider
onLogout={this.props.onLogout}
showBooking={this.props.showBooking}
>
<ExtraInfoStateProvider>
<GuaranteeChatInfo
isChatActive={this.props.isChatActive}
enableChat={this.props.enableChat}
chatConfig={this.props.chatConfig}
onToggleIsClosable={
this.props.onToggleIsClosable
}
>
{isOpen &&
route && (
<EventListener
target="window"
onKeydown={withOptions(this.onKeyDown, {
capture: true,
})}
>
<PageVariantContext.Provider
value={{ variant: 'sidebar' }}
>
<Routes route={route} />
</PageVariantContext.Provider>
</EventListener>
)}
</GuaranteeChatInfo>
</ExtraInfoStateProvider>
</BookingStateProvider>
</SelectedBookingProvider>
</Emergencies.Provider>
</SearchStateProvider>
</UserContext.Provider>
</CloseContext.Provider>
</LanguageContext.Provider>
</intlContext.Provider>
)}
</InitIntl>
</ErrorBoundary>
<style jsx global>
{style}
</style>
<style jsx global>
{`
body {
overflow-y: ${isOpen ? 'hidden' : 'auto'};
}
`}
</style>
<MobileSafariScroll isOpen={isOpen} />
</div>
);
}
render() {
if (typeof window !== 'undefined' && window.Raven) {
return window.Raven.context(() => this.renderApp());
}
return this.renderApp();
}
}
const isLoggedIn = props =>
!!props.user || !!props.simpleToken || !!props.kwAuthToken;
const LogladyTrackedApp = LogladyTracker(
SmartFAQApp,
'Smart FAQ',
'clickOnHelp',
props => ({
loggedIn: isLoggedIn(props),
}),
);
const EnterTrackedApp = EnterTracker(LogladyTrackedApp, 'smartFAQ', props => ({
action: 'clickOnHelp',
loggedIn: isLoggedIn(props),
}));
const LogladyTimeTrackedApp = LogladyTimeTracker(
EnterTrackedApp,
'Smart FAQ',
'clickOnHelp',
props => ({
loggedIn: isLoggedIn(props),
}),
);
const TimeTrackedApp = TimeTracker(
LogladyTimeTrackedApp,
'smartFAQ',
props => ({
action: 'close',
loggedIn: isLoggedIn(props),
}),
);
if (typeof window !== 'undefined') {
window.SmartFAQApp = TimeTrackedApp;
}
export default TimeTrackedApp;