Refactor logic to FiddleContainer
Refactored fiddle logic to be contained in the `FiddleContainer` and the components as purely presentational. Added scroll from summary to errors/warnings/fatal/deployed cards. Added fatal error support (ie network error in api) Removed `lodash`
This commit is contained in:
parent
05b324dffe
commit
6207023bec
|
@ -8,7 +8,6 @@
|
|||
"classnames": "^2.2.6",
|
||||
"connected-react-router": "^4.3.0",
|
||||
"history": "^4.7.2",
|
||||
"lodash": "^4.17.10",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^16.4.1",
|
||||
"react-ace": "^6.1.4",
|
||||
|
|
|
@ -14,7 +14,7 @@ import ContractDeploymentContainer from '../containers/ContractDeploymentContain
|
|||
import ContractProfileContainer from '../containers/ContractProfileContainer';
|
||||
import ContractSourceContainer from '../containers/ContractSourceContainer';
|
||||
|
||||
const ContractLayout = ({match, contract}) => (
|
||||
const ContractLayout = ({match, contractIsFiddle = false}) => (
|
||||
<Grid.Row>
|
||||
<Grid.Col md={3}>
|
||||
<Page.Title className="my-5"> </Page.Title>
|
||||
|
@ -28,7 +28,7 @@ const ContractLayout = ({match, contract}) => (
|
|||
>
|
||||
Overview
|
||||
</List.GroupItem>
|
||||
{!contract.isFiddle ?
|
||||
{!contractIsFiddle &&
|
||||
<List.GroupItem
|
||||
className="d-flex align-items-center"
|
||||
to={`/embark/contracts/${match.params.contractName}/deployment`}
|
||||
|
@ -37,8 +37,6 @@ const ContractLayout = ({match, contract}) => (
|
|||
>
|
||||
Deployment / Utils
|
||||
</List.GroupItem>
|
||||
:
|
||||
''
|
||||
}
|
||||
<List.GroupItem
|
||||
className="d-flex align-items-center"
|
||||
|
@ -89,7 +87,8 @@ const ContractLayout = ({match, contract}) => (
|
|||
);
|
||||
|
||||
ContractLayout.propTypes = {
|
||||
match: PropTypes.object
|
||||
match: PropTypes.object,
|
||||
contractIsFiddle: PropTypes.bool
|
||||
};
|
||||
|
||||
export default withRouter(ContractLayout);
|
||||
|
|
|
@ -1,159 +1,25 @@
|
|||
/* eslint {jsx-a11y/anchor-has-content:"off"} */
|
||||
import React, {Component} from 'react';
|
||||
import {Card, List, Badge, Icon, Dimmer, Button} from 'tabler-react';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import {NavLink} from 'react-router-dom';
|
||||
|
||||
class FiddleResults extends Component {
|
||||
|
||||
constructor(props){
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
errorsCollapsed: false,
|
||||
warningsCollapsed: false,
|
||||
errorsFullscreen: false,
|
||||
warningsFullscreen: false
|
||||
};
|
||||
}
|
||||
|
||||
_toggle(e, type){
|
||||
const className = e.currentTarget.parentElement.className.replace('card-options', '').replace(' ', '');
|
||||
const updatedState = {};
|
||||
updatedState[className + type] = !(this.state[className + type]);
|
||||
this.setState(updatedState);
|
||||
}
|
||||
|
||||
_getFormatted(errors, errorType, loading){
|
||||
const color = (errorType === "error" ? "danger" : errorType);
|
||||
const isFullscreen = Boolean(this.state[errorType + 'sFullscreen']);
|
||||
const classes = classNames({
|
||||
'card-fullscreen': Boolean(this.state[errorType + 'sFullscreen']),
|
||||
'card-collapsed': Boolean(this.state[errorType + 'sCollapsed']) && !isFullscreen
|
||||
});
|
||||
return <Card
|
||||
isCollapsible={true}
|
||||
isFullscreenable={true}
|
||||
statusColor={color}
|
||||
statusSide="true"
|
||||
className={errorType + "s-card " + classes}
|
||||
key={errorType + "s-card"}>
|
||||
<Card.Header>
|
||||
<Card.Title color={color}>{errorType + "s"} <Badge color={color}>{errors.length}</Badge></Card.Title>
|
||||
<Card.Options className={errorType + "s"}>
|
||||
<Card.OptionsItem key="0" type="collapse" icon="chevron-up" onClick={(e) => this._toggle(e, 'Collapsed')}/>
|
||||
<Card.OptionsItem key="1" type="fullscreen" icon="maximize" onClick={(e) => this._toggle(e, 'Fullscreen')} />
|
||||
</Card.Options>
|
||||
</Card.Header>
|
||||
<Card.Body>
|
||||
<Dimmer active={loading ? "active" : ""} loader>
|
||||
<List.Group>
|
||||
{errors.map(error => { return error.node; })}
|
||||
</List.Group>
|
||||
</Dimmer>
|
||||
</Card.Body>
|
||||
</Card>;
|
||||
}
|
||||
|
||||
render() {
|
||||
const {warnings, errors, fatalFiddle, fatalFiddleDeploy, isLoading, deployedContracts} = this.props;
|
||||
const hasFatal = fatalFiddle || fatalFiddleDeploy;
|
||||
let renderings = [];
|
||||
if(hasFatal){
|
||||
if(fatalFiddle){
|
||||
renderings.push(
|
||||
<React.Fragment key="fatal-compile">
|
||||
<a id="fatal-compile" aria-hidden="true"/>
|
||||
<Card
|
||||
statusColor="danger"
|
||||
statusSide="true"
|
||||
className="fatal-card">
|
||||
<Card.Header>
|
||||
<Card.Title color="danger"><Icon name="slash"/> Failed to compile</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Body>
|
||||
<Dimmer active={isLoading ? "active" : ""} loader>
|
||||
{fatalFiddle}
|
||||
</Dimmer>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
if(fatalFiddleDeploy){
|
||||
renderings.push(
|
||||
<React.Fragment key="fatal-deploy">
|
||||
<a id="fatal-deploy" aria-hidden="true"/>
|
||||
<Card
|
||||
statusColor="danger"
|
||||
statusSide="true"
|
||||
className="fatal-card">
|
||||
<Card.Header>
|
||||
<Card.Title color="danger"><Icon name="slash"/> Failed to deploy</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Body>
|
||||
<Dimmer active={isLoading ? "active" : ""} loader>
|
||||
{fatalFiddleDeploy}
|
||||
</Dimmer>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
else if (deployedContracts){
|
||||
renderings.push(
|
||||
<Card
|
||||
statusColor="success"
|
||||
statusSide="true"
|
||||
className="success-card"
|
||||
key="success-card">
|
||||
<Card.Header>
|
||||
<Card.Title color="success"><Icon name="check"/> Contract(s) deployed!</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Body>
|
||||
<Dimmer active={isLoading ? "active" : ""} loader>
|
||||
<Button
|
||||
to={`/embark/contracts/${deployedContracts}/overview`}
|
||||
RootComponent={NavLink}
|
||||
>Play with my contract(s)</Button>
|
||||
</Dimmer>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
else{
|
||||
if (errors.length) renderings.push(
|
||||
<React.Fragment key="errors">
|
||||
<a id="errors" aria-hidden="true"/>
|
||||
{this._getFormatted(errors, "error", isLoading)}
|
||||
</React.Fragment>
|
||||
);
|
||||
if (warnings.length) renderings.push(
|
||||
<React.Fragment key="warnings">
|
||||
<a id="warnings" aria-hidden="true"/>
|
||||
{this._getFormatted(warnings, "warning", isLoading)}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{renderings}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
const FiddleResults = ({warningsCard, errorsCard, fatalFiddleCard, fatalFiddleDeployCard, deployedContractsCard, fatalErrorCard, forwardedRef}) => (
|
||||
<div ref={forwardedRef}>
|
||||
{fatalErrorCard}
|
||||
{fatalFiddleCard}
|
||||
{fatalFiddleDeployCard}
|
||||
{deployedContractsCard}
|
||||
{errorsCard}
|
||||
{warningsCard}
|
||||
</div>
|
||||
);
|
||||
|
||||
FiddleResults.propTypes = {
|
||||
errors: PropTypes.array,
|
||||
warnings: PropTypes.array,
|
||||
fatalFiddle: PropTypes.string,
|
||||
fatalFiddleDeploy: PropTypes.string,
|
||||
isLoading: PropTypes.bool,
|
||||
deployedContracts: PropTypes.string
|
||||
errorsCard: PropTypes.node,
|
||||
warningsCard: PropTypes.node,
|
||||
fatalFiddleCard: PropTypes.node,
|
||||
fatalFiddleDeployCard: PropTypes.node,
|
||||
deployedContractsCard: PropTypes.node,
|
||||
fatalErrorCard: PropTypes.node,
|
||||
forwardedRef: PropTypes.any
|
||||
};
|
||||
|
||||
export default FiddleResults;
|
||||
|
|
|
@ -1,71 +1,69 @@
|
|||
import React, {Component} from 'react';
|
||||
import {Badge, Icon} from 'tabler-react';
|
||||
import {Badge, Icon, Loader} from 'tabler-react';
|
||||
import PropTypes from 'prop-types';
|
||||
import FiddleDeployButton from './FiddleDeployButton';
|
||||
import classNames from 'classnames';
|
||||
|
||||
class FiddleResultsSummary extends Component{
|
||||
class FiddleResultsSummary extends Component {
|
||||
|
||||
render(){
|
||||
const {warnings, errors, isLoading, loadingMessage, hasResult, fatalFiddle, fatalFiddleDeploy} = this.props;
|
||||
let renderings = [];
|
||||
if(isLoading){
|
||||
renderings.push(
|
||||
<React.Fragment key="loading"><div className="loader"></div><span className="loader-text">{loadingMessage}</span></React.Fragment>
|
||||
);
|
||||
}
|
||||
if(fatalFiddle) {
|
||||
renderings.push(
|
||||
<React.Fragment key="errors">
|
||||
<a className="badge-link" href="#fatal-compile"><Badge color="danger"><Icon name="slash"/> Compilation</Badge></a>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
_renderFatal(fatalType, title) {
|
||||
return <a className="badge-link" href={`#fatal-${fatalType}`} onClick={(e) => this.props.onFatalClick(e)}><Badge color="danger"><Icon name="slash" className="mr-1" />{title}</Badge></a>;
|
||||
}
|
||||
|
||||
_renderError(errorType, numErrors) {
|
||||
const color = errorType === 'error' ? 'danger' : 'warning';
|
||||
const clickAction = errorType === 'error' ? this.props.onWarningsClick : this.props.onErrorsClick;
|
||||
return <a className="badge-link" href={`#${errorType}`} onClick={(e) => clickAction(e)}><Badge color={color}>{numErrors} {errorType}{numErrors > 1 ? "s" : ""}</Badge></a>;
|
||||
}
|
||||
|
||||
render() {
|
||||
const {numWarnings, numErrors, isLoading, loadingMessage, isVisible, showDeploy, showFatalFiddle, showFatalFiddleDeploy, showFatalError} = this.props;
|
||||
const classes = classNames("compilation-summary", {
|
||||
'visible': isVisible
|
||||
});
|
||||
|
||||
if(fatalFiddleDeploy) {
|
||||
renderings.push(
|
||||
<React.Fragment key="errors">
|
||||
<a className="badge-link" href="#fatal-deploy"><Badge color="danger"><Icon name="slash"/> Deployment</Badge></a>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
if(errors.length) renderings.push(
|
||||
<React.Fragment key="errors">
|
||||
<a className="badge-link" href="#errors"><Badge color="danger">{errors.length} error{errors.length > 1 ? "s" : ""}</Badge></a>
|
||||
</React.Fragment>
|
||||
);
|
||||
if(warnings.length) renderings.push(
|
||||
<React.Fragment key="warnings">
|
||||
<a className="badge-link" href="#warnings"><Badge color="warning">{warnings.length} warning{warnings.length > 1 ? "s" : ""}</Badge></a>
|
||||
</React.Fragment>
|
||||
);
|
||||
if(hasResult && !errors.length){
|
||||
renderings.push(
|
||||
<React.Fragment key="success">
|
||||
<Badge className="badge-link" color="success">Compiled</Badge>
|
||||
<FiddleDeployButton onDeployClick={(e) => this.props.onDeployClick(e)} />
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={"compilation-summary " + ((hasResult || isLoading) ? "visible" : "")}>
|
||||
{renderings}
|
||||
{!(hasResult || isLoading) ? " " : ""}
|
||||
<div className={classes}>
|
||||
{isLoading &&
|
||||
<Loader className="mr-1">
|
||||
<span className="loader-text">{loadingMessage}</span>
|
||||
</Loader>}
|
||||
|
||||
{showFatalError && this._renderFatal("error", "Error")}
|
||||
|
||||
{showFatalFiddle && this._renderFatal("compile", "Compilation")}
|
||||
|
||||
{showFatalFiddleDeploy && this._renderFatal("deploy", "Deployment")}
|
||||
|
||||
{numErrors > 0 && this._renderError("error", numErrors)}
|
||||
|
||||
{numWarnings > 0 && this._renderError("warning", numWarnings)}
|
||||
|
||||
{showDeploy &&
|
||||
<React.Fragment key="success">
|
||||
<Badge className="badge-link" color="success">Compiled</Badge>
|
||||
<FiddleDeployButton onDeployClick={(e) => this.props.onDeployClick(e)} />
|
||||
</React.Fragment>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
FiddleResultsSummary.propTypes = {
|
||||
errors: PropTypes.array,
|
||||
warnings: PropTypes.array,
|
||||
isLoading: PropTypes.bool,
|
||||
loadingMessage: PropTypes.string,
|
||||
hasResult: PropTypes.bool,
|
||||
fatalFiddle: PropTypes.string,
|
||||
fatalFiddleDeploy: PropTypes.string,
|
||||
onDeployClick: PropTypes.func
|
||||
isVisible: PropTypes.bool,
|
||||
showDeploy: PropTypes.bool,
|
||||
showFatalError: PropTypes.bool,
|
||||
showFatalFiddle: PropTypes.bool,
|
||||
showFatalFiddleDeploy: PropTypes.bool,
|
||||
onDeployClick: PropTypes.func,
|
||||
numErrors: PropTypes.number,
|
||||
numWarnings: PropTypes.number,
|
||||
onWarningsClick: PropTypes.func.isRequired,
|
||||
onErrorsClick: PropTypes.func.isRequired,
|
||||
onFatalClick: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default FiddleResultsSummary;
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/* eslint {jsx-a11y/anchor-has-content:"off"} */
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {Card, Icon, Dimmer} from 'tabler-react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
class LoadingCardWithIcon extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
errorsCollapsed: false,
|
||||
warningsCollapsed: false,
|
||||
errorsFullscreen: false,
|
||||
warningsFullscreen: false
|
||||
};
|
||||
}
|
||||
|
||||
_onToggle(e, type) {
|
||||
const className = e.currentTarget.parentElement.className.replace('card-options', '').replace(' ', '');
|
||||
const updatedState = {};
|
||||
updatedState[className + type] = !(this.state[className + type]);
|
||||
this.setState(updatedState);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
color,
|
||||
className,
|
||||
iconName,
|
||||
headerTitle,
|
||||
isLoading,
|
||||
body,
|
||||
showCardOptions = true,
|
||||
cardOptionsClassName} = this.props;
|
||||
|
||||
const isFullscreen = Boolean(this.state[cardOptionsClassName + 'Fullscreen']);
|
||||
const classes = classNames(className, {
|
||||
'card-fullscreen': showCardOptions && Boolean(this.state[cardOptionsClassName + 'Fullscreen']),
|
||||
'card-collapsed': showCardOptions && Boolean(this.state[cardOptionsClassName + 'Collapsed']) && !isFullscreen
|
||||
});
|
||||
|
||||
return (
|
||||
<Card
|
||||
statusColor={color}
|
||||
statusSide="true"
|
||||
className={classes}
|
||||
isCollapsible={showCardOptions}
|
||||
isFullscreenable={showCardOptions}>
|
||||
<Card.Header>
|
||||
<Card.Title color={color}>{iconName && <Icon name={iconName} className="mr-1" />}{headerTitle}</Card.Title>
|
||||
{showCardOptions &&
|
||||
<Card.Options className={cardOptionsClassName}>
|
||||
<Card.OptionsItem key="0" type="collapse" icon="chevron-up" onClick={(e) => this._onToggle(e, 'Collapsed')} />
|
||||
<Card.OptionsItem key="1" type="fullscreen" icon="maximize" onClick={(e) => this._onToggle(e, 'Fullscreen')} />
|
||||
</Card.Options>
|
||||
}
|
||||
</Card.Header>
|
||||
<Card.Body>
|
||||
<Dimmer active={isLoading ? "active" : ""} loader>
|
||||
{body}
|
||||
</Dimmer>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
LoadingCardWithIcon.propTypes = {
|
||||
color: PropTypes.string.isRequired,
|
||||
className: PropTypes.string.isRequired,
|
||||
iconName: PropTypes.string,
|
||||
headerTitle: PropTypes.any,
|
||||
isLoading: PropTypes.bool.isRequired,
|
||||
body: PropTypes.node,
|
||||
showCardOptions: PropTypes.bool,
|
||||
onOptionToggle: PropTypes.func,
|
||||
cardOptionsClassName: PropTypes.string
|
||||
};
|
||||
|
||||
export default LoadingCardWithIcon;
|
|
@ -14,7 +14,7 @@ class ContractLayoutContainer extends Component {
|
|||
|
||||
render() {
|
||||
if (this.props.contract){
|
||||
return <ContractLayout contract={this.props.contract} />;
|
||||
return <ContractLayout contractIsFiddle={this.props.contract.isFiddle} />;
|
||||
} else {
|
||||
return <React.Fragment />;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,10 @@ import FiddleResultsSummary from '../components/FiddleResultsSummary';
|
|||
import scrollToComponent from 'react-scroll-to-component';
|
||||
import {getFiddle, getFiddleDeploy} from "../reducers/selectors";
|
||||
import CompilerError from "../components/CompilerError";
|
||||
import {List, Badge, Button} from 'tabler-react';
|
||||
import {NavLink} from 'react-router-dom';
|
||||
import LoadingCardWithIcon from '../components/LoadingCardWithIcon';
|
||||
import {hashCode} from '../utils/utils';
|
||||
|
||||
class FiddleContainer extends Component {
|
||||
|
||||
|
@ -22,12 +26,16 @@ class FiddleContainer extends Component {
|
|||
this.state = {
|
||||
value: undefined,
|
||||
loadingMessage: 'Loading...',
|
||||
|
||||
readOnly: true
|
||||
};
|
||||
this.compileTimeout = null;
|
||||
this.ace = null;
|
||||
this.editor = null;
|
||||
this.warningsCardRef = null;
|
||||
this.errorsCardRef = null;
|
||||
this.fatalCardRef = null;
|
||||
this.deployedCardRef = null;
|
||||
this.fiddleResultsRef = React.createRef();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
@ -37,12 +45,20 @@ class FiddleContainer extends Component {
|
|||
|
||||
componentDidUpdate(prevProps) {
|
||||
const {lastFiddle} = this.props;
|
||||
if(this.state.value === '' && prevProps.lastFiddle === lastFiddle) return;
|
||||
if((!this.state.value && lastFiddle && !lastFiddle.error) && this.state.value !== lastFiddle) {
|
||||
if (this.state.value === '' && prevProps.lastFiddle === lastFiddle) return;
|
||||
if ((!this.state.value && lastFiddle && !lastFiddle.error) && this.state.value !== lastFiddle) {
|
||||
this._onCodeChange(lastFiddle, true);
|
||||
}
|
||||
}
|
||||
|
||||
_getRowCol(errorMessage) {
|
||||
const errorSplit = errorMessage.split(':');
|
||||
if (errorSplit.length >= 3) {
|
||||
return {row: errorSplit[1], col: errorSplit[2]};
|
||||
}
|
||||
return {row: 0, col: 0};
|
||||
}
|
||||
|
||||
_onCodeChange(newValue, immediate = false) {
|
||||
this.setState({readOnly: false, value: newValue});
|
||||
if (this.compileTimeout) clearTimeout(this.compileTimeout);
|
||||
|
@ -50,10 +66,25 @@ class FiddleContainer extends Component {
|
|||
this.setState({loadingMessage: 'Compiling...'});
|
||||
this.props.postFiddle(newValue, Date.now());
|
||||
}, immediate ? 0 : 1000);
|
||||
|
||||
}
|
||||
|
||||
_getFormattedErrors(errors, errorType){
|
||||
_onErrorClick(e, annotation) {
|
||||
e.preventDefault();
|
||||
this.editor.gotoLine(annotation.row + 1);
|
||||
scrollToComponent(this.ace);
|
||||
}
|
||||
|
||||
_onErrorSummaryClick(e, refName) {
|
||||
scrollToComponent(this[refName]);
|
||||
}
|
||||
|
||||
_onDeployClick(_e) {
|
||||
this.setState({loadingMessage: 'Deploying...'});
|
||||
this.props.postFiddleDeploy(this.props.fiddle.compilationResult);
|
||||
scrollToComponent(this.deployedCardRef || this.fiddleResultsRef.current); // deployedCardRef null on first Deploy click
|
||||
}
|
||||
|
||||
_renderErrors(errors, errorType) {
|
||||
return errors.reduce(
|
||||
(errors, error, index) => {
|
||||
if (error.severity === errorType) {
|
||||
|
@ -67,13 +98,13 @@ class FiddleContainer extends Component {
|
|||
errors.push({
|
||||
solcError: error,
|
||||
node:
|
||||
<CompilerError
|
||||
onClick={(e) => { this._onErrorClick(e, annotation); }}
|
||||
key={`${errorType}_${index}`}
|
||||
index={index}
|
||||
errorType={errorType}
|
||||
row={errorRowCol.row}
|
||||
errorMessage={error.formattedMessage}/>,
|
||||
<CompilerError
|
||||
onClick={(e) => { this._onErrorClick(e, annotation); }}
|
||||
key={`${errorType}_${index}`}
|
||||
index={index}
|
||||
errorType={errorType}
|
||||
row={errorRowCol.row}
|
||||
errorMessage={error.formattedMessage} />,
|
||||
annotation: annotation
|
||||
});
|
||||
}
|
||||
|
@ -81,46 +112,85 @@ class FiddleContainer extends Component {
|
|||
}, []);
|
||||
}
|
||||
|
||||
_getRowCol(errorMessage){
|
||||
const errorSplit = errorMessage.split(':');
|
||||
if(errorSplit.length >= 3){
|
||||
return {row: errorSplit[1], col: errorSplit[2]};
|
||||
}
|
||||
return {row: 0, col: 0};
|
||||
_renderErrorsCard(errors, errorType) {
|
||||
const color = (errorType === "error" ? "danger" : errorType);
|
||||
|
||||
return (Boolean(errors.length) && <LoadingCardWithIcon
|
||||
anchorId={errorType + "s"}
|
||||
color={color}
|
||||
className={errorType + "s-card "}
|
||||
key={errorType + "s-card"}
|
||||
showCardOptions={true}
|
||||
isLoading={this.props.loading}
|
||||
cardOptionsClassName={errorType + "s"}
|
||||
body={
|
||||
<List.Group>
|
||||
{errors.map(error => { return error.node; })}
|
||||
</List.Group>
|
||||
}
|
||||
headerTitle={
|
||||
<React.Fragment>
|
||||
<span className="mr-1">{errorType + "s"}</span><Badge color={color}>{errors.length}</Badge>
|
||||
</React.Fragment>
|
||||
}
|
||||
ref={cardRef => { this[errorType + "sCardRef"] = cardRef; }}
|
||||
/>);
|
||||
}
|
||||
|
||||
_onErrorClick(e, annotation){
|
||||
e.preventDefault();
|
||||
this.editor.gotoLine(annotation.row + 1);
|
||||
scrollToComponent(this.ace);
|
||||
_renderSuccessCard(title, body) {
|
||||
return this._renderLoadingCard("success", "success-card", "check", title, body, (cardRef) => {
|
||||
this.deployedCardRef = cardRef;
|
||||
});
|
||||
}
|
||||
|
||||
_onDeployClick(_e){
|
||||
this.setState({loadingMessage: 'Deploying...'});
|
||||
this.props.postFiddleDeploy(this.props.fiddle.compilationResult);
|
||||
_renderFatalCard(title, body) {
|
||||
return body && this._renderLoadingCard("danger", "fatal-card", "slash", title, body, (cardRef) => {
|
||||
this.fatalCardRef = cardRef;
|
||||
});
|
||||
}
|
||||
|
||||
_renderLoadingCard(color, className, iconName, headerTitle, body, refCb) {
|
||||
return (<LoadingCardWithIcon
|
||||
color={color}
|
||||
className={className}
|
||||
iconName={iconName}
|
||||
showCardOptions={false}
|
||||
isLoading={this.props.loading}
|
||||
body={body}
|
||||
headerTitle={headerTitle}
|
||||
key={hashCode([className, iconName, headerTitle].join(''))}
|
||||
ref={refCb}
|
||||
/>);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {fiddle, loading, fiddleError, fiddleDeployError, deployedContracts} = this.props;
|
||||
const {fiddle, loading, fiddleError, fiddleDeployError, deployedContracts, fatalError} = this.props;
|
||||
const {loadingMessage, value, readOnly} = this.state;
|
||||
let renderings = [];
|
||||
let warnings = [];
|
||||
let errors = [];
|
||||
if (fiddle && fiddle.errors) {
|
||||
warnings = this._getFormattedErrors(fiddle.errors, "warning");
|
||||
errors = this._getFormattedErrors(fiddle.errors, "error");
|
||||
warnings = this._renderErrors(fiddle.errors, "warning");
|
||||
errors = this._renderErrors(fiddle.errors, "error");
|
||||
}
|
||||
renderings.push(
|
||||
<React.Fragment key="fiddle">
|
||||
const hasResult = Boolean(fiddle);
|
||||
return (
|
||||
<React.Fragment>
|
||||
<h1 className="page-title">Fiddle</h1>
|
||||
<p>Play around with contract code and deploy against your running node.</p>
|
||||
<FiddleResultsSummary
|
||||
errors={errors}
|
||||
warnings={warnings}
|
||||
numErrors={errors.length}
|
||||
numWarnings={warnings.length}
|
||||
isLoading={loading}
|
||||
loadingMessage={loadingMessage}
|
||||
hasResult={Boolean(fiddle)}
|
||||
fatalFiddle={fiddleError}
|
||||
fatalFiddleDeploy={fiddleDeployError}
|
||||
showFatalError={Boolean(fatalError)}
|
||||
showFatalFiddle={Boolean(fiddleError)}
|
||||
showFatalFiddleDeploy={Boolean(fiddleDeployError)}
|
||||
onDeployClick={(e) => this._onDeployClick(e)}
|
||||
isVisible={Boolean(fatalError || hasResult || loading)}
|
||||
showDeploy={hasResult && !errors.length}
|
||||
onWarningsClick={(e) => this._onErrorSummaryClick(e, "errorsCardRef")}
|
||||
onErrorsClick={(e) => this._onErrorSummaryClick(e, "warningsCardRef")}
|
||||
onFatalClick={(e) => this._onErrorSummaryClick(e, "fatalCardRef")}
|
||||
/>
|
||||
<Fiddle
|
||||
value={value}
|
||||
|
@ -129,32 +199,27 @@ class FiddleContainer extends Component {
|
|||
errors={errors}
|
||||
warnings={warnings}
|
||||
ref={(fiddle) => {
|
||||
if(fiddle) {
|
||||
if (fiddle) {
|
||||
this.editor = fiddle.ace.editor;
|
||||
this.ace = fiddle.ace;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
if (fiddle || (this.state.value && (fiddleError || fiddleDeployError))) {
|
||||
renderings.push(
|
||||
<FiddleResults
|
||||
key="results"
|
||||
errors={errors}
|
||||
warnings={warnings}
|
||||
fatalFiddle={fiddleError}
|
||||
fatalFiddleDeploy={fiddleDeployError}
|
||||
isLoading={loading}
|
||||
deployedContracts={deployedContracts}
|
||||
/>);
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<h1 className="page-title">Fiddle</h1>
|
||||
<p>Play around with contract code and deploy against your running node.</p>
|
||||
{renderings}
|
||||
errorsCard={this._renderErrorsCard(errors, "error")}
|
||||
warningsCard={this._renderErrorsCard(warnings, "warning")}
|
||||
fatalErrorCard={this._renderFatalCard("Fatal error", fatalError)}
|
||||
fatalFiddleCard={this._renderFatalCard("Failed to compile", fiddleError)}
|
||||
fatalFiddleDeployCard={this._renderFatalCard("Failed to deploy", fiddleDeployError)}
|
||||
deployedContractsCard={deployedContracts && this._renderSuccessCard("Contract(s) deployed!",
|
||||
<Button
|
||||
to={`/embark/contracts/${deployedContracts}/overview`}
|
||||
RootComponent={NavLink}
|
||||
>Play with my contract(s)</Button>
|
||||
)}
|
||||
forwardedRef={this.fiddleResultsRef}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
@ -168,7 +233,8 @@ function mapStateToProps(state) {
|
|||
fiddleError: fiddle.error,
|
||||
fiddleDeployError: deployedFiddle.error,
|
||||
loading: state.loading,
|
||||
lastFiddle: fiddle.data ? fiddle.data.codeToCompile : undefined
|
||||
lastFiddle: fiddle.data ? fiddle.data.codeToCompile : undefined,
|
||||
fatalError: state.errorMessage
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -181,7 +247,8 @@ FiddleContainer.propTypes = {
|
|||
postFiddleDeploy: PropTypes.func,
|
||||
deployedContracts: PropTypes.string,
|
||||
fetchLastFiddle: PropTypes.func,
|
||||
lastFiddle: PropTypes.any
|
||||
lastFiddle: PropTypes.any,
|
||||
fatalError: PropTypes.string
|
||||
};
|
||||
|
||||
export default connect(
|
||||
|
|
|
@ -37,7 +37,6 @@
|
|||
.compilation-summary {
|
||||
float: right;
|
||||
margin-bottom: 3px;
|
||||
line-height: 30px;
|
||||
visibility: hidden;
|
||||
}
|
||||
.compilation-summary.visible{
|
||||
|
@ -55,11 +54,11 @@
|
|||
}
|
||||
.loader:before, .loader:after{
|
||||
margin: -0.6rem 0 0 -0.6rem;
|
||||
left: 0;
|
||||
}
|
||||
.loader, .loader-text{
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.loader {
|
||||
margin-right: 5px;
|
||||
vertical-align: top;
|
||||
padding-left: 0.6rem;
|
||||
width: auto;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import _ from 'lodash';
|
||||
import {last} from '../utils/utils';
|
||||
|
||||
export function getAccounts(state) {
|
||||
return state.entities.accounts;
|
||||
|
@ -109,7 +109,7 @@ export function getMessages(state) {
|
|||
}
|
||||
|
||||
export function getFiddle(state) {
|
||||
const fiddleCompilation = _.last(state.entities.fiddles.sort((a, b) => { return (a.timestamp || 0) - (b.timestamp || 0); }));
|
||||
const fiddleCompilation = last(state.entities.fiddles.sort((a, b) => { return (a.timestamp || 0) - (b.timestamp || 0); }));
|
||||
const isNoTempFileError = Boolean(fiddleCompilation && fiddleCompilation.codeToCompile && fiddleCompilation.codeToCompile.error && fiddleCompilation.codeToCompile.error.indexOf('ENOENT') > -1);
|
||||
return {
|
||||
data: fiddleCompilation,
|
||||
|
@ -119,7 +119,7 @@ export function getFiddle(state) {
|
|||
|
||||
export function getFiddleDeploy(state) {
|
||||
return {
|
||||
data: _.last(state.entities.fiddleDeploys),
|
||||
data: last(state.entities.fiddleDeploys),
|
||||
error: state.errorEntities.fiddleDeploys
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
export function last(array) {
|
||||
return array && array.length ? array[array.length - 1] : undefined;
|
||||
}
|
||||
/* eslint no-bitwise: "off" */
|
||||
export function hashCode(str) {
|
||||
let hash = 0;
|
||||
if (str.length === 0) return hash;
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const char = str.charCodeAt(i);
|
||||
hash = ((hash << 5) - hash) + char;
|
||||
hash = hash & hash; // Convert to 32bit integer
|
||||
}
|
||||
return hash;
|
||||
}
|
Loading…
Reference in New Issue