diff --git a/src/App.js b/src/App.js index ab5d6c7..2f46cb3 100644 --- a/src/App.js +++ b/src/App.js @@ -19,6 +19,7 @@ import {setProviderState} from './redux/actions/mount_actions'; import {detectUIUpgrade, loadReleases, setActiveRelease, setDismissUIUpgrade, setReleaseUpgradeAvailable} from './redux/actions/release_version_actions'; import {downloadItem, setAllowDownload} from './redux/actions/download_actions'; import {installDependency, installRelease, installUpgrade} from './redux/actions/install_actions'; +import {notifyError} from './redux/actions/error_actions'; const Constants = require('./constants'); const Scheduler = require('node-schedule'); @@ -32,10 +33,6 @@ class App extends IPCContainer { } state = { - DisplayError: false, - Error: null, - ErrorAction: null, - ErrorCritical: false, MissingDependencies: [], InstalledVersion: 'none', }; @@ -56,21 +53,6 @@ class App extends IPCContainer { } }; - closeErrorDisplay = () => { - if (this.state.ErrorAction) { - this.state.ErrorAction(); - } - - if (this.state.ErrorCritical) { - this.sendRequest(Constants.IPC_Shutdown); - } else { - this.setState({ - DisplayError: false, - Error: null, - }); - } - }; - componentDidMount() { this.sendRequest(Constants.IPC_Get_State); this.scheduledUpdateJob = Scheduler.scheduleJob('23 11 * * *', this.updateCheckScheduledJob); @@ -180,7 +162,7 @@ class App extends IPCContainer { if (arg.data.Success) { action(); } else { - this.setErrorState(arg.data.Error, action); + this.props.notifyError(arg.data.Error, false, action); } }; @@ -197,7 +179,7 @@ class App extends IPCContainer { this.installUpgrade(result); break; default: - this.setErrorState('Unknown download type: ' + type, null, false); + this.props.notifyError('Unknown download type: ' + type); break; } } @@ -237,14 +219,16 @@ class App extends IPCContainer { onInstallUpgradeComplete = (source, result) => { if (this.isMounted() && !result.Success) { - this.setErrorState(result.Error, () => { - // TODO Prompt to verify - if (result.AllowSkipVerification) { - this.installUpgrade( { - SkipVerification: true, - Source: source, - Success: true, - }); + this.props.notifyError(result.Error, false,() => { + if (this.isMounted()) { + // TODO Prompt to verify + if (result.AllowSkipVerification) { + this.installUpgrade({ + SkipVerification: true, + Source: source, + Success: true, + }); + } } }, false); } @@ -267,17 +251,6 @@ class App extends IPCContainer { } }; - setErrorState = (error, action, critical) => { - this.setState({ - DisplayError: true, - Error: error, - ErrorAction: action, - ErrorCritical: critical, - }, () => { - this.sendRequest(Constants.IPC_Show_Window); - }); - }; - updateCheckScheduledJob = () => { if (this.props.AppPlatform !== 'unknown') { this.detectUpgrades(); @@ -333,7 +306,7 @@ class App extends IPCContainer { allowConfig; const showUpgrade = this.props.UpgradeAvailable && - !this.state.DisplayError && + !this.props.DisplayError && !showConfig && !this.props.DownloadActive && !this.props.UpgradeDismissed; @@ -343,12 +316,10 @@ class App extends IPCContainer { !this.props.DownloadActive; let errorDisplay = null; - if (this.state.DisplayError) { + if (this.props.DisplayError) { errorDisplay = ( - + ); } @@ -357,8 +328,7 @@ class App extends IPCContainer { if (showConfig) { configDisplay = ( - + ); } @@ -412,7 +382,6 @@ class App extends IPCContainer { )); @@ -466,6 +435,7 @@ const mapStateToProps = state => { AppPlatform: state.common.AppPlatform, AppReady: state.common.AppReady, DisplayConfiguration: state.mounts.DisplayConfiguration, + DisplayError: state.error.DisplayError, DownloadActive: state.download.DownloadActive, DownloadType: state.download.DownloadType, InstallActive: state.install.InstallActive, @@ -492,6 +462,7 @@ const mapDispatchToProps = dispatch => { installRelease: (source, version, completedCallback) => dispatch(installRelease(source, version, completedCallback)), installUpgrade: (source, sha256, signature, skipVerification, completedCallback) => dispatch(installUpgrade(source, sha256, signature, skipVerification, completedCallback)), loadReleases: ()=> dispatch(loadReleases()), + notifyError: (msg, critical, callback) => dispatch(notifyError(msg, critical, callback)), setActiveRelease: (release, version) => dispatch(setActiveRelease(release, version)), setAllowDownload: allow => dispatch(setAllowDownload(allow)), setDismissUIUpgrade: dismiss => dispatch(setDismissUIUpgrade(dismiss)), diff --git a/src/components/ErrorDetails/ErrorDetails.js b/src/components/ErrorDetails/ErrorDetails.js index 6ccd730..571367b 100644 --- a/src/components/ErrorDetails/ErrorDetails.js +++ b/src/components/ErrorDetails/ErrorDetails.js @@ -1,16 +1,30 @@ import React from 'react'; +import {dismissError} from '../../redux/actions/error_actions'; +import {connect} from 'react-redux'; import Box from '../UI/Box/Box'; import Button from '../UI/Button/Button'; import './ErrorDetails.css'; -export default props => { +const mapStateToProps = state => { + return { + ErrorMessage: state.error.ErrorStack.length > 0 ? state.error.ErrorStack[0] : '', + }; +}; + +const mapDispatchToProps = dispatch => { + return { + dismissError: () => dispatch(dismissError()), + }; +}; + +export default connect(mapStateToProps, mapDispatchToProps)(props => { return (

Application Error

-

{props.error.toString()}

+

{props.ErrorMessage}

- +
); -}; \ No newline at end of file +}); \ No newline at end of file diff --git a/src/containers/Configuration/Configuration.js b/src/containers/Configuration/Configuration.js index 1bca79b..a66f4a2 100644 --- a/src/containers/Configuration/Configuration.js +++ b/src/containers/Configuration/Configuration.js @@ -7,20 +7,17 @@ import ConfigurationItem from '../../components/ConfigurationItem/ConfigurationI import Modal from '../../components/UI/Modal/Modal'; import IPCContainer from '../IPCContainer/IPCContainer'; import {displayConfiguration} from '../../redux/actions/mount_actions'; +import {notifyError} from '../../redux/actions/error_actions'; const Constants = require('../../constants'); class Configuration extends IPCContainer { constructor(props) { super(props); + this.setRequestHandler(Constants.IPC_Get_Config_Template_Reply, this.onGetConfigTemplateReply); this.setRequestHandler(Constants.IPC_Get_Config_Reply, this.onGetConfigReply); this.setRequestHandler(Constants.IPC_Set_Config_Values_Reply, this.onSetConfigValuesReply); - - this.sendRequest(Constants.IPC_Get_Config_Template, { - Provider: this.props.DisplayConfiguration, - Version: this.props.version, - }); } state = { @@ -72,6 +69,13 @@ class Configuration extends IPCContainer { } }; + componentDidMount() { + this.sendRequest(Constants.IPC_Get_Config_Template, { + Provider: this.props.DisplayConfiguration, + Version: this.props.version, + }); + } + createItemList = (config, template) => { const objectList = []; const itemList = Object @@ -142,7 +146,7 @@ class Configuration extends IPCContainer { OriginalObjectLookup: objectLookupCopy, }); } else { - this.props.errorHandler(arg.data.Error); + this.props.notifyError(arg.data.Error); } }; @@ -157,8 +161,10 @@ class Configuration extends IPCContainer { }); }); } else { - this.props.errorHandler(arg.data.Error, () => { - this.props.hideConfiguration(); + this.props.notifyError(arg.data.Error, false, () => { + if (this.isMounted()) { + this.props.hideConfiguration(); + } }); } }; @@ -283,6 +289,7 @@ const mapStateToProps = state => { const mapDispatchToProps = dispatch => { return { + notifyError: (msg, critical, callback) => dispatch(notifyError(msg, critical, callback)), hideConfiguration: () => dispatch(displayConfiguration(null)), } }; diff --git a/src/containers/MountItems/MountItems.js b/src/containers/MountItems/MountItems.js index 749756e..47c2d9a 100644 --- a/src/containers/MountItems/MountItems.js +++ b/src/containers/MountItems/MountItems.js @@ -14,6 +14,7 @@ import { setMountState, setProviderState } from '../../redux/actions/mount_actions'; +import {notifyError} from '../../redux/actions/error_actions'; const Constants = require('../../constants'); @@ -101,7 +102,7 @@ class MountItems extends IPCContainer { handleMountUnMount = (provider, mount, location) => { if (!location || (location.trim().length === 0)) { - this.props.errorHandler('Mount location is not set'); + this.props.notifyError('Mount location is not set'); } else { let allowAction = true; if (mount) { @@ -117,22 +118,22 @@ class MountItems extends IPCContainer { }); if (!result.Success) { allowAction = false; - this.props.errorHandler(result.Error.toString()); + this.props.notifyError(result.Error.toString()); } } } else { allowAction = false; if (result.Code === (new Uint32Array([-1]))[0]) { - this.props.errorHandler('Failed to connect to ' + provider + ' daemon'); + this.props.notifyError('Failed to connect to ' + provider + ' daemon'); } else if (result.Code === (new Uint32Array([-3]))[0]) { - this.props.errorHandler('Incompatible ' + provider + ' daemon. Please upgrade ' + provider); + this.props.notifyError('Incompatible ' + provider + ' daemon. Please upgrade ' + provider); } else { - this.props.errorHandler('Version check failed: ' + result.Error); + this.props.notifyError('Version check failed: ' + result.Error); } } } else { allowAction = false; - this.props.errorHandler(result.Error.toString()); + this.props.notifyError(result.Error.toString()); } } @@ -201,7 +202,7 @@ class MountItems extends IPCContainer { this.props.setAutoMountProcessed(true); } else { - this.props.errorHandler(arg.data.Error); + this.props.notifyError(arg.data.Error); } }; @@ -316,6 +317,7 @@ const mapStateToProps = state => { const mapDispatchToProps = dispatch => { return { + notifyError: (msg, critical, callback) => dispatch(notifyError(msg, critical, callback)), setAllowMount: (provider, allow) => dispatch(setAllowMount(provider, allow)), setAutoMountProcessed: processed => dispatch(setAutoMountProcessed(processed)), setMounted: (provider, mounted) => dispatch(setMounted(provider, mounted)), diff --git a/src/redux/actions/common_actions.js b/src/redux/actions/common_actions.js index 04e57ea..a33c93f 100644 --- a/src/redux/actions/common_actions.js +++ b/src/redux/actions/common_actions.js @@ -1,3 +1,26 @@ +import * as Constants from '../../constants'; import {createAction} from 'redux-starter-kit'; -export const setApplicationReady = createAction('common/setApplicationReady'); \ No newline at end of file +let ipcRenderer; +if (!process.versions.hasOwnProperty('electron')) { + ipcRenderer = ((window && window.require) ? window.require('electron').ipcRenderer : null); +} + +export const setApplicationReady = createAction('common/setApplicationReady'); + +export const showWindow = () => { + return dispatch => { + if (ipcRenderer) { + ipcRenderer.send(Constants.IPC_Show_Window); + } + }; +}; + +export const shutdownApplication = () => { + return dispatch => { + dispatch(setApplicationReady(false)); + if (ipcRenderer) { + ipcRenderer.send(Constants.IPC_Shutdown); + } + }; +}; \ No newline at end of file diff --git a/src/redux/actions/error_actions.js b/src/redux/actions/error_actions.js new file mode 100644 index 0000000..00da4a4 --- /dev/null +++ b/src/redux/actions/error_actions.js @@ -0,0 +1,45 @@ +import {showWindow, shutdownApplication} from './common_actions'; + +let ErrorActions = []; + +export const CLEAR_ERROR = 'error/clearError'; +export const clearError = () => { + return { + type: CLEAR_ERROR, + payload: null, + }; +}; + +export const dismissError = () => { + return (dispatch, getState) => { + dispatch(clearError()); + const errorAction = ErrorActions[0]; + ErrorActions = ErrorActions.splice(1); + if (errorAction) { + errorAction(); + } + if (getState().error.ErrorCritical) { + dispatch(shutdownApplication()); + } + }; +}; + +export const notifyError = (msg, critical, callback) => { + return dispatch => { + ErrorActions = [callback, ...ErrorActions]; + msg = msg.toString(); + dispatch(setErrorInfo(msg, critical)); + dispatch(showWindow()); + }; +}; + +export const SET_ERROR_INFO = 'error/setErrorInfo'; +export const setErrorInfo = (msg, critical) => { + return { + type: SET_ERROR_INFO, + payload: { + msg, + critical + } + } +}; \ No newline at end of file diff --git a/src/redux/actions/install_actions.js b/src/redux/actions/install_actions.js index 75f9ee9..2c964bc 100644 --- a/src/redux/actions/install_actions.js +++ b/src/redux/actions/install_actions.js @@ -76,6 +76,5 @@ export const installUpgrade = (source, sha256, signature, skipVerification, comp }; }; - export const setInstallActive = createAction('install/setInstallActive'); export const setInstallComplete = createAction('install/setInstallComplete'); diff --git a/src/redux/reducers/error_reducer.js b/src/redux/reducers/error_reducer.js new file mode 100644 index 0000000..f091548 --- /dev/null +++ b/src/redux/reducers/error_reducer.js @@ -0,0 +1,26 @@ +import {createReducer} from 'redux-starter-kit'; +import {CLEAR_ERROR, SET_ERROR_INFO} from '../actions/error_actions'; + +export const errorReducer = createReducer({ + DisplayError: false, + ErrorCritical: false, + ErrorStack: [], +}, { + [CLEAR_ERROR]: state => { + const errorStack = (state.ErrorStack.length > 0) ? state.ErrorStack.slice(1) : []; + return { + ...state, + DisplayError: (errorStack.length > 0), + ErrorStack: errorStack, + } + }, + [SET_ERROR_INFO]: (state, action) => { + const errorStack = [action.payload.msg, ...state.ErrorStack]; + return { + ...state, + DisplayError: true, + ErrorCritical: state.ErrorCritical || action.payload.critical, + ErrorStack: errorStack, + } + } +}); \ No newline at end of file diff --git a/src/redux/store/createAppStore.js b/src/redux/store/createAppStore.js index 9191959..acb659c 100644 --- a/src/redux/store/createAppStore.js +++ b/src/redux/store/createAppStore.js @@ -1,6 +1,7 @@ import {configureStore, getDefaultMiddleware} from 'redux-starter-kit'; import {createCommonReducer} from '../reducers/common_reducer'; import {downloadReducer} from '../reducers/download_reducer'; +import {errorReducer} from '../reducers/error_reducer'; import {installReducer} from '../reducers/install_reducer'; import {mountReducer} from '../reducers/mount_reducer'; import {releaseVersionReducer} from '../reducers/release_version_reducer'; @@ -9,6 +10,7 @@ export default function createAppStore(platform, appPlatform, version) { const reducer = { common: createCommonReducer(platform, appPlatform, version), download: downloadReducer, + error: errorReducer, install: installReducer, mounts: mountReducer, relver: releaseVersionReducer,