@kiwicom/smart-faq
Version:
Smart FAQ
203 lines (178 loc) • 5.35 kB
JavaScript
// @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 { MemoryRouter } from 'react-router-dom';
import { ThemeProvider } from 'styled-components';
import { getTokens } from '@kiwicom/orbit-design-tokens';
import * as intlContext from '@kiwicom/nitro/lib/services/intl/context';
import InitIntl from '@kiwicom/nitro/lib/components/InitIntl';
import SSRContext from '../shared/ssr/SSRContext';
import { isBrowser } from '../shared/helpers';
import RelayProvider from '../shared/ssr/RelayProvider';
import createEnvironment from '../shared/relay/environment';
import { langInfos } from '../translations/langInfos';
import fallbackTranslations from '../translations/enKeys.json';
const TRANSLATION_FILES = require('../../data/translationsFiles.json');
export const theme = { orbit: getTokens() };
const collectQueryDefinitions = async (
Component: React.ComponentType<Object>,
): Promise<Array<Object>> => {
const fetchers = [];
await reactTreeWalker(<Component />, (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 queryDefinitions = await collectQueryDefinitions(Component);
const Props = {
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 } = this.props;
const langInfo = langInfos[this.props.lng];
const translations = this.props.translations
? this.props.translations
: fallbackTranslations;
const intl = { language: langInfo, translations };
return (
<Container>
<RelayProvider
environment={this.environment}
variables={this.props.variablesFromSSR}
>
<SSRContext.Provider
value={{
data: this.props.dataProps,
}}
>
<InitIntl raw={intl}>
{intl => (
<intlContext.Provider value={intl}>
<MemoryRouter>
<ThemeProvider theme={theme}>
<Component {...pageProps} />
</ThemeProvider>
</MemoryRouter>
</intlContext.Provider>
)}
</InitIntl>
</SSRContext.Provider>
</RelayProvider>
</Container>
);
}
}
export default MyApp;