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:
commit
c1321c2a9e
@ -40,7 +40,7 @@ The whole [portfolio](https://matthiaskretschmann.com) is a React-based Single P
|
|||||||
|
|
||||||
### Lighthouse score
|
### Lighthouse score
|
||||||
|
|
||||||
[](https://travis-ci.com/kremalicious/portfolio)
|
[](https://travis-ci.com/kremalicious/portfolio)
|
||||||
|
|
||||||
### One data file to rule all pages
|
### 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 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
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
|
@ -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>
|
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -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>
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
|
@ -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>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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…
x
Reference in New Issue
Block a user