diff --git a/embark-ui/src/actions/index.js b/embark-ui/src/actions/index.js index fd162838..6a58d48a 100644 --- a/embark-ui/src/actions/index.js +++ b/embark-ui/src/actions/index.js @@ -179,11 +179,18 @@ export const FIDDLE_DEPLOY = createRequestTypes('FIDDLE_DEPLOY'); export const fiddleDeploy = { post: (compiledCode) => action(FIDDLE_DEPLOY[REQUEST], {compiledCode}), success: (response) => { - return action(FIDDLE_DEPLOY[SUCCESS], {fiddleDeploys: [response.contractNames]}); + return action(FIDDLE_DEPLOY[SUCCESS], {fiddleDeploys: response.result}); }, failure: (error) => action(FIDDLE_DEPLOY[FAILURE], {error}) }; +export const FIDDLE_FILE = createRequestTypes('FIDDLE_FILE'); +export const fiddleFile = { + request: () => action(FIDDLE_FILE[REQUEST]), + success: (source) => action(FIDDLE_FILE[SUCCESS], {fiddleFiles: [{source, filename: 'temp'}]}), + failure: (error) => action(FIDDLE_FILE[FAILURE], {error}) +}; + // Web Socket export const WATCH_NEW_PROCESS_LOGS = 'WATCH_NEW_PROCESS_LOGS'; export const WATCH_NEW_CONTRACT_LOGS = 'WATCH_NEW_CONTRACT_LOGS'; diff --git a/embark-ui/src/api/index.js b/embark-ui/src/api/index.js index 319e4ffa..09833a8f 100644 --- a/embark-ui/src/api/index.js +++ b/embark-ui/src/api/index.js @@ -108,6 +108,10 @@ export function fetchContractFile(payload) { return get('/files/contracts', {params: payload}); } +export function fetchLastFiddle() { + return get('/files/lastfiddle', {params: 'temp'}); +} + export function listenToChannel(channel) { return new WebSocket(`${constants.wsEndpoint}/communication/listenTo/${channel}`); } diff --git a/embark-ui/src/components/Contracts.js b/embark-ui/src/components/Contracts.js index 3c807923..e4b18f6c 100644 --- a/embark-ui/src/components/Contracts.js +++ b/embark-ui/src/components/Contracts.js @@ -9,8 +9,8 @@ import { import {Link} from 'react-router-dom'; import {formatContractForDisplay} from '../utils/presentation'; -const Contracts = ({contracts}) => ( - +const Contracts = ({contracts, title = "Contracts"}) => ( + @@ -48,7 +48,8 @@ const Contracts = ({contracts}) => ( ); Contracts.propTypes = { - contracts: PropTypes.arrayOf(PropTypes.object) + contracts: PropTypes.array, + title: PropTypes.string }; export default Contracts; diff --git a/embark-ui/src/components/FiddleResults.js b/embark-ui/src/components/FiddleResults.js index 5bf2460d..de7dfa30 100644 --- a/embark-ui/src/components/FiddleResults.js +++ b/embark-ui/src/components/FiddleResults.js @@ -116,7 +116,7 @@ class FiddleResults extends Component { @@ -153,7 +153,7 @@ FiddleResults.propTypes = { fatalFiddle: PropTypes.string, fatalFiddleDeploy: PropTypes.string, isLoading: PropTypes.bool, - deployedContracts: PropTypes.object + deployedContracts: PropTypes.string }; export default FiddleResults; diff --git a/embark-ui/src/containers/ContractSourceContainer.js b/embark-ui/src/containers/ContractSourceContainer.js index 53590cec..7b6b5c5f 100644 --- a/embark-ui/src/containers/ContractSourceContainer.js +++ b/embark-ui/src/containers/ContractSourceContainer.js @@ -11,7 +11,7 @@ import {getContract, getContractFile} from "../reducers/selectors"; class ContractSourceContainer extends Component { componentDidMount() { - this.props.fetchContractFile(this.props.contract.originalFilename); + this.props.fetchContractFile(this.props.contract.filename); } render() { @@ -27,7 +27,7 @@ class ContractSourceContainer extends Component { function mapStateToProps(state, props) { const contract = getContract(state, props.match.params.contractName); - const contractFile = getContractFile(state, contract.originalFilename); + const contractFile = getContractFile(state, contract.filename); return { contract, diff --git a/embark-ui/src/containers/ContractsContainer.js b/embark-ui/src/containers/ContractsContainer.js index 1d769ede..80f6dcdf 100644 --- a/embark-ui/src/containers/ContractsContainer.js +++ b/embark-ui/src/containers/ContractsContainer.js @@ -5,7 +5,7 @@ import {contracts as contractsAction} from "../actions"; import Contracts from '../components/Contracts'; import DataWrapper from "../components/DataWrapper"; -import {getContracts} from "../reducers/selectors"; +import {getContracts, getFiddleContracts} from "../reducers/selectors"; class ContractsContainer extends Component { componentDidMount() { @@ -14,19 +14,31 @@ class ContractsContainer extends Component { render() { return ( - 0} {...this.props} render={({contracts}) => ( - - )} /> + + 0} {...this.props} render={({contracts}) => ( + + )} /> + 0} {...this.props} render={({fiddleContracts}) => ( + + + + )} /> + ); } } function mapStateToProps(state) { - return {contracts: getContracts(state), error: state.errorMessage, loading: state.loading}; + return { + contracts: getContracts(state), + fiddleContracts: getFiddleContracts(state), + error: state.errorMessage, + loading: state.loading}; } ContractsContainer.propTypes = { contracts: PropTypes.array, + fiddleContracts: PropTypes.array, fetchContracts: PropTypes.func }; diff --git a/embark-ui/src/containers/FiddleContainer.js b/embark-ui/src/containers/FiddleContainer.js index 9dc41067..0354f9f6 100644 --- a/embark-ui/src/containers/FiddleContainer.js +++ b/embark-ui/src/containers/FiddleContainer.js @@ -3,12 +3,12 @@ import React, {Component} from 'react'; import {connect} from 'react-redux'; import PropTypes from 'prop-types'; -import {fiddle as fiddleAction, fiddleDeploy as fiddleDeployAction} from '../actions'; +import {fiddle as fiddleAction, fiddleDeploy as fiddleDeployAction, fiddleFile as fiddleFileAction} from '../actions'; import Fiddle from '../components/Fiddle'; import FiddleResults from '../components/FiddleResults'; import FiddleResultsSummary from '../components/FiddleResultsSummary'; import scrollToComponent from 'react-scroll-to-component'; -import {getFiddle, getFiddleDeploy} from "../reducers/selectors"; +import {getFiddle, getFiddleDeploy, getLastFiddle} from "../reducers/selectors"; import CompilerError from "../components/CompilerError"; class FiddleContainer extends Component { @@ -24,6 +24,16 @@ class FiddleContainer extends Component { this.editor = null; } + componentDidMount() { + this.props.fetchLastFiddle(); + + } + componentDidUpdate(prevProps){ + if(prevProps.lastFiddle !== this.props.lastFiddle){ + this._onCodeChange(this.props.lastFiddle); + } + } + _onCodeChange(newValue) { this.setState({value: newValue}); if (this.compileTimeout) clearTimeout(this.compileTimeout); @@ -82,7 +92,7 @@ class FiddleContainer extends Component { } render() { - const {fiddle, loading, fiddleError, fiddleDeployError, deployedContracts} = this.props; + const {fiddle, loading, fiddleError, fiddleDeployError, deployedContracts, lastFiddle} = this.props; const {loadingMessage} = this.state; let renderings = []; let warnings = []; @@ -104,7 +114,7 @@ class FiddleContainer extends Component { onDeployClick={(e) => this._onDeployClick(e)} /> this._onCodeChange(n)} errors={errors} warnings={warnings} @@ -142,11 +152,13 @@ class FiddleContainer extends Component { function mapStateToProps(state) { const fiddle = getFiddle(state); const deployedFiddle = getFiddleDeploy(state); + const lastFiddle = getLastFiddle(state); return { fiddle: fiddle.data, deployedContracts: deployedFiddle.data, fiddleError: fiddle.error, fiddleDeployError: deployedFiddle.error, + lastFiddle: lastFiddle ? lastFiddle.source : '', loading: state.loading }; } @@ -158,13 +170,16 @@ FiddleContainer.propTypes = { loading: PropTypes.bool, postFiddle: PropTypes.func, postFiddleDeploy: PropTypes.func, - deployedContracts: PropTypes.object + deployedContracts: PropTypes.string, + fetchLastFiddle: PropTypes.func, + lastFiddle: PropTypes.string }; export default connect( mapStateToProps, { postFiddle: fiddleAction.post, - postFiddleDeploy: fiddleDeployAction.post + postFiddleDeploy: fiddleDeployAction.post, + fetchLastFiddle: fiddleFileAction.request }, )(FiddleContainer); diff --git a/embark-ui/src/reducers/index.js b/embark-ui/src/reducers/index.js index 6382dc93..15209cf7 100644 --- a/embark-ui/src/reducers/index.js +++ b/embark-ui/src/reducers/index.js @@ -22,6 +22,7 @@ const entitiesDefaultState = { messageChannels: [], fiddles: [], fiddleDeploys: [], + fiddleFiles: [], versions: [], plugins: [], ensRecords: [] diff --git a/embark-ui/src/reducers/selectors.js b/embark-ui/src/reducers/selectors.js index 9887a181..bd35a969 100644 --- a/embark-ui/src/reducers/selectors.js +++ b/embark-ui/src/reducers/selectors.js @@ -53,7 +53,11 @@ export function getContractLogsByContract(state, contractName) { } export function getContracts(state) { - return state.entities.contracts; + return state.entities.contracts.filter(contract => !contract.isFiddle); +} + +export function getFiddleContracts(state) { + return state.entities.contracts.filter(contract => contract.isFiddle); } export function getContract(state, contractName) { @@ -118,6 +122,10 @@ export function getFiddleDeploy(state) { }; } +export function getLastFiddle(state) { + return state.entities.fiddleFiles.find((fiddleFile => fiddleFile.filename === 'temp')); +} + export function getEnsRecords(state) { return state.entities.ensRecords; } diff --git a/embark-ui/src/sagas/index.js b/embark-ui/src/sagas/index.js index 34d74a23..e1f8a9d4 100644 --- a/embark-ui/src/sagas/index.js +++ b/embark-ui/src/sagas/index.js @@ -5,7 +5,8 @@ import {all, call, fork, put, takeEvery, take} from 'redux-saga/effects'; const {account, accounts, block, blocks, transaction, transactions, processes, commands, processLogs, contracts, contract, contractProfile, messageSend, versions, plugins, messageListen, fiddle, - fiddleDeploy, ensRecord, ensRecords, contractLogs, contractFile, contractFunction, contractDeploy} = actions; + fiddleDeploy, ensRecord, ensRecords, contractLogs, contractFile, contractFunction, contractDeploy, + fiddleFile} = actions; function *doRequest(entity, apiFn, payload) { const {response, error} = yield call(apiFn, payload); @@ -32,6 +33,7 @@ export const fetchContracts = doRequest.bind(null, contracts, api.fetchContracts export const fetchContract = doRequest.bind(null, contract, api.fetchContract); export const fetchContractProfile = doRequest.bind(null, contractProfile, api.fetchContractProfile); export const fetchContractFile = doRequest.bind(null, contractFile, api.fetchContractFile); +export const fetchLastFiddle = doRequest.bind(null, fiddleFile, api.fetchLastFiddle); export const postContractFunction = doRequest.bind(null, contractFunction, api.postContractFunction); export const postContractDeploy = doRequest.bind(null, contractDeploy, api.postContractDeploy); export const postFiddle = doRequest.bind(null, fiddle, api.postFiddle); @@ -96,6 +98,10 @@ export function *watchFetchContractFile() { yield takeEvery(actions.CONTRACT_FILE[actions.REQUEST], fetchContractFile); } +export function *watchFetchLastFiddle() { + yield takeEvery(actions.FIDDLE_FILE[actions.REQUEST], fetchLastFiddle); +} + export function *watchPostContractFunction() { yield takeEvery(actions.CONTRACT_FUNCTION[actions.REQUEST], postContractFunction); } @@ -223,6 +229,7 @@ export default function *root() { fork(watchFetchTransaction), fork(watchPostFiddle), fork(watchPostFiddleDeploy), + fork(watchFetchLastFiddle), fork(watchFetchEnsRecord), fork(watchPostEnsRecords) ]); diff --git a/lib/modules/contracts_manager/index.js b/lib/modules/contracts_manager/index.js index c2fc69c5..a3dde9ad 100644 --- a/lib/modules/contracts_manager/index.js +++ b/lib/modules/contracts_manager/index.js @@ -76,7 +76,8 @@ class ContractsManager { className: contract.className, deploy: contract.deploy, error: contract.error, - address: contract.deployedAddress + address: contract.deployedAddress, + isFiddle: Boolean(contract.isFiddle) }); i += 1; } @@ -181,11 +182,12 @@ class ContractsManager { const builtContracts = _.pick(self.contracts, contractNames); // for each contract, deploy (in parallel) - async.eachOf(builtContracts, (contract, contractName, callback) => { + async.eachOf(builtContracts, (contract, contractName, next) => { contract.args = []; /* TODO: override contract.args */ contract.className = contractName; + contract.isFiddle = true; self.events.request("deploy:contract", contract, (err) => { - callback(err); + next(err); }); }, (err) => { let responseData = {}; diff --git a/lib/modules/pipeline/index.js b/lib/modules/pipeline/index.js index fefbec4e..a05409c5 100644 --- a/lib/modules/pipeline/index.js +++ b/lib/modules/pipeline/index.js @@ -24,6 +24,13 @@ class Pipeline { const self = this; self.events.setCommandHandler("files:contract", (filename, cb) => { + // handle case where we have a fiddle file and not a file stored in the dapp + if(filename.indexOf('.embark/fiddles') > -1){ + return fs.readFile(filename, 'utf8', (err, source) => { + if (err) return cb({error: err}); + cb(source); + }); + } let file = self.contractsFiles.find((file) => file.filename === filename); if (!file) { return cb({error: filename + " not found"}); @@ -39,6 +46,17 @@ class Pipeline { self.events.request('files:contract', req.query.filename, res.send.bind(res)); } ); + + plugin.registerAPICall( + 'get', + '/embark-api/files/lastfiddle', + (req, res) => { + fs.readFile(fs.dappPath('.embark/fiddles/temp.sol'), 'utf8', (err, source) => { + if (err) return res.send({error: err}); + res.send(source); + }); + } + ); } build({modifiedAssets}, callback) { diff --git a/lib/modules/solidity/index.js b/lib/modules/solidity/index.js index ab30cc13..67b928e4 100644 --- a/lib/modules/solidity/index.js +++ b/lib/modules/solidity/index.js @@ -23,8 +23,10 @@ class Solidity { const input = {'fiddle': {content: req.body.code.replace(/\r\n/g, '\n')}}; this.compile_solidity_code(input, {}, true, (errors, compilationResult) => { // write code to filesystem so we can view the source after page refresh - const filePath = `.embark/fiddles/${Object.keys(compilationResult).join('_')}.sol`; - fs.writeFile(filePath, req.body.code, 'utf8'); // async, do not need to wait + const className = !compilationResult ? 'temp' : Object.keys(compilationResult).join('_'); + this._writeFiddleToFileAsync(req.body.code, className, Boolean(compilationResult), (err) => { + if(err) this.logger.trace('Error writing fiddle to filesystem: ', err); + }); // async, do not need to wait const responseData = {errors: errors, compilationResult: compilationResult}; this.logger.trace(`POST response /embark-api/contract/compile:\n ${JSON.stringify(responseData)}`); @@ -34,6 +36,26 @@ class Solidity { ); } + _writeFiddleToFileAsync(code, className, isCompiled, cb){ + fs.mkdirp('.embark/fiddles', (err) => { + if(err) return cb(err); + + // always write to temp.sol file + const filePath = Solidity._getFiddlePath('temp'); + fs.writeFile(filePath, code, 'utf8', cb); + + // if it's compiled, also write to [classname].sol + if(isCompiled){ + const filePath = Solidity._getFiddlePath(className); + fs.writeFile(filePath, code, 'utf8', cb); + } + }); + } + + static _getFiddlePath(className){ + return fs.dappPath(`.embark/fiddles/${className}.sol`); + } + _compile(jsonObj, returnAllErrors, callback) { const self = this; self.solcW.compile(jsonObj, function (err, output) { @@ -128,7 +150,7 @@ class Solidity { const className = contractName; let filename = contractFile; - if(filename === 'fiddle') filename = `.embark/fiddles/${className}.sol`; + if(filename === 'fiddle') filename = Solidity._getFiddlePath(className); compiled_object[className] = {}; compiled_object[className].code = contract.evm.bytecode.object;