Merge branch 'master' into feature/deployment-strategy

This commit is contained in:
Iuri Matias 2018-11-08 16:35:04 -05:00 committed by GitHub
commit 087a2e8c6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 407 additions and 162 deletions

View File

@ -1,24 +0,0 @@
## Overview
**TL;DR**
<One to two sentence description of the issue you are encountering.>
### Extra Detail
#### Steps to reproduce
1.
2.
3.
#### Screenshots
<please upload images of the error to better showcase the problem.>
#### Logs
<please upload logs of the error to better showcase the problem. You can use the following options: --nodashboard --logfile log.txt --loglevel trace>
#### Context (Environment)
* OS:
* Embark Version:
* Node Version:
* NPM Version:
### Questions?
- Leave a comment on this issue! Make sure to use @ mentions if you want a specific person's attention!

44
.github/ISSUE_TEMPLATE/Bug.md vendored Normal file
View File

@ -0,0 +1,44 @@
---
name: 🐞 Bug Report
about: Something is broken? 🔨
---
### Bug Report
#### Summary
<!-- Provide a summary describing the problem you are experiencing. -->
#### Current behavior
<!--
What is the current (buggy) behavior? Feel free to add as much information as possible.
Also, a screenshot often says more than a thousand words. :)
-->
#### How to reproduce
<!--
Provide steps to reproduce the bug.
Adding a failing Unit or Functional Test would help us a lot - you can submit one in a Pull Request separately, referencing this bug report.
-->
#### Expected behavior
<!-- What was the expected (correct) behavior? -->
#### If you encounter an error, please create a logfile using the following command and post the output here
```
$ embark run --nodashboard --logfile log.txt --loglevel trace
```
#### Please provide additional information about your system
**OS**:
**Embark Version**:
**Node Version**:
**NPM Version**:
#### Sometimes issues are related to Embark's installation. Can you provide information on how Embark was installed?

View File

@ -0,0 +1,13 @@
---
name: 🎉 Feature Request
about: You have a neat idea that should be implemented? 🎩
---
### Feature Request
<!-- Fill in the relevant information below to help triage your issue. -->
#### Summary
<!-- Provide a summary of the feature you would like to see implemented. -->

9
.github/ISSUE_TEMPLATE/Proposal.md vendored Normal file
View File

@ -0,0 +1,9 @@
---
name: 🤔 Proposal
about: Have a nice proposal?
---
### Proposal description
<!-- Describe the proposal you want here. -->

View File

@ -1,11 +0,0 @@
## Overview
**TL;DR**
<One to two sentence description of the issue you are encountering or trying to solve.>
### Questions
<If relevant, write a list of questions that you would like to discuss related to the changes that you have made.>
### Review
<use @mentions for quick questions, specific feedback, and progress updates.>
### Cool Spaceship Picture

20
.github/PULL_REQUEST_TEMPLATE/Bugfix.md vendored Normal file
View File

@ -0,0 +1,20 @@
---
name: ⚙ Bugfix
about: Fixed a bug? 🐞
---
### What bug have you fixed?
<!-- Fill in the relevant information below to help triage your issue. -->
#### How did you fix it (give a brief summary)?
<!-- Provide a summary of the improvement you are submitting. -->
### Questions
<If relevant, write a list of questions that you would like to discuss related to the changes that you have made.>
### Review
<use @mentions for quick questions, specific feedback, and progress updates.>
### Cool Spaceship Picture

View File

@ -0,0 +1,20 @@
---
name: ⚙ Feature
about: Implemented a new feature? 🎁
---
### What feature did you implement?
<!-- Fill in the relevant information below to help triage your issue. -->
#### Anything that needs special attention (breaking changes etc)?
<!-- Provide a summary of the improvement you are submitting. -->
### Questions
<If relevant, write a list of questions that you would like to discuss related to the changes that you have made.>
### Review
<use @mentions for quick questions, specific feedback, and progress updates.>
### Cool Spaceship Picture

View File

@ -10121,6 +10121,11 @@
}
}
},
"re-resizable": {
"version": "4.9.3",
"resolved": "https://registry.npmjs.org/re-resizable/-/re-resizable-4.9.3.tgz",
"integrity": "sha512-JKzmZdUAYWs85YErkmZNB7hjGR9qUOHFUZUtEplZlEZBFHRguiWck5J+HFTy/NjlMJtqQsYPQq57nQAO2BuRRg=="
},
"react": {
"version": "16.4.2",
"resolved": "https://registry.npmjs.org/react/-/react-16.4.2.tgz",

View File

@ -50,6 +50,7 @@
"prop-types": "^15.6.2",
"qs": "^6.5.2",
"raf": "3.4.0",
"re-resizable": "^4.9.3",
"react": "^16.4.2",
"react-blockies": "^1.4.0",
"react-bootstrap-typeahead": "^3.2.4",

View File

@ -42,7 +42,7 @@ const LayoutContract = ({contract, children, cardTitle}) => (
<Card>
<CardHeader>
<CardTitle>
<span className={orderClassName(contract.address)}>{contract.index + 1}</span>
<span className={orderClassName(contract.address)}>{contract.deployIndex + 1}</span>
{cardTitle}
</CardTitle>
</CardHeader>
@ -55,7 +55,7 @@ const LayoutContract = ({contract, children, cardTitle}) => (
LayoutContract.propTypes = {
contract: PropTypes.object,
children: PropTypes.array,
cardTitle: PropTypes.string
cardTitle: PropTypes.object
};
const DeploymentResult = ({deployment}) => {
@ -81,7 +81,7 @@ const DeploymentResult = ({deployment}) => {
DeploymentResult.propTypes = {
deployment: PropTypes.object
}
};
const GasEstimateResult = ({gasEstimate}) => {
if (gasEstimate.running) {
@ -244,7 +244,10 @@ const ContractsHeader = ({deploymentPipeline, updateDeploymentPipeline}) => (
);
ContractsHeader.propTypes = {
deploymentPipeline: PropTypes.object,
deploymentPipeline: PropTypes.oneOfType([
PropTypes.object,
PropTypes.string
]),
updateDeploymentPipeline: PropTypes.func
};
@ -262,13 +265,16 @@ const Contract = ({web3, contract, deploymentPipeline, web3Deploy, web3EstimateG
web3Deploy={web3Deploy}
web3EstimateGas={web3EstimateGas}/>;
default:
return <React.Fragment></React.Fragment>;
return <React.Fragment/>;
}
};
Contract.propTypes = {
contract: PropTypes.object,
deploymentPipeline: PropTypes.object,
deploymentPipeline: PropTypes.oneOfType([
PropTypes.object,
PropTypes.string
]),
toggleContractOverview: PropTypes.func,
web3: PropTypes.object,
web3Deploy: PropTypes.func,
@ -299,13 +305,16 @@ class ContractsDeployment extends React.Component {
<Row>
<Col>
<ContractsHeader deploymentPipeline={this.props.deploymentPipeline}
updateDeploymentPipeline={this.props.updateDeploymentPipeline}/>
{this.props.contracts.sort((a, b) => a.index - b.index).map(contract => (
<Contract key={contract.index}
contract={contract}
toggleContractOverview={(contract) => this.toggleContractOverview(contract)}
{...this.props} />
))}
updateDeploymentPipeline={this.props.updateDeploymentPipeline}/>
{this.props.contracts.filter(contract => contract.code || contract.deploy)
.sort((a, b) => a.index - b.index).map((contract, index) => {
contract.deployIndex = index;
return (<Contract key={contract.deployIndex}
contract={contract}
toggleContractOverview={(contract) => this.toggleContractOverview(contract)}
{...this.props} />);
}
)}
</Col>
{this.isContractOverviewOpen() &&
<Col xs={6} md={3}>
@ -322,9 +331,13 @@ class ContractsDeployment extends React.Component {
}
}
ContractsDeployment.propTypes = {
contracts: PropTypes.array,
deploymentPipeline: PropTypes.string,
deploymentPipeline: PropTypes.oneOfType([
PropTypes.object,
PropTypes.string
]),
updateDeploymentPipeline: PropTypes.func,
web3Deployments: PropTypes.object,
web3GasEstimates: PropTypes.object,

View File

@ -21,7 +21,8 @@ const style = (theme) => ({
top: 0,
bottom: '40px',
left: 0,
right: 0
right: 0,
minWidth: '175px'
},
node: {
base: {
@ -214,7 +215,7 @@ class FileExplorer extends React.Component {
style={style(this.props.theme)}
/>
<Label className="hidden-toogle mb-0 pt-2 pr-2 pb-1 border-top text-right">
<Label className="hidden-toggle mb-0 pt-2 pr-2 pb-1 border-top text-right">
<span className="mr-2 align-top" style={{"fontSize": "12px"}}>Show hidden files</span>
<AppSwitch color="success" variant="pill" size="sm" onChange={this.props.toggleShowHiddenFiles}/>
</Label>

View File

@ -4,12 +4,12 @@ import logo from '../images/logo-new.svg';
import './Login.css';
class Login extends React.Component {
constructor(props){
constructor(props) {
super(props);
this.state = props.credentials;
}
handleChange(event){
handleChange(event) {
this.setState({[event.target.name]: event.target.value});
}
@ -28,27 +28,30 @@ class Login extends React.Component {
<p className="text-danger">{this.props.error}</p>
}
<div className="mt-4">
<form onSubmit={(e) => this.handleSubmit(e)}>
<form onSubmit={(e) => this.handleSubmit(e)}>
<div className="form-group">
<label htmlFor="host">Host</label>
<input type="text"
className="form-control form-control-lg"
id="host"
name="host"
placeholder="Enter Embark host"
onChange={(e) => this.handleChange(e)}
value={this.state.host}/>
className="form-control form-control-lg"
id="host"
name="host"
placeholder="Enter Embark host"
onChange={(e) => this.handleChange(e)}
value={this.state.host}/>
</div>
<div className="form-group">
<label htmlFor="token">Token</label>
<input type="text"
className="form-control form-control-lg"
id="token"
name="token"
placeholder="Enter token"
onChange={(e) => this.handleChange(e)}
value={this.state.token}/>
<small className="form-text text-muted">Execute <code>embark run</code> in the command line to get your token.</small>
className="form-control form-control-lg"
id="token"
name="token"
placeholder="Enter token"
onChange={(e) => this.handleChange(e)}
autoComplete="off"
value={this.state.token}/>
<small className="form-text text-muted">Execute <code>embark run</code> in the command line to get
your token.
</small>
</div>
<button type="submit" className="btn btn-pill btn-dark">Enter Cockpit</button>
</form>

View File

@ -46,6 +46,10 @@ class TextEditor extends React.Component {
});
}
componentWillUnmount() {
window.removeEventListener("resize", this.handleResize);
}
handleResize = () => editor.layout();
@ -134,6 +138,10 @@ class TextEditor extends React.Component {
this.handleResize();
}
addEditorTabs(e, file) {
e.preventDefault(); this.props.addEditorTabs(file);
}
renderTabs() {
return (
<ul className="list-inline m-0 p-0">
@ -144,7 +152,7 @@ class TextEditor extends React.Component {
})}>
<a
href="#switch-tab"
onClick={() => this.props.addEditorTabs(file)}
onClick={(e) => this.addEditorTabs(e, file)}
className="p-2 text-muted"
>
{file.name}

View File

@ -4,12 +4,12 @@ import {Button, Nav, NavLink} from 'reactstrap';
import classnames from 'classnames';
import FontAwesomeIcon from 'react-fontawesome';
const TextEditorToolbarTabs = {
Overview: 'overview',
Detail: 'detail',
Transactions: 'transactions',
Debugger: 'debugger',
Browser: 'browser'
export const TextEditorToolbarTabs = {
Interact: { label: 'Interact', icon: 'bolt' },
Details: { label: 'Details', icon: 'info-circle' },
Debugger: { label: 'Debugger', icon: 'bug' },
Transactions: { label: 'Transactions', icon: 'list-alt' },
Browser: { label: 'Browser', icon: 'eye' }
};
class TextEditorToolbar extends Component {
@ -18,6 +18,18 @@ class TextEditorToolbar extends Component {
return this.props.activeTab === tab;
}
isBrowserTab(tab) {
return tab === TextEditorToolbarTabs.Browser;
}
renderTab(tab) {
return (
<NavLink key={tab.label} className={classnames('btn', { active: this.isActiveTab(tab)})} onClick={() => this.props.openAsideTab(tab)}>
<FontAwesomeIcon className="mr-2" name={tab.icon} /> {tab.label}
</NavLink>
);
}
render() {
return (
<ol className="breadcrumb mb-0">
@ -33,27 +45,9 @@ class TextEditorToolbar extends Component {
</li>
<li className="breadcrumb-menu">
<Nav className="btn-group">
{this.props.isContract &&
<React.Fragment>
<NavLink className={classnames('btn', { active: this.isActiveTab(TextEditorToolbarTabs.Overview)})} onClick={() => this.props.openAsideTab(TextEditorToolbarTabs.Overview)}>
<FontAwesomeIcon className="mr-2" name="bolt" />Interact
</NavLink>
<NavLink className={classnames('btn', { active: this.isActiveTab(TextEditorToolbarTabs.Detail)})} href="#" onClick={() => this.props.openAsideTab(TextEditorToolbarTabs.Detail)}>
<FontAwesomeIcon className="mr-2" name="info-circle" />Details
</NavLink>
<NavLink className={classnames('btn', { active: this.isActiveTab(TextEditorToolbarTabs.Transactions)})} href="#" onClick={() => this.props.openAsideTab(TextEditorToolbarTabs.Transactions)}>
<FontAwesomeIcon className="mr-2" name="list-alt" />Transactions
</NavLink>
<NavLink className={classnames('btn', { active: this.isActiveTab(TextEditorToolbarTabs.Debugger)})} href="#" onClick={() => this.props.openAsideTab(TextEditorToolbarTabs.Debugger)}>
<FontAwesomeIcon className="mr-2" name="bug" />Debugger
</NavLink>
</React.Fragment>
}
<NavLink className={classnames('btn', { active: this.isActiveTab(TextEditorToolbarTabs.Browser)})} href="#" onClick={() => this.props.openAsideTab(TextEditorToolbarTabs.Browser)}>
<FontAwesomeIcon className="mr-2" name="eye" /> Preview
</NavLink>
{this.props.isContract && Object.values(TextEditorToolbarTabs).map(tab => !this.isBrowserTab(tab) && this.renderTab(tab))}
{this.renderTab(TextEditorToolbarTabs.Browser)}
</Nav>
</li>
</ol>
);
@ -66,7 +60,7 @@ TextEditorToolbar.propTypes = {
remove: PropTypes.func,
toggleShowHiddenFiles: PropTypes.func,
openAsideTab: PropTypes.func,
activeTab: PropTypes.string
activeTab: PropTypes.object
};
export default TextEditorToolbar;

View File

@ -8,3 +8,7 @@ export const DEPLOYMENT_PIPELINES = {
embark: 'embark'
};
export const DEFAULT_HOST = process.env.NODE_ENV === 'development' ? 'localhost:8000' : window.location.host;
export const OPERATIONS = {
MORE: 1,
LESS: -1
};

View File

@ -46,7 +46,10 @@ function mapStateToProps(state) {
DeploymentContainer.propTypes = {
contracts: PropTypes.array,
deploymentPipeline: PropTypes.object,
deploymentPipeline: PropTypes.oneOfType([
PropTypes.object,
PropTypes.string
]),
fetchContracts: PropTypes.func,
updateDeploymentPipeline: PropTypes.func,
web3: PropTypes.object,

View File

@ -4,11 +4,12 @@
margin-top: -1.5rem !important;
}
.hidden-toogle {
.hidden-toggle {
position: absolute;
bottom: 0;
right: 0;
left: 0;
height: 40px;
}
.text-editor__debuggerLine {
@ -34,8 +35,12 @@
.editor-aside {
position: relative;
}
.aside-opened .text-editor-container {
max-height: 500px;
}
}
.resizer-handle {
width: 15px !important;
}
.resize-handle-horizontal {
height: 15px !important;
}

View File

@ -9,21 +9,51 @@ import TextEditorToolbarContainer from './TextEditorToolbarContainer';
import {fetchEditorTabs as fetchEditorTabsAction} from '../actions';
import {getCurrentFile} from '../reducers/selectors';
import classnames from 'classnames';
import Resizable from 're-resizable';
import {OPERATIONS} from '../constants';
import './EditorContainer.css';
class EditorContainer extends React.Component {
constructor(props) {
super(props);
this.state = {currentAsideTab: '', showHiddenFiles: false, currentFile: this.props.currentFile};
this.DEFAULT_EDITOR_WIDTH = 85;
this.DEFAULT_EDITOR_WIDTH_SMALL = 75;
this.DEFAULT_HEIGHT = 500;
this.SMALL_SIZE = 768;
this.windowWidth = window.innerWidth;
this.state = {
currentAsideTab: {}, showHiddenFiles: false, currentFile: this.props.currentFile,
editorHeight: this.DEFAULT_HEIGHT,
editorWidth: ((this.windowWidth < this.SMALL_SIZE) ? this.DEFAULT_EDITOR_WIDTH_SMALL : this.DEFAULT_EDITOR_WIDTH) + '%',
asideHeight: '100%', asideWidth: '25%',
isSmallSize: (this.windowWidth < this.SMALL_SIZE)
};
}
componentDidMount() {
this.props.fetchEditorTabs();
window.addEventListener("resize", this.updateDimensions.bind(this));
}
componentWillUnmount() {
window.removeEventListener("resize", this.updateDimensions.bind(this));
}
updateDimensions() {
this.windowWidth = window.innerWidth;
const isSmallSize = (this.windowWidth < this.SMALL_SIZE);
if (this.state.isSmallSize !== isSmallSize) {
this.setState({isSmallSize});
this.changeEditorWidth(isSmallSize ? OPERATIONS.MORE : OPERATIONS.LESS);
this.editor.handleResize();
}
}
componentDidUpdate(prevProps) {
if(this.props.currentFile.path !== prevProps.currentFile.path) {
if (this.props.currentFile.path !== prevProps.currentFile.path) {
this.setState({currentFile: this.props.currentFile});
}
}
@ -43,38 +73,116 @@ class EditorContainer extends React.Component {
}
openAsideTab(newTab) {
if (newTab === this.state.currentAsideTab) {
return this.setState({currentAsideTab: ''});
if (!this.state.isSmallSize) {
this.changeEditorWidth((!this.state.currentAsideTab.label) ? OPERATIONS.LESS : OPERATIONS.MORE);
}
if (newTab.label === this.state.currentAsideTab.label) {
this.editor.handleResize();
return this.setState({currentAsideTab: {}});
}
this.setState({currentAsideTab: newTab});
}
textEditorMdSize() {
return this.state.currentAsideTab.length ? 7 : 10;
changeEditorWidth(operation) {
this.setState({
editorWidth: (parseFloat(this.state.editorWidth) + (operation * parseFloat(this.state.asideWidth))) + '%'
});
}
renderTextEditor() {
const height = !!(this.state.isSmallSize && this.state.currentAsideTab.label) ? this.state.editorHeight : 'auto';
return (
<Resizable
size={{width: this.state.editorWidth, height: height}}
minWidth="20%" maxWidth="90%"
handleClasses={{left: 'resizer-handle', right: 'resizer-handle', bottom: 'resize-handle-horizontal'}}
onResizeStop={(e, direction, ref, d) => {
this.setState({
editorWidth: ref.style.width,
editorHeight: this.state.editorHeight + d.height
});
this.editor.handleResize();
}}
className="text-editor-container border-left"
enable={{
top: false,
right: false,
bottom: !!(this.state.isSmallSize && this.state.currentAsideTab.label),
left: true,
topRight: false,
bottomRight: false,
bottomLeft: false,
topLeft: false
}}>
<TextEditorContainer
ref={instance => {
this.editor = instance ? instance.getWrappedInstance().editor : null;
}}
currentFile={this.props.currentFile}
onFileContentChange={(newContent) => this.onFileContentChange(newContent)}/>
</Resizable>
);
}
renderAside() {
const aside = (
<div className="editor-aside">
<TextEditorAsideContainer currentAsideTab={this.state.currentAsideTab}
currentFile={this.props.currentFile}/>
</div>
);
if (this.windowWidth < this.SMALL_SIZE) {
return (<Col sm={12} className="border-left-0 relative">
{aside}
</Col>);
}
return (
<Resizable defaultSize={{width: this.state.asideWidth, height: 'auto'}}
maxWidth='60%' minWidth='17%'
handleClasses={{left: 'resizer-handle', right: 'resizer-handle'}}
className="border-left-0 relative"
enable={{
top: false,
right: false,
bottom: false,
left: true,
topRight: false,
bottomRight: false,
bottomLeft: false,
topLeft: false
}}
onResize={(e, direction, ref, _d) => {
this.setState({
editorWidth: this.DEFAULT_EDITOR_WIDTH - parseFloat(ref.style.width) + '%'
});
}}>
{aside}
</Resizable>
);
}
render() {
return (
<Row noGutters className={classnames('h-100', 'editor--grid', {'aside-opened': this.state.currentAsideTab.length})}>
<Row noGutters
className={classnames('h-100', 'editor--grid', {'aside-opened': this.state.currentAsideTab.label})}>
<Col xs={12}>
<TextEditorToolbarContainer openAsideTab={(newTab) => this.openAsideTab(newTab)}
isContract={this.isContract()}
currentFile={this.props.currentFile}
activeTab={this.state.currentAsideTab}/>
</Col>
<Col sm={4} md={2} xl={2} lg={2} className="border-right">
<FileExplorerContainer showHiddenFiles={this.state.showHiddenFiles} toggleShowHiddenFiles={() => this.toggleShowHiddenFiles()} />
<Col className="border-right">
<FileExplorerContainer showHiddenFiles={this.state.showHiddenFiles}
toggleShowHiddenFiles={() => this.toggleShowHiddenFiles()}/>
</Col>
<Col sm={8} md={this.textEditorMdSize()} className="text-editor-container">
<TextEditorContainer currentFile={this.props.currentFile} onFileContentChange={(newContent) => this.onFileContentChange(newContent)} />
</Col>
{this.state.currentAsideTab &&
<Col sm={12} md={3} className="border-left-0 relative">
<div className="editor-aside">
<TextEditorAsideContainer currentAsideTab={this.state.currentAsideTab}
currentFile={this.props.currentFile}/>
</div>
</Col>}
{this.renderTextEditor()}
{this.state.currentAsideTab.label && this.renderAside()}
</Row>
);
}
@ -95,6 +203,6 @@ EditorContainer.propTypes = {
export default connect(
mapStateToProps,
{fetchEditorTabs: fetchEditorTabsAction.request},
{fetchEditorTabs: fetchEditorTabsAction.request}
)(EditorContainer);

View File

@ -10,6 +10,7 @@ import ContractDetail from '../components/ContractDetail';
import ContractTransactionsContainer from './ContractTransactionsContainer';
import ContractOverviewContainer from '../containers/ContractOverviewContainer';
import ContractDebuggerContainer from '../containers/ContractDebuggerContainer';
import { TextEditorToolbarTabs } from '../components/TextEditorToolbar';
class TextEditorAsideContainer extends Component {
componentDidMount() {
@ -17,29 +18,29 @@ class TextEditorAsideContainer extends Component {
}
renderContent(contract, index) {
switch (this.props.currentAsideTab) {
case 'debugger':
switch (this.props.currentAsideTab.label) {
case TextEditorToolbarTabs.Debugger.label:
return (
<React.Fragment>
<h2>{contract.className} - Debugger</h2>
<ContractDebuggerContainer key={index} contract={contract}/>
</React.Fragment>
);
case 'detail':
case TextEditorToolbarTabs.Details.label:
return (
<React.Fragment>
<h2>{contract.className} - Details</h2>
<ContractDetail key={index} contract={contract}/>
</React.Fragment>
);
case 'transactions':
case TextEditorToolbarTabs.Transactions.label:
return (
<React.Fragment>
<h2>{contract.className} - Transactions</h2>
<ContractTransactionsContainer key={index} contract={contract}/>
</React.Fragment>
);
case 'overview':
case TextEditorToolbarTabs.Interact.label:
return (
<React.Fragment>
<h2>{contract.className} - Interact</h2>
@ -52,7 +53,7 @@ class TextEditorAsideContainer extends Component {
}
render() {
if (this.props.currentAsideTab === 'browser') {
if (this.props.currentAsideTab.label === TextEditorToolbarTabs.Browser.label) {
return <Preview/>;
}
return this.props.contracts.map((contract, index) => {
@ -75,7 +76,7 @@ function mapStateToProps(state, props) {
TextEditorAsideContainer.propTypes = {
currentFile: PropTypes.object,
currentAsideTab: PropTypes.string,
currentAsideTab: PropTypes.object,
contract: PropTypes.array,
fetchContracts: PropTypes.func,
contracts: PropTypes.array

View File

@ -27,7 +27,10 @@ class TextEditorContainer extends React.Component {
addEditorTabs={this.props.addEditorTabs}
debuggerLine={this.props.debuggerLine}
onFileContentChange={this.props.onFileContentChange}
theme={this.props.theme} />
theme={this.props.theme}
ref={instance => {
if (instance) this.editor = instance;
}}/>
);
}
}
@ -45,7 +48,7 @@ TextEditorContainer.propTypes = {
onFileContentChange: PropTypes.func,
toggleBreakpoints: PropTypes.func,
breakpoints: PropTypes.array,
toggleBreakpoint: PropTypes.object,
toggleBreakpoint: PropTypes.func,
fetchEditorTabs: PropTypes.func,
removeEditorTabs: PropTypes.func,
addEditorTabs: PropTypes.func,
@ -57,9 +60,11 @@ TextEditorContainer.propTypes = {
export default connect(
mapStateToProps,
{
toggleBreakpoint,
toggleBreakpoint: toggleBreakpoint.request,
fetchEditorTabs: fetchEditorTabsAction.request,
removeEditorTabs: removeEditorTabsAction.request,
addEditorTabs: addEditorTabsAction.request
},
null,
{ withRef: true }
)(TextEditorContainer);

View File

@ -34,7 +34,7 @@ TextEditorToolbarContainer.propTypes = {
removeFile: PropTypes.func,
toggleShowHiddenFiles: PropTypes.func,
openAsideTab: PropTypes.func,
activeTab: PropTypes.string
activeTab: PropTypes.object
};
export default connect(

View File

@ -153,6 +153,10 @@ export function getEnsRecords(state) {
return state.entities.ensRecords;
}
export function getEnsRecordForName(state, name) {
return state.entities.ensRecords.find((record) => record.name === name);
}
export function getEnsErrors(state) {
return state.errorEntities.ensRecords;
}

View File

@ -1,16 +1,28 @@
import {put, select} from "redux-saga/effects";
import {getAccounts, getBlocks, getTransactions, getContracts} from "../reducers/selectors";
import {fetchAccounts, fetchBlocks, fetchTransactions, fetchContracts} from "./index";
import {getAccounts, getBlocks, getTransactions, getContracts, getEnsRecordForName} from "../reducers/selectors";
import {fetchAccounts, fetchBlocks, fetchTransactions, fetchContracts, fetchEnsRecord} from "./index";
import {ELEMENTS_LIMIT} from '../constants';
export function *searchExplorer(entity, payload) {
let result;
let searchValue = payload.searchValue;
let isENSName = false;
if (searchValue.endsWith('.eth')) {
isENSName = true;
yield fetchEnsRecord({name: payload.searchValue});
const ensRecord = yield select(getEnsRecordForName, searchValue);
if (!ensRecord) {
return yield put(entity.success({error: 'No ENS record for that name'}));
}
searchValue = ensRecord.address;
}
// Accounts
yield fetchAccounts({});
const accounts = yield select(getAccounts);
result = accounts.find(account => {
return account.address === payload.searchValue;
return account.address === searchValue;
});
if (result) {
@ -21,7 +33,7 @@ export function *searchExplorer(entity, payload) {
yield fetchContracts({});
const contracts = yield select(getContracts);
result = contracts.find(contract => {
return contract.address === payload.searchValue;
return contract.address === searchValue;
});
if (result) {
@ -31,9 +43,9 @@ export function *searchExplorer(entity, payload) {
// Blocks
yield fetchBlocks({limit: ELEMENTS_LIMIT});
const blocks = yield select(getBlocks);
const intSearchValue = parseInt(payload.searchValue, 10);
const intSearchValue = parseInt(searchValue, 10);
result = blocks.find(block => {
return block.hash === payload.searchValue || block.number === intSearchValue;
return block.hash === searchValue || block.number === intSearchValue;
});
if (result) {
@ -44,12 +56,16 @@ export function *searchExplorer(entity, payload) {
yield fetchTransactions({blockLimit: ELEMENTS_LIMIT});
const transactions = yield select(getTransactions);
result = transactions.find(transaction => {
return transaction.hash === payload.searchValue;
return transaction.hash === searchValue;
});
if (result) {
return yield put(entity.success(result));
}
if (isENSName) {
return yield put(entity.success({error: `ENS resolved to ${searchValue}, but Embark couldn't find what this address represents`}));
}
return yield put(entity.success({error: `No result found in transactions, accounts, contracts, or blocks. Please note: We limit the search to the last ${ELEMENTS_LIMIT} elements for performance`}));
}

View File

@ -60,5 +60,6 @@
"deploymentStrategy": {
"implicit": "implicit",
"explicit": "explicit"
}
},
"embarkResourceOrigin": "http://embark"
}

View File

@ -148,10 +148,8 @@ Config.prototype._updateBlockchainCors = function(){
corsParts.push(utils.buildUrlFromConfig(storageConfig.upload));
}
}
// add whisper cors
if(this.communicationConfig && this.communicationConfig.enabled && this.communicationConfig.provider === 'whisper'){
corsParts.push('http://embark');
}
// Add cors for the proxy and whisper
corsParts.push(constants.embarkResourceOrigin);
let cors = corsParts.join(',');
if(blockchainConfig.rpcCorsDomain === 'auto'){
@ -208,7 +206,8 @@ Config.prototype.loadBlockchainConfigFile = function() {
"default": {
"enabled": true,
"rpcCorsDomain": "auto",
"wsOrigins": "auto"
"wsOrigins": "auto",
"proxy": true
}
};

View File

@ -1,6 +1,7 @@
const async = require('async');
const AccountParser = require('../../utils/accountParser');
const fundAccount = require('./fundAccount');
const constants = require('../../constants');
class Provider {
constructor(options) {
@ -21,10 +22,10 @@ class Provider {
} else if (this.type === 'ws') {
// Note: don't pass to the provider things like {headers: {Origin: "embark"}}. Origin header is for browser to fill
// to protect user, it has no meaning if it is used server-side. See here for more details: https://github.com/ethereum/go-ethereum/issues/16608
// Moreover, Parity reject origins that are not urls so if you try to connect with Origin: "embark" it gives the followin error:
// Moreover, Parity reject origins that are not urls so if you try to connect with Origin: "embark" it gives the following error:
// << Blocked connection to WebSockets server from untrusted origin: Some("embark") >>
// The best choice is to use void origin, BUT Geth rejects void origin, so to keep both clients happy we can use http://embark
self.provider = new this.web3.providers.WebsocketProvider(self.web3Endpoint, {headers: {Origin: "http://embark"}});
self.provider = new this.web3.providers.WebsocketProvider(self.web3Endpoint, {headers: {Origin: constants.embarkResourceOrigin}});
self.provider.on('error', () => self.logger.error('Websocket Error'));
self.provider.on('end', () => self.logger.error('Websocket connection ended'));

View File

@ -12,7 +12,7 @@ const Ipc = require('../../core/ipc');
const {defaultHost, dockerHostSwap} = require('../../utils/host');
const Logger = require('../../core/logger');
// time between IPC connection attmpts (in ms)
// time between IPC connection attempts (in ms)
const IPC_CONNECT_INTERVAL = 2000;
/*eslint complexity: ["error", 42]*/
@ -60,7 +60,7 @@ var Blockchain = function(userConfig, clientClass) {
targetGasLimit: this.userConfig.targetGasLimit || false,
syncMode: this.userConfig.syncMode || this.userConfig.syncmode,
verbosity: this.userConfig.verbosity,
proxy: this.userConfig.proxy || true
proxy: this.userConfig.proxy
};
if (this.userConfig === {} || this.userConfig.default || JSON.stringify(this.userConfig) === '{"enabled":true}') {

View File

@ -2,6 +2,7 @@ const async = require('async');
const Web3 = require('web3');
const {buildUrl} = require('../../utils/utils.js');
const {readFileSync, dappPath} = require('../../core/fs');
const constants = require('../../constants');
class DevFunds {
constructor(options) {
@ -11,7 +12,7 @@ class DevFunds {
this.password = this.blockchainConfig.account.password ? readFileSync(dappPath(this.blockchainConfig.account.password), 'utf8').replace('\n', '') : 'dev_password';
this.networkId = null;
this.balance = Web3.utils.toWei("1", "ether");
this.provider = options.provider || new Web3.providers.WebsocketProvider(buildUrl('ws', this.blockchainConfig.wsHost, this.blockchainConfig.wsPort), {headers: {Origin: "http://embark"}});
this.provider = options.provider || new Web3.providers.WebsocketProvider(buildUrl('ws', this.blockchainConfig.wsHost, this.blockchainConfig.wsPort), {headers: {Origin: constants.embarkResourceOrigin}});
this.web3 = new Web3(this.provider);
if (this.blockchainConfig.account.balance) {
this.balance = this.blockchainConfig.account.balance;

View File

@ -270,7 +270,7 @@ class ContractsManager {
function getGasPriceForNetwork(callback) {
return callback(null, self.contractsConfig.gasPrice);
},
function prepareContractsFromCompilation(gasPrice, callback) {
function prepareContractsForCompilation(gasPrice, callback) {
let className, compiledContract, contractConfig, contract;
for (className in self.compiledContracts) {
compiledContract = self.compiledContracts[className];

View File

@ -4,6 +4,7 @@ let Web3 = require('web3');
const {parallel} = require('async');
const {sendMessage, listenTo} = require('./js/communicationFunctions');
const messageEvents = require('./js/message_events');
const constants = require('../../constants');
const {canonicalHost, defaultHost} = require('../../utils/host');
@ -44,7 +45,7 @@ class Whisper {
// Moreover, Parity reject origins that are not urls so if you try to connect with Origin: "embark" it gives the followin error:
// << Blocked connection to WebSockets server from untrusted origin: Some("embark") >>
// The best choice is to use void origin, BUT Geth rejects void origin, so to keep both clients happy we can use http://embark
this.web3.setProvider(new Web3.providers.WebsocketProvider(web3Endpoint, {headers: {Origin: "http://embark"}}));
this.web3.setProvider(new Web3.providers.WebsocketProvider(web3Endpoint, {headers: {Origin: constants.embarkResourceOrigin}}));
}
waitForWeb3Ready(cb) {
@ -133,7 +134,7 @@ class Whisper {
const code = `\nEmbarkJS.Messages.setProvider('whisper', ${JSON.stringify(config)});`;
this.embark.addProviderInit('communication', code, shouldInit);
const consoleConfig = Object.assign({}, config, {providerOptions: {headers: {Origin: "http://embark"}}});
const consoleConfig = Object.assign({}, config, {providerOptions: {headers: {Origin: constants.embarkResourceOrigin}}});
const consoleCode = `\nEmbarkJS.Messages.setProvider('whisper', ${JSON.stringify(consoleConfig)});`;
this.embark.addConsoleProviderInit('communication', consoleCode, shouldInit);
}

6
npm-shrinkwrap.json generated
View File

@ -5163,9 +5163,9 @@
}
},
"embarkjs": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/embarkjs/-/embarkjs-0.4.4.tgz",
"integrity": "sha512-HIetHNqngbhUgdESZ8V1gQiRzoti7LMu/P5xcfemn+8b4KwasUyrybaBdvqmo+ock9rExXZFHIgaYA+85s9DkA==",
"version": "0.4.5",
"resolved": "https://registry.npmjs.org/embarkjs/-/embarkjs-0.4.5.tgz",
"integrity": "sha512-WAW9tlvCiSa6DEC6M+ZfFS9WemE91DunOUP4rn7urtFgOpT0m08dFKB0F+Qfcmonb7cozEPfNv5C78xSt1JUsA==",
"requires": {
"@babel/runtime-corejs2": "7.0.0-rc.1",
"async": "2.6.1",

View File

@ -68,7 +68,7 @@
"decompress": "4.2.0",
"deep-equal": "1.0.1",
"ejs": "2.6.1",
"embarkjs": "0.4.4",
"embarkjs": "0.4.5",
"eth-ens-namehash": "2.0.8",
"eth-lib": "0.2.8",
"ethereumjs-wallet": "0.6.0",

View File

@ -41,12 +41,9 @@ describe('embark.Blockchain', function() {
targetGasLimit: false,
syncMode: undefined,
verbosity: undefined,
proxy: true,
proxy: undefined,
silent: undefined
};
// We check also proxy's ports because proxy is set to true
expectedConfig.wsPort += constants.blockchain.servicePortOnProxy;
expectedConfig.rpcPort += constants.blockchain.servicePortOnProxy;
assert.deepEqual(blockchain.config, expectedConfig);
done();

View File

@ -25,6 +25,7 @@ describe('embark.Config', function () {
"isDev": false,
"mineWhenNeeded": true,
"nodiscover": true,
"proxy": true,
"rpcHost": "localhost",
"rpcPort": 8545,
"rpcCorsDomain": "http://localhost:8000",
@ -48,6 +49,7 @@ describe('embark.Config', function () {
"gasPrice": "8000000",
"mineWhenNeeded": true,
"nodiscover": true,
"proxy": true,
"rpcHost": "localhost",
"rpcPort": 8545,
"rpcCorsDomain": "http://localhost:8000",
@ -81,6 +83,7 @@ describe('embark.Config', function () {
"gasPrice": "8000000",
"mineWhenNeeded": true,
"nodiscover": true,
"proxy": true,
"rpcHost": "localhost",
"rpcPort": 8545,
"rpcCorsDomain": "http://localhost:8000",