content: import React from 'react'
  import PropTypes from 'prop-types'
  
  import storage from './storage'
  
  /*
  requests: array = [
  	{
  		propName: string
  		query: string,
  		mapServerData: func // Not required
  	}
  ]
  */
  export default function githubClient(requests) {
  	return function(Component) {
  		return class extends React.Component {
  			static PropTypes = {
  				token: PropTypes.string.required
  			}
  
  			state = {
  				...requests.reduce((state, request) => {
  					state[request.propName] = {
  						loading: true,
  						error: undefined,
  						value: undefined,
  						reload: this.reload.bind(this, request)
  					}
  					return state
  				}, {})
  			}
  
  			componentDidMount() {
  				requests.forEach(async ({ propName, ...restOfRequest }) => {
  					try {
  						let data = this.props[propName]
  						if (data === undefined) {
  							const storageResults = await storage.get(propName)
  							data = storageResults[propName]
  						}
  						if (data === undefined) {
  							data = await this.fetchData({ propName, ...restOfRequest })
  						}
  						this.updateStatePropWithData(propName, data)
  					} catch(error) {
  						this.updateStatePropWithError(propName, error)
  					}
  				})
  			}
  
  			render() {
  				return (
  					<Component
  						{ ...this.props }
  						{ ...this.state }
  					/>
  				)
  			}
  
  			async fetchData({ propName, query, mapServerData = i => i }) {
  				const { token } = this.props
  				if (!token) throw Error('Personal access token required')
  				const response = await fetch('https://api.github.com/graphql', {
  					method: 'POST',
  					headers: {
  						'Content-Type': 'application/json',
  						'Authorization': `bearer ${ token }`
  					},
  					body: JSON.stringify({ query })
  				})
  				let data = await response.json()
  				if (data.message) throw Error(data.message)
  				data = mapServerData(data)
  				await storage.set({ [propName]: data })
  				return data
  			}
  
  			async reload({ propName, ...restOfRequest }) {
  				this.setState({
  					[propName]: {
  						...this.state[propName],
  						loading: true,
  						error: undefined,
  						value: undefined
  					}
  				})
  				await storage.remove(propName)
  				try {
  					const data = await this.fetchData({ propName, ...restOfRequest })
  					this.updateStatePropWithData(propName, data)
  				} catch(error) {
  					this.updateStatePropWithError(propName, error)
  				}
  			}
  
  			updateStatePropWithError(propName, error) {
  				this.setState({
  					[propName]: {
  						...this.state[propName],
  						error,
  						loading: false
  					}
  				})
  			}
  
  			updateStatePropWithData(propName, data) {
  				this.setState({
  					[propName]: {
  						...this.state[propName],
  						value: data,
  						error: undefined,
  						loading: false
  					}
  				})
  			}
  		}
  	}
  }
  