mirror of https://github.com/embarklabs/embark.git
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",
|
"classnames": "^2.2.6",
|
||||||
"connected-react-router": "^4.3.0",
|
"connected-react-router": "^4.3.0",
|
||||||
"history": "^4.7.2",
|
"history": "^4.7.2",
|
||||||
"lodash": "^4.17.10",
|
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.6.2",
|
||||||
"react": "^16.4.1",
|
"react": "^16.4.1",
|
||||||
"react-ace": "^6.1.4",
|
"react-ace": "^6.1.4",
|
||||||
|
|
|
@ -14,7 +14,7 @@ import ContractDeploymentContainer from '../containers/ContractDeploymentContain
|
||||||
import ContractProfileContainer from '../containers/ContractProfileContainer';
|
import ContractProfileContainer from '../containers/ContractProfileContainer';
|
||||||
import ContractSourceContainer from '../containers/ContractSourceContainer';
|
import ContractSourceContainer from '../containers/ContractSourceContainer';
|
||||||
|
|
||||||
const ContractLayout = ({match, contract}) => (
|
const ContractLayout = ({match, contractIsFiddle = false}) => (
|
||||||
<Grid.Row>
|
<Grid.Row>
|
||||||
<Grid.Col md={3}>
|
<Grid.Col md={3}>
|
||||||
<Page.Title className="my-5"> </Page.Title>
|
<Page.Title className="my-5"> </Page.Title>
|
||||||
|
@ -28,7 +28,7 @@ const ContractLayout = ({match, contract}) => (
|
||||||
>
|
>
|
||||||
Overview
|
Overview
|
||||||
</List.GroupItem>
|
</List.GroupItem>
|
||||||
{!contract.isFiddle ?
|
{!contractIsFiddle &&
|
||||||
<List.GroupItem
|
<List.GroupItem
|
||||||
className="d-flex align-items-center"
|
className="d-flex align-items-center"
|
||||||
to={`/embark/contracts/${match.params.contractName}/deployment`}
|
to={`/embark/contracts/${match.params.contractName}/deployment`}
|
||||||
|
@ -37,8 +37,6 @@ const ContractLayout = ({match, contract}) => (
|
||||||
>
|
>
|
||||||
Deployment / Utils
|
Deployment / Utils
|
||||||
</List.GroupItem>
|
</List.GroupItem>
|
||||||
:
|
|
||||||
''
|
|
||||||
}
|
}
|
||||||
<List.GroupItem
|
<List.GroupItem
|
||||||
className="d-flex align-items-center"
|
className="d-flex align-items-center"
|
||||||
|
@ -89,7 +87,8 @@ const ContractLayout = ({match, contract}) => (
|
||||||
);
|
);
|
||||||
|
|
||||||
ContractLayout.propTypes = {
|
ContractLayout.propTypes = {
|
||||||
match: PropTypes.object
|
match: PropTypes.object,
|
||||||
|
contractIsFiddle: PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withRouter(ContractLayout);
|
export default withRouter(ContractLayout);
|
||||||
|
|
|
@ -1,159 +1,25 @@
|
||||||
/* eslint {jsx-a11y/anchor-has-content:"off"} */
|
import React from 'react';
|
||||||
import React, {Component} from 'react';
|
|
||||||
import {Card, List, Badge, Icon, Dimmer, Button} from 'tabler-react';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
|
||||||
import {NavLink} from 'react-router-dom';
|
|
||||||
|
|
||||||
class FiddleResults extends Component {
|
const FiddleResults = ({warningsCard, errorsCard, fatalFiddleCard, fatalFiddleDeployCard, deployedContractsCard, fatalErrorCard, forwardedRef}) => (
|
||||||
|
<div ref={forwardedRef}>
|
||||||
constructor(props){
|
{fatalErrorCard}
|
||||||
super(props);
|
{fatalFiddleCard}
|
||||||
|
{fatalFiddleDeployCard}
|
||||||
this.state = {
|
{deployedContractsCard}
|
||||||
errorsCollapsed: false,
|
{errorsCard}
|
||||||
warningsCollapsed: false,
|
{warningsCard}
|
||||||
errorsFullscreen: false,
|
</div>
|
||||||
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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FiddleResults.propTypes = {
|
FiddleResults.propTypes = {
|
||||||
errors: PropTypes.array,
|
errorsCard: PropTypes.node,
|
||||||
warnings: PropTypes.array,
|
warningsCard: PropTypes.node,
|
||||||
fatalFiddle: PropTypes.string,
|
fatalFiddleCard: PropTypes.node,
|
||||||
fatalFiddleDeploy: PropTypes.string,
|
fatalFiddleDeployCard: PropTypes.node,
|
||||||
isLoading: PropTypes.bool,
|
deployedContractsCard: PropTypes.node,
|
||||||
deployedContracts: PropTypes.string
|
fatalErrorCard: PropTypes.node,
|
||||||
|
forwardedRef: PropTypes.any
|
||||||
};
|
};
|
||||||
|
|
||||||
export default FiddleResults;
|
export default FiddleResults;
|
||||||
|
|
|
@ -1,71 +1,69 @@
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
import {Badge, Icon} from 'tabler-react';
|
import {Badge, Icon, Loader} from 'tabler-react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import FiddleDeployButton from './FiddleDeployButton';
|
import FiddleDeployButton from './FiddleDeployButton';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
class FiddleResultsSummary extends Component{
|
class FiddleResultsSummary extends Component {
|
||||||
|
|
||||||
render(){
|
_renderFatal(fatalType, title) {
|
||||||
const {warnings, errors, isLoading, loadingMessage, hasResult, fatalFiddle, fatalFiddleDeploy} = this.props;
|
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>;
|
||||||
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>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(fatalFiddleDeploy) {
|
_renderError(errorType, numErrors) {
|
||||||
renderings.push(
|
const color = errorType === 'error' ? 'danger' : 'warning';
|
||||||
<React.Fragment key="errors">
|
const clickAction = errorType === 'error' ? this.props.onWarningsClick : this.props.onErrorsClick;
|
||||||
<a className="badge-link" href="#fatal-deploy"><Badge color="danger"><Icon name="slash"/> Deployment</Badge></a>
|
return <a className="badge-link" href={`#${errorType}`} onClick={(e) => clickAction(e)}><Badge color={color}>{numErrors} {errorType}{numErrors > 1 ? "s" : ""}</Badge></a>;
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(errors.length) renderings.push(
|
render() {
|
||||||
<React.Fragment key="errors">
|
const {numWarnings, numErrors, isLoading, loadingMessage, isVisible, showDeploy, showFatalFiddle, showFatalFiddleDeploy, showFatalError} = this.props;
|
||||||
<a className="badge-link" href="#errors"><Badge color="danger">{errors.length} error{errors.length > 1 ? "s" : ""}</Badge></a>
|
const classes = classNames("compilation-summary", {
|
||||||
</React.Fragment>
|
'visible': isVisible
|
||||||
);
|
});
|
||||||
if(warnings.length) renderings.push(
|
|
||||||
<React.Fragment key="warnings">
|
return (
|
||||||
<a className="badge-link" href="#warnings"><Badge color="warning">{warnings.length} warning{warnings.length > 1 ? "s" : ""}</Badge></a>
|
<div className={classes}>
|
||||||
</React.Fragment>
|
{isLoading &&
|
||||||
);
|
<Loader className="mr-1">
|
||||||
if(hasResult && !errors.length){
|
<span className="loader-text">{loadingMessage}</span>
|
||||||
renderings.push(
|
</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">
|
<React.Fragment key="success">
|
||||||
<Badge className="badge-link" color="success">Compiled</Badge>
|
<Badge className="badge-link" color="success">Compiled</Badge>
|
||||||
<FiddleDeployButton onDeployClick={(e) => this.props.onDeployClick(e)} />
|
<FiddleDeployButton onDeployClick={(e) => this.props.onDeployClick(e)} />
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={"compilation-summary " + ((hasResult || isLoading) ? "visible" : "")}>
|
|
||||||
{renderings}
|
|
||||||
{!(hasResult || isLoading) ? " " : ""}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FiddleResultsSummary.propTypes = {
|
FiddleResultsSummary.propTypes = {
|
||||||
errors: PropTypes.array,
|
|
||||||
warnings: PropTypes.array,
|
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
loadingMessage: PropTypes.string,
|
loadingMessage: PropTypes.string,
|
||||||
hasResult: PropTypes.bool,
|
isVisible: PropTypes.bool,
|
||||||
fatalFiddle: PropTypes.string,
|
showDeploy: PropTypes.bool,
|
||||||
fatalFiddleDeploy: PropTypes.string,
|
showFatalError: PropTypes.bool,
|
||||||
onDeployClick: PropTypes.func
|
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;
|
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() {
|
render() {
|
||||||
if (this.props.contract){
|
if (this.props.contract){
|
||||||
return <ContractLayout contract={this.props.contract} />;
|
return <ContractLayout contractIsFiddle={this.props.contract.isFiddle} />;
|
||||||
} else {
|
} else {
|
||||||
return <React.Fragment />;
|
return <React.Fragment />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,10 @@ import FiddleResultsSummary from '../components/FiddleResultsSummary';
|
||||||
import scrollToComponent from 'react-scroll-to-component';
|
import scrollToComponent from 'react-scroll-to-component';
|
||||||
import {getFiddle, getFiddleDeploy} from "../reducers/selectors";
|
import {getFiddle, getFiddleDeploy} from "../reducers/selectors";
|
||||||
import CompilerError from "../components/CompilerError";
|
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 {
|
class FiddleContainer extends Component {
|
||||||
|
|
||||||
|
@ -22,12 +26,16 @@ class FiddleContainer extends Component {
|
||||||
this.state = {
|
this.state = {
|
||||||
value: undefined,
|
value: undefined,
|
||||||
loadingMessage: 'Loading...',
|
loadingMessage: 'Loading...',
|
||||||
|
|
||||||
readOnly: true
|
readOnly: true
|
||||||
};
|
};
|
||||||
this.compileTimeout = null;
|
this.compileTimeout = null;
|
||||||
this.ace = null;
|
this.ace = null;
|
||||||
this.editor = null;
|
this.editor = null;
|
||||||
|
this.warningsCardRef = null;
|
||||||
|
this.errorsCardRef = null;
|
||||||
|
this.fatalCardRef = null;
|
||||||
|
this.deployedCardRef = null;
|
||||||
|
this.fiddleResultsRef = React.createRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -37,12 +45,20 @@ class FiddleContainer extends Component {
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
const {lastFiddle} = this.props;
|
const {lastFiddle} = this.props;
|
||||||
if(this.state.value === '' && prevProps.lastFiddle === lastFiddle) return;
|
if (this.state.value === '' && prevProps.lastFiddle === lastFiddle) return;
|
||||||
if((!this.state.value && lastFiddle && !lastFiddle.error) && this.state.value !== lastFiddle) {
|
if ((!this.state.value && lastFiddle && !lastFiddle.error) && this.state.value !== lastFiddle) {
|
||||||
this._onCodeChange(lastFiddle, true);
|
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) {
|
_onCodeChange(newValue, immediate = false) {
|
||||||
this.setState({readOnly: false, value: newValue});
|
this.setState({readOnly: false, value: newValue});
|
||||||
if (this.compileTimeout) clearTimeout(this.compileTimeout);
|
if (this.compileTimeout) clearTimeout(this.compileTimeout);
|
||||||
|
@ -50,10 +66,25 @@ class FiddleContainer extends Component {
|
||||||
this.setState({loadingMessage: 'Compiling...'});
|
this.setState({loadingMessage: 'Compiling...'});
|
||||||
this.props.postFiddle(newValue, Date.now());
|
this.props.postFiddle(newValue, Date.now());
|
||||||
}, immediate ? 0 : 1000);
|
}, 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(
|
return errors.reduce(
|
||||||
(errors, error, index) => {
|
(errors, error, index) => {
|
||||||
if (error.severity === errorType) {
|
if (error.severity === errorType) {
|
||||||
|
@ -73,7 +104,7 @@ class FiddleContainer extends Component {
|
||||||
index={index}
|
index={index}
|
||||||
errorType={errorType}
|
errorType={errorType}
|
||||||
row={errorRowCol.row}
|
row={errorRowCol.row}
|
||||||
errorMessage={error.formattedMessage}/>,
|
errorMessage={error.formattedMessage} />,
|
||||||
annotation: annotation
|
annotation: annotation
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -81,46 +112,85 @@ class FiddleContainer extends Component {
|
||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
_getRowCol(errorMessage){
|
_renderErrorsCard(errors, errorType) {
|
||||||
const errorSplit = errorMessage.split(':');
|
const color = (errorType === "error" ? "danger" : errorType);
|
||||||
if(errorSplit.length >= 3){
|
|
||||||
return {row: errorSplit[1], col: errorSplit[2]};
|
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>
|
||||||
}
|
}
|
||||||
return {row: 0, col: 0};
|
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){
|
_renderSuccessCard(title, body) {
|
||||||
e.preventDefault();
|
return this._renderLoadingCard("success", "success-card", "check", title, body, (cardRef) => {
|
||||||
this.editor.gotoLine(annotation.row + 1);
|
this.deployedCardRef = cardRef;
|
||||||
scrollToComponent(this.ace);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_onDeployClick(_e){
|
_renderFatalCard(title, body) {
|
||||||
this.setState({loadingMessage: 'Deploying...'});
|
return body && this._renderLoadingCard("danger", "fatal-card", "slash", title, body, (cardRef) => {
|
||||||
this.props.postFiddleDeploy(this.props.fiddle.compilationResult);
|
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() {
|
render() {
|
||||||
const {fiddle, loading, fiddleError, fiddleDeployError, deployedContracts} = this.props;
|
const {fiddle, loading, fiddleError, fiddleDeployError, deployedContracts, fatalError} = this.props;
|
||||||
const {loadingMessage, value, readOnly} = this.state;
|
const {loadingMessage, value, readOnly} = this.state;
|
||||||
let renderings = [];
|
|
||||||
let warnings = [];
|
let warnings = [];
|
||||||
let errors = [];
|
let errors = [];
|
||||||
if (fiddle && fiddle.errors) {
|
if (fiddle && fiddle.errors) {
|
||||||
warnings = this._getFormattedErrors(fiddle.errors, "warning");
|
warnings = this._renderErrors(fiddle.errors, "warning");
|
||||||
errors = this._getFormattedErrors(fiddle.errors, "error");
|
errors = this._renderErrors(fiddle.errors, "error");
|
||||||
}
|
}
|
||||||
renderings.push(
|
const hasResult = Boolean(fiddle);
|
||||||
<React.Fragment key="fiddle">
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<h1 className="page-title">Fiddle</h1>
|
||||||
|
<p>Play around with contract code and deploy against your running node.</p>
|
||||||
<FiddleResultsSummary
|
<FiddleResultsSummary
|
||||||
errors={errors}
|
numErrors={errors.length}
|
||||||
warnings={warnings}
|
numWarnings={warnings.length}
|
||||||
isLoading={loading}
|
isLoading={loading}
|
||||||
loadingMessage={loadingMessage}
|
loadingMessage={loadingMessage}
|
||||||
hasResult={Boolean(fiddle)}
|
showFatalError={Boolean(fatalError)}
|
||||||
fatalFiddle={fiddleError}
|
showFatalFiddle={Boolean(fiddleError)}
|
||||||
fatalFiddleDeploy={fiddleDeployError}
|
showFatalFiddleDeploy={Boolean(fiddleDeployError)}
|
||||||
onDeployClick={(e) => this._onDeployClick(e)}
|
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
|
<Fiddle
|
||||||
value={value}
|
value={value}
|
||||||
|
@ -129,32 +199,27 @@ class FiddleContainer extends Component {
|
||||||
errors={errors}
|
errors={errors}
|
||||||
warnings={warnings}
|
warnings={warnings}
|
||||||
ref={(fiddle) => {
|
ref={(fiddle) => {
|
||||||
if(fiddle) {
|
if (fiddle) {
|
||||||
this.editor = fiddle.ace.editor;
|
this.editor = fiddle.ace.editor;
|
||||||
this.ace = fiddle.ace;
|
this.ace = fiddle.ace;
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
if (fiddle || (this.state.value && (fiddleError || fiddleDeployError))) {
|
|
||||||
renderings.push(
|
|
||||||
<FiddleResults
|
<FiddleResults
|
||||||
key="results"
|
key="results"
|
||||||
errors={errors}
|
errorsCard={this._renderErrorsCard(errors, "error")}
|
||||||
warnings={warnings}
|
warningsCard={this._renderErrorsCard(warnings, "warning")}
|
||||||
fatalFiddle={fiddleError}
|
fatalErrorCard={this._renderFatalCard("Fatal error", fatalError)}
|
||||||
fatalFiddleDeploy={fiddleDeployError}
|
fatalFiddleCard={this._renderFatalCard("Failed to compile", fiddleError)}
|
||||||
isLoading={loading}
|
fatalFiddleDeployCard={this._renderFatalCard("Failed to deploy", fiddleDeployError)}
|
||||||
deployedContracts={deployedContracts}
|
deployedContractsCard={deployedContracts && this._renderSuccessCard("Contract(s) deployed!",
|
||||||
/>);
|
<Button
|
||||||
}
|
to={`/embark/contracts/${deployedContracts}/overview`}
|
||||||
|
RootComponent={NavLink}
|
||||||
return (
|
>Play with my contract(s)</Button>
|
||||||
<React.Fragment>
|
)}
|
||||||
<h1 className="page-title">Fiddle</h1>
|
forwardedRef={this.fiddleResultsRef}
|
||||||
<p>Play around with contract code and deploy against your running node.</p>
|
/>
|
||||||
{renderings}
|
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -168,7 +233,8 @@ function mapStateToProps(state) {
|
||||||
fiddleError: fiddle.error,
|
fiddleError: fiddle.error,
|
||||||
fiddleDeployError: deployedFiddle.error,
|
fiddleDeployError: deployedFiddle.error,
|
||||||
loading: state.loading,
|
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,
|
postFiddleDeploy: PropTypes.func,
|
||||||
deployedContracts: PropTypes.string,
|
deployedContracts: PropTypes.string,
|
||||||
fetchLastFiddle: PropTypes.func,
|
fetchLastFiddle: PropTypes.func,
|
||||||
lastFiddle: PropTypes.any
|
lastFiddle: PropTypes.any,
|
||||||
|
fatalError: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
|
|
|
@ -37,7 +37,6 @@
|
||||||
.compilation-summary {
|
.compilation-summary {
|
||||||
float: right;
|
float: right;
|
||||||
margin-bottom: 3px;
|
margin-bottom: 3px;
|
||||||
line-height: 30px;
|
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
.compilation-summary.visible{
|
.compilation-summary.visible{
|
||||||
|
@ -55,11 +54,11 @@
|
||||||
}
|
}
|
||||||
.loader:before, .loader:after{
|
.loader:before, .loader:after{
|
||||||
margin: -0.6rem 0 0 -0.6rem;
|
margin: -0.6rem 0 0 -0.6rem;
|
||||||
|
left: 0;
|
||||||
}
|
}
|
||||||
.loader, .loader-text{
|
.loader, .loader-text{
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: middle;
|
vertical-align: top;
|
||||||
}
|
padding-left: 0.6rem;
|
||||||
.loader {
|
width: auto;
|
||||||
margin-right: 5px;
|
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import _ from 'lodash';
|
import {last} from '../utils/utils';
|
||||||
|
|
||||||
export function getAccounts(state) {
|
export function getAccounts(state) {
|
||||||
return state.entities.accounts;
|
return state.entities.accounts;
|
||||||
|
@ -109,7 +109,7 @@ export function getMessages(state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFiddle(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);
|
const isNoTempFileError = Boolean(fiddleCompilation && fiddleCompilation.codeToCompile && fiddleCompilation.codeToCompile.error && fiddleCompilation.codeToCompile.error.indexOf('ENOENT') > -1);
|
||||||
return {
|
return {
|
||||||
data: fiddleCompilation,
|
data: fiddleCompilation,
|
||||||
|
@ -119,7 +119,7 @@ export function getFiddle(state) {
|
||||||
|
|
||||||
export function getFiddleDeploy(state) {
|
export function getFiddleDeploy(state) {
|
||||||
return {
|
return {
|
||||||
data: _.last(state.entities.fiddleDeploys),
|
data: last(state.entities.fiddleDeploys),
|
||||||
error: state.errorEntities.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