Add in-app network switch (#203)
* add network select * new network select component * improve network switch * Fixes and refactoring * Refactoring
This commit is contained in:
parent
8acf0348db
commit
1cecd5d169
|
@ -4266,7 +4266,8 @@
|
||||||
},
|
},
|
||||||
"ansi-regex": {
|
"ansi-regex": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"aproba": {
|
"aproba": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
|
@ -4284,11 +4285,13 @@
|
||||||
},
|
},
|
||||||
"balanced-match": {
|
"balanced-match": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"brace-expansion": {
|
"brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"balanced-match": "^1.0.0",
|
"balanced-match": "^1.0.0",
|
||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
|
@ -4301,15 +4304,18 @@
|
||||||
},
|
},
|
||||||
"code-point-at": {
|
"code-point-at": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"concat-map": {
|
"concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"console-control-strings": {
|
"console-control-strings": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"core-util-is": {
|
"core-util-is": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
|
@ -4412,7 +4418,8 @@
|
||||||
},
|
},
|
||||||
"inherits": {
|
"inherits": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"ini": {
|
"ini": {
|
||||||
"version": "1.3.5",
|
"version": "1.3.5",
|
||||||
|
@ -4422,6 +4429,7 @@
|
||||||
"is-fullwidth-code-point": {
|
"is-fullwidth-code-point": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"number-is-nan": "^1.0.0"
|
"number-is-nan": "^1.0.0"
|
||||||
}
|
}
|
||||||
|
@ -4434,17 +4442,20 @@
|
||||||
"minimatch": {
|
"minimatch": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "0.0.8",
|
"version": "0.0.8",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"minipass": {
|
"minipass": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.4",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"safe-buffer": "^5.1.1",
|
"safe-buffer": "^5.1.1",
|
||||||
"yallist": "^3.0.0"
|
"yallist": "^3.0.0"
|
||||||
|
@ -4461,6 +4472,7 @@
|
||||||
"mkdirp": {
|
"mkdirp": {
|
||||||
"version": "0.5.1",
|
"version": "0.5.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"minimist": "0.0.8"
|
"minimist": "0.0.8"
|
||||||
}
|
}
|
||||||
|
@ -4533,7 +4545,8 @@
|
||||||
},
|
},
|
||||||
"number-is-nan": {
|
"number-is-nan": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"object-assign": {
|
"object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
|
@ -4543,6 +4556,7 @@
|
||||||
"once": {
|
"once": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
|
@ -4618,7 +4632,8 @@
|
||||||
},
|
},
|
||||||
"safe-buffer": {
|
"safe-buffer": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"safer-buffer": {
|
"safer-buffer": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
|
@ -4648,6 +4663,7 @@
|
||||||
"string-width": {
|
"string-width": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"code-point-at": "^1.0.0",
|
"code-point-at": "^1.0.0",
|
||||||
"is-fullwidth-code-point": "^1.0.0",
|
"is-fullwidth-code-point": "^1.0.0",
|
||||||
|
@ -4665,6 +4681,7 @@
|
||||||
"strip-ansi": {
|
"strip-ansi": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"ansi-regex": "^2.0.0"
|
"ansi-regex": "^2.0.0"
|
||||||
}
|
}
|
||||||
|
@ -4703,11 +4720,13 @@
|
||||||
},
|
},
|
||||||
"wrappy": {
|
"wrappy": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"yallist": {
|
"yallist": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicons/favicon.ico">
|
<link rel="shortcut icon" href="%PUBLIC_URL%/favicons/favicon.ico">
|
||||||
<link href="https://fonts.googleapis.com/css?family=Nunito:300,400,700" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css?family=Nunito:300,400,700" rel="stylesheet">
|
||||||
<title>POA Network Governance DApp</title>
|
<title>POA Governance DApp</title>
|
||||||
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyD97qDOBYZ2fH86Wq1vzhDOiSUsZGVqbVQ&libraries=places"></script>
|
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyD97qDOBYZ2fH86Wq1vzhDOiSUsZGVqbVQ&libraries=places"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
53
src/App.js
53
src/App.js
|
@ -5,7 +5,7 @@ import { Route, Redirect } from 'react-router-dom'
|
||||||
import { constants } from './utils/constants'
|
import { constants } from './utils/constants'
|
||||||
import { getNetworkBranch } from './utils/utils'
|
import { getNetworkBranch } from './utils/utils'
|
||||||
import { inject, observer } from 'mobx-react'
|
import { inject, observer } from 'mobx-react'
|
||||||
import { messages } from './utils/messages'
|
import messages from './utils/messages'
|
||||||
|
|
||||||
import './assets/stylesheets/index.css'
|
import './assets/stylesheets/index.css'
|
||||||
|
|
||||||
|
@ -41,16 +41,29 @@ class App extends Component {
|
||||||
onNewBallotRender = () => {
|
onNewBallotRender = () => {
|
||||||
const { commonStore, contractsStore } = this.props
|
const { commonStore, contractsStore } = this.props
|
||||||
|
|
||||||
if (!contractsStore.web3Instance) {
|
if (!commonStore.loading) {
|
||||||
if (!commonStore.loading) {
|
if (!contractsStore.injectedWeb3) {
|
||||||
|
commonStore.hideLoading()
|
||||||
swal({
|
swal({
|
||||||
title: 'Error',
|
title: 'Error',
|
||||||
html: messages.NO_METAMASK_MSG,
|
html: messages.NO_METAMASK_MSG,
|
||||||
icon: 'error',
|
|
||||||
type: 'error'
|
type: 'error'
|
||||||
})
|
})
|
||||||
|
} else if (!contractsStore.networkMatch) {
|
||||||
|
commonStore.hideLoading()
|
||||||
|
swal({
|
||||||
|
title: 'Warning!',
|
||||||
|
html: messages.networkMatchError(contractsStore.netId),
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
} else if (contractsStore.votingKey && !contractsStore.isValidVotingKey) {
|
||||||
|
commonStore.hideLoading()
|
||||||
|
swal({
|
||||||
|
title: 'Warning!',
|
||||||
|
html: messages.invalidVotingKeyMsg(contractsStore.votingKey),
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
return <NewBallot networkBranch={this.getVotingNetworkBranch()} />
|
return <NewBallot networkBranch={this.getVotingNetworkBranch()} />
|
||||||
}
|
}
|
||||||
|
@ -60,8 +73,7 @@ class App extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
onSearch = e => {
|
onSearch = e => {
|
||||||
const { commonStore } = this.props
|
this.setSearchTerm(e.target.value)
|
||||||
commonStore.setSearchTerm(e.target.value.toLowerCase())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hideSearch = () => {
|
hideSearch = () => {
|
||||||
|
@ -85,13 +97,28 @@ class App extends Component {
|
||||||
return 'All'
|
return 'All'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setSearchTerm = term => {
|
||||||
|
const { commonStore } = this.props
|
||||||
|
commonStore.setSearchTerm(term.toLowerCase())
|
||||||
|
if (this.refs.searchBar) {
|
||||||
|
this.refs.searchBar.setSearchTerm(term)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onNetworkChange = e => {
|
||||||
|
this.setSearchTerm('')
|
||||||
|
this.props.onNetworkChange(e)
|
||||||
|
}
|
||||||
|
|
||||||
isNewBallotPage() {
|
isNewBallotPage() {
|
||||||
return `${constants.rootPath}/new` === this.props.location.pathname
|
return `${constants.rootPath}/new` === this.props.location.pathname
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { commonStore, contractsStore } = this.props
|
const { commonStore, contractsStore } = this.props
|
||||||
const networkBranch = this.getVotingNetworkBranch()
|
const networkBranch = commonStore.loadingNetworkBranch
|
||||||
|
? commonStore.loadingNetworkBranch
|
||||||
|
: this.getVotingNetworkBranch()
|
||||||
|
|
||||||
return networkBranch ? (
|
return networkBranch ? (
|
||||||
<div
|
<div
|
||||||
|
@ -104,10 +131,18 @@ class App extends Component {
|
||||||
baseRootPath={commonStore.rootPath}
|
baseRootPath={commonStore.rootPath}
|
||||||
netId={contractsStore.netId}
|
netId={contractsStore.netId}
|
||||||
networkBranch={networkBranch}
|
networkBranch={networkBranch}
|
||||||
|
onChange={this.onNetworkChange}
|
||||||
onMenuToggle={this.toggleMobileMenu}
|
onMenuToggle={this.toggleMobileMenu}
|
||||||
showMobileMenu={this.state.showMobileMenu}
|
showMobileMenu={this.state.showMobileMenu}
|
||||||
/>
|
/>
|
||||||
{this.hideSearch() ? null : <SearchBar networkBranch={networkBranch} onSearch={this.onSearch} />}
|
{this.hideSearch() ? null : (
|
||||||
|
<SearchBar
|
||||||
|
networkBranch={networkBranch}
|
||||||
|
onSearch={this.onSearch}
|
||||||
|
searchTerm={commonStore.searchTerm}
|
||||||
|
ref="searchBar"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<MainTitle text={this.getTitle()} />
|
<MainTitle text={this.getTitle()} />
|
||||||
<section
|
<section
|
||||||
className={`lo-App_Content lo-App_Content-${networkBranch} ${
|
className={`lo-App_Content lo-App_Content-${networkBranch} ${
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
height: $header-height;
|
height: $header-height;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
left: 0;
|
left: 0;
|
||||||
overflow: hidden;
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
right: 0;
|
right: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
@ -58,4 +57,4 @@
|
||||||
@media (min-width: $breakpoint-md) {
|
@media (min-width: $breakpoint-md) {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
.nl-IconNetwork_Path {
|
||||||
|
@include menu-icon-colors();
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
.NetworkSelect {
|
||||||
|
display: flex;
|
||||||
|
@media (min-width: $breakpoint-md) {
|
||||||
|
position: relative;
|
||||||
|
margin-left: 34px;
|
||||||
|
&:hover {
|
||||||
|
.NetworkSelect_List {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sw-Header_Content .NetworkSelect {
|
||||||
|
display: none;
|
||||||
|
@media (min-width: $breakpoint-md) {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.NetworkSelect_Top {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
opacity: .7;
|
||||||
|
transition: .15s ease-in;
|
||||||
|
height: 50px;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
@media (min-width: $breakpoint-md) {
|
||||||
|
.nl-IconNetwork_Arrow {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nl-NavigationLinks_Link.opacityFull {
|
||||||
|
opacity: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
height: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nl-IconNetwork_Arrow {
|
||||||
|
margin-left: 10px;
|
||||||
|
transition: .15s ease-in;
|
||||||
|
path {
|
||||||
|
fill: #fff;
|
||||||
|
opacity: .52;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.NetworkSelect_List {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
list-style: none;
|
||||||
|
padding-left: 0;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
@media (min-width: $breakpoint-md) {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
right: 0;
|
||||||
|
background-color: #fff;
|
||||||
|
width: 180px;
|
||||||
|
border-radius: 4px 0 4px 4px;
|
||||||
|
box-shadow: 0 5px 10px rgba(#000, .05);
|
||||||
|
padding-top: 8px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
li.currentNetwork {
|
||||||
|
@media (max-width: $breakpoint-md) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #fff;
|
||||||
|
transition: .15s ease-in;
|
||||||
|
height: 50px;
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
@media (min-width: $breakpoint-md) {
|
||||||
|
padding: 8px 18px;
|
||||||
|
height: auto;
|
||||||
|
color: $base-text-color;
|
||||||
|
justify-content: flex-start;
|
||||||
|
&:hover {
|
||||||
|
background-color: #f7f7f7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
.top-Select {
|
||||||
|
flex-grow: 1;
|
||||||
|
max-width: 140px;
|
||||||
|
|
||||||
|
@media (min-width: $breakpoint-xl) {
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,6 +30,7 @@
|
||||||
@import "IconGithub";
|
@import "IconGithub";
|
||||||
@import "IconLoadMore";
|
@import "IconLoadMore";
|
||||||
@import "IconMobileMenu";
|
@import "IconMobileMenu";
|
||||||
|
@import "IconNetwork";
|
||||||
@import "IconPOA";
|
@import "IconPOA";
|
||||||
@import "IconTelegram";
|
@import "IconTelegram";
|
||||||
@import "IconToFinalize";
|
@import "IconToFinalize";
|
||||||
|
@ -47,9 +48,11 @@
|
||||||
@import "NewBallot";
|
@import "NewBallot";
|
||||||
@import "NewBallotMenu";
|
@import "NewBallotMenu";
|
||||||
@import "NewBallotMenuInfo";
|
@import "NewBallotMenuInfo";
|
||||||
|
@import "NetworkSelect";
|
||||||
@import "SearchBar";
|
@import "SearchBar";
|
||||||
|
@import "Select";
|
||||||
@import "Separator";
|
@import "Separator";
|
||||||
@import "SocialIcons";
|
@import "SocialIcons";
|
||||||
@import "Validator";
|
@import "Validator";
|
||||||
@import "VoteProgressBar";
|
@import "VoteProgressBar";
|
||||||
@import "Votes";
|
@import "Votes";
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -7,7 +7,7 @@ import { BallotInfoContainer } from '../BallotInfoContainer'
|
||||||
import { Votes } from '../Votes'
|
import { Votes } from '../Votes'
|
||||||
import { getNetworkBranch } from '../../utils/utils'
|
import { getNetworkBranch } from '../../utils/utils'
|
||||||
import { inject, observer } from 'mobx-react'
|
import { inject, observer } from 'mobx-react'
|
||||||
import { messages } from '../../utils/messages'
|
import messages from '../../utils/messages'
|
||||||
import { observable, action, computed } from 'mobx'
|
import { observable, action, computed } from 'mobx'
|
||||||
import { sendTransactionByVotingKey } from '../../utils/helpers'
|
import { sendTransactionByVotingKey } from '../../utils/helpers'
|
||||||
|
|
||||||
|
@ -374,6 +374,9 @@ export class BallotCard extends React.Component {
|
||||||
if (!contractsStore.votingKey) {
|
if (!contractsStore.votingKey) {
|
||||||
swal('Warning!', messages.NO_METAMASK_MSG, 'warning')
|
swal('Warning!', messages.NO_METAMASK_MSG, 'warning')
|
||||||
return
|
return
|
||||||
|
} else if (!contractsStore.networkMatch) {
|
||||||
|
swal('Warning!', messages.networkMatchError(contractsStore.netId), 'warning')
|
||||||
|
return
|
||||||
} else if (!contractsStore.isValidVotingKey) {
|
} else if (!contractsStore.isValidVotingKey) {
|
||||||
swal('Warning!', messages.invalidVotingKeyMsg(contractsStore.votingKey), 'warning')
|
swal('Warning!', messages.invalidVotingKeyMsg(contractsStore.votingKey), 'warning')
|
||||||
return
|
return
|
||||||
|
@ -511,6 +514,9 @@ export class BallotCard extends React.Component {
|
||||||
if (!contractsStore.votingKey) {
|
if (!contractsStore.votingKey) {
|
||||||
swal('Warning!', messages.NO_METAMASK_MSG, 'warning')
|
swal('Warning!', messages.NO_METAMASK_MSG, 'warning')
|
||||||
return
|
return
|
||||||
|
} else if (!contractsStore.networkMatch) {
|
||||||
|
swal('Warning!', messages.networkMatchError(contractsStore.netId), 'warning')
|
||||||
|
return
|
||||||
} else if (!contractsStore.isValidVotingKey) {
|
} else if (!contractsStore.isValidVotingKey) {
|
||||||
swal('Warning!', messages.invalidVotingKeyMsg(contractsStore.votingKey), 'warning')
|
swal('Warning!', messages.invalidVotingKeyMsg(contractsStore.votingKey), 'warning')
|
||||||
return
|
return
|
||||||
|
|
|
@ -2,14 +2,12 @@ import React from 'react'
|
||||||
import { FormInput } from '../FormInput'
|
import { FormInput } from '../FormInput'
|
||||||
import { FormSelect } from '../FormSelect'
|
import { FormSelect } from '../FormSelect'
|
||||||
import { inject, observer } from 'mobx-react'
|
import { inject, observer } from 'mobx-react'
|
||||||
import { constants } from '../../utils/constants'
|
|
||||||
import { getNetworkName } from '../../utils/utils'
|
|
||||||
|
|
||||||
@inject('ballotStore', 'contractsStore')
|
@inject('ballotStore')
|
||||||
@observer
|
@observer
|
||||||
export class BallotProxyMetadata extends React.Component {
|
export class BallotProxyMetadata extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
const { ballotStore, contractsStore, networkBranch } = this.props
|
const { ballotStore, networkBranch } = this.props
|
||||||
let options = [
|
let options = [
|
||||||
/*0*/ { value: '', label: '' },
|
/*0*/ { value: '', label: '' },
|
||||||
/*1*/ { value: '1', label: ballotStore.ProxyBallotType[1] }, // KeysManager
|
/*1*/ { value: '1', label: ballotStore.ProxyBallotType[1] }, // KeysManager
|
||||||
|
|
|
@ -1,20 +1,31 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { ButtonNewBallot } from '../ButtonNewBallot'
|
import { ButtonNewBallot } from '../ButtonNewBallot'
|
||||||
import { IconMobileMenu } from '../IconMobileMenu'
|
import { IconMobileMenu } from '../IconMobileMenu'
|
||||||
import { Logo } from '../Logo'
|
import { Logo } from '../Logo'
|
||||||
import { MobileMenuLinks } from '../MobileMenuLinks'
|
import { MobileMenuLinks } from '../MobileMenuLinks'
|
||||||
import { NavigationLinks } from '../NavigationLinks'
|
import { NavigationLinks } from '../NavigationLinks'
|
||||||
|
import NetworkSelect from '../NetworkSelect'
|
||||||
|
|
||||||
export const Header = ({ baseRootPath = '', networkBranch = undefined, onMenuToggle, showMobileMenu = false }) => {
|
export const Header = ({
|
||||||
|
baseRootPath = '',
|
||||||
|
networkBranch = undefined,
|
||||||
|
onChange,
|
||||||
|
onMenuToggle,
|
||||||
|
showMobileMenu = false
|
||||||
|
}) => {
|
||||||
return (
|
return (
|
||||||
<header className={`sw-Header sw-Header-${networkBranch} ${showMobileMenu ? 'sw-Header-menu-open' : ''}`}>
|
<header className={`sw-Header sw-Header-${networkBranch} ${showMobileMenu ? 'sw-Header-menu-open' : ''}`}>
|
||||||
{showMobileMenu ? <MobileMenuLinks networkBranch={networkBranch} onClick={onMenuToggle} /> : null}
|
{showMobileMenu ? (
|
||||||
|
<MobileMenuLinks networkBranch={networkBranch} onClick={onMenuToggle} onNetworkChange={onChange} />
|
||||||
|
) : null}
|
||||||
<div className="sw-Header_Content">
|
<div className="sw-Header_Content">
|
||||||
<Logo networkBranch={networkBranch} href={baseRootPath} />
|
<Logo networkBranch={networkBranch} href={baseRootPath} />
|
||||||
<div className="sw-Header_Links">
|
<div className="sw-Header_Links">
|
||||||
<NavigationLinks networkBranch={networkBranch} />
|
<NavigationLinks networkBranch={networkBranch} />
|
||||||
<ButtonNewBallot networkBranch={networkBranch} />
|
<ButtonNewBallot networkBranch={networkBranch} />
|
||||||
</div>
|
</div>
|
||||||
|
<NetworkSelect networkBranch={networkBranch} onChange={onChange} />
|
||||||
<IconMobileMenu networkBranch={networkBranch} isOpen={showMobileMenu} onClick={onMenuToggle} />
|
<IconMobileMenu networkBranch={networkBranch} isOpen={showMobileMenu} onClick={onMenuToggle} />
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { ButtonNewBallot } from '../ButtonNewBallot'
|
import { ButtonNewBallot } from '../ButtonNewBallot'
|
||||||
import { NavigationLinks } from '../NavigationLinks'
|
import { NavigationLinks } from '../NavigationLinks'
|
||||||
|
import NetworkSelect from '../NetworkSelect'
|
||||||
|
|
||||||
export const MobileMenuLinks = ({ onClick, networkBranch }) => {
|
export const MobileMenuLinks = ({ onClick, networkBranch, onNetworkChange }) => {
|
||||||
return (
|
return (
|
||||||
<div className={`hd-MobileMenuLinks hd-MobileMenuLinks-${networkBranch}`} onClick={onClick}>
|
<div className={`hd-MobileMenuLinks hd-MobileMenuLinks-${networkBranch}`} onClick={onClick}>
|
||||||
<NavigationLinks networkBranch={networkBranch} />
|
<NavigationLinks networkBranch={networkBranch} />
|
||||||
<ButtonNewBallot networkBranch={networkBranch} />
|
<ButtonNewBallot networkBranch={networkBranch} />
|
||||||
|
<NetworkSelect networkBranch={networkBranch} onChange={onNetworkChange} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
import React, { Component } from 'react'
|
||||||
|
import { constants } from '../../utils/constants'
|
||||||
|
|
||||||
|
export default class NetworkSelect extends Component {
|
||||||
|
changeNetworkRPC(e) {
|
||||||
|
e.preventDefault()
|
||||||
|
let getCurrentClickedLink = e.target.innerHTML
|
||||||
|
let getCurrentClickedLinkId = ''
|
||||||
|
for (const _netId in constants.NETWORKS) {
|
||||||
|
if (constants.NETWORKS[_netId].FULLNAME === getCurrentClickedLink) {
|
||||||
|
getCurrentClickedLinkId = _netId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.props.onChange({ value: getCurrentClickedLinkId })
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let networkFullNames = []
|
||||||
|
let currentNetworkFullName = ''
|
||||||
|
|
||||||
|
const networks = constants.NETWORKS
|
||||||
|
|
||||||
|
let netIds = []
|
||||||
|
Object.keys(networks)
|
||||||
|
.sort((a, b) => (networks[a].SORTORDER > networks[b].SORTORDER ? 1 : -1))
|
||||||
|
.forEach(function(_netId) {
|
||||||
|
netIds.push(_netId)
|
||||||
|
})
|
||||||
|
|
||||||
|
let selectedNetworkIndex = -1
|
||||||
|
|
||||||
|
netIds.forEach(_netId => {
|
||||||
|
networkFullNames.push(networks[_netId].FULLNAME)
|
||||||
|
if (networks[_netId].BRANCH === this.props.networkBranch) {
|
||||||
|
currentNetworkFullName = networks[_netId].FULLNAME
|
||||||
|
selectedNetworkIndex = networkFullNames.length - 1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const listItems = networkFullNames.map((name, index) => {
|
||||||
|
let className = ''
|
||||||
|
if (index === selectedNetworkIndex) {
|
||||||
|
className = 'currentNetwork'
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<li key={name.toString()} className={className}>
|
||||||
|
<button onClick={e => this.changeNetworkRPC(e)}>{name}</button>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`NetworkSelect nl-NavigationLinks_Link opacityFull`}>
|
||||||
|
<div className={`NetworkSelect_Top`}>
|
||||||
|
<svg className={`nl-IconNetwork`} xmlns="http://www.w3.org/2000/svg" width="18" height="18">
|
||||||
|
<path
|
||||||
|
className={`nl-IconNetwork_Path nl-IconNetwork_Path-${this.props.networkBranch}`}
|
||||||
|
d="M9 18a9 9 0 0 1-9-9 9 9 0 0 1 9-9 9 9 0 0 1 9 9 9 9 0 0 1-9 9zm6.923-8h-1.974c-.116 1.85-.525 3.539-1.167 4.876A6.993 6.993 0 0 0 15.923 10zM9 16c1.51 0 2.747-2.612 2.957-6H6.043c.21 3.388 1.447 6 2.957 6zm-3.782-1.124C4.576 13.539 4.167 11.85 4.051 10H2.077a6.993 6.993 0 0 0 3.141 4.876zM2.077 8h1.974c.116-1.85.525-3.538 1.167-4.876A6.993 6.993 0 0 0 2.077 8zM9 2C7.49 2 6.253 4.612 6.043 8h5.914C11.747 4.612 10.51 2 9 2zm3.782 1.124C13.424 4.462 13.833 6.15 13.949 8h1.974a6.993 6.993 0 0 0-3.141-4.876z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<span className={`nl-NavigationLinks_Text nl-NavigationLinks_Text-${this.props.networkBranch}`}>
|
||||||
|
{currentNetworkFullName}
|
||||||
|
</span>
|
||||||
|
<svg className={`nl-IconNetwork_Arrow`} xmlns="http://www.w3.org/2000/svg" width="8" height="4">
|
||||||
|
<path d="M0 0h8L4 4 0 0z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<ul className={`NetworkSelect_List`}>{listItems}</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,7 +15,7 @@ import { Validator } from '../Validator'
|
||||||
import { constants } from '../../utils/constants'
|
import { constants } from '../../utils/constants'
|
||||||
import { getNetworkBranch } from '../../utils/utils'
|
import { getNetworkBranch } from '../../utils/utils'
|
||||||
import { inject, observer } from 'mobx-react'
|
import { inject, observer } from 'mobx-react'
|
||||||
import { messages } from '../../utils/messages'
|
import messages from '../../utils/messages'
|
||||||
import { sendTransactionByVotingKey } from '../../utils/helpers'
|
import { sendTransactionByVotingKey } from '../../utils/helpers'
|
||||||
|
|
||||||
@inject('commonStore', 'ballotStore', 'validatorStore', 'contractsStore', 'routing', 'ballotsStore')
|
@inject('commonStore', 'ballotStore', 'validatorStore', 'contractsStore', 'routing', 'ballotsStore')
|
||||||
|
@ -40,9 +40,10 @@ export class NewBallot extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
checkValidation() {
|
checkValidation() {
|
||||||
const { commonStore, contractsStore, ballotStore, validatorStore } = this.props
|
const { commonStore, contractsStore, ballotStore } = this.props
|
||||||
|
|
||||||
// Temporarily commented (until we implement https://github.com/poanetwork/poa-dapps-voting/issues/120)
|
// Temporarily commented (until we implement https://github.com/poanetwork/poa-dapps-voting/issues/120)
|
||||||
|
// const { validatorStore } = this.props
|
||||||
// if (ballotStore.isNewValidatorPersonalData) {
|
// if (ballotStore.isNewValidatorPersonalData) {
|
||||||
// for (let validatorProp in validatorStore) {
|
// for (let validatorProp in validatorStore) {
|
||||||
// if (validatorStore[validatorProp].length === 0) {
|
// if (validatorStore[validatorProp].length === 0) {
|
||||||
|
@ -255,6 +256,9 @@ export class NewBallot extends React.Component {
|
||||||
if (!contractsStore.votingKey) {
|
if (!contractsStore.votingKey) {
|
||||||
swal('Warning!', messages.NO_METAMASK_MSG, 'warning')
|
swal('Warning!', messages.NO_METAMASK_MSG, 'warning')
|
||||||
return
|
return
|
||||||
|
} else if (!contractsStore.networkMatch) {
|
||||||
|
swal('Warning!', messages.networkMatchError(contractsStore.netId), 'warning')
|
||||||
|
return
|
||||||
} else if (!contractsStore.isValidVotingKey) {
|
} else if (!contractsStore.isValidVotingKey) {
|
||||||
swal('Warning!', messages.invalidVotingKeyMsg(contractsStore.votingKey), 'warning')
|
swal('Warning!', messages.invalidVotingKeyMsg(contractsStore.votingKey), 'warning')
|
||||||
return
|
return
|
||||||
|
|
|
@ -1,16 +1,36 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
export const SearchBar = ({ extraClassName = '', networkBranch = false, onSearch }) => {
|
export class SearchBar extends React.Component {
|
||||||
return (
|
constructor(props) {
|
||||||
<div className={`sw-SearchBar sw-SearchBar-${networkBranch} ${extraClassName}`}>
|
super(props)
|
||||||
<div className="sw-SearchBar_Content">
|
this.state = { searchTerm: '' }
|
||||||
<input
|
}
|
||||||
className={`sw-SearchBar_Input sw-SearchBar_Input-${networkBranch}`}
|
|
||||||
onChange={onSearch}
|
setSearchTerm(searchTerm) {
|
||||||
placeholder="Search..."
|
this.setState({ searchTerm })
|
||||||
type="search"
|
}
|
||||||
/>
|
|
||||||
|
componentDidMount() {
|
||||||
|
const { searchTerm } = this.props
|
||||||
|
if (searchTerm !== undefined) {
|
||||||
|
this.setSearchTerm(searchTerm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { networkBranch, onSearch } = this.props
|
||||||
|
return (
|
||||||
|
<div className={`sw-SearchBar sw-SearchBar-${networkBranch}`}>
|
||||||
|
<div className="sw-SearchBar_Content">
|
||||||
|
<input
|
||||||
|
className={`sw-SearchBar_Input sw-SearchBar_Input-${networkBranch}`}
|
||||||
|
onChange={onSearch}
|
||||||
|
placeholder="Search..."
|
||||||
|
type="search"
|
||||||
|
value={this.state.searchTerm}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { FormAutocomplete } from '../FormAutocomplete'
|
//import { FormAutocomplete } from '../FormAutocomplete'
|
||||||
import { FormInput } from '../FormInput'
|
//import { FormInput } from '../FormInput'
|
||||||
import { FormSelect } from '../FormSelect'
|
//import { FormSelect } from '../FormSelect'
|
||||||
import { constants } from '../../utils/constants'
|
import { constants } from '../../utils/constants'
|
||||||
import { geocodeByAddress } from 'react-places-autocomplete'
|
import { geocodeByAddress } from 'react-places-autocomplete'
|
||||||
import { inject, observer } from 'mobx-react'
|
import { inject, observer } from 'mobx-react'
|
||||||
|
@ -64,6 +64,7 @@ export class Validator extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return null // Temporarily empty (until we implement https://github.com/poanetwork/poa-dapps-voting/issues/120)
|
return null // Temporarily empty (until we implement https://github.com/poanetwork/poa-dapps-voting/issues/120)
|
||||||
|
/*
|
||||||
const { validatorStore, networkBranch } = this.props
|
const { validatorStore, networkBranch } = this.props
|
||||||
const inputProps = {
|
const inputProps = {
|
||||||
value: validatorStore.address,
|
value: validatorStore.address,
|
||||||
|
@ -190,5 +191,6 @@ export class Validator extends React.Component {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { constants } from '../utils/constants'
|
import { constants } from '../utils/constants'
|
||||||
import { messages } from '../utils/messages'
|
import messages from '../utils/messages'
|
||||||
import swal from 'sweetalert2'
|
import swal from 'sweetalert2'
|
||||||
|
|
||||||
function addressesURL(branch) {
|
function addressesURL(branch) {
|
||||||
|
|
126
src/index.js
126
src/index.js
|
@ -16,6 +16,7 @@ import { Router, Route } from 'react-router-dom'
|
||||||
import { RouterStore, syncHistoryWithStore } from 'mobx-react-router'
|
import { RouterStore, syncHistoryWithStore } from 'mobx-react-router'
|
||||||
import { constants } from './utils/constants'
|
import { constants } from './utils/constants'
|
||||||
import { getContractsAddresses } from './contracts/addresses'
|
import { getContractsAddresses } from './contracts/addresses'
|
||||||
|
import { getNetworkBranch } from './utils/utils'
|
||||||
|
|
||||||
const browserHistory = createBrowserHistory()
|
const browserHistory = createBrowserHistory()
|
||||||
const routingStore = new RouterStore()
|
const routingStore = new RouterStore()
|
||||||
|
@ -32,56 +33,14 @@ class AppMainRouter extends Component {
|
||||||
super(props)
|
super(props)
|
||||||
commonStore.showLoading()
|
commonStore.showLoading()
|
||||||
|
|
||||||
getWeb3()
|
window.addEventListener('load', () => this.initChain())
|
||||||
|
}
|
||||||
|
|
||||||
|
initChain = () => {
|
||||||
|
const netId = window.localStorage.netId
|
||||||
|
getWeb3(netId, this.onAccountChange)
|
||||||
.then(async web3Config => {
|
.then(async web3Config => {
|
||||||
await getContractsAddresses(constants.NETWORKS[web3Config.netId].BRANCH)
|
await this.initialize(web3Config)
|
||||||
|
|
||||||
contractsStore.setWeb3Instance(web3Config)
|
|
||||||
|
|
||||||
const setPoaConsensus = contractsStore.setPoaConsensus(web3Config)
|
|
||||||
const setBallotsStorage = contractsStore.setBallotsStorage(web3Config)
|
|
||||||
const setKeysManager = contractsStore.setKeysManager(web3Config)
|
|
||||||
const setProxyStorage = contractsStore.setProxyStorage(web3Config)
|
|
||||||
const setVotingToChangeKeys = contractsStore.setVotingToChangeKeys(web3Config)
|
|
||||||
const setVotingToChangeMinThreshold = contractsStore.setVotingToChangeMinThreshold(web3Config)
|
|
||||||
const setVotingToChangeProxy = contractsStore.setVotingToChangeProxy(web3Config)
|
|
||||||
const setValidatorMetadata = contractsStore.setValidatorMetadata(web3Config)
|
|
||||||
|
|
||||||
let promises = [
|
|
||||||
setPoaConsensus,
|
|
||||||
setBallotsStorage,
|
|
||||||
setKeysManager,
|
|
||||||
setProxyStorage,
|
|
||||||
setVotingToChangeKeys,
|
|
||||||
setVotingToChangeMinThreshold,
|
|
||||||
setVotingToChangeProxy,
|
|
||||||
setValidatorMetadata
|
|
||||||
]
|
|
||||||
|
|
||||||
const networkName = constants.NETWORKS[web3Config.netId].NAME.toLowerCase()
|
|
||||||
if (networkName === constants.CORE || networkName === constants.SOKOL) {
|
|
||||||
// if we're in Core or Sokol
|
|
||||||
promises.push(contractsStore.setEmissionFunds(web3Config))
|
|
||||||
promises.push(contractsStore.setVotingToManageEmissionFunds(web3Config))
|
|
||||||
}
|
|
||||||
|
|
||||||
await Promise.all(promises)
|
|
||||||
|
|
||||||
await contractsStore.setMiningKey(web3Config)
|
|
||||||
await contractsStore.setVotingKey(web3Config)
|
|
||||||
|
|
||||||
contractsStore.getKeysBallotThreshold()
|
|
||||||
contractsStore.getProxyBallotThreshold()
|
|
||||||
contractsStore.getBallotCancelingThreshold()
|
|
||||||
|
|
||||||
await contractsStore.getBallotsLimits()
|
|
||||||
|
|
||||||
await contractsStore.getAllValidatorMetadata()
|
|
||||||
await contractsStore.getAllBallots()
|
|
||||||
|
|
||||||
console.log('votingKey', contractsStore.votingKey)
|
|
||||||
console.log('miningKey', contractsStore.miningKey)
|
|
||||||
|
|
||||||
commonStore.hideLoading()
|
commonStore.hideLoading()
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
|
@ -90,17 +49,82 @@ class AppMainRouter extends Component {
|
||||||
swal({
|
swal({
|
||||||
title: 'Error',
|
title: 'Error',
|
||||||
html: generateElement(error.message),
|
html: generateElement(error.message),
|
||||||
icon: 'error',
|
|
||||||
type: 'error'
|
type: 'error'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initialize = async web3Config => {
|
||||||
|
await getContractsAddresses(constants.NETWORKS[web3Config.netId].BRANCH)
|
||||||
|
|
||||||
|
contractsStore.setWeb3Instance(web3Config)
|
||||||
|
|
||||||
|
const setPoaConsensus = contractsStore.setPoaConsensus(web3Config)
|
||||||
|
const setBallotsStorage = contractsStore.setBallotsStorage(web3Config)
|
||||||
|
const setKeysManager = contractsStore.setKeysManager(web3Config)
|
||||||
|
const setProxyStorage = contractsStore.setProxyStorage(web3Config)
|
||||||
|
const setVotingToChangeKeys = contractsStore.setVotingToChangeKeys(web3Config)
|
||||||
|
const setVotingToChangeMinThreshold = contractsStore.setVotingToChangeMinThreshold(web3Config)
|
||||||
|
const setVotingToChangeProxy = contractsStore.setVotingToChangeProxy(web3Config)
|
||||||
|
const setValidatorMetadata = contractsStore.setValidatorMetadata(web3Config)
|
||||||
|
|
||||||
|
let promises = [
|
||||||
|
setPoaConsensus,
|
||||||
|
setBallotsStorage,
|
||||||
|
setKeysManager,
|
||||||
|
setProxyStorage,
|
||||||
|
setVotingToChangeKeys,
|
||||||
|
setVotingToChangeMinThreshold,
|
||||||
|
setVotingToChangeProxy,
|
||||||
|
setValidatorMetadata
|
||||||
|
]
|
||||||
|
|
||||||
|
const networkName = constants.NETWORKS[web3Config.netId].NAME.toLowerCase()
|
||||||
|
if (networkName === constants.CORE || networkName === constants.SOKOL) {
|
||||||
|
// if we're in Core or Sokol
|
||||||
|
promises.push(contractsStore.setEmissionFunds(web3Config))
|
||||||
|
promises.push(contractsStore.setVotingToManageEmissionFunds(web3Config))
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(promises)
|
||||||
|
|
||||||
|
await this.setKeys(web3Config.defaultAccount)
|
||||||
|
|
||||||
|
contractsStore.getKeysBallotThreshold()
|
||||||
|
contractsStore.getProxyBallotThreshold()
|
||||||
|
contractsStore.getBallotCancelingThreshold()
|
||||||
|
|
||||||
|
await contractsStore.getBallotsLimits()
|
||||||
|
|
||||||
|
await contractsStore.getAllValidatorMetadata()
|
||||||
|
await contractsStore.getAllBallots()
|
||||||
|
}
|
||||||
|
|
||||||
|
onNetworkChange = e => {
|
||||||
|
commonStore.showLoading(getNetworkBranch(e.value))
|
||||||
|
window.localStorage.netId = e.value
|
||||||
|
contractsStore.resetContracts()
|
||||||
|
ballotsStore.reset()
|
||||||
|
this.initChain()
|
||||||
|
}
|
||||||
|
|
||||||
|
onAccountChange = account => {
|
||||||
|
this.setKeys(account)
|
||||||
|
}
|
||||||
|
|
||||||
|
setKeys = async account => {
|
||||||
|
await contractsStore.setMiningKey(account)
|
||||||
|
await contractsStore.setVotingKey(account)
|
||||||
|
|
||||||
|
console.log('votingKey', contractsStore.votingKey)
|
||||||
|
console.log('miningKey', contractsStore.miningKey)
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Provider {...stores}>
|
<Provider {...stores}>
|
||||||
<Router history={history}>
|
<Router history={history}>
|
||||||
<Route component={App} />
|
<Route component={props => <App onNetworkChange={this.onNetworkChange} {...props} />} />
|
||||||
</Router>
|
</Router>
|
||||||
</Provider>
|
</Provider>
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,6 +4,10 @@ class BallotsStore {
|
||||||
@observable ballotCards
|
@observable ballotCards
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
this.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
this.ballotCards = []
|
this.ballotCards = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { observable, action } from 'mobx'
|
||||||
|
|
||||||
class CommonStore {
|
class CommonStore {
|
||||||
@observable loading
|
@observable loading
|
||||||
|
@observable loadingNetworkBranch
|
||||||
@observable rootPath
|
@observable rootPath
|
||||||
@observable isActiveFilter
|
@observable isActiveFilter
|
||||||
@observable isToFinalizeFilter
|
@observable isToFinalizeFilter
|
||||||
|
@ -10,6 +11,7 @@ class CommonStore {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
|
this.loadingNetworkBranch = null
|
||||||
this.isActiveFilter = false
|
this.isActiveFilter = false
|
||||||
this.isToFinalizeFilter = false
|
this.isToFinalizeFilter = false
|
||||||
this.rootPath = '/poa-dapps-voting'
|
this.rootPath = '/poa-dapps-voting'
|
||||||
|
@ -17,13 +19,15 @@ class CommonStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
@action('show loading')
|
@action('show loading')
|
||||||
showLoading() {
|
showLoading(loadingNetworkBranch) {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
|
this.loadingNetworkBranch = loadingNetworkBranch
|
||||||
}
|
}
|
||||||
|
|
||||||
@action('hide loading')
|
@action('hide loading')
|
||||||
hideLoading() {
|
hideLoading() {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
|
this.loadingNetworkBranch = null
|
||||||
}
|
}
|
||||||
|
|
||||||
@action('set search term')
|
@action('set search term')
|
||||||
|
|
|
@ -46,6 +46,7 @@ class ContractsStore {
|
||||||
@observable minBallotDuration
|
@observable minBallotDuration
|
||||||
@observable validatorsMetadata
|
@observable validatorsMetadata
|
||||||
@observable netId
|
@observable netId
|
||||||
|
@observable injectedWeb3
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.votingKey = null
|
this.votingKey = null
|
||||||
|
@ -53,6 +54,7 @@ class ContractsStore {
|
||||||
this.validatorsMetadata = {}
|
this.validatorsMetadata = {}
|
||||||
this.validatorLimits = { keys: null, minThreshold: null, proxy: null }
|
this.validatorLimits = { keys: null, minThreshold: null, proxy: null }
|
||||||
this.minBallotDuration = { keys: 0, minThreshold: 0, proxy: 0 }
|
this.minBallotDuration = { keys: 0, minThreshold: 0, proxy: 0 }
|
||||||
|
this.injectedWeb3 = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
|
@ -84,6 +86,22 @@ class ContractsStore {
|
||||||
setWeb3Instance = web3Config => {
|
setWeb3Instance = web3Config => {
|
||||||
this.web3Instance = web3Config.web3Instance
|
this.web3Instance = web3Config.web3Instance
|
||||||
this.netId = web3Config.netId
|
this.netId = web3Config.netId
|
||||||
|
this.injectedWeb3 = web3Config.injectedWeb3
|
||||||
|
this.networkMatch = web3Config.networkMatch
|
||||||
|
}
|
||||||
|
|
||||||
|
@action('Reset contracts')
|
||||||
|
resetContracts = () => {
|
||||||
|
this.poaConsensus = null
|
||||||
|
this.ballotsStorage = null
|
||||||
|
this.emissionFunds = null
|
||||||
|
this.keysManager = null
|
||||||
|
this.proxyStorage = null
|
||||||
|
this.votingToChangeKeys = null
|
||||||
|
this.votingToChangeMinThreshold = null
|
||||||
|
this.votingToChangeProxy = null
|
||||||
|
this.votingToManageEmissionFunds = null
|
||||||
|
this.validatorMetadata = null
|
||||||
}
|
}
|
||||||
|
|
||||||
@action('Set PoA Consensus contract')
|
@action('Set PoA Consensus contract')
|
||||||
|
@ -182,14 +200,14 @@ class ContractsStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
@action('Set voting key')
|
@action('Set voting key')
|
||||||
setVotingKey = web3Config => {
|
setVotingKey = account => {
|
||||||
this.votingKey = web3Config.defaultAccount
|
this.votingKey = account
|
||||||
}
|
}
|
||||||
|
|
||||||
@action('Set mining key')
|
@action('Set mining key')
|
||||||
setMiningKey = async web3Config => {
|
setMiningKey = async account => {
|
||||||
try {
|
try {
|
||||||
this.miningKey = await this.keysManager.instance.methods.miningKeyByVoting(web3Config.defaultAccount).call()
|
this.miningKey = await this.keysManager.instance.methods.miningKeyByVoting(account).call()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e)
|
console.log(e)
|
||||||
this.miningKey = '0x0000000000000000000000000000000000000000'
|
this.miningKey = '0x0000000000000000000000000000000000000000'
|
||||||
|
|
|
@ -59,27 +59,31 @@ constants.KOVAN = 'kovan'
|
||||||
constants.NETWORKS = {
|
constants.NETWORKS = {
|
||||||
'42': {
|
'42': {
|
||||||
NAME: 'Kovan',
|
NAME: 'Kovan',
|
||||||
|
FULLNAME: 'Kovan Testnet',
|
||||||
RPC: 'https://kovan.infura.io/v3/1125fe73d87c4e5396678f4e3089b3dd',
|
RPC: 'https://kovan.infura.io/v3/1125fe73d87c4e5396678f4e3089b3dd',
|
||||||
BRANCH: constants.KOVAN,
|
BRANCH: constants.KOVAN,
|
||||||
TESTNET: true
|
SORTORDER: 3
|
||||||
},
|
},
|
||||||
'77': {
|
'77': {
|
||||||
NAME: 'Sokol',
|
NAME: 'Sokol',
|
||||||
|
FULLNAME: 'Sokol Testnet',
|
||||||
RPC: 'https://sokol.poa.network',
|
RPC: 'https://sokol.poa.network',
|
||||||
BRANCH: constants.SOKOL,
|
BRANCH: constants.SOKOL,
|
||||||
TESTNET: true
|
SORTORDER: 4
|
||||||
},
|
},
|
||||||
'99': {
|
'99': {
|
||||||
NAME: 'Core',
|
NAME: 'Core',
|
||||||
|
FULLNAME: 'POA Core',
|
||||||
RPC: 'https://core.poa.network',
|
RPC: 'https://core.poa.network',
|
||||||
BRANCH: constants.CORE,
|
BRANCH: constants.CORE,
|
||||||
TESTNET: false
|
SORTORDER: 1
|
||||||
},
|
},
|
||||||
'100': {
|
'100': {
|
||||||
NAME: 'Dai',
|
NAME: 'xDai',
|
||||||
|
FULLNAME: 'xDai Stable Chain',
|
||||||
RPC: 'https://dai.poa.network',
|
RPC: 'https://dai.poa.network',
|
||||||
BRANCH: constants.DAI,
|
BRANCH: constants.DAI,
|
||||||
TESTNET: false
|
SORTORDER: 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,80 +1,71 @@
|
||||||
import Web3 from 'web3'
|
import Web3 from 'web3'
|
||||||
import { messages } from './messages'
|
import helpers from './helpers'
|
||||||
import { constants } from './constants'
|
import { constants } from './constants'
|
||||||
import { netIdByName } from './helpers'
|
|
||||||
|
|
||||||
let getWeb3 = () => {
|
const defaultNetId = helpers.netIdByBranch(constants.CORE)
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
// Wait for loading completion to avoid race conditions with web3 injection timing.
|
|
||||||
window.addEventListener('load', async () => {
|
|
||||||
let web3 = null
|
|
||||||
|
|
||||||
// Checking if Web3 has been injected by the browser (Mist/MetaMask)
|
export default async function getWeb3(netId = defaultNetId, onAccountChange) {
|
||||||
if (window.ethereum) {
|
let web3 = null
|
||||||
web3 = new Web3(window.ethereum)
|
netId = Number(netId)
|
||||||
console.log('Injected web3 detected.')
|
|
||||||
try {
|
// Checking if Web3 has been injected by the browser (Mist/MetaMask)
|
||||||
await window.ethereum.enable()
|
if (window.ethereum) {
|
||||||
} catch (e) {
|
web3 = new Web3(window.ethereum)
|
||||||
console.error('User denied account access')
|
console.log('Injected web3 detected.')
|
||||||
reject({ message: messages.USER_DENIED_ACCOUNT_ACCESS })
|
try {
|
||||||
return
|
await window.ethereum.enable()
|
||||||
}
|
} catch (e) {
|
||||||
} else if (typeof window.web3 !== 'undefined') {
|
throw Error('You have denied access to your accounts')
|
||||||
web3 = new Web3(window.web3.currentProvider)
|
}
|
||||||
console.log('Injected web3 detected.')
|
window.ethereum.autoRefreshOnNetworkChange = true
|
||||||
|
} else if (window.web3) {
|
||||||
|
web3 = new Web3(window.web3.currentProvider)
|
||||||
|
console.log('Injected web3 detected.')
|
||||||
|
}
|
||||||
|
|
||||||
|
const network = constants.NETWORKS[netId]
|
||||||
|
const injectedWeb3 = web3 !== null
|
||||||
|
let netIdName = network.NAME
|
||||||
|
let defaultAccount = null
|
||||||
|
let networkMatch = false
|
||||||
|
|
||||||
|
if (web3) {
|
||||||
|
const accounts = await web3.eth.getAccounts()
|
||||||
|
defaultAccount = accounts[0] || null
|
||||||
|
|
||||||
|
if (!defaultAccount) {
|
||||||
|
console.error('Unlock your wallet')
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentAccount = defaultAccount ? defaultAccount.toLowerCase() : ''
|
||||||
|
web3.currentProvider.publicConfigStore.on('update', function(obj) {
|
||||||
|
const account = obj.selectedAddress
|
||||||
|
if (account && account !== currentAccount) {
|
||||||
|
currentAccount = account
|
||||||
|
onAccountChange(account)
|
||||||
}
|
}
|
||||||
|
|
||||||
let errorMsg = null
|
|
||||||
let netIdName
|
|
||||||
let netId
|
|
||||||
let defaultAccount = null
|
|
||||||
|
|
||||||
if (web3) {
|
|
||||||
netId = await web3.eth.net.getId()
|
|
||||||
console.log('netId', netId)
|
|
||||||
|
|
||||||
if (!(netId in constants.NETWORKS)) {
|
|
||||||
netIdName = 'ERROR'
|
|
||||||
errorMsg = messages.WRONG_NETWORK_MSG
|
|
||||||
console.log('This is an unknown network.')
|
|
||||||
} else {
|
|
||||||
netIdName = constants.NETWORKS[netId].NAME
|
|
||||||
console.log(`This is ${netIdName}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const accounts = await web3.eth.getAccounts()
|
|
||||||
|
|
||||||
defaultAccount = accounts[0] || null
|
|
||||||
} else {
|
|
||||||
// Fallback to local if no web3 injection.
|
|
||||||
|
|
||||||
console.log('No web3 instance injected, using Local web3.')
|
|
||||||
console.error('Metamask not found')
|
|
||||||
|
|
||||||
netId = netIdByName(constants.CORE)
|
|
||||||
|
|
||||||
const network = constants.NETWORKS[netId]
|
|
||||||
|
|
||||||
web3 = new Web3(new Web3.providers.HttpProvider(network.RPC))
|
|
||||||
netIdName = network.NAME
|
|
||||||
}
|
|
||||||
|
|
||||||
document.title = `${netIdName} - POA Network Governance DApp`
|
|
||||||
|
|
||||||
if (errorMsg !== null) {
|
|
||||||
reject({ message: errorMsg })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve({
|
|
||||||
web3Instance: web3,
|
|
||||||
netIdName,
|
|
||||||
netId,
|
|
||||||
defaultAccount
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export default getWeb3
|
const web3NetId = await web3.eth.net.getId()
|
||||||
|
if (web3NetId === netId) {
|
||||||
|
networkMatch = true
|
||||||
|
} else {
|
||||||
|
web3 = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!web3) {
|
||||||
|
web3 = new Web3(new Web3.providers.HttpProvider(network.RPC))
|
||||||
|
}
|
||||||
|
|
||||||
|
document.title = `${netIdName} - POA Governance DApp`
|
||||||
|
|
||||||
|
return {
|
||||||
|
web3Instance: web3,
|
||||||
|
netId,
|
||||||
|
netIdName,
|
||||||
|
injectedWeb3,
|
||||||
|
defaultAccount,
|
||||||
|
networkMatch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -58,10 +58,10 @@ function sendTransactionByVotingKey(props, to, data, cb, warning) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function netIdByName(netName) {
|
function netIdByBranch(branch) {
|
||||||
const netNameLowerCase = netName.toLowerCase()
|
const branchLowerCase = branch.toLowerCase()
|
||||||
for (let netId in constants.NETWORKS) {
|
for (let netId in constants.NETWORKS) {
|
||||||
if (constants.NETWORKS[netId].NAME.toLowerCase() === netNameLowerCase) {
|
if (constants.NETWORKS[netId].BRANCH.toLowerCase() === branchLowerCase) {
|
||||||
return netId
|
return netId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,5 +71,5 @@ function netIdByName(netName) {
|
||||||
module.exports = {
|
module.exports = {
|
||||||
toAscii,
|
toAscii,
|
||||||
sendTransactionByVotingKey,
|
sendTransactionByVotingKey,
|
||||||
netIdByName
|
netIdByBranch
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import { getNetworkFullName } from './utils'
|
||||||
|
|
||||||
let messages = {}
|
let messages = {}
|
||||||
messages.invalidVotingKeyMsg = key => {
|
messages.invalidVotingKeyMsg = key => {
|
||||||
return `The key ${key} is not valid voting Key! Please make sure you have loaded correct voting key in Metamask`
|
return `The key ${key} is not valid Voting Key! Please make sure you have loaded correct Voting Key in MetaMask.`
|
||||||
}
|
}
|
||||||
messages.VOTED_SUCCESS_MSG = 'You successfully voted'
|
messages.VOTED_SUCCESS_MSG = 'You successfully voted'
|
||||||
messages.BALLOT_CREATED_SUCCESS_MSG = 'You successfully created a new ballot'
|
messages.BALLOT_CREATED_SUCCESS_MSG = 'You successfully created a new ballot'
|
||||||
|
@ -16,12 +18,7 @@ messages.PROPOSED_ADDRESS_IS_NOT_ADDRESS_MSG = "Proposed address isn't address"
|
||||||
messages.END_TIME_SHOULD_BE_GREATER_THAN_NOW_MSG = 'Ballot end time should be greater than now'
|
messages.END_TIME_SHOULD_BE_GREATER_THAN_NOW_MSG = 'Ballot end time should be greater than now'
|
||||||
messages.BALLOT_TYPE_IS_EMPTY_MSG = 'Ballot type is empty'
|
messages.BALLOT_TYPE_IS_EMPTY_MSG = 'Ballot type is empty'
|
||||||
messages.USER_DENIED_ACCOUNT_ACCESS = 'You have denied access to your accounts'
|
messages.USER_DENIED_ACCOUNT_ACCESS = 'You have denied access to your accounts'
|
||||||
messages.NO_METAMASK_MSG = `You haven't chosen any account in MetaMask.
|
messages.NO_METAMASK_MSG = 'Your MetaMask is locked or not installed.'
|
||||||
Please, choose your voting key in MetaMask and reload the page.
|
|
||||||
Check POA Network <a href='https://github.com/poanetwork/wiki' target='blank'>wiki</a> for more info.`
|
|
||||||
messages.WRONG_NETWORK_MSG = `You aren't connected to POA Network.
|
|
||||||
Please, switch on POA plugin and refresh the page.
|
|
||||||
Check POA Network <a href='https://github.com/poanetwork/wiki' target='blank'>wiki</a> for more info.`
|
|
||||||
messages.ballotIsNotActiveMsg = timeToStart => {
|
messages.ballotIsNotActiveMsg = timeToStart => {
|
||||||
return `The ballot is not active yet. Time to start: ${timeToStart}`
|
return `The ballot is not active yet. Time to start: ${timeToStart}`
|
||||||
}
|
}
|
||||||
|
@ -38,17 +35,20 @@ messages.EMISSION_RELEASE_TIME_IN_FUTURE = emissionReleaseTime => {
|
||||||
}
|
}
|
||||||
messages.PREVIOUS_BALLOT_NOT_FINALIZED = 'Previous ballot should be finalized first.'
|
messages.PREVIOUS_BALLOT_NOT_FINALIZED = 'Previous ballot should be finalized first.'
|
||||||
messages.BALLOT_CREATE_FAILED_TX = `Your transaction was failed. Please make sure you set correct parameters for ballot creation.
|
messages.BALLOT_CREATE_FAILED_TX = `Your transaction was failed. Please make sure you set correct parameters for ballot creation.
|
||||||
Make sure you don't have Transaction Error. Exception thrown in contract code message in Metamask before you sign it.`
|
Make sure you don't have Transaction Error. Exception thrown in contract code message in MetaMask before you sign it.`
|
||||||
messages.VOTE_FAILED_TX = `Your transaction was failed. Please make sure you haven't already voted for this ballot.
|
messages.VOTE_FAILED_TX = `Your transaction was failed. Please make sure you haven't already voted for this ballot.
|
||||||
Make sure you don't have Transaction Error. Exception thrown in contract code message in Metamask before you sign it.`
|
Make sure you don't have Transaction Error. Exception thrown in contract code message in MetaMask before you sign it.`
|
||||||
messages.FINALIZE_FAILED_TX = `Your transaction was failed. Make sure you don't have Transaction Error.
|
messages.FINALIZE_FAILED_TX = `Your transaction was failed. Make sure you don't have Transaction Error.
|
||||||
Exception thrown in contract code message in Metamask before you sign it.`
|
Exception thrown in contract code message in MetaMask before you sign it.`
|
||||||
messages.CANCEL_BALLOT_FAILED_TX = `Your transaction was failed. Make sure you don't have Transaction Error.
|
messages.CANCEL_BALLOT_FAILED_TX = `Your transaction was failed. Make sure you don't have Transaction Error.
|
||||||
Exception thrown in contract code message in Metamask before you sign it.`
|
Exception thrown in contract code message in MetaMask before you sign it.`
|
||||||
messages.DESCRIPTION_IS_EMPTY = 'Description cannot be empty'
|
messages.DESCRIPTION_IS_EMPTY = 'Description cannot be empty'
|
||||||
messages.wrongRepo = repo => {
|
messages.wrongRepo = repo => {
|
||||||
return `There is no contracts.json in configured repo ${repo}`
|
return `There is no contracts.json in configured repo ${repo}`
|
||||||
}
|
}
|
||||||
module.exports = {
|
messages.networkMatchError = function(netId) {
|
||||||
messages
|
const networkName = getNetworkFullName(Number(netId))
|
||||||
|
return `Networks in DApp and MetaMask do not match. Switch MetaMask to <b>${networkName}</b> or change the network in DApp.`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default messages
|
||||||
|
|
|
@ -1,19 +1,11 @@
|
||||||
import { constants } from './constants'
|
import { constants } from './constants'
|
||||||
|
|
||||||
export const isTestnet = netId => {
|
|
||||||
return netId in constants.NETWORKS && constants.NETWORKS[netId].TESTNET
|
|
||||||
}
|
|
||||||
|
|
||||||
export const isValidNetwork = netId => {
|
|
||||||
return netId in constants.NETWORKS
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getNetworkBranch = netId => {
|
export const getNetworkBranch = netId => {
|
||||||
return constants.NETWORKS[netId].BRANCH
|
return constants.NETWORKS[netId].BRANCH
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getNetworkName = netId => {
|
export const getNetworkFullName = netId => {
|
||||||
return constants.NETWORKS[netId].NAME
|
return constants.NETWORKS[netId].FULLNAME
|
||||||
}
|
}
|
||||||
|
|
||||||
export const scrollToTop = () => {
|
export const scrollToTop = () => {
|
||||||
|
|
Loading…
Reference in New Issue