1
0
mirror of https://github.com/kremalicious/blog.git synced 2024-06-28 00:27:58 +02:00

Merge pull request #69 from kremalicious/fix/web3

web3 component split-up
This commit is contained in:
Matthias Kretschmann 2018-10-14 00:24:54 +02:00 committed by GitHub
commit ab00dd2a32
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 246 additions and 150 deletions

View File

@ -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

View File

@ -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
}
}
}

View File

@ -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 <strong>Main</strong> network. You are on <strong>${networkName}</strong> right now.`,
noWeb3:
'No Web3 detected. Install <a href="https://metamask.io">MetaMask</a>, <a href="https://brave.com">Brave</a>, or <a href="https://github.com/ethereum/mist">Mist</a>.',
success: `You are awesome, thanks!<br />
<a href="https://etherscan.io/tx/${transactionHash}">
See your transaction on etherscan.io.
</a>`
})
const Alerts = ({
hasCorrectNetwork,
hasAccount,
networkName,
error,
transactionHash,
web3Connected
}) => {
return !web3Connected ? (
<small dangerouslySetInnerHTML={{ __html: alertMessages().noWeb3 }} />
) : (
<Fragment>
<div className={styles.alert}>
{!hasAccount && <div>{alertMessages().noaccount}</div>}
{!hasCorrectNetwork && (
<div
dangerouslySetInnerHTML={{
__html: alertMessages(networkName).noCorrectNetwork
}}
/>
)}
{error && <div>{error.message}</div>}
</div>
{transactionHash && (
<div
className={styles.success}
dangerouslySetInnerHTML={{
__html: alertMessages(transactionHash).success
}}
/>
)}
</Fragment>
)
}
Alerts.propTypes = {
hasCorrectNetwork: PropTypes.bool,
hasAccount: PropTypes.bool,
networkName: PropTypes.string,
error: PropTypes.object,
transactionHash: PropTypes.string,
web3Connected: PropTypes.bool
}
export default Alerts

View File

@ -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%);
}

View File

@ -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 (
<div className={styles.inputGroup}>
<div className={styles.input}>
<Input
type="number"
disabled={!hasCorrectNetwork || !hasAccount}
value={amount}
onChange={onAmountChange}
min="0"
step="0.01"
/>
<div className={styles.currency}>
<span>ETH</span>
</div>
</div>
<button
className="btn btn-primary"
onClick={handleButton}
disabled={!hasCorrectNetwork || !hasAccount}
>
Make it rain
</button>
</div>
)
}
}

View File

@ -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%);
}

View File

@ -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
}) => (
<div className={styles.inputGroup}>
<div className={styles.input}>
<Input
type="number"
disabled={!(networkId === '1') || !selectedAccount}
value={amount}
onChange={onAmountChange}
min="0"
step="0.01"
/>
<div className={styles.currency}>
<span>ETH</span>
</div>
</div>
<button
className="btn btn-primary"
onClick={handleWeb3Button}
disabled={!(networkId === '1') || !selectedAccount}
>
Make it rain
</button>
</div>
)
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 (
<div className={styles.alert}>
{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}
</div>
)
}
if (transactionHash) {
return (
<div className={styles.success}>
You are awesome, thanks!
<br />
<a href={`https://etherscan.io/tx/${transactionHash}`}>
See your transaction on etherscan.io.
</a>
</div>
)
}
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 (
<div className={styles.web3}>
<header>
@ -217,34 +176,30 @@ export default class Web3Donation extends PureComponent {
<p>Send Ether with MetaMask, Brave, or Mist.</p>
</header>
{this.state.web3Connected ? (
{this.state.web3Connected && (
<div className={styles.web3Row}>
{this.state.loading ? (
'Hang on...'
) : (
<InputGroup
networkId={this.state.networkId}
selectedAccount={this.state.selectedAccount}
hasCorrectNetwork={hasCorrectNetwork}
hasAccount={hasAccount}
amount={this.state.amount}
onAmountChange={this.onAmountChange}
handleWeb3Button={this.handleWeb3Button}
handleButton={this.handleButton}
/>
)}
<Alerts
accounts={this.state.accounts}
networkId={this.state.networkId}
error={this.state.error}
transactionHash={this.state.transactionHash}
/>
</div>
) : (
<small>
No Web3 detected. Install <a href="https://metamask.io">MetaMask</a>
, <a href="https://brave.com">Brave</a>, or{' '}
<a href="https://github.com/ethereum/mist">Mist</a>.
</small>
)}
<Alerts
hasCorrectNetwork={hasCorrectNetwork}
hasAccount={hasAccount}
networkName={this.state.networkName}
error={this.state.error}
transactionHash={this.state.transactionHash}
web3Connected={this.state.web3Connected}
/>
</div>
)
}

View File

@ -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;
}

View File

@ -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
}

View File

@ -25,4 +25,8 @@
border-color: $input-border-focus;
outline: 0;
}
&[disabled] {
color: $brand-grey-dimmed;
}
}

View File

@ -52,6 +52,7 @@
right: ($spacer/4);
color: $brand-grey-light;
font-weight: 500;
outline: 0;
&:hover,
&:focus {

View File

@ -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'