Add toolbar to editor

Rename overview to detail

React to save file for local storage

Add switch for hidden files

move units to ether instead of wei

separate description from link itself which makes UI cleaner

remove unneded explorer header

switch nav menu color to white

replace contract address table with simple paragraph instead

improve contract functions page

improve contract functions view

style/fix functions tab

fix rebase issue

re-style contracts sidebar

improve contract detail sidebar
This commit is contained in:
Anthony Laibe 2018-10-23 12:18:02 +01:00 committed by Iuri Matias
parent f30803946c
commit 00a8d9baea
38 changed files with 627 additions and 555 deletions

View File

@ -288,10 +288,8 @@ export const file = {
export const SAVE_FILE = createRequestTypes('SAVE_FILE');
export const saveFile = {
request: ({name, path, content}) => {
return action(SAVE_FILE[REQUEST], {name, path, content});
},
success: () => action(SAVE_FILE[SUCCESS]),
request: ({name, path, content}) => action(SAVE_FILE[REQUEST], {name, path, content}),
success: (_, {name, path, content}) => action(SAVE_FILE[SUCCESS], {name, path, content}),
failure: (error) => action(SAVE_FILE[FAILURE], {error})
};

View File

@ -10,7 +10,7 @@ const Account = ({account}) => (
<Col>
<Card>
<CardHeader>
<CardTitleIdenticon id={account.address}>Account {account.address}</CardTitleIdenticon>
<CardTitleIdenticon id={account.address}>{account.address}</CardTitleIdenticon>
</CardHeader>
<CardBody>
<dl className="row">

View File

@ -12,15 +12,15 @@ const Accounts = ({accounts}) => (
{accounts.map(account => (
<Card key={account.address}>
<CardHeader>
<Link to={`/embark/explorer/accounts/${account.address}`}>
<CardTitleIdenticon id={account.address}>Account {account.address}</CardTitleIdenticon>
</Link>
<CardTitleIdenticon id={account.address}>Account&nbsp;
<Link to={`/embark/explorer/accounts/${account.address}`}>{account.address}</Link>
</CardTitleIdenticon>
</CardHeader>
<CardBody>
<Row>
<Col>
<strong>Balance</strong>
<div>{account.balance} Wei</div>
<div>{account.balance} Ether</div>
</Col>
<Col>
<strong>Tx Count</strong>

View File

@ -12,7 +12,11 @@ const Blocks = ({blocks}) => (
{blocks.map(block => (
<Card key={block.number}>
<CardHeader>
<Link to={`/embark/explorer/blocks/${block.number}`}> <CardTitleIdenticon id={block.hash}>Block {block.number}</CardTitleIdenticon></Link>
<CardTitleIdenticon id={block.hash}>Block&nbsp;
<Link to={`/embark/explorer/blocks/${block.number}`}>
{block.number}
</Link>
</CardTitleIdenticon>
</CardHeader>
<CardBody>
<Row>

View File

@ -6,4 +6,4 @@ const CardTitleIdenticon = ({id, children}) => (
<CardTitle><Blockies seed={id} className="rounded"/><span className="ml-2 align-top">{children}</span></CardTitle>
)
export default CardTitleIdenticon
export default CardTitleIdenticon

View File

@ -0,0 +1,36 @@
import PropTypes from "prop-types";
import React from 'react';
import ReactJson from "react-json-view";
import {Row, Col, Table} from "reactstrap";
import {formatContractForDisplay} from '../utils/presentation';
import CopyButton from './CopyButton';
const ContractDetail = ({contract}) => {
const contractDisplay = formatContractForDisplay(contract);
return (
<Row>
<Col>
<strong>ABI</strong>
<div className="relative">
<CopyButton text={JSON.stringify(contract.abiDefinition)}
title="Copy bytecode to clipboard"/>
{contract.abiDefinition && <ReactJson src={contract.abiDefinition} theme="monokai" sortKeys={true} collapsed={1} />}
</div>
<br />
<strong>Bytecode</strong>
<div className="text-wrap logs relative">
<CopyButton text={contract.runtimeBytecode}
title="Copy bytecode to clipboard"/>
{contract.runtimeBytecode}
</div>
</Col>
</Row>
);
};
ContractDetail.propTypes = {
contract: PropTypes.object
};
export default ContractDetail;

View File

@ -3,6 +3,7 @@ import React, {Component} from 'react';
import {
Row,
Col,
Form,
FormGroup,
Label,
Input,
@ -12,6 +13,7 @@ import {
CardHeader,
CardTitle,
CardFooter,
Collapse,
ListGroup,
ListGroupItem
} from "reactstrap";
@ -19,13 +21,17 @@ import {
class ContractFunction extends Component {
constructor(props) {
super(props);
this.state = {inputs: {}};
this.state = {inputs: {}, optionsCollapse: false, functionCollapse: false};
}
static isPureCall(method) {
return (method.mutability === 'view' || method.mutability === 'pure');
}
static isEvent(method) {
return !this.isPureCall(method) && (method.type === 'event');
}
buttonTitle() {
const {method} = this.props;
if (method.name === 'constructor') {
@ -56,30 +62,65 @@ class ContractFunction extends Component {
return this.inputsAsArray().length !== this.props.method.inputs.length;
}
toggleOptions() {
this.setState({
optionsCollapse: !this.state.optionsCollapse,
});
}
toggleFunction() {
this.setState({
functionCollapse: !this.state.functionCollapse,
});
}
render() {
return (
<Col xs={12}>
<Card>
<CardHeader>
<CardTitle>{this.props.method.name}</CardTitle>
<CardTitle className="collapsable contractFunction" onClick={() => this.toggleFunction()}>
{ContractFunction.isPureCall(this.props.method) &&
<button class="btn btn-brand btn-sm" style={{ "color": "#fff", "background-color": "#ff4500", "border-color": "#ff4500", "float": "right" }}>call</button>
}
{ContractFunction.isEvent(this.props.method) &&
<button class="btn btn-brand btn-sm" style={{ "color": "#fff", "background-color": "#aad450", "border-color": "#aad450", "float": "right" }}>event</button>
}
{this.props.method.name}({this.props.method.inputs.map(input => input.name).join(', ')})
</CardTitle>
</CardHeader>
<CardBody>
{this.props.method.inputs.map(input => (
<FormGroup key={input.name}>
<Label for={input.name}>{input.name}</Label>
<Input name={input.name} id={input.name} placeholder={input.type} onChange={(e) => this.handleChange(e, input.name)}/>
</FormGroup>
))}
{!ContractFunction.isPureCall(this.props.method) &&
<FormGroup key="gasPrice">
<Label for="gasPrice">Gas Price (in GWei)(optional)</Label>
<Input name="gasPrice" id="gasPrice" onChange={(e) => this.handleChange(e, 'gasPrice')}/>
</FormGroup>
}
<Button color="primary" disabled={this.callDisabled()} onClick={(e) => this.handleCall(e)}>
{this.buttonTitle()}
</Button>
</CardBody>
<Collapse isOpen={this.state.functionCollapse}>
<CardBody>
<Form action="" method="post" inline>
{this.props.method.inputs.map(input => (
<FormGroup key={input.name} className="pr-1">
<Label for={input.name} className="pr-1">{input.name}: </Label>
<Input name={input.name} id={input.name} placeholder={input.type} onChange={(e) => this.handleChange(e, input.name)}/>
</FormGroup>
))}
</Form>
{!ContractFunction.isPureCall(this.props.method) &&
<Col xs={12} style={{"margin-bottom": "5px", "margin-top": "5px"}}>
<Row>
<strong className="collapsable" onClick={() => this.toggleOptions()}><i className={this.state.optionsCollapse ? 'fa fa-caret-down' : 'fa fa-caret-right'}/>Advanced Options</strong>
<Collapse isOpen={this.state.optionsCollapse}>
<Form action="" method="post" inline>
<FormGroup key="gasPrice" className="pr-1">
<Label for="gasPrice" className="pr-1">Gas Price (in GWei)(optional)</Label>
<Input name="gasPrice" id="gasPrice" onChange={(e) => this.handleChange(e, 'gasPrice')}/>
</FormGroup>
</Form>
</Collapse>
</Row>
</Col>
}
<div align="right">
<Button color="primary" disabled={this.callDisabled()} onClick={(e) => this.handleCall(e)} >
{this.buttonTitle()}
</Button>
</div>
</CardBody>
</Collapse>
{this.props.contractFunctions && this.props.contractFunctions.length > 0 && <CardFooter>
<ListGroup>
{this.props.contractFunctions.map(contractFunction => (

View File

@ -3,9 +3,9 @@ import React from 'react';
import { TabContent, TabPane, Nav, NavItem, NavLink, Card, CardBody, CardTitle } from 'reactstrap';
import classnames from 'classnames';
import ContractOverview from '../components/ContractOverview';
import ContractDetail from '../components/ContractDetail';
import ContractLoggerContainer from '../containers/ContractLoggerContainer';
import ContractFunctionsContainer from '../containers/ContractFunctionsContainer';
import ContractOverviewContainer from '../containers/ContractOverviewContainer';
class ContractLayout extends React.Component {
constructor(props) {
@ -44,7 +44,7 @@ class ContractLayout extends React.Component {
className={classnames({ active: this.state.activeTab === '2' })}
onClick={() => { this.toggle('2'); }}
>
Functions
Detail
</NavLink>
</NavItem>
<NavItem>
@ -58,10 +58,10 @@ class ContractLayout extends React.Component {
</Nav>
<TabContent activeTab={this.state.activeTab}>
<TabPane tabId="1">
<ContractOverview contract={this.props.contract} />
<ContractOverviewContainer contract={this.props.contract} />
</TabPane>
<TabPane tabId="2">
<ContractFunctionsContainer contract={this.props.contract} />
<ContractDetail contract={this.props.contract} />
</TabPane>
<TabPane tabId="3">
<ContractLoggerContainer contract={this.props.contract} />

View File

@ -1,54 +1,191 @@
import PropTypes from "prop-types";
import React from 'react';
import ReactJson from "react-json-view";
import {Row, Col, Table} from "reactstrap";
import React, {Component} from 'react';
import {
Row,
Col,
Form,
FormGroup,
Label,
Input,
Button,
Card,
CardBody,
CardHeader,
CardTitle,
CardFooter,
Collapse,
ListGroup,
ListGroupItem
} from "reactstrap";
import {formatContractForDisplay} from '../utils/presentation';
import CopyButton from './CopyButton';
const Contract = ({contract}) => {
class ContractFunction extends Component {
constructor(props) {
super(props);
this.state = {inputs: {}, optionsCollapse: false, functionCollapse: false};
}
static isPureCall(method) {
return (method.mutability === 'view' || method.mutability === 'pure');
}
static isEvent(method) {
return !this.isPureCall(method) && (method.type === 'event');
}
buttonTitle() {
const {method} = this.props;
if (method.name === 'constructor') {
return 'Deploy';
}
return ContractFunction.isPureCall(method) ? 'Call' : 'Send';
}
inputsAsArray() {
return this.props.method.inputs
.map(input => this.state.inputs[input.name])
.filter(value => value);
}
handleChange(e, name) {
let newInputs = this.state.inputs;
newInputs[name] = e.target.value;
this.setState({inputs: newInputs});
}
handleCall(e) {
e.preventDefault();
this.props.postContractFunction(this.props.contractProfile.name, this.props.method.name, this.inputsAsArray(), this.state.inputs.gasPrice * 1000000000);
}
callDisabled() {
return this.inputsAsArray().length !== this.props.method.inputs.length;
}
toggleOptions() {
this.setState({
optionsCollapse: !this.state.optionsCollapse,
});
}
toggleFunction() {
this.setState({
functionCollapse: !this.state.functionCollapse,
});
}
render() {
return (
<Card>
<CardHeader>
<CardTitle className="collapsable contractFunction" onClick={() => this.toggleFunction()}>
{ContractFunction.isPureCall(this.props.method) &&
<button class="btn btn-brand btn-sm" style={{ "color": "#fff", "background-color": "#ff4500", "border-color": "#ff4500", "float": "right" }}>call</button>
}
{ContractFunction.isEvent(this.props.method) &&
<button class="btn btn-brand btn-sm" style={{ "color": "#fff", "background-color": "#aad450", "border-color": "#aad450", "float": "right" }}>event</button>
}
{this.props.method.name}({this.props.method.inputs.map(input => input.name).join(', ')})
</CardTitle>
</CardHeader>
<Collapse isOpen={this.state.functionCollapse}>
<CardBody>
<Form action="" method="post" inline>
{this.props.method.inputs.map(input => (
<FormGroup key={input.name} className="pr-1">
<Label for={input.name} className="pr-1">{input.name}: </Label>
<Input name={input.name} id={input.name} placeholder={input.type} onChange={(e) => this.handleChange(e, input.name)}/>
</FormGroup>
))}
</Form>
{!ContractFunction.isPureCall(this.props.method) &&
<Col xs={12} style={{"margin-bottom": "5px", "margin-top": "5px"}}>
<Row>
<strong className="collapsable" onClick={() => this.toggleOptions()}><i className={this.state.optionsCollapse ? 'fa fa-caret-down' : 'fa fa-caret-right'}/>Advanced Options</strong>
<Col xs={12} style={{"margin-bottom": "5px", "margin-top": "5px"}}>
<Row>
<Collapse isOpen={this.state.optionsCollapse}>
<Form action="" method="post" inline>
<FormGroup key="gasPrice" className="pr-1">
<Label for="gasPrice" className="pr-1">Gas Price (in GWei)(optional)</Label>
<Input name="gasPrice" id="gasPrice" onChange={(e) => this.handleChange(e, 'gasPrice')}/>
</FormGroup>
</Form>
</Collapse>
</Row>
</Col>
</Row>
</Col>
}
<div align="right">
<Button color="primary" disabled={this.callDisabled()} onClick={(e) => this.handleCall(e)}>
{this.buttonTitle()}
</Button>
</div>
</CardBody>
</Collapse>
{this.props.contractFunctions && this.props.contractFunctions.length > 0 && <CardFooter>
<ListGroup>
{this.props.contractFunctions.map(contractFunction => (
<ListGroupItem key={contractFunction.result}>
{contractFunction.inputs.length > 0 && <p>Inputs: {contractFunction.inputs.join(', ')}</p>}
<strong>Result: {contractFunction.result}</strong>
</ListGroupItem>
))}
</ListGroup>
</CardFooter>}
</Card>
);
}
}
ContractFunction.propTypes = {
contractProfile: PropTypes.object,
method: PropTypes.object,
contractFunctions: PropTypes.arrayOf(PropTypes.object),
postContractFunction: PropTypes.func
};
const filterContractFunctions = (contractFunctions, contractName, method) => {
return contractFunctions.filter((contractFunction) => (
contractFunction.contractName === contractName && contractFunction.method === method
));
};
const ContractOverview = (props) => {
const {contractProfile, contract} = props;
const contractDisplay = formatContractForDisplay(contract);
return (
<Row>
<Col>
<Table
responsive
className="text-nowrap"
>
<thead>
<tr>
<th>Name</th>
<th>Address</th>
<th>State</th>
</tr>
</thead>
<tbody>
<tr className={contractDisplay.stateColor}>
<td>{contract.className}</td>
<td>{contractDisplay.address}</td>
<td>{contractDisplay.state}</td>
</tr>
</tbody>
</Table>
<h2>ABI</h2>
<div className="relative">
<CopyButton text={JSON.stringify(contract.abiDefinition)}
title="Copy bytecode to clipboard"/>
{contract.abiDefinition && <ReactJson src={contract.abiDefinition} theme="monokai" sortKeys={true} collapsed={1} />}
</div>
<h2>Bytecode</h2>
<div className="text-wrap logs relative">
<CopyButton text={contract.runtimeBytecode}
title="Copy bytecode to clipboard"/>
{contract.runtimeBytecode}
</div>
</Col>
</Row>
<div>
{(contractDisplay.state === 'Deployed') && <div>Deployed at {contractDisplay.address}</div>}
{(contractDisplay.state !== 'Deployed') && <div>{contractDisplay.address}</div>}
<br />
{contractProfile.methods
.filter((method) => {
return props.onlyConstructor ? method.type === 'constructor' : method.type !== 'constructor';
})
.map(method => <ContractFunction key={method.name}
method={method}
contractFunctions={filterContractFunctions(props.contractFunctions, contractProfile.name, method.name)}
contractProfile={contractProfile}
postContractFunction={props.postContractFunction} />)}
</div>
);
};
Contract.propTypes = {
contract: PropTypes.object
ContractOverview.propTypes = {
contract: PropTypes.object,
onlyConstructor: PropTypes.bool,
contractProfile: PropTypes.object,
contractFunctions: PropTypes.arrayOf(PropTypes.object),
postContractFunction: PropTypes.func
};
export default Contract;
ContractOverview.defaultProps = {
onlyConstructor: false
};
export default ContractOverview;

View File

@ -5,7 +5,7 @@ import ContractsList from './ContractsList';
const Contracts = ({contracts, title = "Contracts"}) => (
<Row>
<Col>
<Col className="mt-3">
<Card>
<CardBody>
<Row>
@ -13,7 +13,7 @@ const Contracts = ({contracts, title = "Contracts"}) => (
<CardTitle className="mb-0">Contracts</CardTitle>
</Col>
</Row>
<div style={{ marginTop: 40 + 'px' }}>
<div className="mt-5">
<ContractsList contracts={contracts}></ContractsList>
</div>
</CardBody>

View File

@ -10,11 +10,6 @@ import TransactionsContainer from '../containers/TransactionsContainer';
const ExplorerDashboardLayout = () => (
<React.Fragment>
<Row>
<Col>
<h1 className="my-5">Explorer</h1>
</Col>
</Row>
<Row>
<Col>
<AccountsContainer />

View File

@ -1,4 +0,0 @@
.fiddle--grid {
margin-left: -30px;
margin-right: -30px;
}

View File

@ -1,29 +0,0 @@
import React from 'react';
import {Row, Col} from 'reactstrap';
import TextEditorAsideContainer from '../containers/TextEditorAsideContainer';
import TextEditorContainer from '../containers/TextEditorContainer';
import FileExplorerContainer from '../containers/FileExplorerContainer';
import './FiddleLayout.css';
const DEFAULT_FILE = {name: 'newContract.sol', content: ''};
class FiddleLayout extends React.Component {
render() {
return (
<Row noGutters className="h-100 fiddle--grid">
<Col sm={4} md={2}>
<FileExplorerContainer />
</Col>
<Col sm={8} md={6}>
<TextEditorContainer defaultFile={DEFAULT_FILE} />
</Col>
<Col sm={12} md={4}>
<TextEditorAsideContainer defaultFile={DEFAULT_FILE} />
</Col>
</Row>
);
}
}
export default FiddleLayout;

View File

@ -1,7 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import {Treebeard, decorators} from 'react-treebeard';
import {Input, Label, FormGroup} from 'reactstrap';
const style = {
tree: {
@ -107,9 +106,7 @@ decorators.Header = Header;
class FileExplorer extends React.Component {
constructor(props) {
super(props);
this.state = {
showHidden: false
};
this.state = {cursor: null};
}
onToggle(node, toggled) {
@ -122,10 +119,6 @@ class FileExplorer extends React.Component {
this.setState({cursor: node});
}
onHiddenToggle(e) {
this.setState({showHidden: e.target.checked});
}
nodeEquals(a, b) {
return a && b && a.path && b.path && a.name && b.name &&
a.path === b.path &&
@ -138,20 +131,21 @@ class FileExplorer extends React.Component {
b.path.indexOf(a.path) > -1;
}
filterHidden(nodes) {
data(nodes) {
let filtered = [];
if (!Array.isArray(nodes)) return filtered;
const cursor = this.state.cursor;
const showHidden = this.props.showHiddenFiles;
// we need a foreach to build an array instead of a
// filter to prevent mutating the original object (in props)
nodes.forEach(node => {
const {showHidden, cursor} = this.state;
if (!showHidden && node.isHidden) return;
let updatedNode = {...node};
// if it's a folder, filter the children
if (node.children) {
const children = this.filterHidden(node.children);
const children = this.data(node.children);
if (children.length) {
updatedNode.children = children;
}
@ -178,14 +172,8 @@ class FileExplorer extends React.Component {
render() {
return (
<div className="h-100 d-flex flex-column">
<FormGroup check>
<Label check>
<Input type="checkbox" onChange={this.onHiddenToggle.bind(this)} />
Show hidden files
</Label>
</FormGroup>
<Treebeard
data={this.filterHidden(this.props.files)}
data={this.data(this.props.files)}
decorators={decorators}
onToggle={this.onToggle.bind(this)}
style={style}
@ -197,7 +185,8 @@ class FileExplorer extends React.Component {
FileExplorer.propTypes = {
files: PropTypes.array,
fetchFile: PropTypes.func
fetchFile: PropTypes.func,
showHiddenFiles: PropTypes.bool,
};
export default FileExplorer;

View File

@ -131,7 +131,7 @@ class Layout extends React.Component {
<DropdownToggle nav>
<FontAwesome name='bars'/>
</DropdownToggle>
<DropdownMenu left style={{ left: 'auto' }}>
<DropdownMenu>
{HEADER_NAV_ITEMS.map((item) => (
<DropdownItem key={item.to} to={item.to} tag={Link}>
<FontAwesome className="mr-2" name={item.icon} />
@ -220,7 +220,7 @@ class Layout extends React.Component {
{searchResult.error}
</Alert>
<Container fluid className="h-100" style={{marginTop: '24px'}}>
<Container fluid className="h-100">
{children}
</Container>
</main>

View File

@ -29,7 +29,7 @@ Process.propTypes = {
};
const Processes = ({processes}) => (
<Row>
<Row className="mt-3">
{processes.map((process) => <Process key={process.name} process={process} />)}
</Row>
);

View File

@ -7,7 +7,7 @@ import './TextEditor.css';
const SUPPORTED_LANGUAGES = ['css', 'sol', 'html', 'json'];
const DEFAULT_LANGUAGE = 'javascript';
const EDITOR_ID = 'react-monaco-editor-container';
const GUTTER_GLYPH_MARGIN = 3;
const GUTTER_GLYPH_MARGIN = 2;
let editor;
@ -62,19 +62,20 @@ class TextEditor extends React.Component {
}
updateMarkers() {
const {errors, warnings} = this.props.contractCompile;
const markers = [].concat(errors).concat(warnings).filter((e) => e).map((e) => {
const {row, col} = this.extractRowCol(e.formattedMessage);
return {
startLineNumber: row,
startColumn: col,
endLineNumber: row,
endColumn: col + 1,
message: e.formattedMessage,
severity: e.severity
};
});
monaco.editor.setModelMarkers(editor.getModel(), 'test', markers);
// TODO: Fetch Result of compilation in embark, via ws
// const {errors, warnings} = this.props.contractCompile;
// const markers = [].concat(errors).concat(warnings).filter((e) => e).map((e) => {
// const {row, col} = this.extractRowCol(e.formattedMessage);
// return {
// startLineNumber: row,
// startColumn: col,
// endLineNumber: row,
// endColumn: col + 1,
// message: e.formattedMessage,
// severity: e.severity
// };
// });
// monaco.editor.setModelMarkers(editor.getModel(), 'test', markers);
}
updateLanguage() {
@ -121,6 +122,7 @@ class TextEditor extends React.Component {
this.updateDecorations();
}
this.updateLanguage();
this.handleResize();
}
render() {
@ -135,7 +137,6 @@ class TextEditor extends React.Component {
TextEditor.propTypes = {
onFileContentChange: PropTypes.func,
file: PropTypes.object,
contractCompile: PropTypes.object,
toggleBreakpoint: PropTypes.func,
breakpoints: PropTypes.array,
debuggerLine: PropTypes.number

View File

@ -1,38 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import {Card, CardHeader, CardTitle, CardBody} from 'reactstrap';
import FontAwesomeIcon from 'react-fontawesome';
import ContractFunctions from '../components/ContractFunctions';
const TextEditorContractDeploy = (props) => {
const name = Object.keys(props.result)[0];
const profile = {
name,
methods: props.result[name].abiDefinition
};
return (
<Card className="bg-success">
<CardHeader>
<CardTitle>
<FontAwesomeIcon className="mr-1" name="check"/>
Deploy Contract
</CardTitle>
</CardHeader>
<CardBody>
<ContractFunctions contractProfile={profile}
contractFunctions={props.contractDeploys}
onlyConstructor
postContractFunction={props.postContractDeploy}/>
</CardBody>
</Card>
);
};
TextEditorContractDeploy.propTypes = {
result: PropTypes.object,
postContractDeploy: PropTypes.func,
contractDeploys: PropTypes.array
};
export default TextEditorContractDeploy;

View File

@ -1,26 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import {Card, CardBody, CardHeader, CardTitle, ListGroup, ListGroupItem} from 'reactstrap';
import FontAwesomeIcon from 'react-fontawesome';
const TextEditorContractErrors = (props) => (
<Card className="bg-danger">
<CardHeader>
<CardTitle>
<FontAwesomeIcon className="mr-1" name="minus-circle"/>
Failed to compile
</CardTitle>
</CardHeader>
<CardBody>
<ListGroup>
{props.errors.map((error, index) => <ListGroupItem key={index}>{error.formattedMessage}</ListGroupItem>)}
</ListGroup>
</CardBody>
</Card>
);
TextEditorContractErrors.propTypes = {
errors: PropTypes.array
};
export default TextEditorContractErrors;

View File

@ -1,32 +0,0 @@
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {Col, Badge} from 'reactstrap';
import FontAwesomeIcon from 'react-fontawesome';
import TextEditorToolbar from './TextEditorToolbar';
class TextEditorContractToolbar extends Component {
render(){
return (
<React.Fragment>
<TextEditorToolbar {...this.props} />
<Col md={6} className="text-right">
{this.props.compilingContract &&
<Badge color="warning"><FontAwesomeIcon name="exclamation-triangle " className="mr-1" />compiling</Badge>}
{!this.props.compilingContract && this.props.contractCompile.result &&
<Badge color="success"><FontAwesomeIcon name="check" className="mr-1" />compiled</Badge>}
</Col>
</React.Fragment>
);
}
}
TextEditorContractToolbar.propTypes = {
currentFile: PropTypes.object,
contractCompile: PropTypes.object,
compilingContract: PropTypes.bool,
deploy: PropTypes.func,
save: PropTypes.func,
remove: PropTypes.func
};
export default TextEditorContractToolbar;

View File

@ -1,26 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import {Card, CardHeader, CardBody, CardTitle, ListGroup, ListGroupItem} from 'reactstrap';
import FontAwesomeIcon from 'react-fontawesome';
const TextEditorContractWarnings = (props) => (
<Card className="bg-warning">
<CardHeader>
<CardTitle color="warning">
<FontAwesomeIcon className="mr-1" name="exclamation-triangle"/>
Warning during compilation
</CardTitle>
</CardHeader>
<CardBody>
<ListGroup>
{props.warnings.map((warning, index) => <ListGroupItem key={index}>{warning.formattedMessage}</ListGroupItem>)}
</ListGroup>
</CardBody>
</Card>
);
TextEditorContractWarnings.propTypes = {
warnings: PropTypes.array
};
export default TextEditorContractWarnings;

View File

@ -1,21 +1,63 @@
import React from 'react';
import PropTypes from 'prop-types';
import {Col, Button} from 'reactstrap';
import {Row, Label, Col, Button} from 'reactstrap';
import FontAwesomeIcon from 'react-fontawesome';
import { AppSwitch } from '@coreui/react'
const TextEditorToolbar = (props) => (
<Col md={6}>
<strong>{props.currentFile.name}</strong>
<span className="mx-2">|</span>
<Button color="green" size="sm" icon="save" onClick={props.save}>Save</Button>
<span className="mx-2">|</span>
<Button color="red" size="sm" icon="delete" onClick={props.remove}>Delete</Button>
</Col>
<Row>
<Col sm={4} md={2}>
<Label className="mb-0 pt-1">
<AppSwitch color='success' variant='pill' size='sm' onChange={props.toggleShowHiddenFiles}/>
<span className="ml-1 align-top">Show hidden files</span>
</Label>
</Col>
<Col sm={4} md={6}>
<strong>{props.currentFile.name}</strong>
<span className="mx-2">|</span>
<Button color="success" size="sm" onClick={props.save}>
<FontAwesomeIcon className="mr-2" name="save"/>
Save
</Button>
<span className="mx-2">|</span>
<Button color="danger" size="sm" onClick={props.remove}>
<FontAwesomeIcon className="mr-2" name="trash"/>
Delete
</Button>
</Col>
<Col sm={4} md={4}>
<div className="float-right mr-2">
{props.isContract &&
<React.Fragment>
<Button size="sm" color="primary" onClick={() => props.openAsideTab('overview')}>
Overview
</Button>
<span className="mx-2">|</span>
<Button size="sm" color="primary" onClick={() => props.openAsideTab('detail')}>
Detail
</Button>
<span className="mx-2">|</span>
<Button size="sm" color="primary" onClick={() => props.openAsideTab('logger')}>
Logger
</Button>
<span className="mx-2">|</span>
</React.Fragment>
}
<Button size="sm" color="primary" onClick={() => props.openAsideTab('browser')}>
Browser
</Button>
</div>
</Col>
</Row>
);
TextEditorToolbar.propTypes = {
currentFile: PropTypes.object,
isContract: PropTypes.bool,
save: PropTypes.func,
remove: PropTypes.func
remove: PropTypes.func,
toggleShowHiddenFiles: PropTypes.func,
openAsideTab: PropTypes.func
};
export default TextEditorToolbar;

View File

@ -5,6 +5,8 @@ import PropTypes from 'prop-types';
import Description from './Description';
import CardTitleIdenticon from './CardTitleIdenticon';
import {utils} from 'web3';
const Transaction = ({transaction}) => (
<Row>
@ -18,7 +20,7 @@ const Transaction = ({transaction}) => (
<Description label="Block" value={<Link to={`/embark/explorer/blocks/${transaction.blockNumber}`}>{transaction.blockNumber}</Link>} />
<Description label="From" value={transaction.from} />
<Description label="To" value={transaction.to} />
<Description label="Value" value={`${transaction.value} Wei`}/>
<Description label="Value" value={`${utils.fromWei(transaction.value)} Ether`}/>
<Description label="Input" value={transaction.input} />
<Description label="Gas" value={transaction.gas} />
<Description label="Gas price" value={`${transaction.gasPrice} Wei`} />

View File

@ -12,9 +12,11 @@ const Transactions = ({transactions}) => (
{transactions.map(transaction => (
<Card key={transaction.hash}>
<CardHeader>
<Link to={`/embark/explorer/transactions/${transaction.hash}`}>
<CardTitleIdenticon id={transaction.hash}>Transaction {transaction.hash}</CardTitleIdenticon>
</Link>
<CardTitleIdenticon id={transaction.hash}>Transaction&nbsp;
<Link to={`/embark/explorer/transactions/${transaction.hash}`}>
{transaction.hash}
</Link>
</CardTitleIdenticon>
</CardHeader>
<CardBody>
<Row>

View File

@ -1,36 +0,0 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import PropTypes from 'prop-types';
import {withRouter} from 'react-router-dom';
import Contract from '../components/Contract';
import DataWrapper from "../components/DataWrapper";
import {getContract} from "../reducers/selectors";
class ContractContainer extends Component {
render() {
return (
<DataWrapper shouldRender={this.props.contract !== undefined } {...this.props} render={({contract}) => (
<Contract contract={contract} />
)} />
);
}
}
function mapStateToProps(state, props) {
return {
contract: getContract(state, props.match.params.contractName),
error: state.errorMessage,
loading: state.loading
};
}
ContractContainer.propTypes = {
match: PropTypes.object,
contract: PropTypes.object,
error: PropTypes.string
};
export default withRouter(connect(
mapStateToProps
)(ContractContainer));

View File

@ -3,12 +3,12 @@ import {connect} from 'react-redux';
import PropTypes from 'prop-types';
import {contractProfile as contractProfileAction, contractFunction as contractFunctionAction} from '../actions';
import ContractFunctions from '../components/ContractFunctions';
import ContractOverview from '../components/ContractOverview';
import DataWrapper from "../components/DataWrapper";
import GasStationContainer from "../containers/GasStationContainer";
import {getContractProfile, getContractFunctions} from "../reducers/selectors";
class ContractFunctionsContainer extends Component {
class ContractOverviewContainer extends Component {
componentDidMount() {
this.props.fetchContractProfile(this.props.contract.className);
}
@ -19,8 +19,9 @@ class ContractFunctionsContainer extends Component {
{...this.props}
render={({contractProfile, contractFunctions, postContractFunction}) => (
<React.Fragment>
<ContractFunctions contractProfile={contractProfile}
<ContractOverview contractProfile={contractProfile}
contractFunctions={contractFunctions}
contract={this.props.contract}
postContractFunction={postContractFunction}/>
<GasStationContainer/>
@ -39,7 +40,7 @@ function mapStateToProps(state, props) {
};
}
ContractFunctionsContainer.propTypes = {
ContractOverviewContainer.propTypes = {
contract: PropTypes.object,
contractProfile: PropTypes.object,
contractFunctions: PropTypes.arrayOf(PropTypes.object),
@ -54,4 +55,4 @@ export default connect(
fetchContractProfile: contractProfileAction.request,
postContractFunction: contractFunctionAction.post
}
)(ContractFunctionsContainer);
)(ContractOverviewContainer);

View File

@ -0,0 +1,4 @@
.editor--grid {
margin-left: -30px !important;
margin-right: -30px !important;
}

View File

@ -0,0 +1,103 @@
import React from 'react';
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import {Row, Col} from 'reactstrap';
import TextEditorAsideContainer from './TextEditorAsideContainer';
import TextEditorContainer from './TextEditorContainer';
import FileExplorerContainer from './FileExplorerContainer';
import TextEditorToolbarContainer from './TextEditorToolbarContainer';
import {currentFile as currentFileAction} from '../actions';
import {getCurrentFile} from '../reducers/selectors';
import './EditorContainer.css';
const DEFAULT_FILE = {name: 'newContract.sol', content: ''};
class EditorContainer extends React.Component {
constructor(props) {
super(props)
this.state = {currentAsideTab: '', showHiddenFiles: false, currentFile: this.props.currentFile}
}
componentDidMount() {
if(this.props.currentFile.content === '') {
this.props.fetchCurrentFile();
}
}
componentDidUpdate(prevProps) {
if(this.props.currentFile.path !== prevProps.currentFile.path) {
this.setState({currentFile: this.props.currentFile});
}
}
isContract() {
return this.state.currentFile.name.endsWith('.sol');
}
onFileContentChange(newContent) {
const newCurrentFile = this.state.currentFile;
newCurrentFile.content = newContent;
this.setState({currentFile: newCurrentFile});
}
toggleShowHiddenFiles() {
this.setState({showHiddenFiles: !this.state.showHiddenFiles});
}
openAsideTab(newTab) {
if (newTab === this.state.currentAsideTab) {
return this.setState({currentAsideTab: ''})
}
this.setState({currentAsideTab: newTab})
}
textEditorMdSize() {
return this.state.currentAsideTab.length ? 5 : 10
}
textEditorXsSize() {
return this.state.currentAsideTab.length ? 2 : 8
}
render() {
return (
<Row noGutters className="h-100 editor--grid">
<Col xs={12}>
<TextEditorToolbarContainer toggleShowHiddenFiles={() => this.toggleShowHiddenFiles()}
openAsideTab={(newTab) => this.openAsideTab(newTab)}
isContract={this.isContract()}
currentFile={this.props.currentFile} />
</Col>
<Col xs={4} md={2}>
<FileExplorerContainer showHiddenFiles={this.state.showHiddenFiles} />
</Col>
<Col xs={this.textEditorXsSize()} md={this.textEditorMdSize()}>
<TextEditorContainer currentFile={this.props.currentFile} onFileContentChange={(newContent)=> this.onFileContentChange(newContent)} />
</Col>
{this.state.currentAsideTab && <Col xs={6} md={5}>
<TextEditorAsideContainer currentAsideTab={this.state.currentAsideTab} currentFile={this.props.currentFile} />
</Col>}
</Row>
);
}
}
function mapStateToProps(state, props) {
const currentFile = getCurrentFile(state) || DEFAULT_FILE;
return {
currentFile
};
}
EditorContainer.propTypes = {
currentFile: PropTypes.object,
fetchCurrentFile: PropTypes.func
};
export default connect(
mapStateToProps,
{fetchCurrentFile: currentFileAction.request},
)(EditorContainer);

View File

@ -1,41 +0,0 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import PropTypes from 'prop-types';
import {contracts as contractsAction} from '../actions';
import ContractLayout from '../components/ContractLayout';
import {getContractsByPath} from "../reducers/selectors";
class FileContractsContainer extends Component {
componentDidMount() {
this.props.fetchContracts();
}
render() {
return (
this.props.contracts.map(contract => <ContractLayout contract={contract} />)
)
}
}
function mapStateToProps(state, props) {
return {
contracts: getContractsByPath(state, props.currentFile.path),
error: state.errorMessage,
loading: state.loading
};
}
FileContractsContainer.propTypes = {
contracts: PropTypes.arrayOf(PropTypes.object),
fetchContractsByPath: PropTypes.func,
error: PropTypes.string,
loading: PropTypes.bool
};
export default connect(
mapStateToProps,
{
fetchContracts: contractsAction.request
}
)(FileContractsContainer);

View File

@ -14,21 +14,22 @@ class FileExplorerContainer extends Component {
render() {
return (
<DataWrapper shouldRender={this.props.files.length > 0} {...this.props} render={({files, fetchFile}) => (
<FileExplorer files={files} fetchFile={fetchFile} />
<DataWrapper shouldRender={this.props.files.length > 0} {...this.props} render={({files, fetchFile, showHiddenFiles}) => (
<FileExplorer files={files} fetchFile={fetchFile} showHiddenFiles={showHiddenFiles} />
)} />
);
}
}
function mapStateToProps(state) {
return {files: getFiles(state), error: state.errorMessage, loading: state.loading};
return {files: getFiles(state)};
}
FileExplorerContainer.propTypes = {
files: PropTypes.array,
fetchFiles: PropTypes.func,
fetchFile: PropTypes.func
fetchFile: PropTypes.func,
showHiddenFiles: PropTypes.bool,
};
export default connect(

View File

@ -1,59 +1,79 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import PropTypes from 'prop-types';
import {
currentFile as currentFileAction,
} from '../actions';
import {getCurrentFile} from '../reducers/selectors';
import { Card, CardBody, CardTitle } from 'reactstrap';
import Preview from '../components/Preview';
import FileContractsContainer from './FileContractsContainer';
import {contracts as contractsAction} from '../actions';
import {getContractsByPath} from "../reducers/selectors";
import ContractDetail from '../components/ContractDetail';
import ContractLoggerContainer from '../containers/ContractLoggerContainer';
import ContractOverviewContainer from '../containers/ContractOverviewContainer';
class TextEditorAsideContainer extends Component {
constructor(props) {
super(props);
this.state = {currentFile: this.props.currentFile};
}
componentDidMount() {
this.props.fetchCurrentFile();
}
componentDidUpdate(prevProps) {
if(this.props.currentFile.path !== prevProps.currentFile.path) {
this.setState({currentFile: this.props.currentFile});
}
}
isContract() {
return this.state.currentFile.name.endsWith('.sol');
this.props.fetchContracts();
}
render() {
return this.isContract() ? <FileContractsContainer currentFile={this.state.currentFile} /> : <Preview />
switch(this.props.currentAsideTab) {
case 'browser':
return <Preview />
case 'detail':
return this.props.contracts.map((contract, index) => {
return (
<Card>
<CardBody>
<CardTitle style={{"font-size": "2em"}}>{contract.className} - Details</CardTitle>
<ContractDetail key={index} contract={contract} />
</CardBody>
</Card>
)
})
case 'logger':
return this.props.contracts.map((contract, index) => {
return (
<Card>
<CardBody>
<CardTitle style={{"font-size": "2em"}}>{contract.className} - Transactions</CardTitle>
<ContractLoggerContainer key={index} contract={contract} />)
</CardBody>
</Card>
)
})
case 'overview':
return this.props.contracts.map((contract, index) => {
return (
<Card>
<CardBody>
<CardTitle style={{"font-size": "2em"}}>{contract.className} - Overview</CardTitle>
<ContractOverviewContainer key={index} contract={contract} />
</CardBody>
</Card>
)
})
default:
return <React.Fragment></React.Fragment>;
}
}
}
function mapStateToProps(state, props) {
const currentFile = getCurrentFile(state) || props.defaultFile
return {
currentFile,
loading: state.loading,
error: state.errorMessage
contracts: getContractsByPath(state, props.currentFile.path)
};
}
TextEditorAsideContainer.propTypes = {
currentFile: PropTypes.object,
fetchCurrentFile: PropTypes.func,
loading: PropTypes.bool,
error: PropTypes.string,
defaultFile: PropTypes.object
currentAsideTab: PropTypes.string,
contract: PropTypes.array,
fetchContracts: PropTypes.func
};
export default connect(
mapStateToProps,
{
fetchCurrentFile: currentFileAction.request,
fetchContracts: contractsAction.request,
},
)(TextEditorAsideContainer);

View File

@ -1,157 +1,32 @@
import React, {Component} from 'react';
import React from 'react';
import {connect} from 'react-redux';
import PropTypes from 'prop-types';
import TextEditor from '../components/TextEditor';
import TextEditorContractErrors from '../components/TextEditorContractErrors';
import TextEditorContractWarnings from '../components/TextEditorContractWarnings';
import TextEditorContractToolbar from '../components/TextEditorContractToolbar';
import TextEditorContractDeploy from '../components/TextEditorContractDeploy';
import TextEditorToolbar from '../components/TextEditorToolbar';
import {
currentFile as currentFileAction,
saveCurrentFile as saveCurrentFileAction,
saveFile as saveFileAction,
removeFile as removeFileAction,
contractCompile as contractCompileAction,
contractDeploy as postContractDeploy,
toggleBreakpoint,
} from '../actions';
import {getCurrentFile, getContractCompile, getContractDeploys, getBreakpointsByFilename} from '../reducers/selectors';
import {getBreakpointsByFilename} from '../reducers/selectors';
class TextEditorContainer extends Component {
constructor(props) {
super(props);
this.state = {currentFile: this.props.currentFile};
}
componentDidMount() {
if(this.props.currentFile.content === '') {
this.props.fetchCurrentFile();
}
}
componentDidUpdate(prevProps) {
if(this.props.currentFile.path !== prevProps.currentFile.path) {
this.setState({currentFile: this.props.currentFile});
}
}
isContract() {
return this.state.currentFile.name.endsWith('.sol');
}
onFileContentChange(newContent) {
const newCurrentFile = this.state.currentFile;
newCurrentFile.content = newContent;
this.setState({currentFile: newCurrentFile});
if (!this.isContract()) return;
this.compileTimeout = setTimeout(() => {
this.props.compileContract(newContent, this.state.currentFile.name);
}, 1000);
}
save() {
this.props.saveFile(this.state.currentFile);
this.props.saveCurrentFile(this.state.currentFile);
}
remove() {
this.props.removeFile(this.state.currentFile);
this.setState({currentFile: this.props.defaultFile});
}
renderContractFooter() {
if (!this.isContract()) return <React.Fragment />;
let components = [];
const {errors, warnings, result} = this.props.contractCompile;
if (errors && errors.length > 0) {
components.push(<TextEditorContractErrors key={1} errors={errors} />);
}
if (warnings && warnings.length > 0) {
components.push(<TextEditorContractWarnings key={2} warnings={warnings} />);
}
if (result) {
components.push(<TextEditorContractDeploy key={3}
result={result}
postContractDeploy={this.props.postContractDeploy}
contractDeploys={this.props.contractDeploys}/>);
}
return <React.Fragment>{components}</React.Fragment>;
}
renderToolbar(){
if (this.isContract()) {
return <TextEditorContractToolbar currentFile={this.state.currentFile}
contractCompile={this.props.contractCompile}
compilingContract={this.props.compilingContract}
save={() => this.save()}
remove={() => this.remove()} />;
}
return <TextEditorToolbar currentFile={this.state.currentFile}
contractCompile={this.props.contractCompile}
save={() => this.save()}
remove={() => this.remove()} />;
}
render() {
return (
<TextEditor file={this.state.currentFile}
contractCompile={this.props.contractCompile}
breakpoints={this.props.breakpoints}
toggleBreakpoint={this.props.toggleBreakpoint}
onFileContentChange={(newContent) => this.onFileContentChange(newContent)} />
);
}
}
const TextEditorContainer = (props) => (
<TextEditor file={props.currentFile}
breakpoints={props.breakpoints}
toggleBreakpoint={props.toggleBreakpoint}
onFileContentChange={props.onFileContentChange} />
)
function mapStateToProps(state, props) {
const currentFile = getCurrentFile(state) || props.defaultFile;
const contractCompile = getContractCompile(state, currentFile) || {};
const contractName = contractCompile.result && Object.keys(contractCompile.result)[0];
const contractDeploys = getContractDeploys(state, contractName);
const breakpoints = getBreakpointsByFilename(state, currentFile.name);
return {
compilingContract: state.compilingContract,
loading: state.loading,
error: state.errorMessage,
currentFile,
contractCompile,
contractDeploys,
breakpoints,
};
const breakpoints = getBreakpointsByFilename(state, props.currentFile.name);
return {breakpoints};
}
TextEditorContainer.propTypes = {
defaultFile: PropTypes.object,
currentFile: PropTypes.object,
contractCompile: PropTypes.object,
saveCurrentFile: PropTypes.func,
fetchCurrentFile: PropTypes.func,
saveFile: PropTypes.func,
removeFile: PropTypes.func,
postContractDeploy: PropTypes.func,
compileContract: PropTypes.func,
compilingContract: PropTypes.bool,
contractDeploys: PropTypes.array,
loading: PropTypes.bool,
error: PropTypes.string,
onFileContentChange: PropTypes.func,
toggleBreakpoints: PropTypes.func,
breakpoints: PropTypes.array,
};
export default connect(
mapStateToProps,
{
fetchCurrentFile: currentFileAction.request,
saveCurrentFile: saveCurrentFileAction.request,
saveFile: saveFileAction.request,
removeFile: removeFileAction.request,
postContractDeploy: postContractDeploy.post,
compileContract: contractCompileAction.post,
toggleBreakpoint,
},
{toggleBreakpoint},
)(TextEditorContainer);

View File

@ -0,0 +1,45 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import PropTypes from 'prop-types';
import TextEditorToolbar from '../components/TextEditorToolbar';
import {
saveFile as saveFileAction,
removeFile as removeFileAction,
} from '../actions';
class TextEditorToolbarContainer extends Component {
save() {
this.props.saveFile(this.props.currentFile);
}
remove() {
this.props.removeFile(this.props.currentFile);
}
render() {
return <TextEditorToolbar currentFile={this.props.currentFile}
isContract={this.props.isContract}
toggleShowHiddenFiles={this.props.toggleShowHiddenFiles}
openAsideTab={this.props.openAsideTab}
save={() => this.save()}
remove={() => this.remove()} />;
}
}
TextEditorToolbarContainer.propTypes = {
currentFile: PropTypes.object,
isContract: PropTypes.bool,
saveFile: PropTypes.func,
removeFile: PropTypes.func,
toggleShowHiddenFiles: PropTypes.func,
openAsideTab: PropTypes.func
};
export default connect(
null,
{
saveFile: saveFileAction.request,
removeFile: removeFileAction.request
},
)(TextEditorToolbarContainer);

View File

@ -1,6 +1,6 @@
.nav-tabs {
.nav-link {
color: $gray-600;
color: $white;
&:hover {
cursor: pointer;
}

View File

@ -33,7 +33,7 @@ $navbar-brand-minimized-width: 50px !default;
$navbar-brand-minimized-bg: $navbar-brand-bg !default;
$navbar-brand-minimized-border: $navbar-brand-border !default;
$navbar-color: $gray-300 !default;
$navbar-color: $white !default;
$navbar-hover-color: $gray-200 !default;
$navbar-active-color: $gray-200 !default;
$navbar-disabled-color: $gray-300 !default;

View File

@ -15,7 +15,7 @@
}
.text-wrap {
word-wrap: break-word;
word-wrap: break-word;
}
.relative {
@ -28,3 +28,11 @@
.react-json-view {
border-radius: .25rem;
}
.collapsable:hover {
cursor: pointer;
}
.contractFunction {
margin-bottom: 0px;
}

View File

@ -4,11 +4,11 @@ import {Route, Switch} from 'react-router-dom';
import HomeContainer from './containers/HomeContainer';
import ContractsContainer from './containers/ContractsContainer';
import ContractLayoutContainer from './containers/ContractLayoutContainer';
import EditorContainer from './containers/EditorContainer';
import DeploymentContainer from './containers/DeploymentContainer';
import NoMatch from './components/NoMatch';
import ExplorerDashboardLayout from './components/ExplorerDashboardLayout';
import ExplorerLayout from './components/ExplorerLayout';
import FiddleLayout from './components/FiddleLayout';
import UtilsLayout from './components/UtilsLayout';
const routes = (
@ -20,7 +20,7 @@ const routes = (
<Route path="/embark/deployment/" component={DeploymentContainer} />
<Route path="/embark/contracts/:contractName" component={ContractLayoutContainer} />
<Route path="/embark/contracts" component={ContractsContainer} />
<Route path="/embark/editor" component={FiddleLayout} />
<Route path="/embark/editor" component={EditorContainer} />
<Route path="/embark/utilities" component={UtilsLayout} />
<Route component={NoMatch} />
</Switch>

View File

@ -200,6 +200,10 @@ export function *watchPostFile() {
yield takeEvery(actions.SAVE_FILE[actions.REQUEST], postFile);
}
export function *watchPostFileSuccess() {
yield takeEvery(actions.SAVE_FILE[actions.SUCCESS], postCurrentFile);
}
export function *watchDeleteFile() {
yield takeEvery(actions.REMOVE_FILE[actions.REQUEST], deleteFile);
}
@ -217,10 +221,6 @@ export function *watchFetchCurrentFile() {
yield takeEvery(actions.CURRENT_FILE[actions.REQUEST], fetchCurrentFile);
}
export function *watchPostCurrentFile() {
yield takeEvery(actions.SAVE_CURRENT_FILE[actions.REQUEST], postCurrentFile);
}
export function *watchFetchEthGas() {
yield takeEvery(actions.GAS_ORACLE[actions.REQUEST], fetchEthGas);
}
@ -433,7 +433,7 @@ export default function *root() {
fork(watchDeleteFileSuccess),
fork(watchFetchFileSuccess),
fork(watchFetchCurrentFile),
fork(watchPostCurrentFile),
fork(watchPostFileSuccess),
fork(watchFetchCredentials),
fork(watchFetchEthGas),
fork(watchAuthenticate),