diff --git a/embark-ui/src/actions/index.js b/embark-ui/src/actions/index.js index c5543c64..59e6fc48 100644 --- a/embark-ui/src/actions/index.js +++ b/embark-ui/src/actions/index.js @@ -63,6 +63,8 @@ export const RECEIVE_PROCESSES_ERROR = 'RECEIVE_PROCESSES_ERROR'; // Process logs export const FETCH_PROCESS_LOGS = 'FETCH_PROCESS_LOGS'; export const RECEIVE_PROCESS_LOGS = 'RECEIVE_PROCESS_LOGS'; +export const WATCH_NEW_PROCESS_LOGS = 'WATCH_NEW_PROCESS_LOGS'; +export const RECEIVE_NEW_PROCESS_LOG = 'RECEIVE_NEW_PROCESS_LOG'; export const RECEIVE_PROCESS_LOGS_ERROR = 'RECEIVE_PROCESS_LOGS_ERROR'; // BlockHeader export const INIT_BLOCK_HEADER = 'INIT_BLOCK_HEADER'; @@ -94,6 +96,13 @@ export function fetchProcessLogs(processName) { }; } +export function listenToProcessLogs(processName) { + return { + type: WATCH_NEW_PROCESS_LOGS, + processName + }; +} + export function receiveProcessLogs(processName, logs) { return { type: RECEIVE_PROCESS_LOGS, diff --git a/embark-ui/src/api/index.js b/embark-ui/src/api/index.js index 65e67739..c1cdfaf1 100644 --- a/embark-ui/src/api/index.js +++ b/embark-ui/src/api/index.js @@ -43,6 +43,10 @@ export function fetchProcessLogs(processName) { return axios.get(`${constants.httpEndpoint}/process-logs/${processName}`); } +export function webSocketProcess(processName) { + return new WebSocket(constants.wsEndpoint + '/process-logs/' + processName); +} + export function webSocketBlockHeader() { return new WebSocket(`${constants.wsEndpoint}/blockchain/blockHeader`); } diff --git a/embark-ui/src/components/Process.js b/embark-ui/src/components/Process.js index 18000340..7a73e064 100644 --- a/embark-ui/src/components/Process.js +++ b/embark-ui/src/components/Process.js @@ -1,86 +1,30 @@ import React, {Component} from 'react'; -import connect from "react-redux/es/connect/connect"; -import {fetchProcessLogs} from "../actions"; -import constants from '../constants'; import PropTypes from 'prop-types'; +import {Page} from "tabler-react"; +import Loading from "./Loading"; class Process extends Component { - constructor(props) { - super(props); - this.state = { - logs: [] - }; - this.gotOriginalLogs = false; - } - - componentDidMount() { - const self = this; - - this.props.fetchProcessLogs(self.props.processName); - - this.ws = new WebSocket(constants.wsEndpoint + '/process-logs/' + self.props.processName); - - this.ws.onmessage = function(evt) { - const log = JSON.parse(evt.data); - const logs = self.state.logs; - logs.push(log); - self.setState({ - logs - }); - }; - - this.ws.onclose = function() { - console.log(self.props.processName + "Log process connection is closed"); - }; - - window.onbeforeunload = function(_event) { - this.ws.close(); - }; - } - - shouldComponentUpdate(nextProps, _nextState) { - if (!this.gotOriginalLogs && nextProps.logs && nextProps.logs[this.props.processName]) { - const logs = nextProps.logs[this.props.processName].concat(this.state.logs); - this.gotOriginalLogs = true; - this.setState({ - logs - }); - } - return true; - } - - componentWillUnmount() { - this.ws.close(); - this.ws = null; - } - render() { + const logs = this.props.logs; return ( -
- State: {this.props.state} + +

State: {this.props.state}

+ {!logs && + } + {logs &&
{ - this.state.logs.map((item, i) =>

{item.msg_clear || item.msg}

) + logs.map((item, i) =>

{item.msg_clear || item.msg}

) } -
-
); + } + ); } } Process.propTypes = { processName: PropTypes.string.isRequired, state: PropTypes.string.isRequired, - fetchProcessLogs: PropTypes.func, - logs: PropTypes.object + logs: PropTypes.array }; -function mapStateToProps(state) { - return {logs: state.processes.logs}; -} - -export default connect( - mapStateToProps, - { - fetchProcessLogs - } -)(Process); +export default Process; diff --git a/embark-ui/src/components/ProcessesLayout.js b/embark-ui/src/components/ProcessesLayout.js new file mode 100644 index 00000000..25e93774 --- /dev/null +++ b/embark-ui/src/components/ProcessesLayout.js @@ -0,0 +1,68 @@ +import PropTypes from "prop-types"; +import React, {Component} from 'react'; +import connect from "react-redux/es/connect/connect"; +import {NavLink, Route, Switch, withRouter} from 'react-router-dom'; +import { + Page, + Grid, + List +} from "tabler-react"; + +import ProcessesContainer from '../containers/ProcessesContainer'; +import Loading from "./Loading"; + +const routePrefix = '/embark/processes'; + +class ProcessesLayout extends Component { + + + render() { + if (!this.props.processes || !this.props.processes.data) { + return ; + } + const processNames = Object.keys(this.props.processes.data) || []; + return ( + + Processes +
+ + {processNames.map((processName, index) => { + return ( + {processName} + ); + })} + + +
+
+ + + + {processNames.map((processName, index) => { + return (); + })} + + +
); + } +} + +ProcessesLayout.propTypes = { + processes: PropTypes.object, + match: PropTypes.object +}; + +function mapStateToProps(state) { + return {processes: state.processes}; +} + +export default connect( + mapStateToProps +)(ProcessesLayout); + diff --git a/embark-ui/src/containers/AppContainer.js b/embark-ui/src/containers/AppContainer.js index 94c83767..962baa73 100644 --- a/embark-ui/src/containers/AppContainer.js +++ b/embark-ui/src/containers/AppContainer.js @@ -6,11 +6,12 @@ import React, {Component} from 'react'; import history from '../history'; import Layout from '../components/Layout'; import routes from '../routes'; -import {initBlockHeader} from '../actions'; +import {initBlockHeader, fetchProcesses} from '../actions'; class AppContainer extends Component { componentDidMount() { this.props.initBlockHeader(); + this.props.fetchProcesses(); } render() { @@ -25,12 +26,14 @@ class AppContainer extends Component { } AppContainer.propTypes = { - initBlockHeader: PropTypes.func + initBlockHeader: PropTypes.func, + fetchProcesses: PropTypes.func }; export default connect( null, { - initBlockHeader + initBlockHeader, + fetchProcesses }, )(AppContainer); diff --git a/embark-ui/src/containers/ProcessesContainer.js b/embark-ui/src/containers/ProcessesContainer.js index c98bc026..9f5c3256 100644 --- a/embark-ui/src/containers/ProcessesContainer.js +++ b/embark-ui/src/containers/ProcessesContainer.js @@ -1,48 +1,48 @@ import React, {Component} from 'react'; import {connect} from 'react-redux'; -import {Tabs, Tab} from 'tabler-react'; import PropTypes from 'prop-types'; +import {fetchProcessLogs, listenToProcessLogs} from '../actions'; -import {fetchProcesses} from '../actions'; -import Loading from '../components/Loading'; - -import "./css/processContainer.css"; import Process from "../components/Process"; class ProcessesContainer extends Component { componentDidMount() { - this.props.fetchProcesses(); + // Get correct process name + const pathParts = this.props.match.path.split('/'); + this.processName = pathParts[pathParts.length - 1]; + // If we are not in a specific process page (eg: processes/ root), get first process + if (Object.keys(this.props.processes.data).indexOf(this.processName) < 0) { + this.processName = Object.keys(this.props.processes.data)[0]; + } + + // Fetch logs for the process + this.props.fetchProcessLogs(this.processName); + + // Only start watching if we are not already watching + if (!this.props.processes.data[this.processName].isListening) { + this.props.listenToProcessLogs(this.processName); + } } render() { - const {processes} = this.props; - if (!processes.data) { - return ; + if (!this.processName) { + return ''; } - - const processNames = Object.keys(processes.data); return (
- {processes.error &&

- Error: {processes.error.message || processes.error} -

} - - {processNames && processNames.length && - {processNames.map(processName => { - return ( - - ); - })} - } - +
); } } ProcessesContainer.propTypes = { + match: PropTypes.object, processes: PropTypes.object, - fetchProcesses: PropTypes.func + fetchProcessLogs: PropTypes.func, + listenToProcessLogs: PropTypes.func }; function mapStateToProps(state) { @@ -52,6 +52,7 @@ function mapStateToProps(state) { export default connect( mapStateToProps, { - fetchProcesses + fetchProcessLogs, + listenToProcessLogs } )(ProcessesContainer); diff --git a/embark-ui/src/containers/css/processContainer.css b/embark-ui/src/general.css similarity index 60% rename from embark-ui/src/containers/css/processContainer.css rename to embark-ui/src/general.css index 1510241f..60588a99 100644 --- a/embark-ui/src/containers/css/processContainer.css +++ b/embark-ui/src/general.css @@ -1,9 +1,4 @@ - -.processes-container .nav-link { - text-transform: capitalize; -} - -.processes-container .logs { +.logs { margin: 10px 0; background-color: #333333; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; @@ -13,15 +8,19 @@ border-radius: 8px; } -.processes-container .logs .error { +.logs .error { color: #dc3546; } -.processes-container .logs .warn { +.logs .warn { color: #fec107; } -.processes-container .logs .debug { +.logs .debug { color: #b7c1cc; } -.processes-container .logs .trace { +.logs .trace { color: #8f98a2; } + +.capitalize { + text-transform: capitalize; +} diff --git a/embark-ui/src/index.js b/embark-ui/src/index.js index 346c9426..40ff2c2d 100644 --- a/embark-ui/src/index.js +++ b/embark-ui/src/index.js @@ -3,6 +3,7 @@ import ReactDOM from 'react-dom'; import {Provider} from 'react-redux'; import "tabler-react/dist/Tabler.css"; +import "./general.css"; import AppContainer from './containers/AppContainer'; import registerServiceWorker from './registerServiceWorker'; diff --git a/embark-ui/src/reducers/processesReducer.js b/embark-ui/src/reducers/processesReducer.js index a7ddae2e..288c1df8 100644 --- a/embark-ui/src/reducers/processesReducer.js +++ b/embark-ui/src/reducers/processesReducer.js @@ -1,4 +1,11 @@ -import {RECEIVE_PROCESSES, RECEIVE_PROCESSES_ERROR, RECEIVE_PROCESS_LOGS, RECEIVE_PROCESS_LOGS_ERROR} from "../actions"; +import { + RECEIVE_PROCESSES, + RECEIVE_PROCESSES_ERROR, + RECEIVE_PROCESS_LOGS, + RECEIVE_PROCESS_LOGS_ERROR, + RECEIVE_NEW_PROCESS_LOG, + WATCH_NEW_PROCESS_LOGS +} from "../actions"; export default function processes(state = {}, action) { switch (action.type) { @@ -9,11 +16,40 @@ export default function processes(state = {}, action) { case RECEIVE_PROCESS_LOGS: return { ...state, - logs: { - ...state.logs, - [action.processName]: action.logs.data + data: { + ...state.data, + [action.processName]: { + ...state.data[action.processName], + logs: action.logs.data + } } }; + case RECEIVE_NEW_PROCESS_LOG: { + const logs = state.data[action.processName].logs || []; + logs.push(action.log); + return { + ...state, + data: { + ...state.data, + [action.processName]: { + ...state.data[action.processName], + logs: logs + } + } + }; + } + case WATCH_NEW_PROCESS_LOGS: { + return { + ...state, + data: { + ...state.data, + [action.processName]: { + ...state.data[action.processName], + isListening: true + } + } + }; + } case RECEIVE_PROCESS_LOGS_ERROR: return Object.assign({}, state, {error: action.error}); default: diff --git a/embark-ui/src/routes.js b/embark-ui/src/routes.js index 0797357d..c7cee5aa 100644 --- a/embark-ui/src/routes.js +++ b/embark-ui/src/routes.js @@ -4,15 +4,14 @@ import {Route, Switch} from 'react-router-dom'; import Home from './components/Home'; import NoMatch from './components/NoMatch'; import ExplorerLayout from './components/ExplorerLayout'; - -import ProcessesContainer from './containers/ProcessesContainer'; +import ProcessesLayout from './components/ProcessesLayout'; const routes = ( - + diff --git a/embark-ui/src/sagas/index.js b/embark-ui/src/sagas/index.js index f3c8c254..3139ff37 100644 --- a/embark-ui/src/sagas/index.js +++ b/embark-ui/src/sagas/index.js @@ -96,6 +96,19 @@ export function *watchInitBlockHeader() { yield takeEvery(actions.INIT_BLOCK_HEADER, initBlockHeader); } +export function *listenToProcessLogs(action) { + const socket = api.webSocketProcess(action.processName); + const channel = yield call(createChannel, socket); + while (true) { + const log = yield take(channel); + yield put({type: actions.RECEIVE_NEW_PROCESS_LOG, processName: action.processName, log}); + } +} + +export function *watchListenToProcessLogs() { + yield takeEvery(actions.WATCH_NEW_PROCESS_LOGS, listenToProcessLogs); +} + export default function *root() { yield all([ fork(watchInitBlockHeader), @@ -103,6 +116,7 @@ export default function *root() { fork(watchFetchAccount), fork(watchFetchProcesses), fork(watchFetchProcessLogs), + fork(watchListenToProcessLogs), fork(watchFetchBlocks), fork(watchFetchBlock), fork(watchFetchTransactions),