UNPKG

@kiwicom/smart-faq

Version:

Smart FAQ

368 lines (338 loc) 13.8 kB
// @flow import * as React from 'react'; import App, { Container } from 'next/app'; import reactTreeWalker from 'react-tree-walker'; import { fetchQuery } from 'react-relay'; import { Environment } from 'relay-runtime'; import { StaticRouter, BrowserRouter } from 'react-router-dom'; import { ThemeProvider, ThemeConsumer } from 'styled-components'; import { getTokens } from '@kiwicom/orbit-design-tokens'; import InitIntl from '@kiwicom/nitro/lib/components/InitIntl'; import { Provider as BrandProvider } from '@kiwicom/nitro/lib/services/brand/context'; import { Provider as IntlProvider } from '@kiwicom/nitro/lib/services/intl/context'; import { Provider as FetchedProvider } from '@kiwicom/nitro/lib/services/fetched/context'; import { Provider as CurrencyProvider } from '@kiwicom/nitro/lib/services/currency/context'; import ModalValue from '@kiwicom/nitro/lib/components/Value'; import { Provider as ModalProvider } from '@kiwicom/nitro/lib/services/modal/context'; import InitCurrency from '@kiwicom/nitro/lib/components/InitCurrency'; import NavBar from '@kiwicom/nitro/lib/components/NavBar'; import PageVariantContext from '../SmartFAQ/context/PageVariant'; import { UserContext } from '../SmartFAQ/context/User'; import fallbackTranslations from '../translations/enKeys.json'; import { langInfos } from '../translations/langInfos'; import createEnvironment from '../shared/relay/environment'; import RelayProvider from '../shared/ssr/RelayProvider'; import { isBrowser } from '../shared/helpers'; import SSRContext from '../shared/ssr/SSRContext'; import countries from './countries.json'; import languages from './languages.json'; import airlines from './airlines.json'; import continents from './continents.json'; import brands from './brands.json'; import brandLanguages from './brandLanguages.json'; const TRANSLATION_FILES = require('../../data/translationsFiles.json'); export const theme = { orbit: getTokens() }; const collectQueryDefinitions = async ( Component: React.ComponentType<Object>, location, ): Promise<Array<Object>> => { const fetchers = []; const WrappedComponent = () => ( <StaticRouter basename="" location={location} context={{}}> <Component /> </StaticRouter> ); 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, }, |}; const IsomorphicRouter = props => isBrowser() ? ( <BrowserRouter {...props}>{props.children}</BrowserRouter> ) : ( <StaticRouter {...props}>{props.children}</StaticRouter> ); 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 queryDefinitions = await collectQueryDefinitions(Component, location); const Props = { location, pageProps: await this.getPageProps(Component, ctx), ...(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 } = 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'; const userContext = { user: null, brand: 'kiwicom', loginToken: null, simpleToken: null, kwAuthToken: null, onLogin: () => {}, onLogout: () => Promise.resolve(null), }; return ( <Container> <BrandProvider value={brand}> <RelayProvider environment={this.environment} variables={this.props.variablesFromSSR} > <SSRContext.Provider value={{ data: this.props.dataProps, }} > <InitIntl raw={intl}> {intl => ( <IntlProvider value={intl}> <FetchedProvider value={fetched}> <InitCurrency brand={brand} countries={countries} affiliate="" ip="1.3.3.7" initialCurrency="EUR" langCurrency={language.currency} onChange={() => undefined} > {currency => ( <CurrencyProvider value={{ ...currency, currency: currency.available[currencyId] || currency.currency, }} > <ModalValue> {modal => ( <ModalProvider value={modal}> <IsomorphicRouter basename={`/${this.props.lng}/`} location={location} context={{}} > <ThemeProvider theme={theme}> <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} /> <UserContext.Provider value={userContext} > <div className="SmartFAQ"> <PageVariantContext.Provider value={{ variant: 'fullPage' }} > <Component {...pageProps} /> </PageVariantContext.Provider> </div> <ThemeConsumer> {// eslint-disable-next-line react/no-danger theme => ( <style dangerouslySetInnerHTML={{ __html: ` .SmartFAQMarkdownField { font-family: ${ theme.orbit.fontFamily }; font-size: ${ theme.orbit .fontSizeTextNormal }; font-weight: ${ theme.orbit .fontWeightNormal }; color: ${ theme.orbit .colorTextPrimary }; line-height: ${ theme.orbit .lineHeightText }; text-align: left; margin: 0; } .SmartFAQ { max-width: 740px; margin: auto; font-family: ${ theme.orbit.fontFamily }; } body { padding-top: 92px; padding-bottom: 40px; background: ${ theme.orbit .backgroundBody }; } `, }} /> )} </ThemeConsumer> </UserContext.Provider> </div> </ThemeProvider> </IsomorphicRouter> <div id="modal-portal" /> </ModalProvider> )} </ModalValue> </CurrencyProvider> )} </InitCurrency> </FetchedProvider> </IntlProvider> )} </InitIntl> </SSRContext.Provider> </RelayProvider> </BrandProvider> </Container> ); } } export default MyApp;