mirror of
https://github.com/kremalicious/portfolio.git
synced 2024-12-23 01:29:41 +01:00
queries refactoring
This commit is contained in:
parent
409a239104
commit
6bbf35b32e
@ -1,72 +1,23 @@
|
||||
import React, { Fragment } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { StaticQuery, graphql } from 'gatsby'
|
||||
import Head from './molecules/Head'
|
||||
import Header from './organisms/Header'
|
||||
import Footer from './organisms/Footer'
|
||||
import styles from './Layout.module.scss'
|
||||
|
||||
const Layout = ({ children, location }) => {
|
||||
return (
|
||||
<StaticQuery
|
||||
query={graphql`
|
||||
query {
|
||||
# the data/meta.yml file
|
||||
dataYaml {
|
||||
title
|
||||
tagline
|
||||
description
|
||||
url
|
||||
email
|
||||
avatar {
|
||||
childImageSharp {
|
||||
original: resize {
|
||||
src
|
||||
}
|
||||
small: resize(width: 256) {
|
||||
src
|
||||
}
|
||||
}
|
||||
}
|
||||
img {
|
||||
childImageSharp {
|
||||
resize(width: 980) {
|
||||
src
|
||||
}
|
||||
}
|
||||
}
|
||||
social {
|
||||
Email
|
||||
Blog
|
||||
Twitter
|
||||
GitHub
|
||||
Dribbble
|
||||
}
|
||||
availability {
|
||||
status
|
||||
}
|
||||
gpg
|
||||
addressbook
|
||||
}
|
||||
}
|
||||
`}
|
||||
render={data => {
|
||||
const meta = data.dataYaml
|
||||
const isHomepage = location.pathname === '/'
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Head meta={meta} />
|
||||
<Header meta={meta} isHomepage={isHomepage} />
|
||||
<Head />
|
||||
<Header isHomepage={isHomepage} />
|
||||
|
||||
<main className={styles.screen}>{children}</main>
|
||||
|
||||
<Footer meta={meta} />
|
||||
<Footer />
|
||||
</Fragment>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
Layout.propTypes = {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { StaticQuery, graphql } from 'gatsby'
|
||||
import Logo from '../svg/Logo'
|
||||
import styles from './LogoUnit.module.scss'
|
||||
|
||||
@ -25,23 +26,39 @@ class LogoUnit extends PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { meta } = this.props
|
||||
return (
|
||||
<StaticQuery
|
||||
query={graphql`
|
||||
query {
|
||||
dataYaml {
|
||||
title
|
||||
tagline
|
||||
}
|
||||
}
|
||||
`}
|
||||
render={data => {
|
||||
const meta = data.dataYaml
|
||||
const { minimal } = this.state
|
||||
|
||||
return (
|
||||
<div className={minimal ? styles.minimal : styles.logounit}>
|
||||
<Logo className={styles.logounit__logo} />
|
||||
<h1 className={styles.logounit__title}>{meta.title.toLowerCase()}</h1>
|
||||
<h1 className={styles.logounit__title}>
|
||||
{meta.title.toLowerCase()}
|
||||
</h1>
|
||||
<p className={styles.logounit__description}>
|
||||
<span>{'{ '}</span> {meta.tagline.toLowerCase()} <span>{' }'}</span>
|
||||
<span>{'{ '}</span> {meta.tagline.toLowerCase()}{' '}
|
||||
<span>{' }'}</span>
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
LogoUnit.propTypes = {
|
||||
meta: PropTypes.object.isRequired,
|
||||
minimal: PropTypes.bool
|
||||
}
|
||||
|
||||
|
@ -4,13 +4,13 @@ import { graphql } from 'gatsby'
|
||||
import Img from 'gatsby-image'
|
||||
import styles from './ProjectImage.module.scss'
|
||||
|
||||
const ProjectImage = props => (
|
||||
const ProjectImage = ({ fluid, alt }) => (
|
||||
<Img
|
||||
className={styles.project__image}
|
||||
outerWrapperClassName={styles.project__imagewrap}
|
||||
backgroundColor="#6b7f88"
|
||||
fluid={props.fluid}
|
||||
alt={props.alt}
|
||||
fluid={fluid}
|
||||
alt={alt}
|
||||
/>
|
||||
)
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react'
|
||||
import Helmet from 'react-helmet'
|
||||
import PropTypes from 'prop-types'
|
||||
import { StaticQuery, graphql } from 'gatsby'
|
||||
|
||||
function truncate(n, useWordBoundary) {
|
||||
if (this.length <= n) {
|
||||
@ -14,7 +15,38 @@ function truncate(n, useWordBoundary) {
|
||||
)
|
||||
}
|
||||
|
||||
const SEO = ({ project, meta }) => {
|
||||
const SEO = ({ project }) => (
|
||||
<StaticQuery
|
||||
query={graphql`
|
||||
query {
|
||||
dataYaml {
|
||||
title
|
||||
tagline
|
||||
description
|
||||
url
|
||||
email
|
||||
img {
|
||||
childImageSharp {
|
||||
resize(width: 980) {
|
||||
src
|
||||
}
|
||||
}
|
||||
}
|
||||
social {
|
||||
Email
|
||||
Blog
|
||||
Twitter
|
||||
GitHub
|
||||
Dribbble
|
||||
}
|
||||
gpg
|
||||
addressbook
|
||||
}
|
||||
}
|
||||
`}
|
||||
render={data => {
|
||||
const meta = data.dataYaml
|
||||
|
||||
const title = project.title ? project.title : meta.title
|
||||
const description = project.description
|
||||
? truncate.apply(project.description, [320, true])
|
||||
@ -47,16 +79,16 @@ const SEO = ({ project, meta }) => {
|
||||
<meta name="twitter:image" content={`${meta.url}${image}`} />
|
||||
</Helmet>
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)
|
||||
|
||||
SEO.propTypes = {
|
||||
project: PropTypes.object,
|
||||
meta: PropTypes.object
|
||||
project: PropTypes.object
|
||||
}
|
||||
|
||||
SEO.defaultProps = {
|
||||
project: {},
|
||||
meta: {}
|
||||
project: {}
|
||||
}
|
||||
|
||||
export default SEO
|
||||
|
@ -1,16 +1,109 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import { StaticQuery, graphql } from 'gatsby'
|
||||
import FileSaver from 'file-saver'
|
||||
import vCard from 'vcf'
|
||||
|
||||
class Vcard extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
const Vcard = () => (
|
||||
<StaticQuery
|
||||
query={graphql`
|
||||
query {
|
||||
dataYaml {
|
||||
title
|
||||
tagline
|
||||
description
|
||||
url
|
||||
email
|
||||
avatar {
|
||||
childImageSharp {
|
||||
original: resize {
|
||||
src
|
||||
}
|
||||
}
|
||||
}
|
||||
social {
|
||||
Email
|
||||
Blog
|
||||
Twitter
|
||||
GitHub
|
||||
Dribbble
|
||||
}
|
||||
gpg
|
||||
addressbook
|
||||
}
|
||||
}
|
||||
`}
|
||||
render={data => {
|
||||
const meta = data.dataYaml
|
||||
|
||||
const constructVcard = () => {
|
||||
const contact = new vCard()
|
||||
const photoSrc = meta.avatar.childImageSharp.original.src
|
||||
|
||||
// first, convert the avatar to base64,
|
||||
// then construct all vCard elements
|
||||
toDataURL(
|
||||
photoSrc,
|
||||
dataUrl => {
|
||||
// stripping this data out of base64 string is required
|
||||
// for vcard to actually display the image for whatever reason
|
||||
const dataUrlCleaned = dataUrl
|
||||
.split('data:image/jpeg;base64,')
|
||||
.join('')
|
||||
contact.set('photo', dataUrlCleaned, {
|
||||
encoding: 'b',
|
||||
type: 'JPEG'
|
||||
})
|
||||
contact.set('fn', meta.title)
|
||||
contact.set('title', meta.tagline)
|
||||
contact.set('email', meta.email)
|
||||
contact.set('url', meta.url, { type: 'Portfolio' })
|
||||
contact.add('url', meta.social.Blog, { type: 'Blog' })
|
||||
contact.set('nickname', 'kremalicious')
|
||||
contact.add('x-socialprofile', meta.social.Twitter, {
|
||||
type: 'twitter'
|
||||
})
|
||||
contact.add('x-socialprofile', meta.social.GitHub, {
|
||||
type: 'GitHub'
|
||||
})
|
||||
|
||||
const vcard = contact.toString('3.0')
|
||||
|
||||
downloadVcard(vcard)
|
||||
},
|
||||
'image/jpeg'
|
||||
)
|
||||
}
|
||||
|
||||
// Construct the download from a blob of the just constructed vCard,
|
||||
// and save it to user's file system
|
||||
const downloadVcard = vcard => {
|
||||
const name = meta.addressbook.split('/').join('')
|
||||
const blob = new Blob([vcard], { type: 'text/x-vcard' })
|
||||
FileSaver.saveAs(blob, name)
|
||||
}
|
||||
|
||||
const handleAddressbookClick = e => {
|
||||
e.preventDefault()
|
||||
constructVcard()
|
||||
}
|
||||
|
||||
return (
|
||||
<a
|
||||
// href is kinda fake, only there for usability
|
||||
// so user knows what to expect when hovering the link before clicking
|
||||
href={meta.addressbook}
|
||||
onClick={handleAddressbookClick}
|
||||
>
|
||||
Add to addressbook
|
||||
</a>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
|
||||
// Helper function to create base64 string from avatar image
|
||||
// without the need to read image file from file system
|
||||
toDataURL(src, callback, outputFormat) {
|
||||
const toDataURL = (src, callback, outputFormat) => {
|
||||
const img = new Image()
|
||||
img.crossOrigin = 'Anonymous'
|
||||
|
||||
@ -36,66 +129,4 @@ class Vcard extends PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
constructVcard() {
|
||||
const meta = this.props.meta
|
||||
const contact = new vCard()
|
||||
const photoSrc = meta.avatar.childImageSharp.original.src
|
||||
|
||||
// first, convert the avatar to base64,
|
||||
// then construct all vCard elements
|
||||
this.toDataURL(
|
||||
photoSrc,
|
||||
dataUrl => {
|
||||
// stripping this data out of base64 string is required
|
||||
// for vcard to actually display the image for whatever reason
|
||||
const dataUrlCleaned = dataUrl.split('data:image/jpeg;base64,').join('')
|
||||
contact.set('photo', dataUrlCleaned, { encoding: 'b', type: 'JPEG' })
|
||||
contact.set('fn', meta.title)
|
||||
contact.set('title', meta.tagline)
|
||||
contact.set('email', meta.email)
|
||||
contact.set('url', meta.url, { type: 'Portfolio' })
|
||||
contact.add('url', meta.social.Blog, { type: 'Blog' })
|
||||
contact.set('nickname', 'kremalicious')
|
||||
contact.add('x-socialprofile', meta.social.Twitter, { type: 'twitter' })
|
||||
contact.add('x-socialprofile', meta.social.GitHub, { type: 'GitHub' })
|
||||
|
||||
const vcard = contact.toString('3.0')
|
||||
|
||||
this.downloadVcard(vcard)
|
||||
},
|
||||
'image/jpeg'
|
||||
)
|
||||
}
|
||||
|
||||
// Construct the download from a blob of the just constructed vCard,
|
||||
// and save it to user's file system
|
||||
downloadVcard(vcard) {
|
||||
const name = this.props.meta.addressbook.split('/').join('')
|
||||
const blob = new Blob([vcard], { type: 'text/x-vcard' })
|
||||
FileSaver.saveAs(blob, name)
|
||||
}
|
||||
|
||||
handleAddressbookClick = e => {
|
||||
e.preventDefault()
|
||||
this.constructVcard()
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<a
|
||||
// href is kinda fake, only there for usability
|
||||
// so user knows what to expect when hovering the link before clicking
|
||||
href={this.props.meta.addressbook}
|
||||
onClick={this.handleAddressbookClick}
|
||||
>
|
||||
Add to addressbook
|
||||
</a>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Vcard.propTypes = {
|
||||
meta: PropTypes.object
|
||||
}
|
||||
|
||||
export default Vcard
|
||||
|
@ -1,11 +1,21 @@
|
||||
import React, { Fragment } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Helmet from 'react-helmet'
|
||||
import { StaticQuery, graphql } from 'gatsby'
|
||||
import SEO from '../atoms/SEO'
|
||||
import Typekit from '../atoms/Typekit'
|
||||
|
||||
const Head = ({ meta }) => {
|
||||
const { title, tagline } = meta
|
||||
const Head = () => (
|
||||
<StaticQuery
|
||||
query={graphql`
|
||||
query {
|
||||
dataYaml {
|
||||
title
|
||||
tagline
|
||||
}
|
||||
}
|
||||
`}
|
||||
render={data => {
|
||||
const { title, tagline } = data.dataYaml
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
@ -13,18 +23,19 @@ const Head = ({ meta }) => {
|
||||
defaultTitle={`${title.toLowerCase()} { ${tagline.toLowerCase()} }`}
|
||||
titleTemplate={`%s // ${title.toLowerCase()} { ${tagline.toLowerCase()} }`}
|
||||
>
|
||||
<meta name="apple-mobile-web-app-title" content={title.toLowerCase()} />
|
||||
<meta
|
||||
name="apple-mobile-web-app-title"
|
||||
content={title.toLowerCase()}
|
||||
/>
|
||||
</Helmet>
|
||||
|
||||
<Typekit />
|
||||
|
||||
<SEO meta={meta} />
|
||||
<SEO />
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
Head.propTypes = {
|
||||
meta: PropTypes.object.isRequired
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)
|
||||
|
||||
export default Head
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { StaticQuery, graphql } from 'gatsby'
|
||||
import { FadeIn } from '../atoms/Animations'
|
||||
|
||||
import Email from '../svg/Email'
|
||||
@ -54,16 +55,30 @@ class Network extends PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<StaticQuery
|
||||
query={graphql`
|
||||
query {
|
||||
dataYaml {
|
||||
social {
|
||||
Email
|
||||
Blog
|
||||
Twitter
|
||||
GitHub
|
||||
Dribbble
|
||||
}
|
||||
}
|
||||
}
|
||||
`}
|
||||
render={data => {
|
||||
const meta = data.dataYaml
|
||||
|
||||
return (
|
||||
!this.props.hide && (
|
||||
<FadeIn>
|
||||
<aside className={this.state.classes}>
|
||||
{Object.keys(this.props.meta.social).map((key, i) => (
|
||||
<a
|
||||
className={styles.link}
|
||||
href={this.props.meta.social[key]}
|
||||
key={i}
|
||||
>
|
||||
{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>
|
||||
@ -72,11 +87,13 @@ class Network extends PureComponent {
|
||||
</FadeIn>
|
||||
)
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Network.propTypes = {
|
||||
meta: PropTypes.object,
|
||||
minimal: PropTypes.bool,
|
||||
hide: PropTypes.bool
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { StaticQuery, graphql } from 'gatsby'
|
||||
import Vcard from '../atoms/Vcard'
|
||||
import LogoUnit from '../atoms/LogoUnit'
|
||||
@ -14,8 +13,6 @@ class Footer extends PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
const meta = this.props.meta
|
||||
|
||||
return (
|
||||
<StaticQuery
|
||||
query={graphql`
|
||||
@ -27,18 +24,24 @@ class Footer extends PureComponent {
|
||||
repository
|
||||
bugs
|
||||
}
|
||||
|
||||
dataYaml {
|
||||
title
|
||||
gpg
|
||||
}
|
||||
}
|
||||
`}
|
||||
render={data => {
|
||||
const pkg = data.portfolioJson
|
||||
const meta = data.dataYaml
|
||||
|
||||
return (
|
||||
<footer className={styles.footer}>
|
||||
<LogoUnit meta={meta} minimal />
|
||||
<Networks meta={meta} minimal />
|
||||
<LogoUnit minimal />
|
||||
<Networks minimal />
|
||||
|
||||
<p className={styles.footer__actions}>
|
||||
<Vcard meta={meta} />
|
||||
<Vcard />
|
||||
<a href={meta.gpg}>PGP/GPG key</a>
|
||||
<a href={pkg.bugs}>Found a bug?</a>
|
||||
</p>
|
||||
@ -56,9 +59,4 @@ class Footer extends PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
Footer.propTypes = {
|
||||
meta: PropTypes.object,
|
||||
pkg: PropTypes.object
|
||||
}
|
||||
|
||||
export default Footer
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import { Link } from 'gatsby'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Link, StaticQuery, graphql } from 'gatsby'
|
||||
import Networks from '../molecules/Networks'
|
||||
import Availability from '../molecules/Availability'
|
||||
import ThemeSwitch from '../molecules/ThemeSwitch'
|
||||
@ -29,28 +29,44 @@ class Header extends PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isHomepage, meta } = this.props
|
||||
const { isHomepage } = this.props
|
||||
const { minimal } = this.state
|
||||
|
||||
return (
|
||||
<StaticQuery
|
||||
query={graphql`
|
||||
query {
|
||||
dataYaml {
|
||||
availability {
|
||||
status
|
||||
}
|
||||
}
|
||||
}
|
||||
`}
|
||||
render={data => {
|
||||
const meta = data.dataYaml
|
||||
|
||||
return (
|
||||
<header className={minimal ? styles.minimal : styles.header}>
|
||||
<ThemeSwitch />
|
||||
|
||||
<Link className={styles.header__link} to={'/'}>
|
||||
<LogoUnit meta={meta} minimal={!isHomepage} />
|
||||
<LogoUnit minimal={!isHomepage} />
|
||||
</Link>
|
||||
|
||||
<Networks meta={meta} hide={!isHomepage} />
|
||||
<Networks hide={!isHomepage} />
|
||||
|
||||
<Availability hide={!isHomepage && !meta.availability.status} />
|
||||
</header>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Header.propTypes = {
|
||||
isHomepage: PropTypes.bool,
|
||||
meta: PropTypes.object
|
||||
isHomepage: PropTypes.bool
|
||||
}
|
||||
|
||||
export default Header
|
||||
|
@ -32,7 +32,6 @@ const ProjectImages = ({ projectImages, title }) => (
|
||||
)
|
||||
|
||||
const Project = ({ data, location }) => {
|
||||
const meta = data.dataYaml
|
||||
const project = data.projectsYaml
|
||||
const projectImages = data.projectImages.edges
|
||||
const { title, links, techstack } = project
|
||||
@ -43,7 +42,7 @@ const Project = ({ data, location }) => {
|
||||
<Layout location={location}>
|
||||
<Helmet title={title} />
|
||||
|
||||
<SEO project={project} meta={meta} />
|
||||
<SEO project={project} />
|
||||
|
||||
<article className={styles.project}>
|
||||
<Content>
|
||||
@ -99,28 +98,6 @@ export const projectAndProjectsQuery = graphql`
|
||||
}
|
||||
}
|
||||
|
||||
# the data/meta.yml file
|
||||
dataYaml {
|
||||
title
|
||||
tagline
|
||||
description
|
||||
url
|
||||
social {
|
||||
Email
|
||||
Blog
|
||||
Twitter
|
||||
GitHub
|
||||
Dribbble
|
||||
}
|
||||
img {
|
||||
childImageSharp {
|
||||
resize(width: 980) {
|
||||
src
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
projectImages: allImageSharp(
|
||||
filter: { fluid: { originalName: { regex: $slug } } }
|
||||
sort: { fields: [fluid___originalName], order: ASC }
|
||||
|
Loading…
Reference in New Issue
Block a user