diff --git a/main/ipc.js b/main/ipc.js index 9fdaf938..29bc4ca8 100644 --- a/main/ipc.js +++ b/main/ipc.js @@ -2,9 +2,10 @@ module.exports = { init: init } -var electron = require('electron') var debug = require('debug')('webtorrent-app:ipcMain') +var electron = require('electron') var ipcMain = electron.ipcMain +var menu = require('./menu') var windows = require('./windows') function init () { @@ -28,8 +29,12 @@ function init () { setProgress(progress) }) - ipcMain.on('toggleFullScreen', function (e) { - windows.main.setFullScreen(!windows.main.isFullScreen()) + ipcMain.on('toggleFullScreen', function (e, flag) { + menu.toggleFullScreen(flag) + }) + + ipcMain.on('setTitle', function (e, title) { + windows.main.setTitle(title) }) ipcMain.on('log', function (e, message) { diff --git a/main/menu.js b/main/menu.js index 73b55f5c..3507bcc5 100644 --- a/main/menu.js +++ b/main/menu.js @@ -5,19 +5,21 @@ var windows = require('./windows') var app = electron.app -function toggleFullScreen () { - debug('toggleFullScreen') +function toggleFullScreen (flag) { + debug('toggleFullScreen %s', flag) if (windows.main && windows.main.isVisible()) { - windows.main.setFullScreen(!windows.main.isFullScreen()) + flag = flag != null ? flag : !windows.main.isFullScreen() + windows.main.setFullScreen(flag) } } // Sets whether the window should always show on top of other windows -function toggleFloatOnTop () { - debug('toggleFloatOnTop %s') +function toggleFloatOnTop (flag) { + debug('toggleFloatOnTop %s', flag) if (windows.main) { - windows.main.setAlwaysOnTop(!windows.main.isAlwaysOnTop()) - getMenuItem('Float on Top').checked = windows.main.isAlwaysOnTop() + flag = flag != null ? flag : !windows.main.isAlwaysOnTop() + windows.main.setAlwaysOnTop(flag) + getMenuItem('Float on Top').checked = flag } } @@ -37,6 +39,7 @@ function reloadWindow () { } function addFakeDevice (device) { + debug('addFakeDevice %s', device) windows.main.send('addFakeDevice', device) } @@ -52,10 +55,11 @@ function onWindowHide () { getMenuItem('Float on Top').enabled = false } -function onToggleFullScreen () { - windows.main.setMenuBarVisibility(!windows.main.isFullScreen()) - getMenuItem('Full Screen').checked = windows.main.isFullScreen() - windows.main.send('fullscreenChanged', windows.main.isFullScreen()) +function onToggleFullScreen (isFullScreen) { + isFullScreen = isFullScreen != null ? isFullScreen : windows.main.isFullScreen() + windows.main.setMenuBarVisibility(!isFullScreen) + getMenuItem('Full Screen').checked = isFullScreen + windows.main.send('fullscreenChanged', isFullScreen) } function getMenuItem (label) { @@ -150,12 +154,12 @@ function getMenuTemplate () { if (process.platform === 'darwin') return 'Ctrl+Command+F' else return 'F11' })(), - click: toggleFullScreen + click: () => toggleFullScreen() }, { label: 'Float on Top', type: 'checkbox', - click: toggleFloatOnTop + click: () => toggleFloatOnTop() }, { type: 'separator' diff --git a/main/windows.js b/main/windows.js index 1ab7c3b0..ecdc2c2f 100644 --- a/main/windows.js +++ b/main/windows.js @@ -1,13 +1,14 @@ +var windows = module.exports = { + main: null, + createMainWindow: createMainWindow +} + var config = require('../config') var debug = require('debug')('webtorrent-app:windows') var electron = require('electron') var app = electron.app -var windows = { - main: null, - createMainWindow: createMainWindow -} var isQuitting = false app.on('before-quit', function () { @@ -41,8 +42,8 @@ function createMainWindow (menu) { win.on('blur', menu.onWindowHide) win.on('focus', menu.onWindowShow) - win.on('enter-full-screen', menu.onToggleFullScreen) - win.on('leave-full-screen', menu.onToggleFullScreen) + win.on('enter-full-screen', () => menu.onToggleFullScreen(true)) + win.on('leave-full-screen', () => menu.onToggleFullScreen(false)) win.on('close', function (e) { if (process.platform === 'darwin' && !isQuitting) { @@ -55,5 +56,3 @@ function createMainWindow (menu) { windows.main = null }) } - -module.exports = windows diff --git a/renderer/index.css b/renderer/index.css index c023a7a4..e8d465df 100644 --- a/renderer/index.css +++ b/renderer/index.css @@ -63,7 +63,7 @@ body { height: 100%; display: flex; flex-flow: column; - animation: fadein 1s; + animation: fadein 0.3s; } /* diff --git a/renderer/index.js b/renderer/index.js index ee0acd96..84045280 100644 --- a/renderer/index.js +++ b/renderer/index.js @@ -1,4 +1,5 @@ console.time('init') + var airplay = require('airplay-js') var cfg = require('application-config')('WebTorrent') var cfgDirectory = require('application-config-path')('WebTorrent') @@ -15,11 +16,12 @@ var path = require('path') var torrentPoster = require('./lib/torrent-poster') var WebTorrent = require('webtorrent') +var App = require('./views/app') var createElement = require('virtual-dom/create-element') var diff = require('virtual-dom/diff') var patch = require('virtual-dom/patch') -var App = require('./views/app') +var ipcRenderer = electron.ipcRenderer // For easy debugging in Developer Tools var state = global.state = require('./state') @@ -66,7 +68,10 @@ function init () { // Calling update() updates the UI given the current state // Do this at least once a second to show latest state for each torrent // (eg % downloaded) and to keep the cursor in sync when playing a video - setInterval(update, 1000) + setInterval(function () { + update() + updateClientProgress() + }, 1000) // All state lives in state.js. `state.saved` is read from and written to a // file. All other state is ephemeral. Here we'll load state.saved: @@ -84,7 +89,7 @@ function init () { // ...same thing if you paste a torrent document.addEventListener('paste', function () { - electron.ipcRenderer.send('addTorrentFromPaste') + ipcRenderer.send('addTorrentFromPaste') }) // ...keyboard shortcuts @@ -103,7 +108,6 @@ function init () { // ...focus and blur. Needed to show correct dock icon text ("badge") in OSX window.addEventListener('focus', function () { state.isFocused = true - if (state.dock.badge > 0) electron.ipcRenderer.send('setBadge', '') state.dock.badge = 0 }) @@ -125,7 +129,21 @@ function render (state) { // Calls render() to go from state -> UI, then applies to vdom to the real DOM. function update () { vdomLoop.update(state) - updateDockIcon() + updateElectron() +} + +function updateElectron () { + if (state.title !== state.prev.title) { + state.prev.title = state.title + ipcRenderer.send('setTitle', state.title) + } + if (state.dock.progress !== state.prev.progress) { + state.prev.progress = state.dock.progress + ipcRenderer.send('setProgress', state.dock.progress) + } + if (state.dock.badge !== state.prev.badge) { + ipcRenderer.send('setBadge', state.dock.badge || '') + } } // Events from the UI never modify state directly. Instead they call dispatch() @@ -175,7 +193,7 @@ function dispatch (action, ...args) { update() } if (action === 'toggleFullScreen') { - electron.ipcRenderer.send('toggleFullScreen') + ipcRenderer.send('toggleFullScreen', args[0]) update() } if (action === 'videoMouseMoved') { @@ -185,20 +203,20 @@ function dispatch (action, ...args) { } function setupIpc () { - electron.ipcRenderer.on('addTorrent', function (e, torrentId) { + ipcRenderer.on('addTorrent', function (e, torrentId) { dispatch('addTorrent', torrentId) }) - electron.ipcRenderer.on('seed', function (e, files) { + ipcRenderer.on('seed', function (e, files) { dispatch('seed', files) }) - electron.ipcRenderer.on('fullscreenChanged', function (e, isFullScreen) { + ipcRenderer.on('fullscreenChanged', function (e, isFullScreen) { state.isFullScreen = isFullScreen update() }) - electron.ipcRenderer.on('addFakeDevice', function (e, device) { + ipcRenderer.on('addFakeDevice', function (e, device) { var player = new EventEmitter() player.play = (networkURL) => console.log(networkURL) state.devices[device] = player @@ -220,7 +238,7 @@ function detectDevices () { function loadState (callback) { cfg.read(function (err, data) { if (err) console.error(err) - electron.ipcRenderer.send('log', 'loaded state from ' + cfg.filePath) + ipcRenderer.send('log', 'loaded state from ' + cfg.filePath) // populate defaults if they're not there state.saved = Object.assign({}, state.defaultSavedState, data) @@ -238,14 +256,14 @@ function resumeTorrents () { // Write state.saved to the JSON state file function saveState () { - electron.ipcRenderer.send('log', 'saving state to ' + cfg.filePath) + ipcRenderer.send('log', 'saving state to ' + cfg.filePath) cfg.write(state.saved, function (err) { if (err) console.error(err) update() }) } -function updateDockIcon () { +function updateClientProgress () { var progress = state.client.progress var activeTorrentsExist = state.client.torrents.some(function (torrent) { return torrent.progress !== 1 @@ -254,10 +272,7 @@ function updateDockIcon () { if (!activeTorrentsExist || progress === 1) { progress = -1 } - if (progress !== state.dock.progress) { - state.dock.progress = progress - electron.ipcRenderer.send('setProgress', progress) - } + state.dock.progress = progress } function onFiles (files) { @@ -365,7 +380,6 @@ function addTorrentEvents (torrent) { if (!state.isFocused) { state.dock.badge += 1 - electron.ipcRenderer.send('setBadge', state.dock.badge) } update() @@ -431,8 +445,10 @@ function openPlayer (infoHash) { function closePlayer () { state.url = '/' state.title = config.APP_NAME + update() + if (state.isFullScreen) { - electron.ipcRenderer.send('toggleFullScreen') + dispatch('toggleFullScreen', false) } restoreBounds() stopServer() @@ -507,14 +523,14 @@ function setDimensions (dimensions) { var x = Math.floor((screenWidth - width) / 2) var y = Math.floor((screenHeight - height) / 2) - electron.ipcRenderer.send('setAspectRatio', aspectRatio) - electron.ipcRenderer.send('setBounds', {x, y, width, height}) + ipcRenderer.send('setAspectRatio', aspectRatio) + ipcRenderer.send('setBounds', {x, y, width, height}) } function restoreBounds () { - electron.ipcRenderer.send('setAspectRatio', 0) + ipcRenderer.send('setAspectRatio', 0) if (state.mainWindowBounds) { - electron.ipcRenderer.send('setBounds', state.mainWindowBounds, true) + ipcRenderer.send('setBounds', state.mainWindowBounds, true) } } diff --git a/renderer/state.js b/renderer/state.js index 4fb84a45..8fe8b7a2 100644 --- a/renderer/state.js +++ b/renderer/state.js @@ -31,6 +31,7 @@ module.exports = { duration: 1, /* seconds */ mouseStationarySince: 0 /* Unix time in ms */ }, + prev: {}, /* used for state diffing in updateElectron() */ /* Saved state is read from and written to a file every time the app runs. * It should be simple and minimal and must be JSON.