From bde652533e59ae5db71198290025852899687dbe Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Mon, 8 Oct 2018 22:19:52 +0200 Subject: [PATCH 01/11] basic transaction interaction --- package.json | 3 +- src/components/molecules/ModalThanks.jsx | 158 +++++++++++++----- .../molecules/ModalThanks.module.scss | 5 + 3 files changed, 126 insertions(+), 40 deletions(-) diff --git a/package.json b/package.json index 531d3431..8c0448e6 100644 --- a/package.json +++ b/package.json @@ -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": "^1.0.0-beta.36" }, "devDependencies": { "@babel/node": "^7.0.0", diff --git a/src/components/molecules/ModalThanks.jsx b/src/components/molecules/ModalThanks.jsx index 6aa723bd..757411f3 100644 --- a/src/components/molecules/ModalThanks.jsx +++ b/src/components/molecules/ModalThanks.jsx @@ -1,7 +1,8 @@ -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 Web3 from 'web3' import Modal from '../atoms/Modal' import { ReactComponent as IconClipboard } from '../../images/clipboard.svg' @@ -20,45 +21,124 @@ const query = graphql` } ` -const ModalThanks = ({ ...props }) => ( - { - const { author } = data.site.siteMetadata +class ModalThanks extends PureComponent { + state = { + minimal: false, + web3Connected: false, + balance: '', + network: '', + accounts: [], + receipt: '' + } - return ( - -
- {Object.keys(author).map((address, i) => ( -
-

{address}

- -
-                  {author[address]}
-                  
-                    
-                  
-                
+ web3 = new Web3(Web3.givenProvider || 'ws://localhost:8546') + + componentDidMount() { + this.getWeb3Account() + } + + getWeb3Account() { + if (this.web3 && this.web3.eth.net.isListening()) { + this.setState({ web3Connected: true }) + + this.web3.eth.net.getId((err, netId) => { + switch (netId) { + case '1': + this.setState({ network: 'Main' }) + break + case '2': + this.setState({ network: 'Morden' }) + break + case '3': + this.setState({ network: 'Ropsten' }) + break + case '4': + this.setState({ network: 'Rinkeby' }) + break + case '42': + this.setState({ network: 'Kovan' }) + break + default: + this.setState({ network: 'unknown' }) + } + }) + + this.web3.eth.getAccounts((error, accounts) => { + this.setState({ accounts }) + this.web3.eth.getBalance(accounts[0]).then(balance => { + this.setState({ balance }) + }) + }) + } + } + + handleWeb3Button = () => { + this.web3.eth + .sendTransaction({ + from: this.state.accounts[0], + to: '0x339dbC44d39bf1961E385ed0Ae88FC6069b87Ea1', + value: '1000000000000000' + }) + .then(receipt => this.setState({ receipt })) + .catch(err => console.error(err)) + } + + render() { + return ( + { + const { author } = data.site.siteMetadata + + return ( + +
+ {this.state.web3Connected && ( +
+ + {this.state.receipt.status && ( +
{this.state.receipt.transactionHash}
+ )} +
+ )} + + {Object.keys(author).map((address, i) => ( +
+

{address}

+ +
+                      {author[address]}
+                      
+                        
+                      
+                    
+
+ ))}
- ))} -
- - ) - }} - /> -) + + ) + }} + /> + ) + } +} export default ModalThanks diff --git a/src/components/molecules/ModalThanks.module.scss b/src/components/molecules/ModalThanks.module.scss index 02155fd3..61d62f95 100644 --- a/src/components/molecules/ModalThanks.module.scss +++ b/src/components/molecules/ModalThanks.module.scss @@ -4,6 +4,11 @@ @media (min-width: $screen-sm) { display: flex; justify-content: space-between; + flex-wrap: wrap; + + > div:first-child { + width: 100%; + } } } From 2e85f7e24b0041cbd6dd24a0452498cb8f5fe7e1 Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Tue, 9 Oct 2018 23:48:25 +0200 Subject: [PATCH 02/11] web3 interaction --- src/components/atoms/PostActions.jsx | 10 +- src/components/atoms/Web3Donation.jsx | 180 ++++++++++++++++++ src/components/atoms/Web3Donation.module.scss | 39 ++++ src/components/molecules/ModalThanks.jsx | 80 +------- .../molecules/ModalThanks.module.scss | 4 - src/components/organisms/Footer.jsx | 10 +- 6 files changed, 234 insertions(+), 89 deletions(-) create mode 100644 src/components/atoms/Web3Donation.jsx create mode 100644 src/components/atoms/Web3Donation.module.scss diff --git a/src/components/atoms/PostActions.jsx b/src/components/atoms/PostActions.jsx index 8c3ff3c3..d663c7a7 100644 --- a/src/components/atoms/PostActions.jsx +++ b/src/components/atoms/PostActions.jsx @@ -48,10 +48,12 @@ export default class PostActions extends PureComponent {

- + {this.state.showModal && ( + + )} ) } diff --git a/src/components/atoms/Web3Donation.jsx b/src/components/atoms/Web3Donation.jsx new file mode 100644 index 00000000..378e670e --- /dev/null +++ b/src/components/atoms/Web3Donation.jsx @@ -0,0 +1,180 @@ +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: '', + loading: false, + error: null + } + + static propTypes = { + address: PropTypes.string + } + + web3 = new Web3(Web3.givenProvider || 'ws://localhost:8546') + interval = null + networkInterval = null + + componentDidMount() { + const { web3 } = this + + if (web3 && web3.eth) { + 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) => { + if (err) { + this.setState({ + networkError: err + }) + } else { + 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 + }) + } else { + 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 }) + }) + } + + render() { + if (this.state.web3Connected) { + return ( +
+

web3

+

Send a donation with your MetaMask or Mist account.

+ + {this.state.web3Connected && ( +
+ {this.state.loading ? ( + 'Hang on...' + ) : ( + + )} + + {this.state.accounts.length === 0 && ( +
+ Web3 detected, but no account. Are you logged into your + MetaMask account? +
+ )} + + {this.state.networkId !== 1 && ( +
+ Please connect to Main network +
+ )} + + {this.state.error && ( +
{this.state.error.message}
+ )} + + {this.state.receipt.status && ( +
+ You are awesome, thanks! +
+ + See your transaction on etherscan.io. + +
+ )} +
+ )} +
+ ) + } else { + return null + } + } +} diff --git a/src/components/atoms/Web3Donation.module.scss b/src/components/atoms/Web3Donation.module.scss new file mode 100644 index 00000000..9905ad60 --- /dev/null +++ b/src/components/atoms/Web3Donation.module.scss @@ -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%); +} diff --git a/src/components/molecules/ModalThanks.jsx b/src/components/molecules/ModalThanks.jsx index 757411f3..87fbc5ce 100644 --- a/src/components/molecules/ModalThanks.jsx +++ b/src/components/molecules/ModalThanks.jsx @@ -2,8 +2,7 @@ import React, { PureComponent } from 'react' import { StaticQuery, graphql } from 'gatsby' import { QRCode } from 'react-qr-svg' import Clipboard from 'react-clipboard.js' -import Web3 from 'web3' - +import Web3Donation from '../atoms/Web3Donation' import Modal from '../atoms/Modal' import { ReactComponent as IconClipboard } from '../../images/clipboard.svg' import styles from './ModalThanks.module.scss' @@ -22,67 +21,6 @@ const query = graphql` ` class ModalThanks extends PureComponent { - state = { - minimal: false, - web3Connected: false, - balance: '', - network: '', - accounts: [], - receipt: '' - } - - web3 = new Web3(Web3.givenProvider || 'ws://localhost:8546') - - componentDidMount() { - this.getWeb3Account() - } - - getWeb3Account() { - if (this.web3 && this.web3.eth.net.isListening()) { - this.setState({ web3Connected: true }) - - this.web3.eth.net.getId((err, netId) => { - switch (netId) { - case '1': - this.setState({ network: 'Main' }) - break - case '2': - this.setState({ network: 'Morden' }) - break - case '3': - this.setState({ network: 'Ropsten' }) - break - case '4': - this.setState({ network: 'Rinkeby' }) - break - case '42': - this.setState({ network: 'Kovan' }) - break - default: - this.setState({ network: 'unknown' }) - } - }) - - this.web3.eth.getAccounts((error, accounts) => { - this.setState({ accounts }) - this.web3.eth.getBalance(accounts[0]).then(balance => { - this.setState({ balance }) - }) - }) - } - } - - handleWeb3Button = () => { - this.web3.eth - .sendTransaction({ - from: this.state.accounts[0], - to: '0x339dbC44d39bf1961E385ed0Ae88FC6069b87Ea1', - value: '1000000000000000' - }) - .then(receipt => this.setState({ receipt })) - .catch(err => console.error(err)) - } - render() { return (
- {this.state.web3Connected && ( -
- - {this.state.receipt.status && ( -
{this.state.receipt.transactionHash}
- )} -
- )} + {Object.keys(author).map((address, i) => (
@@ -118,7 +44,7 @@ class ModalThanks extends PureComponent { bgColor="transparent" fgColor="#6b7f88" level="Q" - style={{ width: 150 }} + style={{ width: 120 }} value={author[address]} />
diff --git a/src/components/molecules/ModalThanks.module.scss b/src/components/molecules/ModalThanks.module.scss
index 61d62f95..0f499ab2 100644
--- a/src/components/molecules/ModalThanks.module.scss
+++ b/src/components/molecules/ModalThanks.module.scss
@@ -5,10 +5,6 @@
         display: flex;
         justify-content: space-between;
         flex-wrap: wrap;
-
-        > div:first-child {
-            width: 100%;
-        }
     }
 }
 
diff --git a/src/components/organisms/Footer.jsx b/src/components/organisms/Footer.jsx
index 1c0b0099..25b4dc90 100644
--- a/src/components/organisms/Footer.jsx
+++ b/src/components/organisms/Footer.jsx
@@ -74,10 +74,12 @@ export default class Footer extends PureComponent {
                     
                   

- + {this.state.showModal && ( + + )} From 0ca1e977cdf832f02bb1d7b13f80eb26128420a9 Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Thu, 11 Oct 2018 20:06:02 +0200 Subject: [PATCH 03/11] web3 detection improvements --- src/components/atoms/PostActions.jsx | 1 + src/components/atoms/Web3Donation.jsx | 148 ++++++++++++-------------- 2 files changed, 72 insertions(+), 77 deletions(-) diff --git a/src/components/atoms/PostActions.jsx b/src/components/atoms/PostActions.jsx index d663c7a7..e6339aae 100644 --- a/src/components/atoms/PostActions.jsx +++ b/src/components/atoms/PostActions.jsx @@ -35,6 +35,7 @@ export default class PostActions extends PureComponent { > @kremalicious + .

diff --git a/src/components/atoms/Web3Donation.jsx b/src/components/atoms/Web3Donation.jsx index 378e670e..af0fa5f5 100644 --- a/src/components/atoms/Web3Donation.jsx +++ b/src/components/atoms/Web3Donation.jsx @@ -22,14 +22,16 @@ export default class Web3Donation extends PureComponent { address: PropTypes.string } - web3 = new Web3(Web3.givenProvider || 'ws://localhost:8546') + web3 = null interval = null networkInterval = null componentDidMount() { - const { web3 } = this - - if (web3 && web3.eth) { + if (typeof window.web3 === 'undefined') { + // no web3 + this.setState({ web3Connected: false }) + } else { + this.web3 = new Web3(Web3.givenProvider || 'ws://localhost:8546') this.setState({ web3Connected: true }) this.fetchAccounts() @@ -64,16 +66,14 @@ export default class Web3Donation extends PureComponent { web3.eth && web3.eth.net.getId((err, netId) => { if (err) { + this.setState({ networkError: err }) + } + + if (netId != this.state.networkId) { this.setState({ - networkError: err + networkError: null, + networkId: netId }) - } else { - if (netId != this.state.networkId) { - this.setState({ - networkError: null, - networkId: netId - }) - } } }) } @@ -85,15 +85,13 @@ export default class Web3Donation extends PureComponent { web3.eth && web3.eth.getAccounts((err, accounts) => { if (err) { - this.setState({ - accountsError: err - }) - } else { - this.setState({ - accounts, - selectedAccount: accounts[0] - }) + this.setState({ accountsError: err }) } + + this.setState({ + accounts, + selectedAccount: accounts[0] + }) }) } @@ -117,64 +115,60 @@ export default class Web3Donation extends PureComponent { } render() { - if (this.state.web3Connected) { - return ( -
-

web3

-

Send a donation with your MetaMask or Mist account.

+ return ( +
+

web3

+

Send a donation with MetaMask or Mist.

- {this.state.web3Connected && ( -
- {this.state.loading ? ( - 'Hang on...' - ) : ( - + )} + + {this.state.accounts.length === 0 && ( +
+ Web3 detected, but no account. Are you logged into your MetaMask + account? +
+ )} + + {this.state.networkId !== 1 && ( +
Please connect to Main network
+ )} + + {this.state.error && ( +
{this.state.error.message}
+ )} + + {this.state.receipt.status && ( + - )} -
- ) - } else { - return null - } + See your transaction on etherscan.io. + +
+ )} +
+ ) : ( +
No Web3 capable browser detected.
+ )} +
+ ) } } From dd9990d947091225e0ee938ab2d7fbac50e1ae81 Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Thu, 11 Oct 2018 20:35:10 +0200 Subject: [PATCH 04/11] components moving --- src/components/{atoms => Post}/PostActions.jsx | 0 .../{atoms => Post}/PostActions.module.scss | 0 src/components/{atoms => Post}/PostContent.jsx | 0 src/components/{atoms => Post}/PostImage.jsx | 2 +- .../{atoms => Post}/PostImage.module.scss | 0 src/components/{atoms => Post}/PostLead.jsx | 0 .../{atoms => Post}/PostLead.module.scss | 0 src/components/{atoms => Post}/PostLinkActions.jsx | 0 .../{atoms => Post}/PostLinkActions.module.scss | 0 src/components/{molecules => Post}/PostMeta.jsx | 0 .../{molecules => Post}/PostMeta.module.scss | 0 src/components/{atoms => Post}/PostMore.jsx | 0 .../{atoms => Post}/PostMore.module.scss | 0 src/components/{atoms => Post}/PostTitle.jsx | 0 .../{atoms => Post}/PostTitle.module.scss | 0 src/components/{molecules => Search}/Search.jsx | 6 +++--- .../{molecules => Search}/Search.module.scss | 0 src/components/{atoms => Search}/SearchButton.jsx | 0 .../{atoms => Search}/SearchButton.module.scss | 0 src/components/{atoms => Search}/SearchInput.jsx | 2 +- .../{atoms => Search}/SearchInput.module.scss | 0 src/components/{atoms => Search}/SearchResults.jsx | 0 .../{atoms => Search}/SearchResults.module.scss | 0 src/components/organisms/Header.jsx | 2 +- src/pages/goodies.jsx | 2 +- src/templates/Post.jsx | 14 +++++++------- src/templates/Posts.jsx | 12 ++++++------ 27 files changed, 20 insertions(+), 20 deletions(-) rename src/components/{atoms => Post}/PostActions.jsx (100%) rename src/components/{atoms => Post}/PostActions.module.scss (100%) rename src/components/{atoms => Post}/PostContent.jsx (100%) rename src/components/{atoms => Post}/PostImage.jsx (94%) rename src/components/{atoms => Post}/PostImage.module.scss (100%) rename src/components/{atoms => Post}/PostLead.jsx (100%) rename src/components/{atoms => Post}/PostLead.module.scss (100%) rename src/components/{atoms => Post}/PostLinkActions.jsx (100%) rename src/components/{atoms => Post}/PostLinkActions.module.scss (100%) rename src/components/{molecules => Post}/PostMeta.jsx (100%) rename src/components/{molecules => Post}/PostMeta.module.scss (100%) rename src/components/{atoms => Post}/PostMore.jsx (100%) rename src/components/{atoms => Post}/PostMore.module.scss (100%) rename src/components/{atoms => Post}/PostTitle.jsx (100%) rename src/components/{atoms => Post}/PostTitle.module.scss (100%) rename src/components/{molecules => Search}/Search.jsx (92%) rename src/components/{molecules => Search}/Search.module.scss (100%) rename src/components/{atoms => Search}/SearchButton.jsx (100%) rename src/components/{atoms => Search}/SearchButton.module.scss (100%) rename src/components/{atoms => Search}/SearchInput.jsx (90%) rename src/components/{atoms => Search}/SearchInput.module.scss (100%) rename src/components/{atoms => Search}/SearchResults.jsx (100%) rename src/components/{atoms => Search}/SearchResults.module.scss (100%) diff --git a/src/components/atoms/PostActions.jsx b/src/components/Post/PostActions.jsx similarity index 100% rename from src/components/atoms/PostActions.jsx rename to src/components/Post/PostActions.jsx diff --git a/src/components/atoms/PostActions.module.scss b/src/components/Post/PostActions.module.scss similarity index 100% rename from src/components/atoms/PostActions.module.scss rename to src/components/Post/PostActions.module.scss diff --git a/src/components/atoms/PostContent.jsx b/src/components/Post/PostContent.jsx similarity index 100% rename from src/components/atoms/PostContent.jsx rename to src/components/Post/PostContent.jsx diff --git a/src/components/atoms/PostImage.jsx b/src/components/Post/PostImage.jsx similarity index 94% rename from src/components/atoms/PostImage.jsx rename to src/components/Post/PostImage.jsx index 4232c0d0..682a834e 100644 --- a/src/components/atoms/PostImage.jsx +++ b/src/components/Post/PostImage.jsx @@ -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 }) => ( diff --git a/src/components/atoms/PostImage.module.scss b/src/components/Post/PostImage.module.scss similarity index 100% rename from src/components/atoms/PostImage.module.scss rename to src/components/Post/PostImage.module.scss diff --git a/src/components/atoms/PostLead.jsx b/src/components/Post/PostLead.jsx similarity index 100% rename from src/components/atoms/PostLead.jsx rename to src/components/Post/PostLead.jsx diff --git a/src/components/atoms/PostLead.module.scss b/src/components/Post/PostLead.module.scss similarity index 100% rename from src/components/atoms/PostLead.module.scss rename to src/components/Post/PostLead.module.scss diff --git a/src/components/atoms/PostLinkActions.jsx b/src/components/Post/PostLinkActions.jsx similarity index 100% rename from src/components/atoms/PostLinkActions.jsx rename to src/components/Post/PostLinkActions.jsx diff --git a/src/components/atoms/PostLinkActions.module.scss b/src/components/Post/PostLinkActions.module.scss similarity index 100% rename from src/components/atoms/PostLinkActions.module.scss rename to src/components/Post/PostLinkActions.module.scss diff --git a/src/components/molecules/PostMeta.jsx b/src/components/Post/PostMeta.jsx similarity index 100% rename from src/components/molecules/PostMeta.jsx rename to src/components/Post/PostMeta.jsx diff --git a/src/components/molecules/PostMeta.module.scss b/src/components/Post/PostMeta.module.scss similarity index 100% rename from src/components/molecules/PostMeta.module.scss rename to src/components/Post/PostMeta.module.scss diff --git a/src/components/atoms/PostMore.jsx b/src/components/Post/PostMore.jsx similarity index 100% rename from src/components/atoms/PostMore.jsx rename to src/components/Post/PostMore.jsx diff --git a/src/components/atoms/PostMore.module.scss b/src/components/Post/PostMore.module.scss similarity index 100% rename from src/components/atoms/PostMore.module.scss rename to src/components/Post/PostMore.module.scss diff --git a/src/components/atoms/PostTitle.jsx b/src/components/Post/PostTitle.jsx similarity index 100% rename from src/components/atoms/PostTitle.jsx rename to src/components/Post/PostTitle.jsx diff --git a/src/components/atoms/PostTitle.module.scss b/src/components/Post/PostTitle.module.scss similarity index 100% rename from src/components/atoms/PostTitle.module.scss rename to src/components/Post/PostTitle.module.scss diff --git a/src/components/molecules/Search.jsx b/src/components/Search/Search.jsx similarity index 92% rename from src/components/molecules/Search.jsx rename to src/components/Search/Search.jsx index 88185b10..6be43e6e 100644 --- a/src/components/molecules/Search.jsx +++ b/src/components/Search/Search.jsx @@ -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' diff --git a/src/components/molecules/Search.module.scss b/src/components/Search/Search.module.scss similarity index 100% rename from src/components/molecules/Search.module.scss rename to src/components/Search/Search.module.scss diff --git a/src/components/atoms/SearchButton.jsx b/src/components/Search/SearchButton.jsx similarity index 100% rename from src/components/atoms/SearchButton.jsx rename to src/components/Search/SearchButton.jsx diff --git a/src/components/atoms/SearchButton.module.scss b/src/components/Search/SearchButton.module.scss similarity index 100% rename from src/components/atoms/SearchButton.module.scss rename to src/components/Search/SearchButton.module.scss diff --git a/src/components/atoms/SearchInput.jsx b/src/components/Search/SearchInput.jsx similarity index 90% rename from src/components/atoms/SearchInput.jsx rename to src/components/Search/SearchInput.jsx index 654dc90a..72883070 100644 --- a/src/components/atoms/SearchInput.jsx +++ b/src/components/Search/SearchInput.jsx @@ -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 => ( diff --git a/src/components/atoms/SearchInput.module.scss b/src/components/Search/SearchInput.module.scss similarity index 100% rename from src/components/atoms/SearchInput.module.scss rename to src/components/Search/SearchInput.module.scss diff --git a/src/components/atoms/SearchResults.jsx b/src/components/Search/SearchResults.jsx similarity index 100% rename from src/components/atoms/SearchResults.jsx rename to src/components/Search/SearchResults.jsx diff --git a/src/components/atoms/SearchResults.module.scss b/src/components/Search/SearchResults.module.scss similarity index 100% rename from src/components/atoms/SearchResults.module.scss rename to src/components/Search/SearchResults.module.scss diff --git a/src/components/organisms/Header.jsx b/src/components/organisms/Header.jsx index b9104bd2..18b76d90 100644 --- a/src/components/organisms/Header.jsx +++ b/src/components/organisms/Header.jsx @@ -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' diff --git a/src/pages/goodies.jsx b/src/pages/goodies.jsx index 044a49ff..701c2dff 100644 --- a/src/pages/goodies.jsx +++ b/src/pages/goodies.jsx @@ -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' diff --git a/src/templates/Post.jsx b/src/templates/Post.jsx index e095d2a7..9052d8ac 100644 --- a/src/templates/Post.jsx +++ b/src/templates/Post.jsx @@ -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' diff --git a/src/templates/Posts.jsx b/src/templates/Posts.jsx index 722c7a06..41a6837a 100644 --- a/src/templates/Posts.jsx +++ b/src/templates/Posts.jsx @@ -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' From 51178c7352b9507c4a8bd220e4e660f54ed7d665 Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Thu, 11 Oct 2018 21:14:09 +0200 Subject: [PATCH 05/11] move QR functionality into own component --- src/components/atoms/Qr.jsx | 31 ++++++++++++++++++++++++ src/components/molecules/ModalThanks.jsx | 23 +++--------------- 2 files changed, 34 insertions(+), 20 deletions(-) create mode 100644 src/components/atoms/Qr.jsx diff --git a/src/components/atoms/Qr.jsx b/src/components/atoms/Qr.jsx new file mode 100644 index 00000000..6f3b0202 --- /dev/null +++ b/src/components/atoms/Qr.jsx @@ -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 }) => ( + + {title &&

{title}

} + +
+      {address}
+      
+        
+      
+    
+
+) + +Qr.propTypes = { + address: PropTypes.string.isRequired, + title: PropTypes.string +} + +export default Qr diff --git a/src/components/molecules/ModalThanks.jsx b/src/components/molecules/ModalThanks.jsx index 87fbc5ce..0e86656e 100644 --- a/src/components/molecules/ModalThanks.jsx +++ b/src/components/molecules/ModalThanks.jsx @@ -1,10 +1,9 @@ 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` @@ -39,23 +38,7 @@ class ModalThanks extends PureComponent { {Object.keys(author).map((address, i) => (
-

{address}

- -
-                      {author[address]}
-                      
-                        
-                      
-                    
+
))}
From d3ac72149ec25bbbdbb32bb86354194c8f462dd6 Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Thu, 11 Oct 2018 21:14:15 +0200 Subject: [PATCH 06/11] docs updates --- README.md | 58 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 7a37cc5c..61cad950 100644 --- a/README.md +++ b/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: screen shot 2018-10-09 at 23 59 39 -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). + +screen shot 2018-10-11 at 21 01 37 + +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) +screen shot 2018-10-11 at 21 03 03 + +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). + +screen shot 2018-10-11 at 21 09 49 + +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 From 2f27668b9b09e20d61aea299de9860dd3759dd64 Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Thu, 11 Oct 2018 21:26:32 +0200 Subject: [PATCH 07/11] package update --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 8c0448e6..d0979cc1 100644 --- a/package.json +++ b/package.json @@ -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", From 6d53d8a9f3c1955bf9d5f1a968d5c83f7daf9c98 Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Thu, 11 Oct 2018 21:30:59 +0200 Subject: [PATCH 08/11] link to new post script --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 61cad950..88b68622 100644 --- a/README.md +++ b/README.md @@ -163,7 +163,7 @@ npm run format:css npm run new "Hello" ``` -... +- [`scripts/new.js`](scripts/new.js) ## 🚚 Deployment From 765e9eccb5381cf24b3a565f34ce28deec272519 Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Thu, 11 Oct 2018 21:39:33 +0200 Subject: [PATCH 09/11] exclude /page & /tag paths from sitemap --- gatsby-config.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/gatsby-config.js b/gatsby-config.js index 790ed403..e178bf05 100644 --- a/gatsby-config.js +++ b/gatsby-config.js @@ -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', From 314b7bf855c224a3eb0acc10d8950a9f5734a9d7 Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Thu, 11 Oct 2018 22:00:01 +0200 Subject: [PATCH 10/11] build fix --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index edab949d..a6b29c30 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,9 @@ cache: - public install: + - npm install -g scrypt - npm i + - ln -s ./node_modules/scrypt /usr/local/lib/node_modules/scrypt/build script: - npm test From 6ad87ca1d6698eed8b70b9f935c07eb00ec02734 Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Thu, 11 Oct 2018 23:18:03 +0200 Subject: [PATCH 11/11] downgrade to Web3 0.20.7 --- .travis.yml | 2 -- package.json | 2 +- src/components/atoms/Web3Donation.jsx | 50 +++++++++++++++++---------- 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/.travis.yml b/.travis.yml index a6b29c30..edab949d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,9 +7,7 @@ cache: - public install: - - npm install -g scrypt - npm i - - ln -s ./node_modules/scrypt /usr/local/lib/node_modules/scrypt/build script: - npm test diff --git a/package.json b/package.json index d0979cc1..c8ab57f6 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "react-time": "^4.3.0", "react-transition-group": "^2.5.0", "slugify": "^1.3.1", - "web3": "^1.0.0-beta.36" + "web3": "^0.20.7" }, "devDependencies": { "@babel/node": "^7.0.0", diff --git a/src/components/atoms/Web3Donation.jsx b/src/components/atoms/Web3Donation.jsx index af0fa5f5..68c26e05 100644 --- a/src/components/atoms/Web3Donation.jsx +++ b/src/components/atoms/Web3Donation.jsx @@ -13,7 +13,8 @@ export default class Web3Donation extends PureComponent { networkId: null, accounts: [], selectedAccount: null, - receipt: '', + receipt: null, + transactionHash: null, loading: false, error: null } @@ -31,7 +32,8 @@ export default class Web3Donation extends PureComponent { // no web3 this.setState({ web3Connected: false }) } else { - this.web3 = new Web3(Web3.givenProvider || 'ws://localhost:8546') + // this.web3 = new Web3(Web3.givenProvider || 'ws://localhost:8546') + this.web3 = new Web3(window.web3.currentProvider) this.setState({ web3Connected: true }) this.fetchAccounts() @@ -64,7 +66,8 @@ export default class Web3Donation extends PureComponent { web3 && web3.eth && - web3.eth.net.getId((err, netId) => { + //web3.eth.net.getId((err, netId) => { + web3.version.getNetwork((err, netId) => { if (err) { this.setState({ networkError: err }) } @@ -100,18 +103,31 @@ export default class Web3Donation extends PureComponent { this.setState({ loading: true }) - web3.eth - .sendTransaction({ + // 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' - }) - .then(receipt => { - this.setState({ receipt, loading: false }) - }) - .catch(error => { - this.setState({ error, loading: false }) - }) + }, + (error, transactionHash) => { + if (error) this.setState({ error, loading: false }) + if (!transactionHash) this.setState({ loading: true }) + this.setState({ transactionHash, loading: false }) + } + ) } render() { @@ -129,7 +145,7 @@ export default class Web3Donation extends PureComponent { className="btn btn-primary" onClick={this.handleWeb3Button} disabled={ - !(this.state.networkId === 1) || !this.state.selectedAccount + !(this.state.networkId === '1') || !this.state.selectedAccount } > Make it rain 0.01 Ξ @@ -143,7 +159,7 @@ export default class Web3Donation extends PureComponent {
)} - {this.state.networkId !== 1 && ( + {this.state.networkId !== '1' && (
Please connect to Main network
)} @@ -151,14 +167,12 @@ export default class Web3Donation extends PureComponent {
{this.state.error.message}
)} - {this.state.receipt.status && ( + {this.state.transactionHash && (
You are awesome, thanks!
See your transaction on etherscan.io.