Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
node_modules/
yarn.lock
package-lock.json
package-lock.json
.DS_Store
26 changes: 6 additions & 20 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,15 @@
"[typescriptreact]": {
"editor.formatOnSave": false
},
"eslint.validate": [
"javascript",
"javascriptreact",
"html",
{
"language": "typescript",
"autoFix": true
},
{
"language": "typescriptreact",
"autoFix": true
}
],
"eslint.autoFixOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll": true
},
// Optional BUT IMPORTANT: If you have the prettier extension enabled for other languages like CSS and HTML, turn it off for JS since we are doing it through Eslint already
"prettier.disableLanguages": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"typescriptreact"
],
"eslint.workingDirectories": [
"packages/web",
"packages/backend"
]
}
"eslint.workingDirectories": ["packages/web", "packages/backend"]
}
1 change: 1 addition & 0 deletions packages/web/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ ADIFY_GA_TRACKING_ID="Any Google Analytics code"
ADIFY_STEIN_API_KEY="some Stein API key"
ADIFY_STEIN_API_USERNAME="some Stein API username"
ADIFY_STEIN_API_PASSWORD="some Stein API password"
ADIFY_BACKEND_URL="http://localhost:4000"
1 change: 1 addition & 0 deletions packages/web/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module.exports = withSass({
ADIFY_STEIN_API_KEY: process.env.ADIFY_STEIN_API_KEY,
ADIFY_STEIN_API_USERNAME: process.env.ADIFY_STEIN_API_USERNAME,
ADIFY_STEIN_API_PASSWORD: process.env.ADIFY_STEIN_API_PASSWORD,
ADIFY_BACKEND_URL: process.env.ADIFY_BACKEND_URL,
},
cssModules: true,
})
19 changes: 16 additions & 3 deletions packages/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
"main": "index.js",
"license": "MIT",
"scripts": {
"dev": "npm run gulp && next -p 3001",
"start": "npm run gulp && next start",
"build": "npm run gulp && next build",
"dev": "NODE_ENV=development npm run gulp && next -p 3001",
"start": "NODE_ENV=production npm run gulp && next start",
"build": "NODE_ENV=production npm run gulp && next build",
"gulp": "gulp",
"gulp:watch": "gulp watch",
"lint": "eslint src/** pages/**",
Expand All @@ -15,12 +15,25 @@
"test:ci": "NODE_ENV=test jest --passWithNoTests"
},
"dependencies": {
"@apollo/react-hooks": "^3.1.3",
"@zeit/next-sass": "^1.0.1",
"apollo-cache-inmemory": "^1.6.3",
"apollo-client": "^2.6.4",
"apollo-link": "^1.2.13",
"apollo-link-context": "^1.0.19",
"apollo-link-error": "^1.1.12",
"apollo-link-http": "^1.5.16",
"dotenv": "^8.1.0",
"graphql-tag": "^2.10.1",
"isomorphic-fetch": "^2.2.1",
"isomorphic-unfetch": "^3.0.0",
"next": "^9.0.5",
"prop-types": "^15.7.2",
"react": "^16.9.0",
"react-apollo": "^3.1.3",
"react-dom": "^16.9.0",
"react-scroll": "^1.7.14",
"redux": "^4.0.4",
"stein-js-client": "0.0.2",
"styled-components": "^4.3.2"
},
Expand Down
3 changes: 2 additions & 1 deletion packages/web/pages/_app.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react'
import App, { Container } from 'next/app'

import Page from '../src/components/Page'
import withData from '../src/lib/withData'

class Wrapper extends App {
static async getInitialProps({ Component, ctx }) {
Expand Down Expand Up @@ -29,4 +30,4 @@ class Wrapper extends App {
}
}

export default Wrapper
export default withData(Wrapper)
23 changes: 23 additions & 0 deletions packages/web/pages/apolloTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react'
import gql from 'graphql-tag'
import { useQuery } from '@apollo/react-hooks'

const EXAMPLE_QUERY = gql`
query EXAMPLE_QUERY {
exampleQuery
}
`

export default function ApolloTestPage() {
const { data, loading, error } = useQuery(EXAMPLE_QUERY)

return (
<>
<h1>
{loading && 'Loading...'}
{data && data.exampleQuery}
{error && 'Some error occured. Check out the console.'}
</h1>
</>
)
}
4 changes: 2 additions & 2 deletions packages/web/src/components/Meta.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ class Meta extends Component {
<meta name="twitter:image" content={meta.coverImage} />
<meta name="twitter:image:alt" content={meta.meta_ogTitle} />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content={meta.social} />
<meta name="twitter:creator" content={meta.social} />
<meta name="twitter:site" content={`@${meta.social}`} />
<meta name="twitter:creator" content={`@${meta.social}`} />
</Head>
)
}
Expand Down
6 changes: 6 additions & 0 deletions packages/web/src/lib/apolloResolvers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const resolvers = {
Mutation: {},
Query: {},
}

export default resolvers
80 changes: 80 additions & 0 deletions packages/web/src/lib/initApollo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { ApolloClient } from 'apollo-client'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { HttpLink } from 'apollo-link-http'
import { ApolloLink } from 'apollo-link'
import { onError } from 'apollo-link-error'
import { setContext } from 'apollo-link-context'
import fetch from 'isomorphic-unfetch'

import resolvers from './apolloResolvers'

const httpLink = new HttpLink({
uri:
process.env.NODE_ENV !== 'production'
? `${process.env.ADIFY_BACKEND_URL}/api/graphql`
: `/api/graphql`,
// credentials: 'include', // Comment this out because it gave CORS error on localhost.
})

const cache = new InMemoryCache()

let link = null

if (process.browser) {
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
graphQLErrors.map(({ message, locations, path }) =>
// tslint:disable-next-line:no-console
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
)
)
}

if (networkError) {
// tslint:disable-next-line:no-console
console.log(`[Network error]: ${networkError}`)
}
})

link = ApolloLink.from([errorLink, httpLink])
}

let apolloClient = null

// Polyfill fetch() on the server (used by apollo-client)
if (!process.browser) {
global.fetch = fetch
}

function create(initialState, cookie = null) {
const authLink = setContext((_, { headers }) => ({
headers: {
...headers,
Cookie: cookie,
},
}))

return new ApolloClient({
connectToDevTools: process.browser,
ssrMode: !process.browser, // Disables forceFetch on the server (so queries are only run once)
link: process.browser ? link : authLink.concat(httpLink),
cache: cache.restore(initialState || {}),
resolvers,
})
}

export default function initApollo(initialState, cookie) {
// Make sure to create a new client for every server-side request so that data
// isn't shared between connections (which would be bad)
if (!process.browser) {
return create(initialState, cookie)
}

// Reuse client on the client-side
if (!apolloClient) {
apolloClient = create(initialState)
}

return apolloClient
}
40 changes: 40 additions & 0 deletions packages/web/src/lib/initRedux.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { createStore, combineReducers, applyMiddleware, compose } from 'redux'
import reducers from './redux/reducers'

let reduxStore = null

// Get the Redux DevTools extension and fallback to a no-op function
let devtools = f => f
if (process.browser && window.__REDUX_DEVTOOLS_EXTENSION__) {
devtools = window.__REDUX_DEVTOOLS_EXTENSION__()
}

function create(apollo, initialState = {}) {
return createStore(
combineReducers({
// Setup reducers
...reducers,
// apollo: apollo.reducer(),
}),
initialState, // Hydrate the store with server-side data
compose(
// applyMiddleware(apollo.middleware()), // Add additional middleware here
devtools
)
)
}

export default function initRedux(apollo, initialState) {
// Make sure to create a new store for every server-side request so that data
// isn't shared between connections (which would be bad)
if (!process.browser) {
return create(apollo, initialState)
}

// Reuse store on the client-side
if (!reduxStore) {
reduxStore = create(apollo, initialState)
}

return reduxStore
}
12 changes: 12 additions & 0 deletions packages/web/src/lib/redux/reducers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export default {
example: (state = {}, data) => {
switch (data.type) {
case 'EXAMPLE_ACTION':
return {
...state,
}
default:
return state
}
},
}
78 changes: 78 additions & 0 deletions packages/web/src/lib/withData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/* eslint-disable react/prop-types */

import React from 'react'
import Head from 'next/head'
import { ApolloProvider, getDataFromTree } from 'react-apollo'

import initApollo from './initApollo'

export default App =>
class Apollo extends React.Component {
static displayName = 'withApollo(App)'

static async getInitialProps(ctx) {
const { Component, router } = ctx

let appProps = {}
if (App.getInitialProps) {
appProps = await App.getInitialProps(ctx)
}

const apolloState = {}
const apollo = !process.browser
? initApollo(null, ctx.ctx.req.headers.cookie)
: initApollo()

// Run all GraphQL queries in the component tree
// and extract the resulting data
try {
// Run all GraphQL queries
await getDataFromTree(
<ApolloProvider client={apollo}>
<App
{...appProps}
Component={Component}
router={router}
apolloState={apolloState}
apolloClient={apollo}
/>
</ApolloProvider>
)
} catch (error) {
// Prevent Apollo Client GraphQL errors from crashing SSR.
// Handle them in components via the data.error prop:
// http://dev.apollodata.com/react/api-queries.html#graphql-query-data-error
console.error('Error while running `getDataFromTree`', error)
}

if (!process.browser) {
// getDataFromTree does not call componentWillUnmount
// head side effect therefore need to be cleared manually
Head.rewind()
}

// Extract query data from the Apollo store
apolloState.data = apollo.cache.extract()

return {
...appProps,
apolloState,
}
}

constructor(props) {
super(props)
// `getDataFromTree` renders the component first, the client is passed off as a property.
// After that rendering is done using Next's normal rendering pipeline
this.apolloClient =
props.apolloClient || initApollo(props.apolloState.data)
}

render() {
return (
<ApolloProvider client={this.apolloClient}>
<App {...this.props} apolloClient={this.apolloClient} />
</ApolloProvider>
)
}
}
Loading