diff --git a/main/ipc.js b/main/ipc.js index af85c0d6..491ac01a 100644 --- a/main/ipc.js +++ b/main/ipc.js @@ -9,6 +9,10 @@ var menu = require('./menu') var windows = require('./windows') function init () { + ipcMain.on('showOpenTorrentFile', function (e) { + menu.showOpenTorrentFile() + }) + ipcMain.on('setBounds', function (e, bounds) { setBounds(bounds) }) diff --git a/main/menu.js b/main/menu.js index 641918c8..17f71b7c 100644 --- a/main/menu.js +++ b/main/menu.js @@ -71,6 +71,35 @@ function getMenuItem (label) { } } +// Prompts the user for a file or folder, then makes a torrent out of the data +function showCreateTorrent () { + electron.dialog.showOpenDialog({ + title: 'Select a file or folder for the torrent file.', + properties: [ 'openFile', 'openDirectory', 'multiSelections' ] + }, function (filenames) { + if (!Array.isArray(filenames)) return + windows.main.send('dispatch', 'seed', filenames) + }) +} + +// Prompts the user to choose a torrent file, then adds it to the app +function showOpenTorrentFile () { + electron.dialog.showOpenDialog(windows.main, { + title: 'Select a .torrent file to open.', + properties: [ 'openFile', 'multiSelections' ] + }, function (filenames) { + if (!Array.isArray(filenames)) return + filenames.forEach(function (filename) { + windows.main.send('dispatch', 'addTorrent', filename) + }) + }) +} + +// Prompts the user for the URL of a torrent file, then downloads and adds it +function showOpenTorrentAddress () { + windows.main.send('showOpenTorrentAddress') +} + function getMenuTemplate () { var template = [ { @@ -79,44 +108,25 @@ function getMenuTemplate () { { label: 'Create New Torrent...', accelerator: 'CmdOrCtrl+N', - click: function () { - electron.dialog.showOpenDialog({ - title: 'Select a file or folder for the torrent file.', - properties: [ 'openFile', 'openDirectory', 'multiSelections' ] - }, function (filenames) { - if (!Array.isArray(filenames)) return - windows.main.send('dispatch', 'seed', filenames) - }) - } + click: showCreateTorrent }, { label: 'Open Torrent File...', accelerator: 'CmdOrCtrl+O', - click: function () { - electron.dialog.showOpenDialog(windows.main, { - title: 'Select a .torrent file to open.', - properties: [ 'openFile', 'multiSelections' ] - }, function (filenames) { - if (!Array.isArray(filenames)) return - filenames.forEach(function (filename) { - windows.main.send('dispatch', 'addTorrent', filename) - }) - }) - } + click: showOpenTorrentFile }, { label: 'Open Torrent Address...', accelerator: 'CmdOrCtrl+U', - click: function () { electron.dialog.showMessageBox({ message: 'TODO', buttons: ['OK'] }) } + click: showOpenTorrentAddress }, { type: 'separator' }, { - label: (function () { - if (process.platform === 'darwin') return 'Close Window' - else return 'Close' - })(), + label: process.platform === 'darwin' + ? 'Close Window' + : 'Close', accelerator: 'CmdOrCtrl+W', role: 'close' } @@ -153,10 +163,9 @@ function getMenuTemplate () { { label: 'Full Screen', type: 'checkbox', - accelerator: (function () { - if (process.platform === 'darwin') return 'Ctrl+Command+F' - else return 'F11' - })(), + accelerator: process.platform === 'darwin' + ? 'Ctrl+Command+F' + : 'F11', click: toggleFullScreen }, { @@ -177,10 +186,9 @@ function getMenuTemplate () { }, { label: 'Developer Tools', - accelerator: (function () { - if (process.platform === 'darwin') return 'Alt+Command+I' - else return 'Ctrl+Shift+I' - })(), + accelerator: process.platform === 'darwin' + ? 'Alt+Command+I' + : 'Ctrl+Shift+I', click: toggleDevTools }, { @@ -215,18 +223,18 @@ function getMenuTemplate () { submenu: [ { label: 'Learn more about ' + config.APP_NAME, - click: function () { electron.shell.openExternal('https://webtorrent.io') } + click: () => electron.shell.openExternal('https://webtorrent.io') }, { label: 'Contribute on GitHub', - click: function () { electron.shell.openExternal('https://github.com/feross/webtorrent-app') } + click: () => electron.shell.openExternal('https://github.com/feross/webtorrent-app') }, { type: 'separator' }, { label: 'Report an Issue...', - click: function () { electron.shell.openExternal('https://github.com/feross/webtorrent-app/issues') } + click: () => electron.shell.openExternal('https://github.com/feross/webtorrent-app/issues') } ] } @@ -299,5 +307,6 @@ module.exports = { onToggleFullScreen: onToggleFullScreen, onWindowHide: onWindowHide, onWindowShow: onWindowShow, - toggleFullScreen: toggleFullScreen + toggleFullScreen: toggleFullScreen, + showOpenTorrentFile: showOpenTorrentFile } diff --git a/renderer/index.css b/renderer/index.css index b28d2b49..c0adf45d 100644 --- a/renderer/index.css +++ b/renderer/index.css @@ -254,6 +254,81 @@ a:not(.disabled):hover, i:not(.disabled):hover { margin-top: 0; } +/* + * MODAL POPOVERS + */ + +.modal .modal-background { + content: ' '; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: black; + opacity: 0.5; +} + +.modal .modal-content { + position: fixed; + top: 45px; + left: 0; + right: 0; + margin: 0 auto; + width: calc(100% - 20px); + max-width: 600px; + box-shadow: 2px 2px 10px 0px rgba(0,0,0,0.4); + background-color: white; + color: #222; + padding: 20px; +} + +.open-torrent-address-modal input { + width: calc(100% - 100px) +} + +/* + * BUTTONS + */ + +button { + background: transparent; + margin-left: 10px; + padding: 0; + border: none; + font-size: 14px; + font-weight: bold; + text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1); + cursor: pointer; + color: #aaa; +} + +button.primary { + color: #0cf; +} + +button:hover { + -webkit-filter: brightness(1.1); +} + +button:active { + -webkit-filter: brightness(1.1); + text-shadow: none; +} + +/* + * OTHER FORM ELEMENT DEFAULTS + */ + +input { + background: transparent; + width: 300px; + padding: 6px; + border: 1px solid #bbb; + border-radius: 3px; + box-shadow: 1px 1px 1px 0px rgba(0,0,0,0.1); +} + /* * PLAYER */ @@ -384,6 +459,10 @@ body.drag .torrent-placeholder span { background-color: #F44336; } +.torrent.timeout .play { + padding-top: 8px; +} + .torrent.requested .play { border-top: 6px solid rgba(255, 255, 255, 0.2); border-right: 6px solid rgba(255, 255, 255, 0.2); diff --git a/renderer/index.js b/renderer/index.js index 42f21157..848eb3af 100644 --- a/renderer/index.js +++ b/renderer/index.js @@ -4,7 +4,6 @@ var airplay = require('airplay-js') var cfg = require('application-config')('WebTorrent') var cfgDirectory = require('application-config-path')('WebTorrent') var chromecasts = require('chromecasts')() -var config = require('../config') var createTorrent = require('create-torrent') var dragDrop = require('drag-drop') var electron = require('electron') @@ -13,16 +12,21 @@ var fs = require('fs') var mainLoop = require('main-loop') var networkAddress = require('network-address') 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 clipboard = electron.clipboard +var App = require('./views/app') +var config = require('../config') +var torrentPoster = require('./lib/torrent-poster') + +// Electron apps have two processes: a main process (node) runs first and starts +// a renderer process (essentially a Chrome window). We're in the renderer process, +// and this IPC channel receives from and sends messages to the main process var ipcRenderer = electron.ipcRenderer +var clipboard = electron.clipboard // For easy debugging in Developer Tools var state = global.state = require('./state') @@ -94,7 +98,9 @@ function init () { // ...keyboard shortcuts document.addEventListener('keydown', function (e) { if (e.which === 27) { /* ESC means either exit fullscreen or go back */ - if (state.window.isFullScreen) { + if (state.modal) { + dispatch('exitModal') + } else if (state.window.isFullScreen) { dispatch('toggleFullScreen') } else { dispatch('back') @@ -157,6 +163,9 @@ function dispatch (action, ...args) { if (action === 'addTorrent') { addTorrent(args[0] /* torrent */) } + if (action === 'showOpenTorrentFile') { + ipcRenderer.send('showOpenTorrentFile') + } if (action === 'seed') { seed(args[0] /* files */) } @@ -207,6 +216,10 @@ function dispatch (action, ...args) { state.video.mouseStationarySince = new Date().getTime() update() } + if (action === 'exitModal') { + state.modal = null + update() + } } function setupIpc () { @@ -214,6 +227,11 @@ function setupIpc () { dispatch(action, ...args) }) + ipcRenderer.on('showOpenTorrentAddress', function (e) { + state.modal = 'open-torrent-address-modal' + update() + }) + ipcRenderer.on('fullscreenChanged', function (e, isFullScreen) { state.window.isFullScreen = isFullScreen update() @@ -288,7 +306,9 @@ function onFiles (files) { dispatch('seed', files.filter(isNotTorrentFile)) } -function onPaste () { +function onPaste (e) { + if (e.target.tagName.toLowerCase() === 'input') return + var torrentIds = clipboard.readText().split('\n') torrentIds.forEach(function (torrentId) { torrentId = torrentId.trim() @@ -460,6 +480,7 @@ function startServerFromReadyTorrent (torrent, cb) { } function stopServer () { + if (!state.server) return state.server.server.destroy() state.server = null } @@ -520,7 +541,7 @@ function toggleTorrent (torrentSummary) { function deleteTorrent (torrentSummary) { var infoHash = torrentSummary.infoHash var torrent = getTorrent(infoHash) - torrent.destroy() + if (torrent) torrent.destroy() var index = state.saved.torrents.findIndex((x) => x.infoHash === infoHash) if (index > -1) state.saved.torrents.splice(index, 1) diff --git a/renderer/views/app.js b/renderer/views/app.js index 8b12fb98..844ef045 100644 --- a/renderer/views/app.js +++ b/renderer/views/app.js @@ -7,8 +7,9 @@ var hx = hyperx(h) var Header = require('./header') var Player = require('./player') var TorrentList = require('./torrent-list') - -var isOSX = process.platform === 'darwin' +var Modals = { + 'open-torrent-address-modal': require('./open-torrent-address-modal') +} function App (state, dispatch) { // Hide player controls while playing video, if the mouse stays still for a while @@ -28,21 +29,36 @@ function App (state, dispatch) { if (state.window.isFullScreen) cls.push('is-fullscreen') if (state.window.isFocused) cls.push('is-focused') if (hideControls) cls.push('hide-video-controls') - return hx`
Enter torrent address or magnet link
++ + + +
+