Move unrelated code out of menu.js and ipc.js

This commit is contained in:
Feross Aboukhadijeh
2016-05-29 01:09:42 -07:00
parent d4efebd694
commit 62cb304971
16 changed files with 469 additions and 403 deletions

View File

@@ -5,8 +5,10 @@ module.exports = {
openTorrentAddress openTorrentAddress
} }
var config = require('../config')
var electron = require('electron') var electron = require('electron')
var config = require('../config')
var log = require('./log')
var windows = require('./windows') var windows = require('./windows')
/** /**
@@ -14,6 +16,7 @@ var windows = require('./windows')
*/ */
function openSeedFile () { function openSeedFile () {
if (!windows.main.win) return if (!windows.main.win) return
log('openSeedFile')
var opts = { var opts = {
title: 'Select a file for the torrent.', title: 'Select a file for the torrent.',
properties: [ 'openFile' ] properties: [ 'openFile' ]
@@ -22,7 +25,7 @@ function openSeedFile () {
electron.dialog.showOpenDialog(windows.main.win, opts, function (selectedPaths) { electron.dialog.showOpenDialog(windows.main.win, opts, function (selectedPaths) {
resetTitle() resetTitle()
if (!Array.isArray(selectedPaths)) return if (!Array.isArray(selectedPaths)) return
windows.main.send('dispatch', 'showCreateTorrent', selectedPaths) windows.main.dispatch('showCreateTorrent', selectedPaths)
}) })
} }
@@ -33,6 +36,7 @@ function openSeedFile () {
*/ */
function openSeedDirectory () { function openSeedDirectory () {
if (!windows.main.win) return if (!windows.main.win) return
log('openSeedDirectory')
var opts = process.platform === 'darwin' var opts = process.platform === 'darwin'
? { ? {
title: 'Select a file or folder for the torrent.', title: 'Select a file or folder for the torrent.',
@@ -46,7 +50,7 @@ function openSeedDirectory () {
electron.dialog.showOpenDialog(windows.main.win, opts, function (selectedPaths) { electron.dialog.showOpenDialog(windows.main.win, opts, function (selectedPaths) {
resetTitle() resetTitle()
if (!Array.isArray(selectedPaths)) return if (!Array.isArray(selectedPaths)) return
windows.main.send('dispatch', 'showCreateTorrent', selectedPaths) windows.main.dispatch('showCreateTorrent', selectedPaths)
}) })
} }
@@ -55,6 +59,7 @@ function openSeedDirectory () {
*/ */
function openTorrentFile () { function openTorrentFile () {
if (!windows.main.win) return if (!windows.main.win) return
log('openTorrentFile')
var opts = { var opts = {
title: 'Select a .torrent file to open.', title: 'Select a .torrent file to open.',
filters: [{ name: 'Torrent Files', extensions: ['torrent'] }], filters: [{ name: 'Torrent Files', extensions: ['torrent'] }],
@@ -65,7 +70,7 @@ function openTorrentFile () {
resetTitle() resetTitle()
if (!Array.isArray(selectedPaths)) return if (!Array.isArray(selectedPaths)) return
selectedPaths.forEach(function (selectedPath) { selectedPaths.forEach(function (selectedPath) {
windows.main.send('dispatch', 'addTorrent', selectedPath) windows.main.dispatch('addTorrent', selectedPath)
}) })
}) })
} }
@@ -74,7 +79,8 @@ function openTorrentFile () {
* Show modal dialog to open a torrent URL (magnet uri, http torrent link, etc.) * Show modal dialog to open a torrent URL (magnet uri, http torrent link, etc.)
*/ */
function openTorrentAddress () { function openTorrentAddress () {
windows.main.send('showOpenTorrentAddress') log('openTorrentAddress')
windows.main.dispatch('openTorrentAddress')
} }
/** /**
@@ -82,7 +88,7 @@ function openTorrentAddress () {
*/ */
function setTitle (title) { function setTitle (title) {
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
windows.main.send('dispatch', 'setTitle', title) windows.main.dispatch('setTitle', title)
} }
} }

59
main/dock.js Normal file
View File

@@ -0,0 +1,59 @@
module.exports = {
downloadFinished,
init,
setBadge
}
var electron = require('electron')
var app = electron.app
var dialog = require('./dialog')
var log = require('./log')
/**
* Add a right-click menu to the dock icon. (OS X)
*/
function init () {
if (!app.dock) return
var menu = electron.Menu.buildFromTemplate(getMenuTemplate())
app.dock.setMenu(menu)
}
/**
* Bounce the Downloads stack if `path` is inside the Downloads folder. (OS X)
*/
function downloadFinished (path) {
if (!app.dock) return
log(`downloadFinished: ${path}`)
app.dock.downloadFinished(path)
}
/**
* Display string in dock badging area. (OS X)
*/
function setBadge (text) {
if (!app.dock) return
log(`setBadge: ${text}`)
app.dock.setBadge(String(text))
}
function getMenuTemplate () {
return [
{
label: 'Create New Torrent...',
accelerator: 'CmdOrCtrl+N',
click: () => dialog.openSeedDirectory()
},
{
label: 'Open Torrent File...',
accelerator: 'CmdOrCtrl+O',
click: () => dialog.openTorrentFile()
},
{
label: 'Open Torrent Address...',
accelerator: 'CmdOrCtrl+U',
click: () => dialog.openTorrentAddress()
}
]
}

View File

@@ -9,6 +9,7 @@ var announcement = require('./announcement')
var config = require('../config') var config = require('../config')
var crashReporter = require('../crash-reporter') var crashReporter = require('../crash-reporter')
var dialog = require('./dialog') var dialog = require('./dialog')
var dock = require('./dock')
var handlers = require('./handlers') var handlers = require('./handlers')
var ipc = require('./ipc') var ipc = require('./ipc')
var log = require('./log') var log = require('./log')
@@ -61,8 +62,8 @@ function init () {
app.on('ready', function () { app.on('ready', function () {
isReady = true isReady = true
windows.main.create() windows.main.init()
windows.webtorrent.create() windows.webtorrent.init()
menu.init() menu.init()
// To keep app startup fast, some code is delayed. // To keep app startup fast, some code is delayed.
@@ -80,20 +81,21 @@ function init () {
app.isQuitting = true app.isQuitting = true
e.preventDefault() e.preventDefault()
windows.main.send('dispatch', 'saveState') // try to save state on exit windows.main.dispatch('saveState') // try to save state on exit
ipcMain.once('savedState', () => app.quit()) ipcMain.once('savedState', () => app.quit())
setTimeout(() => app.quit(), 2000) // quit after 2 secs, at most setTimeout(() => app.quit(), 2000) // quit after 2 secs, at most
}) })
app.on('activate', function () { app.on('activate', function () {
if (isReady) windows.main.create() if (isReady) windows.main.show()
}) })
} }
function delayedInit () { function delayedInit () {
announcement.init() announcement.init()
tray.init() dock.init()
handlers.install() handlers.install()
tray.init()
updater.init() updater.init()
} }
@@ -101,7 +103,7 @@ function onOpen (e, torrentId) {
e.preventDefault() e.preventDefault()
if (app.ipcReady) { if (app.ipcReady) {
windows.main.send('dispatch', 'onOpen', torrentId) windows.main.dispatch('onOpen', torrentId)
// Magnet links opened from Chrome won't focus the app without a setTimeout. // Magnet links opened from Chrome won't focus the app without a setTimeout.
// The confirmation dialog Chrome shows causes Chrome to steal back the focus. // The confirmation dialog Chrome shows causes Chrome to steal back the focus.
// Electron issue: https://github.com/atom/electron/issues/4338 // Electron issue: https://github.com/atom/electron/issues/4338
@@ -148,6 +150,6 @@ function processArgv (argv) {
} }
}) })
if (paths.length > 0) { if (paths.length > 0) {
windows.main.send('dispatch', 'onOpen', paths) windows.main.dispatch('onOpen', paths)
} }
} }

View File

@@ -6,9 +6,12 @@ var electron = require('electron')
var app = electron.app var app = electron.app
var dialog = require('./dialog')
var dock = require('./dock')
var log = require('./log') var log = require('./log')
var menu = require('./menu') var menu = require('./menu')
var powerSaveBlocker = require('./power-save-blocker') var powerSaveBlocker = require('./power-save-blocker')
var shell = require('./shell')
var shortcuts = require('./shortcuts') var shortcuts = require('./shortcuts')
var vlc = require('./vlc') var vlc = require('./vlc')
var windows = require('./windows') var windows = require('./windows')
@@ -20,15 +23,15 @@ var messageQueueMainToWebTorrent = []
var vlcProcess var vlcProcess
function init () { function init () {
var ipcMain = electron.ipcMain var ipc = electron.ipcMain
ipcMain.on('ipcReady', function (e) { ipc.on('ipcReady', function (e) {
windows.main.show() windows.main.show()
app.ipcReady = true app.ipcReady = true
app.emit('ipcReady') app.emit('ipcReady')
}) })
ipcMain.on('ipcReadyWebTorrent', function (e) { ipc.on('ipcReadyWebTorrent', function (e) {
app.ipcReadyWebTorrent = true app.ipcReadyWebTorrent = true
log('sending %d queued messages from the main win to the webtorrent window', log('sending %d queued messages from the main win to the webtorrent window',
messageQueueMainToWebTorrent.length) messageQueueMainToWebTorrent.length)
@@ -38,109 +41,111 @@ function init () {
}) })
}) })
ipcMain.on('showOpenTorrentFile', () => menu.showOpenTorrentFile()) /**
* Dialog
*/
ipcMain.on('setBounds', function (e, bounds, maximize) { ipc.on('openTorrentFile', () => dialog.openTorrentFile())
setBounds(bounds, maximize)
})
ipcMain.on('setAspectRatio', function (e, aspectRatio) { /**
setAspectRatio(aspectRatio) * Dock
}) */
ipcMain.on('setBadge', function (e, text) { ipc.on('setBadge', (e, ...args) => dock.setBadge(...args))
setBadge(text) ipc.on('downloadFinished', (e, ...args) => dock.downloadFinished(...args))
})
ipcMain.on('setProgress', function (e, progress) { /**
setProgress(progress) * Events
}) */
ipcMain.on('toggleFullScreen', function (e, flag) { ipc.on('onPlayerOpen', function () {
windows.main.toggleFullScreen(flag)
})
ipcMain.on('setTitle', function (e, title) {
windows.main.win.setTitle(title)
})
ipcMain.on('openItem', function (e, path) {
ipc.on('show', (e, ...args) => windows.main.show(...args))
log('open item: ' + path)
electron.shell.openItem(path)
})
ipcMain.on('showItemInFolder', function (e, path) {
log('show item in folder: ' + path)
electron.shell.showItemInFolder(path)
})
ipcMain.on('blockPowerSave', () => powerSaveBlocker.start())
ipcMain.on('unblockPowerSave', () => powerSaveBlocker.stop())
ipcMain.on('onPlayerOpen', function () {
menu.onPlayerOpen() menu.onPlayerOpen()
shortcuts.onPlayerOpen() shortcuts.onPlayerOpen()
}) })
ipcMain.on('onPlayerClose', function () { ipc.on('onPlayerClose', function () {
menu.onPlayerClose() menu.onPlayerClose()
shortcuts.onPlayerOpen() shortcuts.onPlayerOpen()
}) })
ipcMain.on('downloadFinished', function (e, filePath) { /**
if (app.dock) { * Power Save Blocker
// Bounces the Downloads stack if the filePath is inside the Downloads folder. */
app.dock.downloadFinished(filePath)
}
})
ipcMain.on('checkForVLC', function (e) { ipc.on('blockPowerSave', () => powerSaveBlocker.start())
ipc.on('unblockPowerSave', () => powerSaveBlocker.stop())
/**
* Shell
*/
ipc.on('openItem', (e, ...args) => shell.openItem(...args))
ipc.on('showItemInFolder', (e, ...args) => shell.showItemInFolder(...args))
/**
* Windows: Main
*/
var main = windows.main
ipc.on('setAspectRatio', (e, ...args) => main.setAspectRatio(...args))
ipc.on('setBounds', (e, ...args) => main.setBounds(...args))
ipc.on('setProgress', (e, ...args) => main.setProgress(...args))
ipc.on('setTitle', (e, ...args) => main.setTitle(...args))
ipc.on('show', () => main.show())
ipc.on('toggleFullScreen', (e, ...args) => main.toggleFullScreen(...args))
/**
* VLC
* TODO: Move most of this code to vlc.js
*/
ipc.on('checkForVLC', function (e) {
vlc.checkForVLC(function (isInstalled) { vlc.checkForVLC(function (isInstalled) {
windows.main.send('checkForVLC', isInstalled) windows.main.send('checkForVLC', isInstalled)
}) })
}) })
ipcMain.on('vlcPlay', function (e, url) { ipc.on('vlcPlay', function (e, url) {
var args = ['--play-and-exit', '--video-on-top', '--no-video-title-show', '--quiet', url] var args = ['--play-and-exit', '--video-on-top', '--no-video-title-show', '--quiet', url]
console.log('Running vlc ' + args.join(' ')) log('Running vlc ' + args.join(' '))
vlc.spawn(args, function (err, proc) { vlc.spawn(args, function (err, proc) {
if (err) return windows.main.send('dispatch', 'vlcNotFound') if (err) return windows.main.dispatch('vlcNotFound')
vlcProcess = proc vlcProcess = proc
// If it works, close the modal after a second // If it works, close the modal after a second
var closeModalTimeout = setTimeout(() => var closeModalTimeout = setTimeout(() =>
windows.main.send('dispatch', 'exitModal'), 1000) windows.main.dispatch('exitModal'), 1000)
vlcProcess.on('close', function (code) { vlcProcess.on('close', function (code) {
clearTimeout(closeModalTimeout) clearTimeout(closeModalTimeout)
if (!vlcProcess) return // Killed if (!vlcProcess) return // Killed
console.log('VLC exited with code ', code) log('VLC exited with code ', code)
if (code === 0) { if (code === 0) {
windows.main.send('dispatch', 'backToList') windows.main.dispatch('backToList')
} else { } else {
windows.main.send('dispatch', 'vlcNotFound') windows.main.dispatch('vlcNotFound')
} }
vlcProcess = null vlcProcess = null
}) })
vlcProcess.on('error', function (e) { vlcProcess.on('error', function (e) {
console.log('VLC error', e) log('VLC error', e)
}) })
}) })
}) })
ipcMain.on('vlcQuit', function () { ipc.on('vlcQuit', function () {
if (!vlcProcess) return if (!vlcProcess) return
console.log('Killing VLC, pid ' + vlcProcess.pid) log('Killing VLC, pid ' + vlcProcess.pid)
vlcProcess.kill('SIGKILL') // kill -9 vlcProcess.kill('SIGKILL') // kill -9
vlcProcess = null vlcProcess = null
}) })
// Capture all events // Capture all events
var oldEmit = ipcMain.emit var oldEmit = ipc.emit
ipcMain.emit = function (name, e, ...args) { ipc.emit = function (name, e, ...args) {
// Relay messages between the main window and the WebTorrent hidden window // Relay messages between the main window and the WebTorrent hidden window
if (name.startsWith('wt-') && !app.isQuitting) { if (name.startsWith('wt-') && !app.isQuitting) {
if (e.sender.browserWindowOptions.title === 'webtorrent-hidden-window') { if (e.sender.browserWindowOptions.title === 'webtorrent-hidden-window') {
@@ -163,68 +168,6 @@ function init () {
} }
// Emit all other events normally // Emit all other events normally
oldEmit.call(ipcMain, name, e, ...args) oldEmit.call(ipc, name, e, ...args)
} }
} }
function setBounds (bounds, maximize) {
// Do nothing in fullscreen
if (!windows.main.win || windows.main.win.isFullScreen()) {
log('setBounds: not setting bounds because we\'re in full screen')
return
}
// Maximize or minimize, if the second argument is present
var willBeMaximized
if (maximize === true) {
if (!windows.main.win.isMaximized()) {
log('setBounds: maximizing')
windows.main.win.maximize()
}
willBeMaximized = true
} else if (maximize === false) {
if (windows.main.win.isMaximized()) {
log('setBounds: unmaximizing')
windows.main.win.unmaximize()
}
willBeMaximized = false
} else {
willBeMaximized = windows.main.win.isMaximized()
}
// Assuming we're not maximized or maximizing, set the window size
if (!willBeMaximized) {
log('setBounds: setting bounds to ' + JSON.stringify(bounds))
if (bounds.x === null && bounds.y === null) {
// X and Y not specified? By default, center on current screen
var scr = electron.screen.getDisplayMatching(windows.main.win.getBounds())
bounds.x = Math.round(scr.bounds.x + scr.bounds.width / 2 - bounds.width / 2)
bounds.y = Math.round(scr.bounds.y + scr.bounds.height / 2 - bounds.height / 2)
log('setBounds: centered to ' + JSON.stringify(bounds))
}
windows.main.win.setBounds(bounds, true)
} else {
log('setBounds: not setting bounds because of window maximization')
}
}
function setAspectRatio (aspectRatio) {
log('setAspectRatio %o', aspectRatio)
if (!windows.main.win) return
windows.main.win.setAspectRatio(aspectRatio)
}
// Display string in dock badging area (OS X)
function setBadge (text) {
log('setBadge %s', text)
if (app.dock) {
app.dock.setBadge(String(text))
}
}
// Show progress bar. Valid range is [0, 1]. Remove when < 0; indeterminate when > 1.
function setProgress (progress) {
log('setProgress %s', progress)
if (!windows.main.win) return
windows.main.win.setProgressBar(progress)
}

View File

@@ -2,9 +2,10 @@ module.exports = {
init, init,
onPlayerClose, onPlayerClose,
onPlayerOpen, onPlayerOpen,
onToggleAlwaysOnTop,
onToggleFullScreen, onToggleFullScreen,
onWindowHide, onWindowBlur,
onWindowShow onWindowFocus
} }
var electron = require('electron') var electron = require('electron')
@@ -13,148 +14,66 @@ var app = electron.app
var config = require('../config') var config = require('../config')
var dialog = require('./dialog') var dialog = require('./dialog')
var log = require('./log') var shell = require('./shell')
var windows = require('./windows') var windows = require('./windows')
var appMenu var menu
function init () { function init () {
appMenu = electron.Menu.buildFromTemplate(getAppMenuTemplate()) menu = electron.Menu.buildFromTemplate(getMenuTemplate())
electron.Menu.setApplicationMenu(appMenu) electron.Menu.setApplicationMenu(menu)
if (app.dock) {
var dockMenu = electron.Menu.buildFromTemplate(getDockMenuTemplate())
app.dock.setMenu(dockMenu)
}
}
// Sets whether the window should always show on top of other windows
function toggleFloatOnTop (flag) {
if (!windows.main.win) return
log('toggleFloatOnTop %s', flag)
flag = flag != null ? flag : !windows.main.isAlwaysOnTop()
windows.main.setAlwaysOnTop(flag)
getMenuItem('Float on Top').checked = flag
}
function toggleDevTools () {
if (!windows.main.win) return
log('toggleDevTools')
windows.main.toggleDevTools()
}
function showWebTorrentWindow () {
log('showWebTorrentWindow')
windows.webtorrent.show()
windows.webtorrent.win.webContents.openDevTools({ detach: true })
}
function playPause () {
if (!windows.main.win) return
windows.main.send('dispatch', 'playPause')
}
function increaseVolume () {
if (!windows.main.win) return
windows.main.send('dispatch', 'changeVolume', 0.1)
}
function decreaseVolume () {
if (!windows.main.win) return
windows.main.send('dispatch', 'changeVolume', -0.1)
}
function openSubtitles () {
if (!windows.main.win) return
windows.main.send('dispatch', 'openSubtitles')
}
function skipForward () {
if (!windows.main.win) return
windows.main.send('dispatch', 'skip', 1)
}
function skipBack () {
if (!windows.main.win) return
windows.main.send('dispatch', 'skip', -1)
}
function increasePlaybackRate () {
if (!windows.main.win) return
windows.main.send('dispatch', 'changePlaybackRate', 1)
}
function decreasePlaybackRate () {
if (!windows.main.win) return
windows.main.send('dispatch', 'changePlaybackRate', -1)
}
// Open the preferences window
function showPreferences () {
if (!windows.main.win) return
windows.main.send('dispatch', 'preferences')
}
function escapeBack () {
if (!windows.main.win) return
windows.main.send('dispatch', 'escapeBack')
}
function onWindowShow () {
log('onWindowShow')
getMenuItem('Full Screen').enabled = true
getMenuItem('Float on Top').enabled = true
}
function onWindowHide () {
log('onWindowHide')
getMenuItem('Full Screen').enabled = false
getMenuItem('Float on Top').enabled = false
}
function onPlayerOpen () {
log('onPlayerOpen')
getMenuItem('Play/Pause').enabled = true
getMenuItem('Increase Volume').enabled = true
getMenuItem('Decrease Volume').enabled = true
getMenuItem('Add Subtitles File...').enabled = true
getMenuItem('Step Forward').enabled = true
getMenuItem('Step Backward').enabled = true
getMenuItem('Increase Speed').enabled = true
getMenuItem('Decrease Speed').enabled = true
} }
function onPlayerClose () { function onPlayerClose () {
log('onPlayerClose')
getMenuItem('Play/Pause').enabled = false getMenuItem('Play/Pause').enabled = false
getMenuItem('Increase Volume').enabled = false getMenuItem('Increase Volume').enabled = false
getMenuItem('Decrease Volume').enabled = false getMenuItem('Decrease Volume').enabled = false
getMenuItem('Add Subtitles File...').enabled = false
getMenuItem('Step Forward').enabled = false getMenuItem('Step Forward').enabled = false
getMenuItem('Step Backward').enabled = false getMenuItem('Step Backward').enabled = false
getMenuItem('Increase Speed').enabled = false getMenuItem('Increase Speed').enabled = false
getMenuItem('Decrease Speed').enabled = false getMenuItem('Decrease Speed').enabled = false
getMenuItem('Add Subtitles File...').enabled = false
} }
function onToggleFullScreen (isFullScreen) { function onPlayerOpen () {
if (isFullScreen == null) { getMenuItem('Play/Pause').enabled = true
isFullScreen = windows.main.win.isFullScreen() getMenuItem('Increase Volume').enabled = true
} getMenuItem('Decrease Volume').enabled = true
windows.main.win.setMenuBarVisibility(!isFullScreen) getMenuItem('Step Forward').enabled = true
getMenuItem('Full Screen').checked = isFullScreen getMenuItem('Step Backward').enabled = true
windows.main.send('fullscreenChanged', isFullScreen) getMenuItem('Increase Speed').enabled = true
getMenuItem('Decrease Speed').enabled = true
getMenuItem('Add Subtitles File...').enabled = true
}
function onToggleAlwaysOnTop (flag) {
getMenuItem('Float on Top').checked = flag
}
function onToggleFullScreen (flag) {
getMenuItem('Full Screen').checked = flag
}
function onWindowBlur () {
getMenuItem('Full Screen').enabled = false
getMenuItem('Float on Top').enabled = false
}
function onWindowFocus () {
getMenuItem('Full Screen').enabled = true
getMenuItem('Float on Top').enabled = true
} }
function getMenuItem (label) { function getMenuItem (label) {
for (var i = 0; i < appMenu.items.length; i++) { for (var i = 0; i < menu.items.length; i++) {
var menuItem = appMenu.items[i].submenu.items.find(function (item) { var menuItem = menu.items[i].submenu.items.find(function (item) {
return item.label === label return item.label === label
}) })
if (menuItem) return menuItem if (menuItem) return menuItem
} }
} }
function getAppMenuTemplate () { function getMenuTemplate () {
var template = [ var template = [
{ {
label: 'File', label: 'File',
@@ -217,7 +136,7 @@ function getAppMenuTemplate () {
{ {
label: 'Preferences', label: 'Preferences',
accelerator: 'CmdOrCtrl+,', accelerator: 'CmdOrCtrl+,',
click: () => showPreferences() click: () => windows.main.dispatch('preferences')
} }
] ]
}, },
@@ -235,7 +154,7 @@ function getAppMenuTemplate () {
{ {
label: 'Float on Top', label: 'Float on Top',
type: 'checkbox', type: 'checkbox',
click: () => toggleFloatOnTop() click: () => windows.toggleAlwaysOnTop()
}, },
{ {
type: 'separator' type: 'separator'
@@ -243,7 +162,7 @@ function getAppMenuTemplate () {
{ {
label: 'Go Back', label: 'Go Back',
accelerator: 'Esc', accelerator: 'Esc',
click: escapeBack click: () => windows.main.dispatch('escapeBack')
}, },
{ {
type: 'separator' type: 'separator'
@@ -256,14 +175,14 @@ function getAppMenuTemplate () {
accelerator: process.platform === 'darwin' accelerator: process.platform === 'darwin'
? 'Alt+Command+I' ? 'Alt+Command+I'
: 'Ctrl+Shift+I', : 'Ctrl+Shift+I',
click: toggleDevTools click: () => windows.main.toggleDevTools()
}, },
{ {
label: 'Show WebTorrent Process', label: 'Show WebTorrent Process',
accelerator: process.platform === 'darwin' accelerator: process.platform === 'darwin'
? 'Alt+Command+P' ? 'Alt+Command+P'
: 'Ctrl+Shift+P', : 'Ctrl+Shift+P',
click: showWebTorrentWindow click: () => windows.webtorrent.toggleDevTools()
} }
] ]
} }
@@ -275,7 +194,7 @@ function getAppMenuTemplate () {
{ {
label: 'Play/Pause', label: 'Play/Pause',
accelerator: 'Space', accelerator: 'Space',
click: playPause, click: () => windows.main.dispatch('playPause'),
enabled: false enabled: false
}, },
{ {
@@ -284,13 +203,13 @@ function getAppMenuTemplate () {
{ {
label: 'Increase Volume', label: 'Increase Volume',
accelerator: 'CmdOrCtrl+Up', accelerator: 'CmdOrCtrl+Up',
click: increaseVolume, click: () => windows.main.dispatch('changeVolume', 0.1),
enabled: false enabled: false
}, },
{ {
label: 'Decrease Volume', label: 'Decrease Volume',
accelerator: 'CmdOrCtrl+Down', accelerator: 'CmdOrCtrl+Down',
click: decreaseVolume, click: () => windows.main.dispatch('changeVolume', -0.1),
enabled: false enabled: false
}, },
{ {
@@ -299,13 +218,13 @@ function getAppMenuTemplate () {
{ {
label: 'Step Forward', label: 'Step Forward',
accelerator: 'CmdOrCtrl+Alt+Right', accelerator: 'CmdOrCtrl+Alt+Right',
click: skipForward, click: () => windows.main.dispatch('skip', 1),
enabled: false enabled: false
}, },
{ {
label: 'Step Backward', label: 'Step Backward',
accelerator: 'CmdOrCtrl+Alt+Left', accelerator: 'CmdOrCtrl+Alt+Left',
click: skipBack, click: () => windows.main.dispatch('skip', -1),
enabled: false enabled: false
}, },
{ {
@@ -314,13 +233,13 @@ function getAppMenuTemplate () {
{ {
label: 'Increase Speed', label: 'Increase Speed',
accelerator: 'CmdOrCtrl+=', accelerator: 'CmdOrCtrl+=',
click: increasePlaybackRate, click: () => windows.main.dispatch('changePlaybackRate', 1),
enabled: false enabled: false
}, },
{ {
label: 'Decrease Speed', label: 'Decrease Speed',
accelerator: 'CmdOrCtrl+-', accelerator: 'CmdOrCtrl+-',
click: decreasePlaybackRate, click: () => windows.main.dispatch('changePlaybackRate', -1),
enabled: false enabled: false
}, },
{ {
@@ -328,7 +247,7 @@ function getAppMenuTemplate () {
}, },
{ {
label: 'Add Subtitles File...', label: 'Add Subtitles File...',
click: openSubtitles, click: () => windows.main.dispatch('openSubtitles'),
enabled: false enabled: false
} }
] ]
@@ -339,18 +258,18 @@ function getAppMenuTemplate () {
submenu: [ submenu: [
{ {
label: 'Learn more about ' + config.APP_NAME, label: 'Learn more about ' + config.APP_NAME,
click: () => electron.shell.openExternal(config.HOME_PAGE_URL) click: () => shell.openExternal(config.HOME_PAGE_URL)
}, },
{ {
label: 'Contribute on GitHub', label: 'Contribute on GitHub',
click: () => electron.shell.openExternal(config.GITHUB_URL) click: () => shell.openExternal(config.GITHUB_URL)
}, },
{ {
type: 'separator' type: 'separator'
}, },
{ {
label: 'Report an Issue...', label: 'Report an Issue...',
click: () => electron.shell.openExternal(config.GITHUB_URL_ISSUES) click: () => shell.openExternal(config.GITHUB_URL_ISSUES)
} }
] ]
} }
@@ -371,7 +290,7 @@ function getAppMenuTemplate () {
{ {
label: 'Preferences', label: 'Preferences',
accelerator: 'Cmd+,', accelerator: 'Cmd+,',
click: () => showPreferences() click: () => windows.main.dispatch('preferences')
}, },
{ {
type: 'separator' type: 'separator'
@@ -430,7 +349,8 @@ function getAppMenuTemplate () {
}) })
} }
// In Linux and Windows it is not possible to open both folders and files // On Windows and Linux, open dialogs do not support selecting both files and
// folders and files, so add an extra menu item so there is one for each type.
if (process.platform === 'linux' || process.platform === 'win32') { if (process.platform === 'linux' || process.platform === 'win32') {
// File menu (Windows, Linux) // File menu (Windows, Linux)
template[0].submenu.unshift({ template[0].submenu.unshift({
@@ -445,12 +365,12 @@ function getAppMenuTemplate () {
}, },
{ {
label: 'About ' + config.APP_NAME, label: 'About ' + config.APP_NAME,
click: () => windows.about.create() click: () => windows.about.init()
} }
) )
} }
// Add "File > Quit" menu item so Linux distros where the system tray icon is missing // Add "File > Quit" menu item so Linux distros where the system tray icon is
// will have a way to quit the app. // missing will have a way to quit the app.
if (process.platform === 'linux') { if (process.platform === 'linux') {
// File menu (Linux) // File menu (Linux)
template[0].submenu.push({ template[0].submenu.push({
@@ -461,23 +381,3 @@ function getAppMenuTemplate () {
return template return template
} }
function getDockMenuTemplate () {
return [
{
label: 'Create New Torrent...',
accelerator: 'CmdOrCtrl+N',
click: () => dialog.openSeedDirectory()
},
{
label: 'Open Torrent File...',
accelerator: 'CmdOrCtrl+O',
click: () => dialog.openTorrentFile()
},
{
label: 'Open Torrent Address...',
accelerator: 'CmdOrCtrl+U',
click: () => dialog.openTorrentAddress()
}
]
}

View File

@@ -6,20 +6,25 @@ module.exports = {
var electron = require('electron') var electron = require('electron')
var log = require('./log') var log = require('./log')
var powerSaveBlockerId = 0 var blockId = 0
/**
* Block the system from entering low-power (sleep) mode or turning off the
* display.
*/
function start () { function start () {
// Stop the previous power saver block, if one exists. stop() // Stop the previous power saver block, if one exists.
stop() blockId = electron.powerSaveBlocker.start('prevent-display-sleep')
log(`powerSaveBlocker.start: ${blockId}`)
powerSaveBlockerId = electron.powerSaveBlocker.start('prevent-display-sleep')
log('powerSaveBlocker.start %d', powerSaveBlockerId)
} }
/**
* Stop blocking the system from entering low-power mode.
*/
function stop () { function stop () {
if (!electron.powerSaveBlocker.isStarted(powerSaveBlockerId)) { if (!electron.powerSaveBlocker.isStarted(blockId)) {
return return
} }
electron.powerSaveBlocker.stop(powerSaveBlockerId) electron.powerSaveBlocker.stop(blockId)
log('powerSaveBlocker.stop %d', powerSaveBlockerId) log(`powerSaveBlocker.stop: ${blockId}`)
} }

32
main/shell.js Normal file
View File

@@ -0,0 +1,32 @@
module.exports = {
openExternal,
openItem,
showItemInFolder
}
var electron = require('electron')
var log = require('./log')
/**
* Open the given external protocol URL in the desktops default manner.
*/
function openExternal (url) {
log(`openExternal: ${url}`)
electron.shell.openExternal(url)
}
/**
* Open the given file in the desktops default manner.
*/
function openItem (path) {
log(`openItem: ${path}`)
electron.shell.openItem(path)
}
/**
* Show the given file in a file manager. If possible, select the file.
*/
function showItemInFolder (path) {
log(`showItemInFolder: ${path}`)
electron.shell.showItemInFolder(path)
}

View File

@@ -7,13 +7,14 @@ var electron = require('electron')
var windows = require('./windows') var windows = require('./windows')
function onPlayerOpen () { function onPlayerOpen () {
// Register special "media key" for play/pause, available on some keyboards // Register play/pause media key, available on some keyboards.
electron.globalShortcut.register( electron.globalShortcut.register(
'MediaPlayPause', 'MediaPlayPause',
() => windows.main.send('dispatch', 'playPause') () => windows.main.dispatch('playPause')
) )
} }
function onPlayerClose () { function onPlayerClose () {
// Return the media key to the OS, so other apps can use it.
electron.globalShortcut.unregister('MediaPlayPause') electron.globalShortcut.unregister('MediaPlayPause')
} }

View File

@@ -12,8 +12,8 @@ var app = electron.app
var handlers = require('./handlers') var handlers = require('./handlers')
var exeName = path.basename(process.execPath) var EXE_NAME = path.basename(process.execPath)
var updateDotExe = path.join(process.execPath, '..', '..', 'Update.exe') var UPDATE_EXE = path.join(process.execPath, '..', '..', 'Update.exe')
function handleEvent (cmd) { function handleEvent (cmd) {
if (cmd === '--squirrel-install') { if (cmd === '--squirrel-install') {
@@ -102,12 +102,12 @@ function spawn (command, args, cb) {
// Spawn Squirrel's Update.exe with the given arguments and invoke the callback when the // Spawn Squirrel's Update.exe with the given arguments and invoke the callback when the
// command completes. // command completes.
function spawnUpdate (args, cb) { function spawnUpdate (args, cb) {
spawn(updateDotExe, args, cb) spawn(UPDATE_EXE, args, cb)
} }
// Create desktop/start menu shortcuts using the Squirrel Update.exe command line API // Create desktop/start menu shortcuts using the Squirrel Update.exe command line API
function createShortcuts (cb) { function createShortcuts (cb) {
spawnUpdate(['--createShortcut', exeName], cb) spawnUpdate(['--createShortcut', EXE_NAME], cb)
} }
// Update desktop/start menu shortcuts using the Squirrel Update.exe command line API // Update desktop/start menu shortcuts using the Squirrel Update.exe command line API
@@ -135,5 +135,5 @@ function updateShortcuts (cb) {
// Remove desktop/start menu shortcuts using the Squirrel Update.exe command line API // Remove desktop/start menu shortcuts using the Squirrel Update.exe command line API
function removeShortcuts (cb) { function removeShortcuts (cb) {
spawnUpdate(['--removeShortcut', exeName], cb) spawnUpdate(['--removeShortcut', EXE_NAME], cb)
} }

View File

@@ -1,8 +1,8 @@
module.exports = { module.exports = {
hasTray, hasTray,
init, init,
onWindowHide, onWindowBlur,
onWindowShow onWindowFocus
} }
var electron = require('electron') var electron = require('electron')
@@ -24,8 +24,24 @@ function init () {
// OS X apps generally do not have menu bar icons // OS X apps generally do not have menu bar icons
} }
/**
* Returns true if there a tray icon is active.
*/
function hasTray () {
return !!tray
}
function onWindowBlur () {
if (!tray) return
updateTrayMenu()
}
function onWindowFocus () {
if (!tray) return
updateTrayMenu()
}
function initLinux () { function initLinux () {
// Check for libappindicator1 support before creating tray icon
checkLinuxTraySupport(function (supportsTray) { checkLinuxTraySupport(function (supportsTray) {
if (supportsTray) createTray() if (supportsTray) createTray()
}) })
@@ -35,6 +51,9 @@ function initWin32 () {
createTray() createTray()
} }
/**
* Check for libappindicator1 support before creating tray icon
*/
function checkLinuxTraySupport (cb) { function checkLinuxTraySupport (cb) {
var cp = require('child_process') var cp = require('child_process')
@@ -49,65 +68,46 @@ function checkLinuxTraySupport (cb) {
}) })
} }
function hasTray () {
return !!tray
}
function createTray () { function createTray () {
tray = new electron.Tray(getIconPath()) tray = new electron.Tray(getIconPath())
// On Windows, left click opens the app, right click opens the context menu. // On Windows, left click opens the app, right click opens the context menu.
// On Linux, any click (left or right) opens the context menu. // On Linux, any click (left or right) opens the context menu.
tray.on('click', showApp) tray.on('click', () => windows.main.show())
// Show the tray context menu, and keep the available commands up to date // Show the tray context menu, and keep the available commands up to date
updateTrayMenu() updateTrayMenu()
} }
function onWindowHide () {
updateTrayMenu()
}
function onWindowShow () {
updateTrayMenu()
}
function updateTrayMenu () { function updateTrayMenu () {
if (!tray) return var contextMenu = electron.Menu.buildFromTemplate(getMenuTemplate)
tray.setContextMenu(contextMenu)
}
var contextMenu = electron.Menu.buildFromTemplate([ function getMenuTemplate () {
return [
getToggleItem(), getToggleItem(),
{ {
label: 'Quit', label: 'Quit',
click: () => app.quit() click: () => app.quit()
} }
]) ]
tray.setContextMenu(contextMenu)
function getToggleItem () { function getToggleItem () {
if (windows.main.win.isVisible()) { if (windows.main.win.isVisible()) {
return { return {
label: 'Hide to tray', label: 'Hide to tray',
click: hideApp click: () => windows.main.hide()
} }
} else { } else {
return { return {
label: 'Show WebTorrent', label: 'Show WebTorrent',
click: showApp click: () => windows.main.show()
} }
} }
} }
} }
function showApp () {
windows.main.show()
}
function hideApp () {
windows.main.hide()
windows.main.send('dispatch', 'backToList')
}
function getIconPath () { function getIconPath () {
return process.platform === 'win32' return process.platform === 'win32'
? config.APP_ICON + '.ico' ? config.APP_ICON + '.ico'

View File

@@ -21,27 +21,27 @@ function init () {
} }
} }
// The Electron auto-updater does not support Linux yet, so manually check for updates and // The Electron auto-updater does not support Linux yet, so manually check for
// `show the user a modal notification. // updates and show the user a modal notification.
function initLinux () { function initLinux () {
get.concat(AUTO_UPDATE_URL, onResponse) get.concat(AUTO_UPDATE_URL, onResponse)
}
function onResponse (err, res, data) { function onResponse (err, res, data) {
if (err) return log(`Update error: ${err.message}`) if (err) return log(`Update error: ${err.message}`)
if (res.statusCode === 200) { if (res.statusCode === 200) {
// Update available // Update available
try { try {
data = JSON.parse(data) data = JSON.parse(data)
} catch (err) { } catch (err) {
return log(`Update error: Invalid JSON response: ${err.message}`) return log(`Update error: Invalid JSON response: ${err.message}`)
}
windows.main.send('dispatch', 'updateAvailable', data.version)
} else if (res.statusCode === 204) {
// No update available
} else {
// Unexpected status code
log(`Update error: Unexpected status code: ${res.statusCode}`)
} }
windows.main.dispatch('updateAvailable', data.version)
} else if (res.statusCode === 204) {
// No update available
} else {
// Unexpected status code
log(`Update error: Unexpected status code: ${res.statusCode}`)
} }
} }

View File

@@ -1,12 +1,12 @@
var about = module.exports = { var about = module.exports = {
create, init,
win: null win: null
} }
var config = require('../../config') var config = require('../../config')
var electron = require('electron') var electron = require('electron')
function create () { function init () {
if (about.win) { if (about.win) {
return about.win.show() return about.win.show()
} }

View File

@@ -1,8 +1,15 @@
var main = module.exports = { var main = module.exports = {
create, dispatch,
hide, hide,
init,
send, send,
setAspectRatio,
setBounds,
setProgress,
setTitle,
show, show,
toggleAlwaysOnTop,
toggleDevTools,
toggleFullScreen, toggleFullScreen,
win: null win: null
} }
@@ -19,7 +26,7 @@ var tray = require('../tray')
var HEADER_HEIGHT = 37 var HEADER_HEIGHT = 37
var TORRENT_HEIGHT = 100 var TORRENT_HEIGHT = 100
function create () { function init () {
if (main.win) { if (main.win) {
return main.win.show() return main.win.show()
} }
@@ -42,21 +49,30 @@ function create () {
if (win.setSheetOffset) win.setSheetOffset(HEADER_HEIGHT) if (win.setSheetOffset) win.setSheetOffset(HEADER_HEIGHT)
win.webContents.on('dom-ready', function () { win.webContents.on('dom-ready', function () {
menu.onToggleFullScreen() menu.onToggleFullScreen(main.win.isFullScreen())
}) })
win.on('blur', function () { win.on('blur', function () {
menu.onWindowHide() menu.onWindowBlur()
tray.onWindowHide() tray.onWindowBlur()
}) })
win.on('focus', function () { win.on('focus', function () {
menu.onWindowShow() menu.onWindowFocus()
tray.onWindowShow() tray.onWindowFocus()
}) })
win.on('enter-full-screen', () => menu.onToggleFullScreen(true)) win.on('enter-full-screen', function () {
win.on('leave-full-screen', () => menu.onToggleFullScreen(false)) menu.onToggleFullScreen(true)
send('fullscreenChanged', true)
win.setMenuBarVisibility(false)
})
win.on('leave-full-screen', function () {
menu.onToggleFullScreen(false)
send('fullscreenChanged', false)
win.setMenuBarVisibility(true)
})
win.on('close', function (e) { win.on('close', function (e) {
if (process.platform !== 'darwin' && !tray.hasTray()) { if (process.platform !== 'darwin' && !tray.hasTray()) {
@@ -64,15 +80,115 @@ function create () {
} else if (!app.isQuitting) { } else if (!app.isQuitting) {
e.preventDefault() e.preventDefault()
win.hide() win.hide()
win.send('dispatch', 'backToList')
} }
}) })
} }
function getIconPath () { function dispatch (...args) {
return process.platform === 'win32' send('dispatch', ...args)
? config.APP_ICON + '.ico' }
: config.APP_ICON + '.png'
function hide () {
if (!main.win) return
main.win.send('dispatch', 'backToList')
main.win.hide()
}
function send (...args) {
if (!main.win) return
main.win.send(...args)
}
/**
* Enforce window aspect ratio. Remove with 0. (OS X)
*/
function setAspectRatio (aspectRatio) {
if (!main.win) return
main.win.setAspectRatio(aspectRatio)
}
/**
* Change the size of the window.
* TODO: Clean this up? Seems overly complicated.
*/
function setBounds (bounds, maximize) {
// Do nothing in fullscreen
if (!main.win || main.win.isFullScreen()) {
log('setBounds: not setting bounds because we\'re in full screen')
return
}
// Maximize or minimize, if the second argument is present
var willBeMaximized
if (maximize === true) {
if (!main.win.isMaximized()) {
log('setBounds: maximizing')
main.win.maximize()
}
willBeMaximized = true
} else if (maximize === false) {
if (main.win.isMaximized()) {
log('setBounds: unmaximizing')
main.win.unmaximize()
}
willBeMaximized = false
} else {
willBeMaximized = main.win.isMaximized()
}
// Assuming we're not maximized or maximizing, set the window size
if (!willBeMaximized) {
log('setBounds: setting bounds to ' + JSON.stringify(bounds))
if (bounds.x === null && bounds.y === null) {
// X and Y not specified? By default, center on current screen
var scr = electron.screen.getDisplayMatching(main.win.getBounds())
bounds.x = Math.round(scr.bounds.x + scr.bounds.width / 2 - bounds.width / 2)
bounds.y = Math.round(scr.bounds.y + scr.bounds.height / 2 - bounds.height / 2)
log('setBounds: centered to ' + JSON.stringify(bounds))
}
main.win.setBounds(bounds, true)
} else {
log('setBounds: not setting bounds because of window maximization')
}
}
/**
* Set progress bar to [0, 1]. Indeterminate when > 1. Remove with < 0.
*/
function setProgress (progress) {
if (!main.win) return
main.win.setProgressBar(progress)
}
function setTitle (title) {
if (!main.win) return
main.win.setTitle(title)
}
function show () {
if (!main.win) return
main.win.show()
}
// Sets whether the window should always show on top of other windows
function toggleAlwaysOnTop (flag) {
if (!main.win) return
if (flag == null) {
flag = !main.isAlwaysOnTop()
}
log(`toggleAlwaysOnTop ${flag}`)
main.setAlwaysOnTop(flag)
menu.onToggleAlwaysOnTop(flag)
}
function toggleDevTools () {
if (!main.win) return
log('toggleDevTools')
if (main.win.webContents.isDevToolsOpened()) {
main.win.webContents.closeDevTools()
} else {
main.win.webContents.openDevTools({ detach: true })
}
} }
function toggleFullScreen (flag) { function toggleFullScreen (flag) {
@@ -82,29 +198,18 @@ function toggleFullScreen (flag) {
if (flag == null) flag = !main.win.isFullScreen() if (flag == null) flag = !main.win.isFullScreen()
log('toggleFullScreen %s', flag) log(`toggleFullScreen ${flag}`)
if (flag) { if (flag) {
// Fullscreen behaves oddly unless the aspect ratio is disabled. (OS X) // Fullscreen and aspect ratio do not play well together. (OS X)
main.win.setAspectRatio(0) main.win.setAspectRatio(0)
} }
main.win.setFullScreen(flag) main.win.setFullScreen(flag)
} }
function send (...args) { function getIconPath () {
if (!main.win) return return process.platform === 'win32'
main.win.send(...args) ? config.APP_ICON + '.ico'
} : config.APP_ICON + '.png'
function show () {
if (!main.win) return
main.win.show()
}
function hide () {
if (!main.win) return
main.win.hide()
}
} }

View File

@@ -1,14 +1,17 @@
var webtorrent = module.exports = { var webtorrent = module.exports = {
create, init,
send, send,
show, show,
toggleDevTools,
win: null win: null
} }
var config = require('../../config')
var electron = require('electron') var electron = require('electron')
function create () { var config = require('../../config')
var log = require('../log')
function init () {
var win = webtorrent.win = new electron.BrowserWindow({ var win = webtorrent.win = new electron.BrowserWindow({
backgroundColor: '#1E1E1E', backgroundColor: '#1E1E1E',
center: true, center: true,
@@ -46,3 +49,14 @@ function send (...args) {
if (!webtorrent.win) return if (!webtorrent.win) return
webtorrent.win.send(...args) webtorrent.win.send(...args)
} }
function toggleDevTools () {
if (!webtorrent.win) return
log('toggleDevTools')
if (webtorrent.win.webContents.isDevToolsOpened()) {
webtorrent.win.webContents.closeDevTools()
webtorrent.win.hide()
} else {
webtorrent.win.webContents.openDevTools({ detach: true })
}
}

View File

@@ -224,12 +224,16 @@ function dispatch (action, ...args) {
if (action === 'addTorrent') { if (action === 'addTorrent') {
addTorrent(args[0] /* torrent */) addTorrent(args[0] /* torrent */)
} }
if (action === 'showOpenTorrentFile') { if (action === 'openTorrentFile') {
ipcRenderer.send('showOpenTorrentFile') /* open torrent file */ ipcRenderer.send('openTorrentFile') /* open torrent file */
} }
if (action === 'showCreateTorrent') { if (action === 'showCreateTorrent') {
showCreateTorrent(args[0] /* paths */) showCreateTorrent(args[0] /* paths */)
} }
if (action === 'openTorrentAddress') {
state.modal = { id: 'open-torrent-address-modal' }
update()
}
if (action === 'createTorrent') { if (action === 'createTorrent') {
createTorrent(args[0] /* options */) createTorrent(args[0] /* options */)
} }
@@ -511,11 +515,6 @@ function setupIpc () {
ipcRenderer.on('dispatch', (e, ...args) => dispatch(...args)) ipcRenderer.on('dispatch', (e, ...args) => dispatch(...args))
ipcRenderer.on('showOpenTorrentAddress', function (e) {
state.modal = { id: 'open-torrent-address-modal' }
update()
})
ipcRenderer.on('fullscreenChanged', function (e, isFullScreen) { ipcRenderer.on('fullscreenChanged', function (e, isFullScreen) {
state.window.isFullScreen = isFullScreen state.window.isFullScreen = isFullScreen
if (!isFullScreen) { if (!isFullScreen) {

View File

@@ -39,7 +39,7 @@ function Header (state) {
<i <i
class='icon add' class='icon add'
title='Add torrent' title='Add torrent'
onclick=${dispatcher('showOpenTorrentFile')}> onclick=${dispatcher('openTorrentFile')}>
add add
</i> </i>
` `