mirror of
https://github.com/kremalicious/blog.git
synced 2024-11-15 09:35:21 +01:00
commit
ea6c0c3372
60
README.md
60
README.md
@ -18,10 +18,12 @@
|
|||||||
|
|
||||||
- [🎉 Features](#-features)
|
- [🎉 Features](#-features)
|
||||||
- [🎆 EXIF extraction](#-exif-extraction)
|
- [🎆 EXIF extraction](#-exif-extraction)
|
||||||
|
- [💰 Cryptocurrency donation via Web3/MetaMask](#-cryptocurrency-donation-via-web3-metamask)
|
||||||
- [🕸 Related Posts](#-related-posts)
|
- [🕸 Related Posts](#-related-posts)
|
||||||
|
- [🐝 Coinhive](#-coinhive)
|
||||||
- [🏆 SEO component](#-seo-component)
|
- [🏆 SEO component](#-seo-component)
|
||||||
- [📈 Matomo (formerly Piwik) analytics tracking](#-matomo-formerly-piwik-analytics-tracking)
|
- [📈 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)
|
- [💎 Importing SVG assets](#-importing-svg-assets)
|
||||||
- [🍬 Typekit component](#-typekit-component)
|
- [🍬 Typekit component](#-typekit-component)
|
||||||
- [✨ Development](#-development)
|
- [✨ 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">
|
<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
|
### 🕸 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.
|
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
|
### 🏆 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.
|
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
|
### 📈 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
|
### gatsby-redirect-from
|
||||||
|
|
||||||
https://github.com/kremalicious/gatsby-redirect-from
|
- [gatsby-redirect-from](https://github.com/kremalicious/gatsby-redirect-from)
|
||||||
|
|
||||||
### 💎 Importing SVG assets
|
### 💎 Importing SVG assets
|
||||||
|
|
||||||
@ -82,7 +124,9 @@ import { ReactComponent as Logo } from './components/svg/Logo'
|
|||||||
|
|
||||||
Includes a component for adding the Typekit snippet.
|
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
|
## ✨ Development
|
||||||
|
|
||||||
@ -119,7 +163,7 @@ npm run format:css
|
|||||||
npm run new "Hello"
|
npm run new "Hello"
|
||||||
```
|
```
|
||||||
|
|
||||||
...
|
- [`scripts/new.js`](scripts/new.js)
|
||||||
|
|
||||||
## 🚚 Deployment
|
## 🚚 Deployment
|
||||||
|
|
||||||
|
@ -212,11 +212,16 @@ module.exports = {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
resolve: 'gatsby-plugin-sitemap',
|
||||||
|
options: {
|
||||||
|
exclude: ['/page/*', '/tag/*']
|
||||||
|
}
|
||||||
|
},
|
||||||
'gatsby-plugin-webpack-size',
|
'gatsby-plugin-webpack-size',
|
||||||
'gatsby-plugin-react-helmet',
|
'gatsby-plugin-react-helmet',
|
||||||
'gatsby-plugin-sharp',
|
'gatsby-plugin-sharp',
|
||||||
'gatsby-transformer-sharp',
|
'gatsby-transformer-sharp',
|
||||||
'gatsby-plugin-sitemap',
|
|
||||||
'gatsby-plugin-catch-links',
|
'gatsby-plugin-catch-links',
|
||||||
'gatsby-redirect-from',
|
'gatsby-redirect-from',
|
||||||
'gatsby-plugin-meta-redirect',
|
'gatsby-plugin-meta-redirect',
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
"dms2dec": "^1.1.0",
|
"dms2dec": "^1.1.0",
|
||||||
"fast-exif": "^1.0.1",
|
"fast-exif": "^1.0.1",
|
||||||
"fraction.js": "^4.0.9",
|
"fraction.js": "^4.0.9",
|
||||||
"gatsby": "^2.0.20",
|
"gatsby": "^2.0.21",
|
||||||
"gatsby-image": "^2.0.13",
|
"gatsby-image": "^2.0.13",
|
||||||
"gatsby-plugin-catch-links": "^2.0.4",
|
"gatsby-plugin-catch-links": "^2.0.4",
|
||||||
"gatsby-plugin-favicon": "^3.1.4",
|
"gatsby-plugin-favicon": "^3.1.4",
|
||||||
@ -60,7 +60,7 @@
|
|||||||
"load-script": "^1.0.0",
|
"load-script": "^1.0.0",
|
||||||
"node-sass": "^4.9.3",
|
"node-sass": "^4.9.3",
|
||||||
"nord": "^0.2.1",
|
"nord": "^0.2.1",
|
||||||
"pigeon-maps": "^0.11.1",
|
"pigeon-maps": "^0.11.2",
|
||||||
"pigeon-marker": "^0.3.4",
|
"pigeon-marker": "^0.3.4",
|
||||||
"react": "^16.5.2",
|
"react": "^16.5.2",
|
||||||
"react-clipboard.js": "^2.0.1",
|
"react-clipboard.js": "^2.0.1",
|
||||||
@ -71,7 +71,8 @@
|
|||||||
"react-qr-svg": "^2.1.0",
|
"react-qr-svg": "^2.1.0",
|
||||||
"react-time": "^4.3.0",
|
"react-time": "^4.3.0",
|
||||||
"react-transition-group": "^2.5.0",
|
"react-transition-group": "^2.5.0",
|
||||||
"slugify": "^1.3.1"
|
"slugify": "^1.3.1",
|
||||||
|
"web3": "^0.20.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/node": "^7.0.0",
|
"@babel/node": "^7.0.0",
|
||||||
|
@ -35,6 +35,7 @@ export default class PostActions extends PureComponent {
|
|||||||
>
|
>
|
||||||
@kremalicious
|
@kremalicious
|
||||||
</a>
|
</a>
|
||||||
|
.
|
||||||
</p>
|
</p>
|
||||||
</article>
|
</article>
|
||||||
<article className={styles.action}>
|
<article className={styles.action}>
|
||||||
@ -48,10 +49,12 @@ export default class PostActions extends PureComponent {
|
|||||||
</p>
|
</p>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
|
{this.state.showModal && (
|
||||||
<ModalThanks
|
<ModalThanks
|
||||||
isOpen={this.state.showModal}
|
isOpen={this.state.showModal}
|
||||||
handleCloseModal={this.toggleModal}
|
handleCloseModal={this.toggleModal}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
</aside>
|
</aside>
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import Image from './Image'
|
import Image from '../atoms/Image'
|
||||||
import styles from './PostImage.module.scss'
|
import styles from './PostImage.module.scss'
|
||||||
|
|
||||||
const PostImage = ({ title, fluid, fixed, alt }) => (
|
const PostImage = ({ title, fluid, fixed, alt }) => (
|
@ -2,9 +2,9 @@ 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 { CSSTransition } from 'react-transition-group'
|
import { CSSTransition } from 'react-transition-group'
|
||||||
import SearchInput from '../atoms/SearchInput'
|
import SearchInput from './SearchInput'
|
||||||
import SearchButton from '../atoms/SearchButton'
|
import SearchButton from './SearchButton'
|
||||||
import SearchResults from '../atoms/SearchResults'
|
import SearchResults from './SearchResults'
|
||||||
|
|
||||||
import styles from './Search.module.scss'
|
import styles from './Search.module.scss'
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
import React, { Fragment } from 'react'
|
import React, { Fragment } from 'react'
|
||||||
import Input from './Input'
|
import Input from '../atoms/Input'
|
||||||
import styles from './SearchInput.module.scss'
|
import styles from './SearchInput.module.scss'
|
||||||
|
|
||||||
const SearchInput = props => (
|
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 { 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 Modal from '../atoms/Modal'
|
||||||
import { ReactComponent as IconClipboard } from '../../images/clipboard.svg'
|
|
||||||
import styles from './ModalThanks.module.scss'
|
import styles from './ModalThanks.module.scss'
|
||||||
|
|
||||||
const query = graphql`
|
const query = graphql`
|
||||||
@ -20,7 +19,9 @@ const query = graphql`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const ModalThanks = ({ ...props }) => (
|
class ModalThanks extends PureComponent {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
<StaticQuery
|
<StaticQuery
|
||||||
query={query}
|
query={query}
|
||||||
render={data => {
|
render={data => {
|
||||||
@ -28,30 +29,16 @@ const ModalThanks = ({ ...props }) => (
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
{...props}
|
{...this.props}
|
||||||
contentLabel="Say thanks with Bitcoin or Ether"
|
contentLabel="Say thanks with Bitcoin or Ether"
|
||||||
title="Say thanks"
|
title="Say thanks"
|
||||||
>
|
>
|
||||||
<div className={styles.modalThanks}>
|
<div className={styles.modalThanks}>
|
||||||
|
<Web3Donation address={author.ether} />
|
||||||
|
|
||||||
{Object.keys(author).map((address, i) => (
|
{Object.keys(author).map((address, i) => (
|
||||||
<div key={i} className={styles.coin}>
|
<div key={i} className={styles.coin}>
|
||||||
<h4>{address}</h4>
|
<Qr title={address} address={author[address]} />
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@ -59,6 +46,8 @@ const ModalThanks = ({ ...props }) => (
|
|||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default ModalThanks
|
export default ModalThanks
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
@media (min-width: $screen-sm) {
|
@media (min-width: $screen-sm) {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,10 +74,12 @@ export default class Footer extends PureComponent {
|
|||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
{this.state.showModal && (
|
||||||
<ModalThanks
|
<ModalThanks
|
||||||
isOpen={this.state.showModal}
|
isOpen={this.state.showModal}
|
||||||
handleCloseModal={this.toggleModal}
|
handleCloseModal={this.toggleModal}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
</section>
|
</section>
|
||||||
</Container>
|
</Container>
|
||||||
</footer>
|
</footer>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { PureComponent } from 'react'
|
import React, { PureComponent } from 'react'
|
||||||
import { Link } from 'gatsby'
|
import { Link } from 'gatsby'
|
||||||
import Container from '../atoms/Container'
|
import Container from '../atoms/Container'
|
||||||
import Search from '../molecules/Search'
|
import Search from '../Search/Search'
|
||||||
import Menu from '../molecules/Menu'
|
import Menu from '../molecules/Menu'
|
||||||
|
|
||||||
import styles from './Header.module.scss'
|
import styles from './Header.module.scss'
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { graphql, Link } from 'gatsby'
|
import { graphql, Link } from 'gatsby'
|
||||||
import PostImage from '../components/atoms/PostImage'
|
import PostImage from '../components/Post/PostImage'
|
||||||
import Page from '../templates/Page'
|
import Page from '../templates/Page'
|
||||||
|
|
||||||
import styles from './goodies.module.scss'
|
import styles from './goodies.module.scss'
|
||||||
|
@ -3,15 +3,15 @@ import PropTypes from 'prop-types'
|
|||||||
import Helmet from 'react-helmet'
|
import Helmet from 'react-helmet'
|
||||||
import { graphql } from 'gatsby'
|
import { graphql } from 'gatsby'
|
||||||
import Layout from '../components/Layout'
|
import Layout from '../components/Layout'
|
||||||
import PostImage from '../components/atoms/PostImage'
|
import PostImage from '../components/Post/PostImage'
|
||||||
import PostTitle from '../components/atoms/PostTitle'
|
import PostTitle from '../components/Post/PostTitle'
|
||||||
import PostLead from '../components/atoms/PostLead'
|
import PostLead from '../components/Post/PostLead'
|
||||||
import PostContent from '../components/atoms/PostContent'
|
import PostContent from '../components/Post/PostContent'
|
||||||
import PostActions from '../components/atoms/PostActions'
|
import PostActions from '../components/Post/PostActions'
|
||||||
import PostLinkActions from '../components/atoms/PostLinkActions'
|
import PostLinkActions from '../components/Post/PostLinkActions'
|
||||||
import SEO from '../components/atoms/SEO'
|
import SEO from '../components/atoms/SEO'
|
||||||
import Coinhive from '../components/atoms/Coinhive'
|
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 Exif from '../components/atoms/Exif'
|
||||||
import RelatedPosts from '../components/molecules/RelatedPosts'
|
import RelatedPosts from '../components/molecules/RelatedPosts'
|
||||||
import styles from './Post.module.scss'
|
import styles from './Post.module.scss'
|
||||||
|
@ -2,12 +2,12 @@ import React, { Fragment } from 'react'
|
|||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { Link, graphql } from 'gatsby'
|
import { Link, graphql } from 'gatsby'
|
||||||
import Layout from '../components/Layout'
|
import Layout from '../components/Layout'
|
||||||
import PostImage from '../components/atoms/PostImage'
|
import PostImage from '../components/Post/PostImage'
|
||||||
import PostTitle from '../components/atoms/PostTitle'
|
import PostTitle from '../components/Post/PostTitle'
|
||||||
import PostLead from '../components/atoms/PostLead'
|
import PostLead from '../components/Post/PostLead'
|
||||||
import PostContent from '../components/atoms/PostContent'
|
import PostContent from '../components/Post/PostContent'
|
||||||
import PostMore from '../components/atoms/PostMore'
|
import PostMore from '../components/Post/PostMore'
|
||||||
import PostLinkActions from '../components/atoms/PostLinkActions'
|
import PostLinkActions from '../components/Post/PostLinkActions'
|
||||||
import SEO from '../components/atoms/SEO'
|
import SEO from '../components/atoms/SEO'
|
||||||
import Pagination from '../components/molecules/Pagination'
|
import Pagination from '../components/molecules/Pagination'
|
||||||
import Featured from '../components/molecules/Featured'
|
import Featured from '../components/molecules/Featured'
|
||||||
|
Loading…
Reference in New Issue
Block a user