@kiwicom/smart-faq
Version:
Smart FAQ
315 lines (286 loc) • 8.96 kB
JavaScript
// @flow
import * as React from 'react';
import App, { Container } from 'next/app';
import reactTreeWalker from '@jaredlunde/react-tree-walker';
import { fetchQuery, ReactRelayContext } from 'react-relay';
import { Environment } from 'relay-runtime';
import { getTokens } from '@kiwicom/orbit-design-tokens';
import NavBar from '@kiwicom/nitro/lib/components/NavBar';
import Heading from '@kiwicom/orbit-components/lib/Heading';
import Translate from '@kiwicom/nitro/lib/components/Translate';
import InputField from '@kiwicom/orbit-components/lib/InputField';
import Search from '@kiwicom/orbit-components/lib/icons/Search';
import Stack from '@kiwicom/orbit-components/lib/Stack';
import { Consumer as IntlConsumer } from '@kiwicom/nitro/lib/services/intl/context';
import MobileDetect from 'mobile-detect';
import fallbackTranslations from '../translations/enKeys.json';
import { langInfos } from '../translations/langInfos';
import createEnvironment from '../shared/relay/environment';
import Contexts from '../shared/ssr/Contexts';
import { isBrowser } from '../shared/helpers';
import SSRContext from '../shared/ssr/SSRContext';
import countries from '../../data/countries.json';
import languages from '../../data/languages.json';
import airlines from '../../data/airlines.json';
import continents from '../../data/continents.json';
import brands from '../../data/brands.json';
import brandLanguages from '../../data/brandLanguages.json';
import GlobalStyles from '../FullPage/GlobalStyles';
const TRANSLATION_FILES = require('../../data/translationsFiles.json');
export const theme = { orbit: getTokens() };
const userContext = {
user: null,
brand: 'kiwicom',
loginToken: null,
simpleToken: null,
kwAuthToken: null,
onLogin: () => {},
onLogout: () => Promise.resolve(null),
};
const collectQueryDefinitions = async (
Component: React.ComponentType<Object>,
location,
screenWidth,
): Promise<Array<Object>> => {
const fetchers = [];
const WrappedComponent = () => (
<Contexts
location={location}
lng="en"
userContext={userContext}
theme={theme}
currencyId=""
countries={{}}
brand={{}}
language={{}}
fetched={{}}
intl={{}}
screenWidth={screenWidth}
>
<Component />
</Contexts>
);
await reactTreeWalker(<WrappedComponent />, (element, instance) => {
const fetcher = (value => value && value.fetchSSRData)(instance || element);
if (fetcher) {
fetchers.push(fetcher);
}
});
return fetchers.map(fetcher => fetcher());
};
const getTranslations = (phraseAppLanguageCode: string) => {
// eslint-disable-next-line import/no-dynamic-require
return require(`../../data/${TRANSLATION_FILES[phraseAppLanguageCode]}`);
};
type QueryDefinition = {|
query: string,
variables: {
[string]: mixed,
},
|};
class MyApp extends App {
static async fetchData(
environment: Environment,
query: string,
variablesMap: Object,
) {
try {
return await fetchQuery(environment, query, variablesMap);
} catch (e) {
console.warn('GraphQL error:', e); // eslint-disable-line no-console
return { relayFetchError: true };
}
}
static async getStaticInitialProps(
environment: Environment,
Props: { [string]: mixed },
) {
return {
dataProps: {},
variablesFromSSR: {},
recordStoreFromSSR: isBrowser()
? {}
: environment
.getStore()
.getSource()
.toJSON(),
...Props,
};
}
static async getDynamicInitialProps(
queryDefinitions: Array<QueryDefinition>,
environment: Environment,
ctx: Object,
Props: { [string]: mixed },
) {
const { variables } = queryDefinitions[0];
const dataProps = await this.fetchData(
environment,
queryDefinitions[0].query,
variables,
);
if (dataProps.relayFetchError && ctx.res) {
ctx.res.status(500);
}
return {
dataProps,
variablesFromSSR: variables,
recordStoreFromSSR: isBrowser()
? {}
: environment
.getStore()
.getSource()
.toJSON(),
...Props,
};
}
static getPhraseAppLanguageCode(lng: string) {
try {
return langInfos[lng].phraseApp;
} catch (e) {
return 'en-GB';
}
}
static async getTranslationData(ctx: Object) {
const lng = ctx.query.lng || 'en';
const phraseAppLanguageCode = this.getPhraseAppLanguageCode(lng);
return {
lng,
phraseAppLanguageCode,
translations: getTranslations(phraseAppLanguageCode),
};
}
static async getInitialProps({ Component, ctx }: Object) {
const fullPath = isBrowser() ? window.location.pathname : ctx?.req?.url;
const location = fullPath.slice(3);
const device = new MobileDetect(ctx.req.headers['user-agent']);
const screenWidth = device.is('mobile')
? 320
: device.is('tablet') ? 640 : 1280;
const queryDefinitions = await collectQueryDefinitions(
Component,
location,
screenWidth,
);
const Props = {
location,
pageProps: await this.getPageProps(Component, ctx),
screenWidth,
...(await this.getTranslationData(ctx)),
};
const environment = createEnvironment(null, null, Props.lng);
if (queryDefinitions.length === 0) {
return await this.getStaticInitialProps(environment, Props);
}
return await this.getDynamicInitialProps(
queryDefinitions,
environment,
ctx,
Props,
);
}
static async getPageProps(Component: Object, ctx: Object) {
if (Component.getInitialProps) {
return Component.getInitialProps(ctx);
}
return {};
}
environment = createEnvironment(
null,
null,
this.props.lng,
this.props.recordStoreFromSSR,
);
render() {
const { Component, pageProps, location, screenWidth } = this.props;
const langInfo = langInfos[this.props.lng];
const translations = this.props.translations
? this.props.translations
: fallbackTranslations;
const intl = { language: langInfo, translations };
const brandId = 'kiwicom';
const localeId = 'en';
const brand = brands[brandId];
const language = languages[localeId];
const fetched = {
brandLanguage: brandLanguages[brandId][localeId],
countries,
airlines,
continents,
};
const currencyId = 'eur';
return (
<Container>
<ReactRelayContext.Provider
value={{
environment: this.environment,
variables: this.props.variablesFromSSR,
}}
>
<SSRContext.Provider
value={{
data: this.props.dataProps,
}}
>
<Contexts
location={location}
lng={this.props.lng}
userContext={userContext}
theme={theme}
currencyId={currencyId}
language={language}
countries={countries}
brand={brand}
fetched={fetched}
intl={intl}
screenWidth={screenWidth}
>
<div>
<NavBar
headerLinks={<div />}
chat={<h1>Chat</h1>}
subscription={<h1>Subscription</h1>}
debug={<h1>Debug</h1>}
portal="modal-portal"
starred=""
onOpenFaq={() => undefined}
onSetModal={() => undefined}
onLogoClick={() => undefined}
onSaveLanguage={() => undefined}
onSelectTrip={() => undefined}
/>
<div className="SmartFAQ">
<Stack spaceAfter="largest">
<Heading type="display" element="h1" spaceAfter="medium">
<Translate
t={__('smartfaq.full_page.how_can_we_help_you')}
/>
</Heading>
<IntlConsumer>
{intl => (
<InputField
size="small"
type="text"
value=""
onChange={() => {}}
placeholder={intl.translate(
__('smartfaq.full_page.search.placeholder'),
)}
prefix={<Search />}
/>
)}
</IntlConsumer>
</Stack>
<Component {...pageProps} />
</div>
<GlobalStyles />
</div>
</Contexts>
<div id="modal-portal" />
</SSRContext.Provider>
</ReactRelayContext.Provider>
</Container>
);
}
}
export default MyApp;