diff --git a/README.md b/README.md index 749b88b0..e3b8dd4c 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ As a fallback, QR codes are generated with [react-qr-svg](https://github.com/no2 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/Web3Donation/index.jsx`](src/components/Web3Donation/index.jsx) - [`src/components/atoms/Qr.jsx`](src/components/atoms/Qr.jsx) ### 🕸 Related Posts diff --git a/gatsby-config.js b/gatsby-config.js index e178bf05..a7807d10 100644 --- a/gatsby-config.js +++ b/gatsby-config.js @@ -91,22 +91,18 @@ module.exports = { // Attributes for custom indexing logic. See https://lunrjs.com/docs/lunr.Builder.html for details fields: [ { name: 'title', store: true, attributes: { boost: 20 } }, - { name: 'content' }, { name: 'excerpt', attributes: { boost: 10 } }, - { name: 'category', store: true, attributes: { boost: 5 } }, - { name: 'tags', store: true }, - { name: 'url', store: true } + { name: 'tags', store: true, attributes: { boost: 5 } }, + { name: 'content' } ], // How to resolve each field's value for a supported node type resolvers: { // For any node of type MarkdownRemark, list how to resolve the fields' values MarkdownRemark: { title: node => node.frontmatter.title, - content: node => node.rawMarkdownBody, - excerpt: node => node.frontmatter.excerpt, - category: node => node.frontmatter.category, + excerpt: node => node.excerpt, tags: node => node.frontmatter.tags, - url: node => node.fields.slug + content: node => node.rawMarkdownBody } } } diff --git a/src/components/Web3Donation/Alerts.jsx b/src/components/Web3Donation/Alerts.jsx new file mode 100644 index 00000000..84ffd6f6 --- /dev/null +++ b/src/components/Web3Donation/Alerts.jsx @@ -0,0 +1,62 @@ +import React, { Fragment } from 'react' +import PropTypes from 'prop-types' +import styles from './Alerts.module.scss' + +const alertMessages = (networkName, transactionHash) => ({ + noAccount: + 'Web3 detected, but no account. Are you logged into your MetaMask account?', + noCorrectNetwork: `Please connect to Main network. You are on ${networkName} right now.`, + noWeb3: + 'No Web3 detected. Install MetaMask, Brave, or Mist.', + success: `You are awesome, thanks!
+ + See your transaction on etherscan.io. + ` +}) + +const Alerts = ({ + hasCorrectNetwork, + hasAccount, + networkName, + error, + transactionHash, + web3Connected +}) => { + return !web3Connected ? ( + + ) : ( + +
+ {!hasAccount &&
{alertMessages().noaccount}
} + {!hasCorrectNetwork && ( +
+ )} + {error &&
{error.message}
} +
+ + {transactionHash && ( +
+ )} + + ) +} + +Alerts.propTypes = { + hasCorrectNetwork: PropTypes.bool, + hasAccount: PropTypes.bool, + networkName: PropTypes.string, + error: PropTypes.object, + transactionHash: PropTypes.string, + web3Connected: PropTypes.bool +} + +export default Alerts diff --git a/src/components/Web3Donation/Alerts.module.scss b/src/components/Web3Donation/Alerts.module.scss new file mode 100644 index 00000000..f5b301e9 --- /dev/null +++ b/src/components/Web3Donation/Alerts.module.scss @@ -0,0 +1,13 @@ +@import 'variables'; +@import 'mixins'; + +.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/Web3Donation/InputGroup.jsx b/src/components/Web3Donation/InputGroup.jsx new file mode 100644 index 00000000..397cba0c --- /dev/null +++ b/src/components/Web3Donation/InputGroup.jsx @@ -0,0 +1,49 @@ +import React, { PureComponent } from 'react' +import PropTypes from 'prop-types' +import Input from '../atoms/Input' +import styles from './InputGroup.module.scss' + +export default class InputGroup extends PureComponent { + static propTypes = { + hasCorrectNetwork: PropTypes.bool, + hasAccount: PropTypes.bool, + amount: PropTypes.number, + onAmountChange: PropTypes.func, + handleButton: PropTypes.func + } + + render() { + const { + hasCorrectNetwork, + hasAccount, + amount, + onAmountChange, + handleButton + } = this.props + + return ( +
+
+ +
+ ETH +
+
+ +
+ ) + } +} diff --git a/src/components/atoms/Web3Donation.module.scss b/src/components/Web3Donation/InputGroup.module.scss similarity index 77% rename from src/components/atoms/Web3Donation.module.scss rename to src/components/Web3Donation/InputGroup.module.scss index dcf801d6..e273de90 100644 --- a/src/components/atoms/Web3Donation.module.scss +++ b/src/components/Web3Donation/InputGroup.module.scss @@ -1,26 +1,6 @@ @import 'variables'; @import 'mixins'; -.web3 { - @include divider; - - width: 100%; - text-align: center; - margin-top: $spacer / 2; - margin-bottom: $spacer; - padding-bottom: $spacer * 1.5; - - small { - color: darken($alert-info, 60%); - margin-top: -($spacer / 2); - display: block; - } -} - -.web3Row { - min-height: 58px; -} - .inputGroup { max-width: 17rem; margin: auto; @@ -92,14 +72,3 @@ display: flex; align-items: center; } - -.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/atoms/Web3Donation.jsx b/src/components/Web3Donation/index.jsx similarity index 53% rename from src/components/atoms/Web3Donation.jsx rename to src/components/Web3Donation/index.jsx index c81283af..347d7bc4 100644 --- a/src/components/atoms/Web3Donation.jsx +++ b/src/components/Web3Donation/index.jsx @@ -1,94 +1,22 @@ import React, { PureComponent } from 'react' import PropTypes from 'prop-types' import Web3 from 'web3' -import Input from '../atoms/Input' -import styles from './Web3Donation.module.scss' +import InputGroup from './InputGroup' +import Alerts from './Alerts' +import styles from './index.module.scss' +import { getNetworkName } from './utils' const ONE_SECOND = 1000 const ONE_MINUTE = ONE_SECOND * 60 -const InputGroup = ({ - networkId, - selectedAccount, - amount, - onAmountChange, - handleWeb3Button -}) => ( -
-
- -
- ETH -
-
- -
-) - -InputGroup.propTypes = { - networkId: PropTypes.string, - selectedAccount: PropTypes.string, - amount: PropTypes.number, - onAmountChange: PropTypes.func, - handleWeb3Button: PropTypes.func -} - -const Alerts = ({ accounts, networkId, error, transactionHash }) => { - if (error || accounts.length === 0) { - return ( -
- {accounts.length === 0 && - 'Web3 detected, but no account. Are you logged into your MetaMask account?'} - {networkId !== '1' && 'Please connect to Main network'} - {error && error.message} -
- ) - } - - if (transactionHash) { - return ( -
- You are awesome, thanks! -
- - See your transaction on etherscan.io. - -
- ) - } - - return null -} - -Alerts.propTypes = { - accounts: PropTypes.array, - networkId: PropTypes.string, - error: PropTypes.object, - transactionHash: PropTypes.string -} - export default class Web3Donation extends PureComponent { state = { web3Connected: false, - networkError: null, networkId: null, + networkName: null, accounts: [], selectedAccount: null, amount: 0.01, - receipt: null, transactionHash: null, loading: false, error: null @@ -103,28 +31,55 @@ export default class Web3Donation extends PureComponent { networkInterval = null componentDidMount() { - if (typeof window.web3 === 'undefined') { - // no web3 - this.setState({ web3Connected: false }) - } else { + this.initAllTheTings() + } + + componentWillUnmount() { + this.resetAllTheThings() + } + + // getPermissions = async ethereum => { + // try { + // // Request account access if needed + // await ethereum.enable() + // } catch (error) { + // // User denied account access... + // console.log(error) + // } + // } + + initAllTheTings() { + // Modern dapp browsers... + // if (window.ethereum) { + // this.web3 = new Web3(window.ethereum) + // this.setState({ web3Connected: true }) + // this.getPermissions(this.web3.eth) + // } + + // Legacy dapp browsers... + if (window.web3) { // 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.initAccountsPoll() this.initNetworkPoll() } + // Non-dapp browsers... + else { + this.setState({ web3Connected: false }) + } } - componentWillUnmount() { + resetAllTheThings() { clearInterval(this.interval) clearInterval(this.networkInterval) this.setState({ web3Connected: false }) } - initPoll() { + initAccountsPoll() { if (!this.interval) { this.interval = setInterval(this.fetchAccounts, ONE_SECOND) } @@ -143,15 +98,17 @@ export default class Web3Donation extends PureComponent { web3.eth && //web3.eth.net.getId((err, netId) => { web3.version.getNetwork((err, netId) => { - if (err) { - this.setState({ networkError: err }) - } + if (err) this.setState({ error: err }) if (netId != this.state.networkId) { this.setState({ - networkError: null, + error: null, networkId: netId }) + + getNetworkName(netId).then(networkName => { + this.setState({ networkName: networkName }) + }) } }) } @@ -162,18 +119,17 @@ export default class Web3Donation extends PureComponent { web3 && web3.eth && web3.eth.getAccounts((err, accounts) => { - if (err) { - this.setState({ accountsError: err }) - } + if (err) this.setState({ error: err }) this.setState({ + error: null, accounts, selectedAccount: accounts[0] }) }) } - handleWeb3Button = () => { + handleButton = () => { const { web3 } = this this.setState({ loading: true }) @@ -210,6 +166,9 @@ export default class Web3Donation extends PureComponent { } render() { + const hasCorrectNetwork = this.state.networkId === '1' + const hasAccount = this.state.accounts.length !== 0 + return (
@@ -217,34 +176,30 @@ export default class Web3Donation extends PureComponent {

Send Ether with MetaMask, Brave, or Mist.

- {this.state.web3Connected ? ( + {this.state.web3Connected && (
{this.state.loading ? ( 'Hang on...' ) : ( )} - -
- ) : ( - - No Web3 detected. Install MetaMask - , Brave, or{' '} - Mist. - )} + +
) } diff --git a/src/components/Web3Donation/index.module.scss b/src/components/Web3Donation/index.module.scss new file mode 100644 index 00000000..5796a2e1 --- /dev/null +++ b/src/components/Web3Donation/index.module.scss @@ -0,0 +1,22 @@ +@import 'variables'; +@import 'mixins'; + +.web3 { + @include divider; + + width: 100%; + text-align: center; + margin-top: $spacer / 2; + margin-bottom: $spacer; + padding-bottom: $spacer * 1.5; + + small { + color: darken($alert-info, 60%); + margin-top: -($spacer / 2); + display: block; + } +} + +.web3Row { + min-height: 58px; +} diff --git a/src/components/Web3Donation/utils.js b/src/components/Web3Donation/utils.js new file mode 100644 index 00000000..92ccdd51 --- /dev/null +++ b/src/components/Web3Donation/utils.js @@ -0,0 +1,25 @@ +export const getNetworkName = async netId => { + let networkName + + switch (netId) { + case '1': + networkName = 'Main' + break + case '2': + networkName = 'Morden' + break + case '3': + networkName = 'Ropsten' + break + case '4': + networkName = 'Rinkeby' + break + case '42': + networkName = 'Kovan' + break + default: + networkName = 'Private' + } + + return networkName +} diff --git a/src/components/atoms/Input.module.scss b/src/components/atoms/Input.module.scss index 1550d2c6..43bb6708 100644 --- a/src/components/atoms/Input.module.scss +++ b/src/components/atoms/Input.module.scss @@ -25,4 +25,8 @@ border-color: $input-border-focus; outline: 0; } + + &[disabled] { + color: $brand-grey-dimmed; + } } diff --git a/src/components/atoms/Modal.module.scss b/src/components/atoms/Modal.module.scss index cfade373..28ae690b 100644 --- a/src/components/atoms/Modal.module.scss +++ b/src/components/atoms/Modal.module.scss @@ -52,6 +52,7 @@ right: ($spacer/4); color: $brand-grey-light; font-weight: 500; + outline: 0; &:hover, &:focus { diff --git a/src/components/molecules/ModalThanks.jsx b/src/components/molecules/ModalThanks.jsx index d035be71..118156be 100644 --- a/src/components/molecules/ModalThanks.jsx +++ b/src/components/molecules/ModalThanks.jsx @@ -1,7 +1,7 @@ import React, { PureComponent } from 'react' import { StaticQuery, graphql } from 'gatsby' -import Web3Donation from '../atoms/Web3Donation' +import Web3Donation from '../Web3Donation' import Qr from '../atoms/Qr' import Modal from '../atoms/Modal' import styles from './ModalThanks.module.scss'