diff --git a/app/index.js b/app/index.js
index afa9910b068..a761e6a5dca 100644
--- a/app/index.js
+++ b/app/index.js
@@ -4,21 +4,18 @@
'use strict'
const electron = require('electron')
+const BrowserWindow = electron.BrowserWindow
const ipcMain = electron.ipcMain
const app = electron.app
-const BrowserWindow = electron.BrowserWindow
const Menu = require('./menu')
-const LocalShortcuts = require('./localShortcuts')
const Updater = require('./updater')
const messages = require('../js/constants/messages')
+const AppActions = require('../js/actions/appActions')
+require('../js/stores/appStore')
// Report crashes
electron.crashReporter.start()
-// Keep a global reference of the window object, if you don't, the window will
-// be closed automatically when the JavaScript object is garbage collected.
-let windows = []
-
app.on('window-all-closed', function () {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
@@ -27,36 +24,8 @@ app.on('window-all-closed', function () {
}
})
-const spawnWindow = () => {
- let mainWindow = new BrowserWindow({
- width: 1360,
- height: 800,
- minWidth: 400,
- // A frame but no title bar and windows buttons in titlebar.
- // This only currently has an effect on 10.10 OSX and up and is
- // ignore on other platforms.
- 'title-bar-style': 'hidden'
- })
- if (process.env.NODE_ENV === 'development') {
- mainWindow.loadURL('file://' + __dirname + '/index-dev.html')
- } else {
- mainWindow.loadURL('file://' + __dirname + '/index.html')
- }
- mainWindow.on('closed', function () {
- LocalShortcuts.unregister(mainWindow)
-
- var index = windows.indexOf(mainWindow)
- if (index > -1) {
- windows.splice(index, 1)
- }
- })
-
- LocalShortcuts.register(mainWindow)
- return mainWindow
-}
-
app.on('ready', function () {
- windows.push(spawnWindow())
+ AppActions.newWindow()
ipcMain.on(messages.QUIT_APPLICATION, () => {
app.quit()
@@ -66,12 +35,6 @@ app.on('ready', function () {
BrowserWindow.getFocusedWindow().webContents.send(messages.CONTEXT_MENU_OPENED, nodeName)
})
- ipcMain.on(messages.NEW_WINDOW, () => windows.push(spawnWindow()))
- process.on(messages.NEW_WINDOW, () => windows.push(spawnWindow()))
-
- ipcMain.on(messages.CLOSE_WINDOW, () => BrowserWindow.getFocusedWindow().close())
- process.on(messages.CLOSE_WINDOW, () => BrowserWindow.getFocusedWindow().close())
-
Menu.init()
ipcMain.on(messages.UPDATE_REQUESTED, () => {
diff --git a/app/menu.js b/app/menu.js
index ad1e672f5be..8ca3a8e5471 100644
--- a/app/menu.js
+++ b/app/menu.js
@@ -7,6 +7,7 @@ const app = electron.app
const Menu = require('menu')
const messages = require('../js/constants/messages')
const dialog = electron.dialog
+const AppActions = require('../js/actions/appActions')
/**
* Sends a message to the web contents of the focused window.
@@ -40,7 +41,7 @@ const init = () => {
click: function (item, focusedWindow) {
if (!sendToFocusedWindow(focusedWindow, [messages.SHORTCUT_NEW_FRAME])) {
// no active windows
- process.emit(messages.NEW_WINDOW)
+ AppActions.newWindow()
}
}
}, {
@@ -52,11 +53,11 @@ const init = () => {
}, {
label: 'New Window',
accelerator: 'CmdOrCtrl+N',
- click: () => process.emit(messages.NEW_WINDOW)
+ click: () => AppActions.newWindow()
}, {
label: 'New Private Window',
accelerator: 'CmdOrCtrl+Alt+N',
- click: () => process.emit(messages.NEW_WINDOW)
+ click: () => AppActions.newWindow()
}, {
type: 'separator'
}, {
@@ -105,7 +106,7 @@ const init = () => {
accelerator: 'CmdOrCtrl+Shift+W',
click: function (item, focusedWindow) {
if (focusedWindow) {
- process.emit(messages.CLOSE_WINDOW)
+ AppActions.closeWindow(focusedWindow.id)
}
}
}, {
diff --git a/js/actions/appActions.js b/js/actions/appActions.js
index cfc088d75f2..d2ef3474c1f 100644
--- a/js/actions/appActions.js
+++ b/js/actions/appActions.js
@@ -3,10 +3,8 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict'
-
const AppDispatcher = require('../dispatcher/appDispatcher')
const AppConstants = require('../constants/appConstants')
-const ipc = global.require('electron').ipcRenderer
const messages = require('../constants/messages')
const AppActions = {
@@ -14,15 +12,25 @@ const AppActions = {
* Dispatches an event to the main process to create a new window
*/
newWindow: function () {
- ipc.send(messages.NEW_WINDOW)
+ AppDispatcher.dispatch({
+ actionType: AppConstants.APP_NEW_WINDOW
+ })
+ },
+
+ closeWindow: function (appWindowId) {
+ AppDispatcher.dispatch({
+ actionType: AppConstants.APP_CLOSE_WINDOW,
+ appWindowId
+ })
},
/**
* Dispatches an event to the main process to update the browser
*/
updateRequested: function () {
+ // TODO - change to dispatcher
console.log('appActions updateRequested')
- ipc.send(messages.UPDATE_REQUESTED)
+ global.require('electron').ipcRenderer.send(messages.UPDATE_REQUESTED)
},
/**
diff --git a/js/actions/windowActions.js b/js/actions/windowActions.js
index f47a2dcc38a..a8c65cc9a2f 100644
--- a/js/actions/windowActions.js
+++ b/js/actions/windowActions.js
@@ -8,10 +8,13 @@ const WindowDispatcher = require('../dispatcher/windowDispatcher')
const WindowConstants = require('../constants/windowConstants')
const Config = require('../constants/config')
const UrlUtil = require('../../node_modules/urlutil.js/dist/node-urlutil.js')
-const ipc = global.require('electron').ipcRenderer
+const electron = global.require('electron')
+const ipc = electron.ipcRenderer
+const remote = electron.remote
const messages = require('../constants/messages')
+const AppActions = require('./appActions')
-const AppActions = {
+const WindowActions = {
/**
* Dispatches a message to the store to load a new URL for the active frame.
* Both the frame's src and location properties will be updated accordingly.
@@ -144,17 +147,10 @@ const AppActions = {
frameProps
})
} else {
- this.closeWindow()
+ AppActions.closeWindow(remote.getCurrentWindow().id)
}
},
- /**
- * Dispatches an event to the main process to close the current window
- */
- closeWindow: function () {
- ipc.send(messages.CLOSE_WINDOW)
- },
-
/**
* Dispatches a message to the store to undo a closed frame
* The new frame is expected to appear at the index it was last closed at
@@ -447,4 +443,4 @@ const AppActions = {
}
}
-module.exports = AppActions
+module.exports = WindowActions
diff --git a/js/components/window.js b/js/components/window.js
index 145d2ba61b5..a74ece73b1e 100644
--- a/js/components/window.js
+++ b/js/components/window.js
@@ -7,20 +7,25 @@
const React = require('react')
const Immutable = require('immutable')
const WindowStore = require('../stores/windowStore')
-const AppStore = require('../stores/appStore')
const Main = require('./main')
+const ipc = global.require('electron').ipcRenderer
+const messages = require('../constants/messages')
class Window extends React.Component {
- constructor () {
- super()
+ constructor (props) {
+ super(props)
+
+ // initialize appState from props
+ // and then listen for updates
+ this.appState = this.props.appState
this.state = {
immutableData: {
windowState: WindowStore.getState(),
- appState: AppStore.getState()
+ appState: this.appState
}
}
+ ipc.on(messages.APP_STATE_CHANGE, this.onAppStateChange.bind(this))
WindowStore.addChangeListener(this.onChange.bind(this))
- AppStore.addChangeListener(this.onChange.bind(this))
}
render () {
@@ -31,6 +36,7 @@ class Window extends React.Component {
componentWillUnmount () {
WindowStore.removeChangeListener(this.onChange.bind(this))
+ ipc.removeListener(this.onAppStateChange)
}
shouldComponentUpdate (nextProps, nextState) {
@@ -41,11 +47,16 @@ class Window extends React.Component {
this.setState({
immutableData: {
windowState: WindowStore.getState(),
- appState: AppStore.getState()
+ appState: this.appState
}
})
}
+ onAppStateChange (appState) {
+ this.appState = appState
+ this.onChange()
+ }
}
+Window.propTypes = { appState: React.PropTypes.object }
module.exports = Window
diff --git a/js/constants/appConstants.js b/js/constants/appConstants.js
index 61ee7b3c631..a99c912b74f 100644
--- a/js/constants/appConstants.js
+++ b/js/constants/appConstants.js
@@ -6,6 +6,8 @@ const mapValuesByKeys = require('../lib/functional').mapValuesByKeys
const _ = null
const AppConstants = {
+ APP_NEW_WINDOW: _,
+ APP_CLOSE_WINDOW: _,
APP_ADD_SITE: _,
APP_REMOVE_SITE: _
}
diff --git a/js/constants/messages.js b/js/constants/messages.js
index eff636f25f3..9c01496dc6e 100644
--- a/js/constants/messages.js
+++ b/js/constants/messages.js
@@ -29,9 +29,6 @@ const messages = {
SHORTCUT_FRAME_RELOAD: _, /** @arg {number} key of frame */
SHORTCUT_NEXT_TAB: _,
SHORTCUT_PREV_TAB: _,
- // Window management
- CLOSE_WINDOW: _,
- NEW_WINDOW: _,
QUIT_APPLICATION: _,
// Updates
UPDATE_REQUESTED: _,
@@ -44,7 +41,9 @@ const messages = {
ZOOM_RESET: _,
PRINT_PAGE: _,
SET_AD_DIV_CANDIDATES: _, /** @arg {Array} adDivCandidates, @arg {string} placeholderUrl */
- CONTEXT_MENU_OPENED: _ /** @arg {string} nodeName of node being clicked */
+ CONTEXT_MENU_OPENED: _, /** @arg {string} nodeName of node being clicked */
+ APP_STATE_CHANGE: _,
+ APP_ACTION: _
}
module.exports = mapValuesByKeys(messages)
diff --git a/js/dispatcher/appDispatcher.js b/js/dispatcher/appDispatcher.js
index 135f812fa07..fdff7c2e05d 100644
--- a/js/dispatcher/appDispatcher.js
+++ b/js/dispatcher/appDispatcher.js
@@ -2,7 +2,22 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
-var Dispatcher = require('./dispatcher')
+'use strict'
+const messages = require('../constants/messages')
-const appDispatcher = new Dispatcher()
+class AppDispatcher {
+ /**
+ * dispatch
+ * @param {object} payload The data from the action.
+ */
+ dispatch (payload) {
+ if (process.type === 'renderer') {
+ global.require('electron').ipcRenderer.send(messages.APP_ACTION, payload)
+ } else {
+ process.emit(messages.APP_ACTION, payload)
+ }
+ }
+}
+
+const appDispatcher = new AppDispatcher()
module.exports = appDispatcher
diff --git a/js/entry.js b/js/entry.js
index 61a8a050f61..5415657983c 100644
--- a/js/entry.js
+++ b/js/entry.js
@@ -10,10 +10,15 @@ require('../less/navigationBar.less')
require('../less/tabs.less')
require('../node_modules/font-awesome/css/font-awesome.css')
+const URL = require('url')
+const Immutable = require('immutable')
const React = require('react')
const ReactDOM = require('react-dom')
const Window = require('./components/window')
+// get appStore from url
+var appState = Immutable.fromJS(JSON.parse(URL.parse(window.location.href, true).query.appState))
+
ReactDOM.render(
- ,
+ ,
document.getElementById('windowContainer'))
diff --git a/js/lib/siteUtil.js b/js/lib/siteUtil.js
index 93789539a24..a00faa8a7fd 100644
--- a/js/lib/siteUtil.js
+++ b/js/lib/siteUtil.js
@@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
-import Immutable from 'immutable'
+const Immutable = require('immutable')
/**
* Obtains the index of the location in sites
diff --git a/js/state/siteUtil.js b/js/state/siteUtil.js
index 4f612a85b05..34750ec6275 100644
--- a/js/state/siteUtil.js
+++ b/js/state/siteUtil.js
@@ -2,7 +2,10 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
-import Immutable from 'immutable'
+'use strict'
+const Immutable = require('immutable')
+
+var exports = {}
/**
* Obtains the index of the location in sites
@@ -11,7 +14,7 @@ import Immutable from 'immutable'
* @param location The frameProps of the page in question
* @return index of the location or -1 if not found.
*/
-function getSiteUrlIndex (sites, location) {
+exports.getSiteUrlIndex = function (sites, location) {
return sites.findIndex(site => site.get('location') === location)
}
@@ -23,8 +26,8 @@ function getSiteUrlIndex (sites, location) {
* @param tag The tag of the site to check
* @return true if the location is already bookmarked
*/
-export function isSiteInList (sites, location, tag) {
- let index = getSiteUrlIndex(sites, location)
+exports.isSiteInList = function (sites, location, tag) {
+ let index = exports.getSiteUrlIndex(sites, location)
if (index === -1) {
return false
}
@@ -42,8 +45,8 @@ export function isSiteInList (sites, location, tag) {
* Otherwise it's only considered to be a history item
* @return The new sites Immutable object
*/
-export function addSite (sites, frameProps, tag) {
- let index = getSiteUrlIndex(sites, frameProps.get('location'))
+exports.addSite = function (sites, frameProps, tag) {
+ let index = exports.getSiteUrlIndex(sites, frameProps.get('location'))
let tags = sites.getIn([index, 'tags']) || new Immutable.List()
if (tag) {
tags = tags.toSet().add(tag).toList()
@@ -76,8 +79,8 @@ export function addSite (sites, frameProps, tag) {
* @param frameProps The frameProps of the page in question
* @return The new sites Immutable object
*/
-export function removeSite (sites, frameProps, tag) {
- let index = getSiteUrlIndex(sites, frameProps.get('location'))
+exports.removeSite = function (sites, frameProps, tag) {
+ let index = exports.getSiteUrlIndex(sites, frameProps.get('location'))
if (index === -1) {
return sites
}
@@ -85,3 +88,5 @@ export function removeSite (sites, frameProps, tag) {
let tags = sites.getIn([index, 'tags'])
return sites.setIn([index, 'tags'], tags.toSet().remove(tag).toList())
}
+
+module.exports = exports
diff --git a/js/stores/appStore.js b/js/stores/appStore.js
index 963a626a032..74dade91ca4 100644
--- a/js/stores/appStore.js
+++ b/js/stores/appStore.js
@@ -2,48 +2,79 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
-const AppDispatcher = require('../dispatcher/appDispatcher')
-const EventEmitter = require('events').EventEmitter
+'use strict'
const AppConstants = require('../constants/appConstants')
const Immutable = require('immutable')
const SiteUtil = require('../state/siteUtil')
-const ipc = global.require('electron').ipcRenderer
+const electron = require('electron')
+const ipcMain = electron.ipcMain
const messages = require('../constants/messages')
+const BrowserWindow = electron.BrowserWindow
+const LocalShortcuts = require('../../app/localShortcuts')
-// For this simple example, store immutable data object for a simple counter.
-// This is of course very silly, but this is just for an app template with top
-// level immutable data.
let appState = Immutable.fromJS({
+ windows: [],
sites: [],
visits: [],
updateAvailable: false
})
-var CHANGE_EVENT = 'change'
+const spawnWindow = () => {
+ let mainWindow = new BrowserWindow({
+ width: 1360,
+ height: 800,
+ minWidth: 400,
+ // Neither a frame nor a titlebar
+ // frame: false,
+ // A frame but no title bar and windows buttons in titlebar 10.10 OSX and up only?
+ 'title-bar-style': 'hidden'
+ })
-class AppStore extends EventEmitter {
- getState () {
- return appState
- }
+ // pass the appState into the query string for initialization
+ // This seems kind of hacky, maybe there is a better way to make
+ // sure that the window has the app state before it opens?
+ let queryString = 'appState=' + encodeURIComponent(JSON.stringify(appState))
- emitChange () {
- this.emit(CHANGE_EVENT)
+ if (process.env.NODE_ENV === 'development') {
+ mainWindow.loadURL('file://' + __dirname + '/../../app/index-dev.html?' + queryString)
+ } else {
+ mainWindow.loadURL('file://' + __dirname + '/../../app/index.html?' + queryString)
}
+ mainWindow.on('closed', function () {
+ LocalShortcuts.unregister(mainWindow)
+ mainWindow = null
+ })
- addChangeListener (callback) {
- this.on(CHANGE_EVENT, callback)
+ LocalShortcuts.register(mainWindow)
+ return mainWindow
+}
+
+class AppStore {
+ getState () {
+ return appState
}
- removeChangeListener (callback) {
- this.removeListener(CHANGE_EVENT, callback)
+ emitChange () {
+ ipcMain.emit(messages.APP_STATE_CHANGE, this.getState())
}
}
const appStore = new AppStore()
-// Register callback to handle all updates
-AppDispatcher.register((action) => {
+const handleAppAction = (action) => {
switch (action.actionType) {
+ case AppConstants.APP_NEW_WINDOW:
+ appState = appState.set('windows', appState.get('windows').push(spawnWindow()))
+ appStore.emitChange()
+ break
+ case AppConstants.APP_CLOSE_WINDOW:
+ let appWindow = BrowserWindow.fromId(action.appWindowId)
+ appWindow.close()
+
+ let windows = appState.get('windows')
+ appState = appState.set('windows', windows.delete(windows.indexOf(appWindow)))
+ appStore.emitChange()
+ break
case AppConstants.APP_ADD_SITE:
appState = appState.set('sites', SiteUtil.addSite(appState.get('sites'), action.frameProps, action.tag))
appStore.emitChange()
@@ -54,9 +85,13 @@ AppDispatcher.register((action) => {
break
default:
}
-})
+}
+
+// Register callback to handle all updates
+ipcMain.on(messages.APP_ACTION, (event, action) => handleAppAction(action))
+process.on(messages.APP_ACTION, handleAppAction)
-ipc.on(messages.UPDATE_AVAILABLE, () => {
+process.on(messages.UPDATE_AVAILABLE, () => {
console.log('appStore update-available')
appState = appState.merge({
updateAvailable: true
diff --git a/js/stores/windowStore.js b/js/stores/windowStore.js
index 54a681d9913..57531dc0481 100644
--- a/js/stores/windowStore.js
+++ b/js/stores/windowStore.js
@@ -10,9 +10,6 @@ const FrameStateUtil = require('../state/frameStateUtil')
const ipc = global.require('electron').ipcRenderer
const messages = require('../constants/messages')
-// For this simple example, store immutable data object for a simple counter.
-// This is of course very silly, but this is just for an app template with top
-// level immutable data.
let windowState = Immutable.fromJS({
activeFrameKey: null,
frames: [],