mirror of
https://github.com/kremalicious/portfolio.git
synced 2025-02-01 20:39:45 +01:00
Merge pull request #32 from kremalicious/feature/context
setup global store
This commit is contained in:
commit
c1321c2a9e
@ -40,7 +40,7 @@ The whole [portfolio](https://matthiaskretschmann.com) is a React-based Single P
|
||||
|
||||
### 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
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
import React from 'react'
|
||||
import AppProvider from './src/store/provider'
|
||||
import wrapPageElementWithTransition from './src/helpers/wrapPageElement'
|
||||
import './src/styles/global.scss'
|
||||
|
||||
@ -6,5 +8,11 @@ if (typeof window !== 'undefined' && !window.IntersectionObserver) {
|
||||
import('intersection-observer')
|
||||
}
|
||||
|
||||
// React Context in Browser
|
||||
// eslint-disable-next-line
|
||||
export const wrapRootElement = ({ element }) => {
|
||||
return <AppProvider>{element}</AppProvider>
|
||||
}
|
||||
|
||||
// Page Transitions & Layout
|
||||
export const wrapPageElement = wrapPageElementWithTransition
|
||||
|
@ -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'
|
||||
|
||||
export const replaceRenderer = ({ bodyComponent, replaceBodyHTMLString }) => {
|
||||
// React Context in SSR/build
|
||||
const ConnectedBody = () => <AppProvider>{bodyComponent}</AppProvider>
|
||||
replaceBodyHTMLString(renderToString(<ConnectedBody />))
|
||||
}
|
||||
|
||||
// Page Transitions & Layout
|
||||
export const wrapPageElement = wrapPageElementWithTransition
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { StaticQuery, graphql } from 'gatsby'
|
||||
import posed, { PoseGroup } from 'react-pose'
|
||||
import posed from 'react-pose'
|
||||
import { fadeIn } from '../atoms/Transitions'
|
||||
import styles from './Availability.module.scss'
|
||||
|
||||
@ -32,21 +32,19 @@ export default class Availability extends PureComponent {
|
||||
|
||||
return (
|
||||
!this.props.hide && (
|
||||
<PoseGroup animateOnMount={true}>
|
||||
<Animation
|
||||
className={
|
||||
status
|
||||
? `${styles.availability} ${styles.available}`
|
||||
: `${styles.availability}`
|
||||
}
|
||||
>
|
||||
<p
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: status ? available : unavailable
|
||||
}}
|
||||
/>
|
||||
</Animation>
|
||||
</PoseGroup>
|
||||
<Animation
|
||||
className={
|
||||
status
|
||||
? `${styles.availability} ${styles.available}`
|
||||
: `${styles.availability}`
|
||||
}
|
||||
>
|
||||
<p
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: status ? available : unavailable
|
||||
}}
|
||||
/>
|
||||
</Animation>
|
||||
)
|
||||
)
|
||||
}}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { StaticQuery, graphql } from 'gatsby'
|
||||
import posed, { PoseGroup } from 'react-pose'
|
||||
import posed from 'react-pose'
|
||||
import { moveInBottom } from '../atoms/Transitions'
|
||||
|
||||
import Logo from '../svg/Logo'
|
||||
@ -48,19 +48,17 @@ export default class LogoUnit extends PureComponent {
|
||||
const { minimal } = this.state
|
||||
|
||||
return (
|
||||
<PoseGroup animateOnMount={true}>
|
||||
<Animation>
|
||||
<div className={minimal ? styles.minimal : styles.logounit}>
|
||||
<Logo className={styles.logounit__logo} />
|
||||
<h1 className={styles.logounit__title}>
|
||||
{title.toLowerCase()}
|
||||
</h1>
|
||||
<p className={styles.logounit__description}>
|
||||
{tagline.toLowerCase()}
|
||||
</p>
|
||||
</div>
|
||||
</Animation>
|
||||
</PoseGroup>
|
||||
<Animation>
|
||||
<div className={minimal ? styles.minimal : styles.logounit}>
|
||||
<Logo className={styles.logounit__logo} />
|
||||
<h1 className={styles.logounit__title}>
|
||||
{title.toLowerCase()}
|
||||
</h1>
|
||||
<p className={styles.logounit__description}>
|
||||
{tagline.toLowerCase()}
|
||||
</p>
|
||||
</div>
|
||||
</Animation>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { StaticQuery, graphql } from 'gatsby'
|
||||
import posed, { PoseGroup } from 'react-pose'
|
||||
import posed from 'react-pose'
|
||||
import { moveInTop } from '../atoms/Transitions'
|
||||
|
||||
import Email from '../svg/Email'
|
||||
@ -81,16 +81,14 @@ export default class Network extends PureComponent {
|
||||
|
||||
return (
|
||||
!this.props.hide && (
|
||||
<PoseGroup animateOnMount={true}>
|
||||
<Animation className={this.state.classes}>
|
||||
{Object.keys(meta.social).map((key, i) => (
|
||||
<a className={styles.link} href={meta.social[key]} key={i}>
|
||||
<NetworkIcon title={key} className={icons.icon} />
|
||||
<span className={styles.title}>{key}</span>
|
||||
</a>
|
||||
))}
|
||||
</Animation>
|
||||
</PoseGroup>
|
||||
<Animation className={this.state.classes}>
|
||||
{Object.keys(meta.social).map((key, i) => (
|
||||
<a className={styles.link} href={meta.social[key]} key={i}>
|
||||
<NetworkIcon title={key} className={icons.icon} />
|
||||
<span className={styles.title}>{key}</span>
|
||||
</a>
|
||||
))}
|
||||
</Animation>
|
||||
)
|
||||
)
|
||||
}}
|
||||
|
@ -1,7 +1,8 @@
|
||||
import React, { PureComponent, Fragment } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
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 Day from '../svg/Day'
|
||||
@ -22,66 +23,41 @@ ThemeToggle.propTypes = {
|
||||
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 {
|
||||
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() {
|
||||
return (
|
||||
<Fragment>
|
||||
<Helmet>
|
||||
<body className={this.isDark() ? 'dark' : null} />
|
||||
</Helmet>
|
||||
<PoseGroup animateOnMount={true}>
|
||||
<Animation className={styles.themeSwitch}>
|
||||
<label className={styles.checkbox}>
|
||||
<span className={styles.label}>Toggle Night Mode</span>
|
||||
<input
|
||||
onChange={this.handleChange}
|
||||
type="checkbox"
|
||||
name="toggle"
|
||||
value="toggle"
|
||||
aria-describedby="toggle"
|
||||
checked={this.isDark()}
|
||||
/>
|
||||
<ThemeToggle dark={this.isDark()} />
|
||||
</label>
|
||||
</Animation>
|
||||
</PoseGroup>
|
||||
</Fragment>
|
||||
<Consumer>
|
||||
{({ dark, toggleDark }) => (
|
||||
<Fragment>
|
||||
<Helmet>
|
||||
<body className={dark ? 'dark' : null} />
|
||||
</Helmet>
|
||||
<Animation className={styles.themeSwitch}>
|
||||
<label className={styles.checkbox}>
|
||||
<span className={styles.label}>Toggle Night Mode</span>
|
||||
<ThemeToggleInput dark={dark} toggleDark={toggleDark} />
|
||||
<ThemeToggle dark={dark} />
|
||||
</label>
|
||||
</Animation>
|
||||
</Fragment>
|
||||
)}
|
||||
</Consumer>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
5
src/store/createContext.jsx
Normal file
5
src/store/createContext.jsx
Normal file
@ -0,0 +1,5 @@
|
||||
import { createContext } from 'react'
|
||||
|
||||
const { Provider, Consumer } = createContext()
|
||||
|
||||
export { Provider, Consumer }
|
57
src/store/provider.jsx
Normal file
57
src/store/provider.jsx
Normal 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>
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user