From 16bcd867929f79e77c27f2bde3a97b7dc0765e84 Mon Sep 17 00:00:00 2001 From: Dick Choi Date: Sat, 19 Dec 2015 00:54:11 +0900 Subject: [PATCH] Refactor main process and add exception handler for Socket server --- atom-lib/finder-window.js | 9 +- atom-lib/main-window.js | 6 + hotkey.js | 62 ++++++++ main.js | 310 ++++++++++++++++++++++++-------------- package.json | 4 +- 5 files changed, 275 insertions(+), 116 deletions(-) create mode 100644 hotkey.js diff --git a/atom-lib/finder-window.js b/atom-lib/finder-window.js index 6defe494..96023702 100644 --- a/atom-lib/finder-window.js +++ b/atom-lib/finder-window.js @@ -6,7 +6,7 @@ const app = electron.app const ipcMain = electron.ipcMain const Tray = electron.Tray const path = require('path') -const nodeIpc = require('node-ipc') +const nodeIpc = require('@rokt33r/node-ipc') var isFinderLoaded = false @@ -18,6 +18,13 @@ nodeIpc.connectTo( 'main', path.join(app.getPath('userData'), 'boost.service'), function () { + nodeIpc.of.main.on( + 'error', + function (err) { + nodeIpc.log('<< ## err ##'.rainbow, nodeIpc.config.delay) + nodeIpc.log(err) + } + ) nodeIpc.of.main.on( 'connect', function () { diff --git a/atom-lib/main-window.js b/atom-lib/main-window.js index 710e6fd6..6802a2b7 100644 --- a/atom-lib/main-window.js +++ b/atom-lib/main-window.js @@ -1,4 +1,5 @@ const electron = require('electron') +const app = electron.app const BrowserWindow = electron.BrowserWindow const path = require('path') @@ -20,4 +21,9 @@ mainWindow.webContents.on('new-window', function (e) { e.preventDefault() }) +app.on('activate', function () { + if (mainWindow == null) return null + mainWindow.show() +}) + module.exports = mainWindow diff --git a/hotkey.js b/hotkey.js new file mode 100644 index 00000000..353a509f --- /dev/null +++ b/hotkey.js @@ -0,0 +1,62 @@ +const electron = require('electron') +const app = electron.app +const Menu = electron.Menu +const ipc = electron.ipcMain +const globalShortcut = electron.globalShortcut +const jetpack = require('fs-jetpack') +const path = require('path') +const mainWindow = require('./atom-lib/main-window') +const nodeIpc = require('@rokt33r/node-ipc') + +var userDataPath = app.getPath('userData') +if (!jetpack.cwd(userDataPath).exists('keymap.json')) { + jetpack.cwd(userDataPath).file('keymap.json', {content: '{}'}) +} +try { + global.keymap = JSON.parse(jetpack.cwd(userDataPath).read('keymap.json', 'utf-8')) +} catch (err) { + jetpack.cwd(userDataPath).file('keymap.json', {content: '{}'}) + global.keymap = {} +} +if (global.keymap.toggleFinder == null) global.keymap.toggleFinder = 'ctrl+tab+shift' +var toggleFinderKey = global.keymap.toggleFinder + +try { + globalShortcut.register(toggleFinderKey, function () { + emitToFinder('open-finder') + mainWindow.webContents.send('open-finder', {}) + }) +} catch (err) { + console.log(err.name) +} + +ipc.on('hotkeyUpdated', function (event, newKeymap) { + console.log('got new keymap') + console.log(newKeymap) + globalShortcut.unregisterAll() + global.keymap = newKeymap + jetpack.cwd(userDataPath).file('keymap.json', {content: JSON.stringify(global.keymap)}) + + var toggleFinderKey = global.keymap.toggleFinder != null ? global.keymap.toggleFinder : 'ctrl+tab+shift' + try { + globalShortcut.register(toggleFinderKey, function () { + emitToFinder('open-finder') + mainWindow.webContents.send('open-finder', {}) + }) + mainWindow.webContents.send('APP_SETTING_DONE', {}) + } catch (err) { + console.error(err) + mainWindow.webContents.send('APP_SETTING_ERROR', { + message: 'Failed to apply hotkey: Invalid format' + }) + } +}) + + +function emitToFinder (type, data) { + var payload = { + type: type, + data: data + } + nodeIpc.server.broadcast('message', payload) +} diff --git a/main.js b/main.js index 0833500f..f5409ba0 100644 --- a/main.js +++ b/main.js @@ -2,22 +2,99 @@ const electron = require('electron') const app = electron.app const Menu = electron.Menu const ipc = electron.ipcMain -const globalShortcut = electron.globalShortcut const autoUpdater = electron.autoUpdater -const jetpack = require('fs-jetpack') const path = require('path') const ChildProcess = require('child_process') const _ = require('lodash') // electron.crashReporter.start() var mainWindow = null -var finderProcess +var finderProcess = null +var finderWindow = null var update = null // app.on('window-all-closed', function () { // if (process.platform !== 'darwin') app.quit() // }) +const appRootPath = path.join(process.execPath, '../..') +const updateDotExePath = path.join(appRootPath, 'Update.exe') +const exeName = path.basename(process.execPath) + +function spawnUpdate (args, cb) { + var stdout = '' + var updateProcess = null + try { + updateProcess = ChildProcess.spawn(updateDotExePath, args) + } catch (e) { + process.nextTick(function () { + cb(e) + }) + } + + updateProcess.stdout.on('data', function (data) { + stdout += data + }) + + error = null + updateProcess.on('error', function (_error) { + error = _error + }) + updateProcess.on('close', function (code, signal) { + if (code !== 0) { + error = new Error("Command failed: #{signal ? code}") + error.code = code + error.stdout = stdout + } + + cb(error, stdout) + }) +} + +var handleStartupEvent = function () { + if (process.platform !== 'win32') { + return false + } + + var squirrelCommand = process.argv[1]; + switch (squirrelCommand) { + case '--squirrel-install': + spawnUpdate(['--createShortcut', exeName], function (err) { + quitApp() + }) + return true + case '--squirrel-updated': + quitApp() + return true + case '--squirrel-uninstall': + spawnUpdate(['--removeShortcut', exeName], function (err) { + quitApp() + }) + quitApp() + return true + case '--squirrel-obsolete': + quitApp() + return true + } +} + +if (handleStartupEvent()) { + return +} + +var shouldQuit = app.makeSingleInstance(function(commandLine, workingDirectory) { + if (mainWindow) { + if (mainWindow.isMinimized()) mainWindow.restore() + mainWindow.focus() + } + return true +}) + +if (shouldQuit) { + app.quit() + return +} + var appQuit = false var version = app.getVersion() @@ -33,46 +110,83 @@ function notify (title, body) { } } -autoUpdater - .on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) { - update = quitAndUpdate +var isUpdateReady = false +if (process.platform === 'darwin') { + autoUpdater.setFeedURL('https://orbital.b00st.io/rokt33r/boost-app/latest?version=' + version) + autoUpdater + .on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) { + update = quitAndUpdate + if (mainWindow != null) { + notify('Ready to Update! ' + releaseName, 'Click update button on Main window.') + mainWindow.webContents.send('update-available', 'Update available!') + } + }) + .on('error', function (err, message) { + console.error('error') + console.error(err) + if (!versionNotified) { + notify('Updater error!', message) + } + }) + .on('update-available', function () { + notify('Update is available!', 'Download started.. wait for the update ready.') + }) + .on('update-not-available', function () { + if (!versionNotified) { + versionNotified = true + notify('Latest Build!! ' + versionText, 'Hope you to enjoy our app :D') + } + }) +} else if (process.platform === 'win32') { + var GhReleases = require('electron-gh-releases') + + var ghReleasesOpts = { + repo: 'BoostIO/boost-releases', + currentVersion: app.getVersion() + } + + const updater = new GhReleases(ghReleasesOpts) + + // Check for updates + // `status` returns true if there is a new update available + function checkUpdate () { + updater.check((err, status) => { + if (err) { + console.error(err) + if (!versionNotified) notify('Updater error!', message) + } + if (!err) { + if (status) { + notify('Update is available!', 'Download started.. wait for the update ready.') + updater.download() + } else { + if (!versionNotified) { + versionNotified = true + notify('Latest Build!! ' + versionText, 'Hope you to enjoy our app :D') + } + } + } + }) + } + + updater.on('update-downloaded', (info) => { if (mainWindow != null) { notify('Ready to Update! ' + releaseName, 'Click update button on Main window.') mainWindow.webContents.send('update-available', 'Update available!') + isUpdateReady = true } }) - .on('error', function (err, message) { - console.error(err) - if (!versionNotified) { - notify('Updater error!', message) - } - }) - // .on('checking-for-update', function () { - // // Connecting - // console.log('checking...') - // }) - .on('update-available', function () { - notify('Update is available!', 'Download started.. wait for the update ready.') - }) - .on('update-not-available', function () { - if (mainWindow != null && !versionNotified) { - versionNotified = true - notify('Latest Build!! ' + versionText, 'Hope you to enjoy our app :D') - } - }) +} - -const nodeIpc = require('node-ipc') -var isNodeIpcReady = false +const nodeIpc = require('@rokt33r/node-ipc') nodeIpc.config.id = 'node' nodeIpc.config.retry = 1500 -nodeIpc.config.silent = true +// nodeIpc.config.silent = true nodeIpc.serve( path.join(app.getPath('userData'), 'boost.service'), - function(){ - isNodeIpcReady = true + function () { nodeIpc.server.on( 'message', function (data, socket) { @@ -80,6 +194,12 @@ nodeIpc.serve( format(data) } ) + nodeIpc.server.on( + 'error', + function (err) { + console.log('>>', err) + } + ) } ) @@ -93,37 +213,61 @@ function format (payload) { mainWindow.webContents.send('copy-finder') break case 'quit-app': - appQuit = true - app.quit() + quitApp() break } } +function quitApp () { + appQuit = true + if (finderProcess) finderProcess.kill() + app.quit() +} app.on('ready', function () { app.on('before-quit', function () { if (finderProcess) finderProcess.kill() appQuit = true }) - autoUpdater.setFeedURL('https://orbital.b00st.io/rokt33r/boost-app/latest?version=' + version) var template = require('./atom-lib/menu-template') var menu = Menu.buildFromTemplate(template) Menu.setApplicationMenu(menu) - setInterval(function () { - if (update == null) autoUpdater.checkForUpdates() - }, 1000 * 60 * 60 * 24) + if (process.platform === 'darwin') { + setInterval(function () { + if (update == null) autoUpdater.checkForUpdates() + }, 1000 * 60 * 60 * 24) - ipc.on('check-update', function (event, msg) { - if (update == null) autoUpdater.checkForUpdates() - }) + ipc.on('check-update', function (event, msg) { + if (update == null) autoUpdater.checkForUpdates() + }) - ipc.on('update-app', function (event, msg) { - if (update != null) { - appQuit = true - update() - } - }) + ipc.on('update-app', function (event, msg) { + if (update != null) { + appQuit = true + update() + } + }) + + autoUpdater.checkForUpdates() + } else if (process.platform === 'win32') { + setInterval(function () { + checkUpdate() + }, 1000 * 60 * 60 * 24) + + ipc.on('check-update', function (event, msg) { + if (update == null) checkUpdate() + }) + + ipc.on('update-app', function (event, msg) { + if (isUpdateReady) { + appQuit = true + update.install() + } + }) + + checkUpdate() + } mainWindow = require('./atom-lib/main-window') mainWindow.on('close', function (e) { @@ -132,25 +276,12 @@ app.on('ready', function () { mainWindow.hide() }) - function emitToFinder (type, data) { - if (!isNodeIpcReady) { - console.log('server is not ready') - } - var payload = { - type: type, - data: data - } - nodeIpc.server.broadcast('message', payload) - } - mainWindow.webContents.on('did-finish-load', function () { if (finderProcess == null && process.platform === 'darwin') { var finderArgv = [path.join(__dirname, 'finder.js'), '--finder'] if (_.find(process.argv, a => a === '--hot')) finderArgv.push('--hot') finderProcess = ChildProcess .execFile(process.execPath, finderArgv) - - nodeIpc.server.start() } else { finderWindow = require('./atom-lib/finder-window') @@ -159,63 +290,16 @@ app.on('ready', function () { e.preventDefault() finderWindow.hide() }) - nodeIpc.server.start() } - if (update != null) { - mainWindow.webContents.send('update-available', 'whoooooooh!') - } else { - autoUpdater.checkForUpdates() - } - }) - - app.on('activate', function () { - if (mainWindow == null) return null - mainWindow.show() - }) - - - var userDataPath = app.getPath('userData') - if (!jetpack.cwd(userDataPath).exists('keymap.json')) { - jetpack.cwd(userDataPath).file('keymap.json', {content: '{}'}) - } - try { - global.keymap = JSON.parse(jetpack.cwd(userDataPath).read('keymap.json', 'utf-8')) - } catch (err) { - jetpack.cwd(userDataPath).file('keymap.json', {content: '{}'}) - global.keymap = {} - } - if (global.keymap.toggleFinder == null) global.keymap.toggleFinder = 'ctrl+tab+shift' - var toggleFinderKey = global.keymap.toggleFinder - - try { - globalShortcut.register(toggleFinderKey, function () { - emitToFinder('open-finder') - mainWindow.webContents.send('open-finder', {}) + nodeIpc.server.start(function (err) { + if (err.code === 'EADDRINUSE') { + notify('Error occurs!', 'You have to kill other Boostnote processes.') + quitApp() + } }) - } catch (err) { - console.log(err.name) - } - ipc.on('hotkeyUpdated', function (event, newKeymap) { - console.log('got new keymap') - console.log(newKeymap) - globalShortcut.unregisterAll() - global.keymap = newKeymap - jetpack.cwd(userDataPath).file('keymap.json', {content: JSON.stringify(global.keymap)}) - - var toggleFinderKey = global.keymap.toggleFinder != null ? global.keymap.toggleFinder : 'ctrl+tab+shift' - try { - globalShortcut.register(toggleFinderKey, function () { - emitToFinder('open-finder') - mainWindow.webContents.send('open-finder', {}) - }) - mainWindow.webContents.send('APP_SETTING_DONE', {}) - } catch (err) { - console.error(err) - mainWindow.webContents.send('APP_SETTING_ERROR', { - message: 'Failed to apply hotkey: Invalid format' - }) - } }) + + require('./hotkey') }) diff --git a/package.json b/package.json index 9fc50387..0129153e 100644 --- a/package.json +++ b/package.json @@ -34,13 +34,14 @@ "short code" ], "author": "Dick Choi (http://kazup.co)", - "license": "No License", "bugs": { "url": "https://github.com/Rokt33r/codexen-app/issues" }, "homepage": "https://github.com/Rokt33r/codexen-app#readme", "dependencies": { + "@rokt33r/node-ipc": "^5.0.4", "devicon": "^2.0.0", + "electron-gh-releases": "^2.0.2", "font-awesome": "^4.3.0", "fs-jetpack": "^0.7.0", "highlight.js": "^8.9.1", @@ -49,7 +50,6 @@ "markdown-it-emoji": "^1.1.0", "md5": "^2.0.0", "moment": "^2.10.3", - "node-ipc": "^5.0.0", "socket.io-client": "^1.3.6", "superagent": "^1.2.0", "superagent-promise": "^1.0.3"