import React from 'react'
import fetcher from 'isomorphic-unfetch'
import { ApolloLink, split } from 'apollo-link' // yarn add apollo-link
import { getMainDefinition } from 'apollo-utilities'
import { ApolloProvider } from '@apollo/react-hooks'
import { ApolloClient } from 'apollo-client'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { createHttpLink } from 'apollo-link-http'
import { setContext } from 'apollo-link-context'
import { onError } from 'apollo-link-error'
import { UserManager, AppManager } from '@app/managers'
import { AppConfig } from '@app/configs/app-config'
import { resolvers } from './resolvers'
import { WebSocketLink } from './ws-link'
import { upgradeRequired } from '@app/managers/app'
const createLink = (): ApolloLink => {
const httpLink = createHttpLink({
uri: AppConfig.graphQLUrl,
fetch: fetcher,
credentials: 'include',
})
const authLink = setContext((_: any, { headers }: any) => {
return {
headers: Object.assign({}, headers, {
authorization: UserManager?.token ? `Bearer ${UserManager?.token}` : '',
}),
}
})
const errorLink = onError(({ graphQLErrors, networkError }: any) => {
if (
typeof window !== 'undefined' &&
window.location.pathname !== '/api-info'
) {
let graphErrors = ''
graphQLErrors?.forEach(({ message }: { message?: string }) => {
if (message) {
const invalidSignature =
message.includes('Context creation failed: invalid signature') ||
message.includes('Error please sign in to continue')
// TODO: CLEAR AUTH SS FOR USER AND redirect CS
if (message.includes('JWT:') || invalidSignature) {
UserManager.clearUser()
window.location.href = '/'
}
const errorMessage = invalidSignature ? 'Please re-login' : message
graphErrors += `${errorMessage}\n`
}
})
if (graphErrors) {
// ignore api info error for displaying jwt
let errors = graphErrors.trim()
if (
typeof window !== 'undefined' &&
window.location.pathname === '/api-info' &&
errors ===
'Authorization token not found. Please add your authorization header and try again.'
) {
return
}
AppManager.toggleSnack(true, errors, 'error', upgradeRequired(errors))
}
if (networkError) {
console.error(`[Network error]:`, networkError)
}
}
})
let httpSplit = httpLink
if (typeof window !== 'undefined') {
const wsLink = new WebSocketLink({
url: AppConfig.webSocketUrl + '',
connectionParams: {
authorization: UserManager?.token ? `Bearer ${UserManager?.token}` : '',
},
})
httpSplit = split(
({ query }: any) => {
const definition = getMainDefinition(query)
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
)
},
// @ts-ignore
wsLink,
httpLink
)
}
return ApolloLink.from([errorLink, authLink, httpSplit])
}
function createApolloClient(initialState: any = {}) {
const link = createLink()
return new ApolloClient({
ssrMode: false,
link,
cache: new InMemoryCache({
dataIdFromObject: (object: any) => object._id || object.id || null,
}).restore(initialState),
resolvers,
})
}
export function withApollo(
PageComponent: any,
defaultProps: { ssr: boolean } = { ssr: false }
) {
const WithApollo = ({ apolloClient, apolloState, ...pageProps }: any) => {
const client = apolloClient || createApolloClient(apolloState)
return (
)
}
WithApollo.displayName = PageComponent.name || PageComponent.displayName
if (PageComponent.meta) {
WithApollo.meta = PageComponent.meta
}
if (defaultProps.ssr || PageComponent.getInitialProps) {
WithApollo.getInitialProps = async (ctx: any) => {
const { AppTree } = ctx
if (!ctx.apolloClient) {
ctx.apolloClient = createApolloClient()
}
const apolloClient = ctx?.apolloClient
let pageProps = {}
if (PageComponent.getInitialProps) {
try {
pageProps = await PageComponent.getInitialProps(ctx)
} catch (e) {
console.error(e)
}
}
if (typeof window === 'undefined') {
if (ctx?.res?.finished) {
return pageProps
}
if (defaultProps.ssr) {
try {
const { getDataFromTree } = await import('@apollo/react-ssr')
await getDataFromTree(
)
} catch (error) {
console.error('Error while running `getDataFromTree`', error)
}
}
}
return Object.freeze(
Object.assign({}, pageProps, {
apolloState: apolloClient.cache.extract(),
})
)
}
}
return WithApollo
}