mirror of
https://github.com/kremalicious/blog.git
synced 2024-11-15 01:25:28 +01:00
commit
ea6c0c3372
60
README.md
60
README.md
@ -18,10 +18,12 @@
|
||||
|
||||
- [🎉 Features](#-features)
|
||||
- [🎆 EXIF extraction](#-exif-extraction)
|
||||
- [💰 Cryptocurrency donation via Web3/MetaMask](#-cryptocurrency-donation-via-web3-metamask)
|
||||
- [🕸 Related Posts](#-related-posts)
|
||||
- [🐝 Coinhive](#-coinhive)
|
||||
- [🏆 SEO component](#-seo-component)
|
||||
- [📈 Matomo (formerly Piwik) analytics tracking](#-matomo-formerly-piwik-analytics-tracking)
|
||||
- [gatsby-redirect-from](#-gatsby-redirect-from)
|
||||
- [gatsby-redirect-from](#gatsby-redirect-from)
|
||||
- [💎 Importing SVG assets](#-importing-svg-assets)
|
||||
- [🍬 Typekit component](#-typekit-component)
|
||||
- [✨ Development](#-development)
|
||||
@ -46,27 +48,67 @@ In the end looks like this, including location display with [pigeon-maps](https:
|
||||
|
||||
<img width="878" alt="screen shot 2018-10-09 at 23 59 39" src="https://user-images.githubusercontent.com/90316/46701262-6ed05680-cc1f-11e8-81c4-f4ea18b89bc0.png">
|
||||
|
||||
If you want to know how, have a look at the respective component under [`src/components/atoms/Exif.jsx`](src/components/atoms/Exif.jsx) and the EXIF node fields creation in [`gatsby-node.js`](gatsby-node.js).
|
||||
If you want to know how this works, have a look at the respective component under
|
||||
|
||||
- [`src/components/atoms/Exif.jsx`](src/components/atoms/Exif.jsx)
|
||||
- the EXIF node fields creation in [`gatsby-node.js`](gatsby-node.js)
|
||||
|
||||
### 💰 Cryptocurrency donation via Web3/MetaMask
|
||||
|
||||
Lets visitors say thanks with Bitcoin or Ether. Includes full Web3 client for sending Ether via MetaMask or Mist.
|
||||
|
||||
As a fallback, QR codes are generated with [react-qr-svg](https://github.com/no23reason/react-qr-svg) from the addresses defined in [`config.js`](config.js).
|
||||
|
||||
<img width="743" alt="screen shot 2018-10-11 at 21 01 37" src="https://user-images.githubusercontent.com/90316/46827443-e1187680-cd98-11e8-9daf-00a37c0ee13a.png">
|
||||
|
||||
If you want to know how this works, have a look at the respective components under
|
||||
|
||||
- [`src/components/atoms/Web3Donation.jsx`](src/components/atoms/Web3Donation.jsx)
|
||||
- [`src/components/atoms/Qr.jsx`](src/components/atoms/Qr.jsx)
|
||||
|
||||
### 🕸 Related Posts
|
||||
|
||||
Under each post a list of related posts is displayed which are based on the tags of the currently viewed post. Also allows loading more related posts in place.
|
||||
|
||||
If you want to know how, have a look at the respective component under [`src/components/molecules/Pagination.jsx`](src/components/molecules/Pagination.jsx)
|
||||
<img width="691" alt="screen shot 2018-10-11 at 21 03 03" src="https://user-images.githubusercontent.com/90316/46827531-14f39c00-cd99-11e8-84aa-0e851c32c89c.png">
|
||||
|
||||
If you want to know how this works, have a look at the respective component under
|
||||
|
||||
- [`src/components/molecules/RelatedPosts.jsx`](src/components/molecules/RelatedPosts.jsx)
|
||||
|
||||
### 🐝 Coinhive
|
||||
|
||||
Includes a component for mining Monero with JavaScript via [Coinhive](https://coinhive.com).
|
||||
|
||||
<img width="166" alt="screen shot 2018-10-11 at 21 09 49" src="https://user-images.githubusercontent.com/90316/46827858-03f75a80-cd9a-11e8-84f1-65b7d0027124.png">
|
||||
|
||||
Functionality is opt-in on a post basis. Simply add this to any post's frontmatter to activate it for this post:
|
||||
|
||||
```yaml
|
||||
coinhive: true
|
||||
```
|
||||
|
||||
If you want to know how this works, have a look at the respective component under
|
||||
|
||||
- [`src/components/atoms/Coinhive.jsx`](src/components/atoms/Coinhive.jsx)
|
||||
|
||||
### 🏆 SEO component
|
||||
|
||||
Includes a SEO component which automatically switches all required `meta` tags for search engines, Twitter Cards, and Facebook OpenGraph tags based on the browsed route/page.
|
||||
|
||||
If you want to know how, have a look at the respective component under [`src/components/atoms/SEO.jsx`](src/components/atoms/SEO.jsx)
|
||||
If you want to know how this works, have a look at the respective component under
|
||||
|
||||
- [`src/components/atoms/SEO.jsx`](src/components/atoms/SEO.jsx)
|
||||
|
||||
### 📈 Matomo (formerly Piwik) analytics tracking
|
||||
|
||||
Site sends usage statistics to my own [Matomo](https://matomo.org) installation. To make this work in Gatsby, I created and open sourced a plugin, [gatsby-plugin-matomo](https://github.com/kremalicious/gatsby-plugin-matomo), which is in use on this site.
|
||||
Site sends usage statistics to my own [Matomo](https://matomo.org) installation. To make this work in Gatsby, I created and open sourced a plugin which is in use on this site.
|
||||
|
||||
- [gatsby-plugin-matomo](https://github.com/kremalicious/gatsby-plugin-matomo)
|
||||
|
||||
### gatsby-redirect-from
|
||||
|
||||
https://github.com/kremalicious/gatsby-redirect-from
|
||||
- [gatsby-redirect-from](https://github.com/kremalicious/gatsby-redirect-from)
|
||||
|
||||
### 💎 Importing SVG assets
|
||||
|
||||
@ -82,7 +124,9 @@ import { ReactComponent as Logo } from './components/svg/Logo'
|
||||
|
||||
Includes a component for adding the Typekit snippet.
|
||||
|
||||
If you want to know how, have a look at the respective component under [`src/components/atoms/Typekit.jsx`](src/components/atoms/Typekit.jsx)
|
||||
If you want to know how this works, have a look at the respective component under
|
||||
|
||||
- [`src/components/atoms/Typekit.jsx`](src/components/atoms/Typekit.jsx)
|
||||
|
||||
## ✨ Development
|
||||
|
||||
@ -119,7 +163,7 @@ npm run format:css
|
||||
npm run new "Hello"
|
||||
```
|
||||
|
||||
...
|
||||
- [`scripts/new.js`](scripts/new.js)
|
||||
|
||||
## 🚚 Deployment
|
||||
|
||||
|
@ -212,11 +212,16 @@ module.exports = {
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
resolve: 'gatsby-plugin-sitemap',
|
||||
options: {
|
||||
exclude: ['/page/*', '/tag/*']
|
||||
}
|
||||
},
|
||||
'gatsby-plugin-webpack-size',
|
||||
'gatsby-plugin-react-helmet',
|
||||
'gatsby-plugin-sharp',
|
||||
'gatsby-transformer-sharp',
|
||||
'gatsby-plugin-sitemap',
|
||||
'gatsby-plugin-catch-links',
|
||||
'gatsby-redirect-from',
|
||||
'gatsby-plugin-meta-redirect',
|
||||
|
@ -31,7 +31,7 @@
|
||||
"dms2dec": "^1.1.0",
|
||||
"fast-exif": "^1.0.1",
|
||||
"fraction.js": "^4.0.9",
|
||||
"gatsby": "^2.0.20",
|
||||
"gatsby": "^2.0.21",
|
||||
"gatsby-image": "^2.0.13",
|
||||
"gatsby-plugin-catch-links": "^2.0.4",
|
||||
"gatsby-plugin-favicon": "^3.1.4",
|
||||
@ -60,7 +60,7 @@
|
||||
"load-script": "^1.0.0",
|
||||
"node-sass": "^4.9.3",
|
||||
"nord": "^0.2.1",
|
||||
"pigeon-maps": "^0.11.1",
|
||||
"pigeon-maps": "^0.11.2",
|
||||
"pigeon-marker": "^0.3.4",
|
||||
"react": "^16.5.2",
|
||||
"react-clipboard.js": "^2.0.1",
|
||||
@ -71,7 +71,8 @@
|
||||
"react-qr-svg": "^2.1.0",
|
||||
"react-time": "^4.3.0",
|
||||
"react-transition-group": "^2.5.0",
|
||||
"slugify": "^1.3.1"
|
||||
"slugify": "^1.3.1",
|
||||
"web3": "^0.20.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/node": "^7.0.0",
|
||||
|
@ -35,6 +35,7 @@ export default class PostActions extends PureComponent {
|
||||
>
|
||||
@kremalicious
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
</article>
|
||||
<article className={styles.action}>
|
||||
@ -48,10 +49,12 @@ export default class PostActions extends PureComponent {
|
||||
</p>
|
||||
</article>
|
||||
|
||||
{this.state.showModal && (
|
||||
<ModalThanks
|
||||
isOpen={this.state.showModal}
|
||||
handleCloseModal={this.toggleModal}
|
||||
/>
|
||||
)}
|
||||
</aside>
|
||||
)
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Image from './Image'
|
||||
import Image from '../atoms/Image'
|
||||
import styles from './PostImage.module.scss'
|
||||
|
||||
const PostImage = ({ title, fluid, fixed, alt }) => (
|
@ -2,9 +2,9 @@ import React, { PureComponent, Fragment } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Helmet from 'react-helmet'
|
||||
import { CSSTransition } from 'react-transition-group'
|
||||
import SearchInput from '../atoms/SearchInput'
|
||||
import SearchButton from '../atoms/SearchButton'
|
||||
import SearchResults from '../atoms/SearchResults'
|
||||
import SearchInput from './SearchInput'
|
||||
import SearchButton from './SearchButton'
|
||||
import SearchResults from './SearchResults'
|
||||
|
||||
import styles from './Search.module.scss'
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { Fragment } from 'react'
|
||||
import Input from './Input'
|
||||
import Input from '../atoms/Input'
|
||||
import styles from './SearchInput.module.scss'
|
||||
|
||||
const SearchInput = props => (
|
31
src/components/atoms/Qr.jsx
Normal file
31
src/components/atoms/Qr.jsx
Normal file
@ -0,0 +1,31 @@
|
||||
import React, { Fragment } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { QRCode } from 'react-qr-svg'
|
||||
import Clipboard from 'react-clipboard.js'
|
||||
import { ReactComponent as IconClipboard } from '../../images/clipboard.svg'
|
||||
|
||||
const Qr = ({ address, title }) => (
|
||||
<Fragment>
|
||||
{title && <h4>{title}</h4>}
|
||||
<QRCode
|
||||
bgColor="transparent"
|
||||
fgColor="#6b7f88"
|
||||
level="Q"
|
||||
style={{ width: 120 }}
|
||||
value={address}
|
||||
/>
|
||||
<pre>
|
||||
<code>{address}</code>
|
||||
<Clipboard data-clipboard-text={address} button-title="Copy to clipboard">
|
||||
<IconClipboard />
|
||||
</Clipboard>
|
||||
</pre>
|
||||
</Fragment>
|
||||
)
|
||||
|
||||
Qr.propTypes = {
|
||||
address: PropTypes.string.isRequired,
|
||||
title: PropTypes.string
|
||||
}
|
||||
|
||||
export default Qr
|
188
src/components/atoms/Web3Donation.jsx
Normal file
188
src/components/atoms/Web3Donation.jsx
Normal file
@ -0,0 +1,188 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Web3 from 'web3'
|
||||
import styles from './Web3Donation.module.scss'
|
||||
|
||||
const ONE_SECOND = 1000
|
||||
const ONE_MINUTE = ONE_SECOND * 60
|
||||
|
||||
export default class Web3Donation extends PureComponent {
|
||||
state = {
|
||||
web3Connected: false,
|
||||
networkError: null,
|
||||
networkId: null,
|
||||
accounts: [],
|
||||
selectedAccount: null,
|
||||
receipt: null,
|
||||
transactionHash: null,
|
||||
loading: false,
|
||||
error: null
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
address: PropTypes.string
|
||||
}
|
||||
|
||||
web3 = null
|
||||
interval = null
|
||||
networkInterval = null
|
||||
|
||||
componentDidMount() {
|
||||
if (typeof window.web3 === 'undefined') {
|
||||
// no web3
|
||||
this.setState({ web3Connected: false })
|
||||
} else {
|
||||
// this.web3 = new Web3(Web3.givenProvider || 'ws://localhost:8546')
|
||||
this.web3 = new Web3(window.web3.currentProvider)
|
||||
this.setState({ web3Connected: true })
|
||||
|
||||
this.fetchAccounts()
|
||||
this.fetchNetwork()
|
||||
this.initPoll()
|
||||
this.initNetworkPoll()
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearInterval(this.interval)
|
||||
clearInterval(this.networkInterval)
|
||||
this.setState({ web3Connected: false })
|
||||
}
|
||||
|
||||
initPoll() {
|
||||
if (!this.interval) {
|
||||
this.interval = setInterval(this.fetchAccounts, ONE_SECOND)
|
||||
}
|
||||
}
|
||||
|
||||
initNetworkPoll() {
|
||||
if (!this.networkInterval) {
|
||||
this.networkInterval = setInterval(this.fetchNetwork, ONE_MINUTE)
|
||||
}
|
||||
}
|
||||
|
||||
fetchNetwork = () => {
|
||||
const { web3 } = this
|
||||
|
||||
web3 &&
|
||||
web3.eth &&
|
||||
//web3.eth.net.getId((err, netId) => {
|
||||
web3.version.getNetwork((err, netId) => {
|
||||
if (err) {
|
||||
this.setState({ networkError: err })
|
||||
}
|
||||
|
||||
if (netId != this.state.networkId) {
|
||||
this.setState({
|
||||
networkError: null,
|
||||
networkId: netId
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fetchAccounts = () => {
|
||||
const { web3 } = this
|
||||
|
||||
web3 &&
|
||||
web3.eth &&
|
||||
web3.eth.getAccounts((err, accounts) => {
|
||||
if (err) {
|
||||
this.setState({ accountsError: err })
|
||||
}
|
||||
|
||||
this.setState({
|
||||
accounts,
|
||||
selectedAccount: accounts[0]
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
handleWeb3Button = () => {
|
||||
const { web3 } = this
|
||||
|
||||
this.setState({ loading: true })
|
||||
|
||||
// web3.eth
|
||||
// .sendTransaction({
|
||||
// from: this.state.selectedAccount,
|
||||
// to: this.props.address,
|
||||
// value: '10000000000000000'
|
||||
// })
|
||||
// .then(receipt => {
|
||||
// this.setState({ receipt, loading: false })
|
||||
// })
|
||||
// .catch(error => {
|
||||
// this.setState({ error, loading: false })
|
||||
// })
|
||||
|
||||
web3.eth.sendTransaction(
|
||||
{
|
||||
from: this.state.selectedAccount,
|
||||
to: this.props.address,
|
||||
value: '10000000000000000'
|
||||
},
|
||||
(error, transactionHash) => {
|
||||
if (error) this.setState({ error, loading: false })
|
||||
if (!transactionHash) this.setState({ loading: true })
|
||||
this.setState({ transactionHash, loading: false })
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className={styles.web3}>
|
||||
<h4>web3</h4>
|
||||
<p>Send a donation with MetaMask or Mist.</p>
|
||||
|
||||
{this.state.web3Connected ? (
|
||||
<div>
|
||||
{this.state.loading ? (
|
||||
'Hang on...'
|
||||
) : (
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
onClick={this.handleWeb3Button}
|
||||
disabled={
|
||||
!(this.state.networkId === '1') || !this.state.selectedAccount
|
||||
}
|
||||
>
|
||||
Make it rain 0.01 Ξ
|
||||
</button>
|
||||
)}
|
||||
|
||||
{this.state.accounts.length === 0 && (
|
||||
<div className={styles.alert}>
|
||||
Web3 detected, but no account. Are you logged into your MetaMask
|
||||
account?
|
||||
</div>
|
||||
)}
|
||||
|
||||
{this.state.networkId !== '1' && (
|
||||
<div className={styles.alert}>Please connect to Main network</div>
|
||||
)}
|
||||
|
||||
{this.state.error && (
|
||||
<div className={styles.alert}>{this.state.error.message}</div>
|
||||
)}
|
||||
|
||||
{this.state.transactionHash && (
|
||||
<div className={styles.success}>
|
||||
You are awesome, thanks!
|
||||
<br />
|
||||
<a
|
||||
href={`https://etherscan.io/tx/${this.state.transactionHash}`}
|
||||
>
|
||||
See your transaction on etherscan.io.
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.alert}>No Web3 capable browser detected.</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
39
src/components/atoms/Web3Donation.module.scss
Normal file
39
src/components/atoms/Web3Donation.module.scss
Normal file
@ -0,0 +1,39 @@
|
||||
@import 'variables';
|
||||
@import 'mixins';
|
||||
|
||||
.web3 {
|
||||
@include divider;
|
||||
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin-top: $spacer / 2;
|
||||
margin-bottom: $spacer;
|
||||
padding-bottom: $spacer * 1.5;
|
||||
|
||||
button {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: $font-size-large;
|
||||
margin-top: 0;
|
||||
margin-bottom: $spacer / 4;
|
||||
color: $brand-grey;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
p {
|
||||
color: $brand-grey-light;
|
||||
}
|
||||
}
|
||||
|
||||
.alert {
|
||||
margin-top: $spacer / 2;
|
||||
font-size: $font-size-small;
|
||||
color: darken($alert-error, 60%);
|
||||
}
|
||||
|
||||
.success {
|
||||
composes: alert;
|
||||
color: darken($alert-success, 60%);
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
import React from 'react'
|
||||
import React, { PureComponent } from 'react'
|
||||
import { StaticQuery, graphql } from 'gatsby'
|
||||
import { QRCode } from 'react-qr-svg'
|
||||
import Clipboard from 'react-clipboard.js'
|
||||
|
||||
import Web3Donation from '../atoms/Web3Donation'
|
||||
import Qr from '../atoms/Qr'
|
||||
import Modal from '../atoms/Modal'
|
||||
import { ReactComponent as IconClipboard } from '../../images/clipboard.svg'
|
||||
import styles from './ModalThanks.module.scss'
|
||||
|
||||
const query = graphql`
|
||||
@ -20,7 +19,9 @@ const query = graphql`
|
||||
}
|
||||
`
|
||||
|
||||
const ModalThanks = ({ ...props }) => (
|
||||
class ModalThanks extends PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<StaticQuery
|
||||
query={query}
|
||||
render={data => {
|
||||
@ -28,30 +29,16 @@ const ModalThanks = ({ ...props }) => (
|
||||
|
||||
return (
|
||||
<Modal
|
||||
{...props}
|
||||
{...this.props}
|
||||
contentLabel="Say thanks with Bitcoin or Ether"
|
||||
title="Say thanks"
|
||||
>
|
||||
<div className={styles.modalThanks}>
|
||||
<Web3Donation address={author.ether} />
|
||||
|
||||
{Object.keys(author).map((address, i) => (
|
||||
<div key={i} className={styles.coin}>
|
||||
<h4>{address}</h4>
|
||||
<QRCode
|
||||
bgColor="transparent"
|
||||
fgColor="#6b7f88"
|
||||
level="Q"
|
||||
style={{ width: 150 }}
|
||||
value={author[address]}
|
||||
/>
|
||||
<pre>
|
||||
<code>{author[address]}</code>
|
||||
<Clipboard
|
||||
data-clipboard-text={author[address]}
|
||||
button-title="Copy to clipboard"
|
||||
>
|
||||
<IconClipboard />
|
||||
</Clipboard>
|
||||
</pre>
|
||||
<Qr title={address} address={author[address]} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@ -59,6 +46,8 @@ const ModalThanks = ({ ...props }) => (
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default ModalThanks
|
||||
|
@ -4,6 +4,7 @@
|
||||
@media (min-width: $screen-sm) {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,10 +74,12 @@ export default class Footer extends PureComponent {
|
||||
</button>
|
||||
</p>
|
||||
|
||||
{this.state.showModal && (
|
||||
<ModalThanks
|
||||
isOpen={this.state.showModal}
|
||||
handleCloseModal={this.toggleModal}
|
||||
/>
|
||||
)}
|
||||
</section>
|
||||
</Container>
|
||||
</footer>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import { Link } from 'gatsby'
|
||||
import Container from '../atoms/Container'
|
||||
import Search from '../molecules/Search'
|
||||
import Search from '../Search/Search'
|
||||
import Menu from '../molecules/Menu'
|
||||
|
||||
import styles from './Header.module.scss'
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { graphql, Link } from 'gatsby'
|
||||
import PostImage from '../components/atoms/PostImage'
|
||||
import PostImage from '../components/Post/PostImage'
|
||||
import Page from '../templates/Page'
|
||||
|
||||
import styles from './goodies.module.scss'
|
||||
|
@ -3,15 +3,15 @@ import PropTypes from 'prop-types'
|
||||
import Helmet from 'react-helmet'
|
||||
import { graphql } from 'gatsby'
|
||||
import Layout from '../components/Layout'
|
||||
import PostImage from '../components/atoms/PostImage'
|
||||
import PostTitle from '../components/atoms/PostTitle'
|
||||
import PostLead from '../components/atoms/PostLead'
|
||||
import PostContent from '../components/atoms/PostContent'
|
||||
import PostActions from '../components/atoms/PostActions'
|
||||
import PostLinkActions from '../components/atoms/PostLinkActions'
|
||||
import PostImage from '../components/Post/PostImage'
|
||||
import PostTitle from '../components/Post/PostTitle'
|
||||
import PostLead from '../components/Post/PostLead'
|
||||
import PostContent from '../components/Post/PostContent'
|
||||
import PostActions from '../components/Post/PostActions'
|
||||
import PostLinkActions from '../components/Post/PostLinkActions'
|
||||
import SEO from '../components/atoms/SEO'
|
||||
import Coinhive from '../components/atoms/Coinhive'
|
||||
import PostMeta from '../components/molecules/PostMeta'
|
||||
import PostMeta from '../components/Post/PostMeta'
|
||||
import Exif from '../components/atoms/Exif'
|
||||
import RelatedPosts from '../components/molecules/RelatedPosts'
|
||||
import styles from './Post.module.scss'
|
||||
|
@ -2,12 +2,12 @@ import React, { Fragment } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Link, graphql } from 'gatsby'
|
||||
import Layout from '../components/Layout'
|
||||
import PostImage from '../components/atoms/PostImage'
|
||||
import PostTitle from '../components/atoms/PostTitle'
|
||||
import PostLead from '../components/atoms/PostLead'
|
||||
import PostContent from '../components/atoms/PostContent'
|
||||
import PostMore from '../components/atoms/PostMore'
|
||||
import PostLinkActions from '../components/atoms/PostLinkActions'
|
||||
import PostImage from '../components/Post/PostImage'
|
||||
import PostTitle from '../components/Post/PostTitle'
|
||||
import PostLead from '../components/Post/PostLead'
|
||||
import PostContent from '../components/Post/PostContent'
|
||||
import PostMore from '../components/Post/PostMore'
|
||||
import PostLinkActions from '../components/Post/PostLinkActions'
|
||||
import SEO from '../components/atoms/SEO'
|
||||
import Pagination from '../components/molecules/Pagination'
|
||||
import Featured from '../components/molecules/Featured'
|
||||
|
Loading…
Reference in New Issue
Block a user