1
0
mirror of https://github.com/kremalicious/portfolio.git synced 2025-02-14 21:10:41 +01:00

Merge pull request #32 from kremalicious/feature/context

setup global store
This commit is contained in:
Matthias Kretschmann 2018-09-17 01:09:23 +02:00 committed by GitHub
commit c1321c2a9e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 149 additions and 100 deletions

View File

@ -40,7 +40,7 @@ The whole [portfolio](https://matthiaskretschmann.com) is a React-based Single P
### Lighthouse score ### Lighthouse score
[![Lighthouse scores](https://lighthouse.now.sh/?perf=99&pwa=100&a11y=100&bp=100&seo=100)](https://travis-ci.com/kremalicious/portfolio) [![Lighthouse scores](https://lighthouse.now.sh/?perf=100&pwa=100&a11y=100&bp=100&seo=100)](https://travis-ci.com/kremalicious/portfolio)
### One data file to rule all pages ### One data file to rule all pages

View File

@ -1,3 +1,5 @@
import React from 'react'
import AppProvider from './src/store/provider'
import wrapPageElementWithTransition from './src/helpers/wrapPageElement' import wrapPageElementWithTransition from './src/helpers/wrapPageElement'
import './src/styles/global.scss' import './src/styles/global.scss'
@ -6,5 +8,11 @@ if (typeof window !== 'undefined' && !window.IntersectionObserver) {
import('intersection-observer') import('intersection-observer')
} }
// React Context in Browser
// eslint-disable-next-line
export const wrapRootElement = ({ element }) => {
return <AppProvider>{element}</AppProvider>
}
// Page Transitions & Layout // Page Transitions & Layout
export const wrapPageElement = wrapPageElementWithTransition export const wrapPageElement = wrapPageElementWithTransition

View File

@ -1,4 +1,13 @@
import React from 'react'
import { renderToString } from 'react-dom/server'
import AppProvider from './src/store/provider'
import wrapPageElementWithTransition from './src/helpers/wrapPageElement' import wrapPageElementWithTransition from './src/helpers/wrapPageElement'
export const replaceRenderer = ({ bodyComponent, replaceBodyHTMLString }) => {
// React Context in SSR/build
const ConnectedBody = () => <AppProvider>{bodyComponent}</AppProvider>
replaceBodyHTMLString(renderToString(<ConnectedBody />))
}
// Page Transitions & Layout // Page Transitions & Layout
export const wrapPageElement = wrapPageElementWithTransition export const wrapPageElement = wrapPageElementWithTransition

View File

@ -1,7 +1,7 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { StaticQuery, graphql } from 'gatsby' import { StaticQuery, graphql } from 'gatsby'
import posed, { PoseGroup } from 'react-pose' import posed from 'react-pose'
import { fadeIn } from '../atoms/Transitions' import { fadeIn } from '../atoms/Transitions'
import styles from './Availability.module.scss' import styles from './Availability.module.scss'
@ -32,21 +32,19 @@ export default class Availability extends PureComponent {
return ( return (
!this.props.hide && ( !this.props.hide && (
<PoseGroup animateOnMount={true}> <Animation
<Animation className={
className={ status
status ? `${styles.availability} ${styles.available}`
? `${styles.availability} ${styles.available}` : `${styles.availability}`
: `${styles.availability}` }
} >
> <p
<p dangerouslySetInnerHTML={{
dangerouslySetInnerHTML={{ __html: status ? available : unavailable
__html: status ? available : unavailable }}
}} />
/> </Animation>
</Animation>
</PoseGroup>
) )
) )
}} }}

View File

@ -1,7 +1,7 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { StaticQuery, graphql } from 'gatsby' import { StaticQuery, graphql } from 'gatsby'
import posed, { PoseGroup } from 'react-pose' import posed from 'react-pose'
import { moveInBottom } from '../atoms/Transitions' import { moveInBottom } from '../atoms/Transitions'
import Logo from '../svg/Logo' import Logo from '../svg/Logo'
@ -48,19 +48,17 @@ export default class LogoUnit extends PureComponent {
const { minimal } = this.state const { minimal } = this.state
return ( return (
<PoseGroup animateOnMount={true}> <Animation>
<Animation> <div className={minimal ? styles.minimal : styles.logounit}>
<div className={minimal ? styles.minimal : styles.logounit}> <Logo className={styles.logounit__logo} />
<Logo className={styles.logounit__logo} /> <h1 className={styles.logounit__title}>
<h1 className={styles.logounit__title}> {title.toLowerCase()}
{title.toLowerCase()} </h1>
</h1> <p className={styles.logounit__description}>
<p className={styles.logounit__description}> {tagline.toLowerCase()}
{tagline.toLowerCase()} </p>
</p> </div>
</div> </Animation>
</Animation>
</PoseGroup>
) )
}} }}
/> />

View File

@ -1,7 +1,7 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { StaticQuery, graphql } from 'gatsby' import { StaticQuery, graphql } from 'gatsby'
import posed, { PoseGroup } from 'react-pose' import posed from 'react-pose'
import { moveInTop } from '../atoms/Transitions' import { moveInTop } from '../atoms/Transitions'
import Email from '../svg/Email' import Email from '../svg/Email'
@ -81,16 +81,14 @@ export default class Network extends PureComponent {
return ( return (
!this.props.hide && ( !this.props.hide && (
<PoseGroup animateOnMount={true}> <Animation className={this.state.classes}>
<Animation className={this.state.classes}> {Object.keys(meta.social).map((key, i) => (
{Object.keys(meta.social).map((key, i) => ( <a className={styles.link} href={meta.social[key]} key={i}>
<a className={styles.link} href={meta.social[key]} key={i}> <NetworkIcon title={key} className={icons.icon} />
<NetworkIcon title={key} className={icons.icon} /> <span className={styles.title}>{key}</span>
<span className={styles.title}>{key}</span> </a>
</a> ))}
))} </Animation>
</Animation>
</PoseGroup>
) )
) )
}} }}

View File

@ -1,7 +1,8 @@
import React, { PureComponent, Fragment } from 'react' import React, { PureComponent, Fragment } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import Helmet from 'react-helmet' import Helmet from 'react-helmet'
import posed, { PoseGroup } from 'react-pose' import posed from 'react-pose'
import { Consumer } from '../../store/createContext'
import { fadeIn } from '../atoms/Transitions' import { fadeIn } from '../atoms/Transitions'
import Day from '../svg/Day' import Day from '../svg/Day'
@ -22,66 +23,41 @@ ThemeToggle.propTypes = {
dark: PropTypes.bool dark: PropTypes.bool
} }
const ThemeToggleInput = ({ dark, toggleDark }) => (
<input
onChange={toggleDark}
type="checkbox"
name="toggle"
value="toggle"
aria-describedby="toggle"
checked={dark}
/>
)
ThemeToggleInput.propTypes = {
dark: PropTypes.bool,
toggleDark: PropTypes.func
}
export default class ThemeSwitch extends PureComponent { export default class ThemeSwitch extends PureComponent {
state = { dark: null }
isDark = () => this.state.dark === true
darkLocalStorageMode = darkLocalStorage => {
if (darkLocalStorage === 'true') {
this.setState({ dark: true })
} else {
this.setState({ dark: false })
}
}
darkMode = now => {
if (!this.isDark() && (now >= 19 || now <= 7)) {
this.setState({ dark: true })
} else {
this.setState({ dark: null })
}
}
componentDidMount() {
const now = new Date().getHours()
const darkLocalStorage = localStorage.getItem('dark')
if (darkLocalStorage) {
this.darkLocalStorageMode(darkLocalStorage)
} else {
this.darkMode(now)
}
}
handleChange = event => {
this.setState({ dark: event.target.checked })
localStorage.setItem('dark', event.target.checked)
}
render() { render() {
return ( return (
<Fragment> <Consumer>
<Helmet> {({ dark, toggleDark }) => (
<body className={this.isDark() ? 'dark' : null} /> <Fragment>
</Helmet> <Helmet>
<PoseGroup animateOnMount={true}> <body className={dark ? 'dark' : null} />
<Animation className={styles.themeSwitch}> </Helmet>
<label className={styles.checkbox}> <Animation className={styles.themeSwitch}>
<span className={styles.label}>Toggle Night Mode</span> <label className={styles.checkbox}>
<input <span className={styles.label}>Toggle Night Mode</span>
onChange={this.handleChange} <ThemeToggleInput dark={dark} toggleDark={toggleDark} />
type="checkbox" <ThemeToggle dark={dark} />
name="toggle" </label>
value="toggle" </Animation>
aria-describedby="toggle" </Fragment>
checked={this.isDark()} )}
/> </Consumer>
<ThemeToggle dark={this.isDark()} />
</label>
</Animation>
</PoseGroup>
</Fragment>
) )
} }
} }

View File

@ -0,0 +1,5 @@
import { createContext } from 'react'
const { Provider, Consumer } = createContext()
export { Provider, Consumer }

57
src/store/provider.jsx Normal file
View File

@ -0,0 +1,57 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { Provider } from './createContext'
export default class AppProvider extends Component {
state = {
dark: false,
toggleDark: () => {
this.setState({ dark: !this.state.dark })
if (this.store) {
this.store.setItem('dark', !this.state.dark)
}
}
}
static propTypes = {
children: PropTypes.any.isRequired
}
store = typeof localStorage === 'undefined' ? null : localStorage
darkLocalStorageMode = darkLocalStorage => {
if (darkLocalStorage === 'true') {
this.setState({ dark: true })
} else {
this.setState({ dark: false })
}
}
darkMode = now => {
if (!this.state.dark && (now >= 19 || now <= 7)) {
this.setState({ dark: true })
} else {
this.setState({ dark: null })
}
}
checkDark = () => {
const now = new Date().getHours()
const darkLocalStorage = this.store.getItem('dark')
if (darkLocalStorage) {
this.darkLocalStorageMode(darkLocalStorage)
} else {
this.darkMode(now)
}
}
componentDidMount() {
this.checkDark()
}
render() {
return <Provider value={this.state}>{this.props.children}</Provider>
}
}