From 9d2c208a0829b7d9c51cedd30bc1516395590c9d Mon Sep 17 00:00:00 2001 From: Burkhard Reffeling Date: Thu, 12 May 2016 23:31:32 +0100 Subject: [PATCH] keep store on main process, forward actions via IPC --- ...dex.development.js => createMainWindow.js} | 40 ++++-------- app/main/index.js | 36 ++++++++++ app/renderer/api/.gitkeep | 0 app/renderer/containers/JobsPage.js | 2 +- app/renderer/index.js | 11 +++- app/renderer/reducers/index.js | 10 --- .../store/configureStore.development.js | 37 ----------- .../store/configureStore.production.js | 13 ---- app/renderer/utils/.gitkeep | 0 app/{renderer => shared}/actions/job.js | 9 +++ app/shared/reducers/index.js | 17 +++++ app/{renderer => shared}/reducers/job.js | 0 .../store/configureStore.development.js | 65 +++++++++++++++++++ .../store/configureStore.js | 0 app/shared/store/configureStore.production.js | 16 +++++ app/shared/store/middleware/forwardToMain.js | 14 ++++ .../store/middleware/forwardToRenderer.js | 20 ++++++ package.js | 2 +- package.json | 2 +- webpack.config.electron.js | 2 +- 20 files changed, 201 insertions(+), 95 deletions(-) rename app/main/{index.development.js => createMainWindow.js} (88%) create mode 100644 app/main/index.js delete mode 100644 app/renderer/api/.gitkeep delete mode 100644 app/renderer/reducers/index.js delete mode 100644 app/renderer/store/configureStore.development.js delete mode 100644 app/renderer/store/configureStore.production.js delete mode 100644 app/renderer/utils/.gitkeep rename app/{renderer => shared}/actions/job.js (80%) create mode 100644 app/shared/reducers/index.js rename app/{renderer => shared}/reducers/job.js (100%) create mode 100644 app/shared/store/configureStore.development.js rename app/{renderer => shared}/store/configureStore.js (100%) create mode 100644 app/shared/store/configureStore.production.js create mode 100644 app/shared/store/middleware/forwardToMain.js create mode 100644 app/shared/store/middleware/forwardToRenderer.js diff --git a/app/main/index.development.js b/app/main/createMainWindow.js similarity index 88% rename from app/main/index.development.js rename to app/main/createMainWindow.js index 5001812..57e2349 100644 --- a/app/main/index.development.js +++ b/app/main/createMainWindow.js @@ -1,19 +1,19 @@ -import { app, BrowserWindow, Menu, crashReporter, shell } from 'electron'; +import { app, BrowserWindow, Menu } from 'electron'; -let menu; -let template; -let mainWindow = null; -if (process.env.NODE_ENV === 'development') { - require('electron-debug')(); -} +export default function createWindow(store) { + let menu; + let template; -function createWindow() { - mainWindow = new BrowserWindow({ + const mainWindow = new BrowserWindow({ show: false, width: 1024, height: 728 }); + + // set main redux store + mainWindow.initialState = store.getState(); + mainWindow.maximize(); mainWindow.loadURL(`file://${__dirname}/../renderer/app.html`); @@ -23,10 +23,6 @@ function createWindow() { mainWindow.focus(); }); - mainWindow.on('closed', () => { - mainWindow = null; - }); - if (process.env.NODE_ENV === 'development') { mainWindow.openDevTools(); } @@ -230,20 +226,6 @@ function createWindow() { menu = Menu.buildFromTemplate(template); mainWindow.setMenu(menu); } -} - - -app.on('window-all-closed', () => { - if (process.platform !== 'darwin') app.quit(); -}); - -app.on('activate', () => { - // On OS X it's common to re-create a window in the app when the - // dock icon is clicked and there are no other windows open. - if (mainWindow === null) { - createWindow(); - } -}); - -app.on('ready', createWindow); + return mainWindow; +} diff --git a/app/main/index.js b/app/main/index.js new file mode 100644 index 0000000..a748802 --- /dev/null +++ b/app/main/index.js @@ -0,0 +1,36 @@ +import { app, ipcMain } from 'electron'; +import createMainWindow from './createMainWindow'; +import configureStore from '../shared/store/configureStore'; + +const store = configureStore(undefined, 'main'); +let mainWindow = null; + +if (process.env.NODE_ENV === 'development') { + require('electron-debug')(); +} + +function doCreateMainWindow() { + mainWindow = createMainWindow(store); + mainWindow.on('closed', () => { + mainWindow = null; + }); +} + + +ipcMain.on('redux-action', (event, payload) => { + store.dispatch(payload); +}); + +app.on('window-all-closed', () => { + if (process.platform !== 'darwin') app.quit(); +}); + +app.on('activate', () => { + // On OS X it's common to re-create a window in the app when the + // dock icon is clicked and there are no other windows open. + if (mainWindow === null) { + doCreateMainWindow(); + } +}); + +app.on('ready', doCreateMainWindow); diff --git a/app/renderer/api/.gitkeep b/app/renderer/api/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/app/renderer/containers/JobsPage.js b/app/renderer/containers/JobsPage.js index dfa1929..34e0e69 100644 --- a/app/renderer/containers/JobsPage.js +++ b/app/renderer/containers/JobsPage.js @@ -1,7 +1,7 @@ import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; import Jobs from '../components/Jobs'; -import * as JobActions from '../actions/job'; +import * as JobActions from '../../shared/actions/job'; function mapStateToProps(state) { return { diff --git a/app/renderer/index.js b/app/renderer/index.js index 05680b6..9e75e35 100644 --- a/app/renderer/index.js +++ b/app/renderer/index.js @@ -1,15 +1,22 @@ +import { ipcRenderer, remote } from 'electron'; import React from 'react'; import { render } from 'react-dom'; import { Provider } from 'react-redux'; import { Router, hashHistory } from 'react-router'; import { syncHistoryWithStore } from 'react-router-redux'; import routes from './routes'; -import configureStore from './store/configureStore'; +import configureStore from '../shared/store/configureStore'; import './app.global.css'; -const store = configureStore(); +const { initialState } = remote.getCurrentWindow(); + +const store = configureStore(initialState, 'renderer'); const history = syncHistoryWithStore(hashHistory, store); +ipcRenderer.on('redux-action', (event, payload) => { + store.dispatch(payload); +}); + render( diff --git a/app/renderer/reducers/index.js b/app/renderer/reducers/index.js deleted file mode 100644 index 72558a8..0000000 --- a/app/renderer/reducers/index.js +++ /dev/null @@ -1,10 +0,0 @@ -import { combineReducers } from 'redux'; -import { routerReducer as routing } from 'react-router-redux'; -import job from './job'; - -const rootReducer = combineReducers({ - routing, - job, -}); - -export default rootReducer; diff --git a/app/renderer/store/configureStore.development.js b/app/renderer/store/configureStore.development.js deleted file mode 100644 index 59ab795..0000000 --- a/app/renderer/store/configureStore.development.js +++ /dev/null @@ -1,37 +0,0 @@ -import { createStore, applyMiddleware, compose } from 'redux'; -import { persistState } from 'redux-devtools'; -import thunk from 'redux-thunk'; -import createLogger from 'redux-logger'; -import { hashHistory } from 'react-router'; -import { routerMiddleware } from 'react-router-redux'; -import rootReducer from '../reducers'; -import DevTools from '../containers/DevTools'; - -const logger = createLogger({ - level: 'info', - collapsed: true, -}); - -const router = routerMiddleware(hashHistory); - -const enhancer = compose( - applyMiddleware(thunk, router, logger), - DevTools.instrument(), - persistState( - window.location.href.match( - /[?&]debug_session=([^&]+)\b/ - ) - ) -); - -export default function configureStore(initialState) { - const store = createStore(rootReducer, initialState, enhancer); - - if (module.hot) { - module.hot.accept('../reducers', () => - store.replaceReducer(require('../reducers')) - ); - } - - return store; -} diff --git a/app/renderer/store/configureStore.production.js b/app/renderer/store/configureStore.production.js deleted file mode 100644 index 7c13728..0000000 --- a/app/renderer/store/configureStore.production.js +++ /dev/null @@ -1,13 +0,0 @@ -import { createStore, applyMiddleware } from 'redux'; -import thunk from 'redux-thunk'; -import { hashHistory } from 'react-router'; -import { routerMiddleware } from 'react-router-redux'; -import rootReducer from '../reducers'; - -const router = routerMiddleware(hashHistory); - -const enhancer = applyMiddleware(thunk, router); - -export default function configureStore(initialState) { - return createStore(rootReducer, initialState, enhancer); -} diff --git a/app/renderer/utils/.gitkeep b/app/renderer/utils/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/app/renderer/actions/job.js b/app/shared/actions/job.js similarity index 80% rename from app/renderer/actions/job.js rename to app/shared/actions/job.js index 6b929d7..6c727d7 100644 --- a/app/renderer/actions/job.js +++ b/app/shared/actions/job.js @@ -9,6 +9,9 @@ export function startJob(time = new Date()) { payload: { time, }, + meta: { + scope: 'main', + }, }; } @@ -19,6 +22,9 @@ export function pauseJob(id, time = new Date()) { id, time, }, + meta: { + scope: 'main', + }, }; } @@ -29,5 +35,8 @@ export function stopJob(id, time = new Date()) { id, time, }, + meta: { + scope: 'main', + }, }; } diff --git a/app/shared/reducers/index.js b/app/shared/reducers/index.js new file mode 100644 index 0000000..c5f84db --- /dev/null +++ b/app/shared/reducers/index.js @@ -0,0 +1,17 @@ +import { combineReducers } from 'redux'; +import { routerReducer as routing } from 'react-router-redux'; +import job from './job'; + +export default function getRootReducer(scope = 'main') { + let reducers = { + job, + }; + + if (scope === 'renderer') { + reducers = { + ...reducers, + routing, + }; + } + return combineReducers({ ...reducers }); +} diff --git a/app/renderer/reducers/job.js b/app/shared/reducers/job.js similarity index 100% rename from app/renderer/reducers/job.js rename to app/shared/reducers/job.js diff --git a/app/shared/store/configureStore.development.js b/app/shared/store/configureStore.development.js new file mode 100644 index 0000000..cdafa4f --- /dev/null +++ b/app/shared/store/configureStore.development.js @@ -0,0 +1,65 @@ +import { createStore, applyMiddleware, compose } from 'redux'; +import { persistState } from 'redux-devtools'; +import thunk from 'redux-thunk'; +import createLogger from 'redux-logger'; +import { hashHistory } from 'react-router'; +import { routerMiddleware } from 'react-router-redux'; +import getRootReducer from '../reducers'; +import forwardToMain from './middleware/forwardToMain'; +import forwardToRenderer from './middleware/forwardToRenderer'; +import DevTools from '../../renderer/containers/DevTools'; + +export default function configureStore(initialState, scope = 'main') { + const logger = createLogger({ + level: 'info', + collapsed: true, + }); + const router = routerMiddleware(hashHistory); + + let middleware = [ + thunk, + logger, + ]; + + if (scope === 'renderer') { + middleware = [ + forwardToMain, + router, + ...middleware, + ]; + } + if (scope === 'main') { + middleware = [ + ...middleware, + forwardToRenderer, + ]; + } + + let enhanced = [ + applyMiddleware(...middleware), + ]; + + if (scope === 'renderer') { + enhanced = [ + ...enhanced, + DevTools.instrument(), + persistState( + window.location.href.match( + /[?&]debug_session=([^&]+)\b/ + ) + ), + ]; + } + + const rootReducer = getRootReducer(scope); + const enhancer = compose(...enhanced); + const store = createStore(rootReducer, initialState, enhancer); + + if (module.hot) { + module.hot.accept('../reducers', () => { + store.replaceReducer(require('../reducers')); + }); + } + + return store; +} diff --git a/app/renderer/store/configureStore.js b/app/shared/store/configureStore.js similarity index 100% rename from app/renderer/store/configureStore.js rename to app/shared/store/configureStore.js diff --git a/app/shared/store/configureStore.production.js b/app/shared/store/configureStore.production.js new file mode 100644 index 0000000..cb48c51 --- /dev/null +++ b/app/shared/store/configureStore.production.js @@ -0,0 +1,16 @@ +// see dev version!!! + + +// import { createStore, applyMiddleware } from 'redux'; +// import thunk from 'redux-thunk'; +// import { hashHistory } from 'react-router'; +// import { routerMiddleware } from 'react-router-redux'; +// import rootReducer from '../reducers'; +// +// const router = routerMiddleware(hashHistory); +// +// const enhancer = applyMiddleware(thunk, router); +// +// export default function configureStore(initialState) { +// return createStore(rootReducer, initialState, enhancer); +// } diff --git a/app/shared/store/middleware/forwardToMain.js b/app/shared/store/middleware/forwardToMain.js new file mode 100644 index 0000000..94467c2 --- /dev/null +++ b/app/shared/store/middleware/forwardToMain.js @@ -0,0 +1,14 @@ +import { ipcRenderer } from 'electron'; + +const forwardToMain = store => next => action => { + if (action.meta && action.meta.scope && action.meta.scope === 'main') { + ipcRenderer.send('redux-action', action); + + // stop action in-flight + return; + } + + return next(action); +}; + +export default forwardToMain; diff --git a/app/shared/store/middleware/forwardToRenderer.js b/app/shared/store/middleware/forwardToRenderer.js new file mode 100644 index 0000000..399d9cd --- /dev/null +++ b/app/shared/store/middleware/forwardToRenderer.js @@ -0,0 +1,20 @@ +import { ipcMain, BrowserWindow } from 'electron'; + +const forwardToRenderer = store => next => action => { + // remove scope to avoid endless-loop + const rendererAction = Object.assign({}, action, { + meta: { + scope: 'renderer' + }, + }); + + const openWindows = BrowserWindow.getAllWindows(); + openWindows.forEach(({ webContents }) => { + webContents.send('redux-action', rendererAction); + }); + + // ipcMain.send('redux-action', rendererAction); + return next(action); +}; + +export default forwardToRenderer; diff --git a/package.js b/package.js index 77834e2..e547ab2 100644 --- a/package.js +++ b/package.js @@ -27,7 +27,7 @@ const DEFAULT_OPTS = { '^/test($|/)', '^/tools($|/)', '^/release($|/)', - '^/app/main/index.development.js' + '^/app/main/index.js' ].concat(devDeps.map(name => `/node_modules/${name}($|/)`)) .concat( deps.filter(name => !electronCfg.externals.includes(name)) diff --git a/package.json b/package.json index c9163f4..3178740 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "build-renderer": "cross-env NODE_ENV=production node -r babel-register ./node_modules/webpack/bin/webpack --config webpack.config.production.js --progress --profile --colors", "build": "npm run build-main && npm run build-renderer", "start": "cross-env NODE_ENV=production electron ./", - "start-hot": "cross-env HOT=1 NODE_ENV=development electron -r babel-register ./app/main/index.development", + "start-hot": "cross-env HOT=1 NODE_ENV=development electron -r babel-register ./app/main/index", "package": "cross-env NODE_ENV=production node -r babel-register package.js", "package-all": "npm run package -- --all", "postinstall": "node node_modules/fbjs-scripts/node/check-dev-engines.js package.json", diff --git a/webpack.config.electron.js b/webpack.config.electron.js index 0479ea5..fe76a09 100644 --- a/webpack.config.electron.js +++ b/webpack.config.electron.js @@ -6,7 +6,7 @@ export default { devtool: 'source-map', - entry: './app/main/index.development', + entry: './app/main/index', output: { path: __dirname,