Merge pull request #543 from feross/on-open

Improve open behavior; Fix bugs in LocationHistory
This commit is contained in:
Feross Aboukhadijeh
2016-05-20 16:10:11 -07:00
5 changed files with 139 additions and 109 deletions

View File

@@ -87,7 +87,7 @@ function init () {
// OS integrations: // OS integrations:
// ...drag and drop a torrent or video file to play or seed // ...drag and drop a torrent or video file to play or seed
dragDrop('body', onDrag) dragDrop('body', onOpen)
// ...same thing if you paste a torrent // ...same thing if you paste a torrent
document.addEventListener('paste', onPaste) document.addEventListener('paste', onPaste)
@@ -213,7 +213,6 @@ function dispatch (action, ...args) {
onOpen(args[0] /* files */) onOpen(args[0] /* files */)
} }
if (action === 'addTorrent') { if (action === 'addTorrent') {
backToList()
addTorrent(args[0] /* torrent */) addTorrent(args[0] /* torrent */)
} }
if (action === 'showOpenTorrentFile') { if (action === 'showOpenTorrentFile') {
@@ -274,15 +273,14 @@ function dispatch (action, ...args) {
playPause() playPause()
} }
if (action === 'play') { if (action === 'play') {
if (state.location.pending()) return
state.location.go({ state.location.go({
url: 'player', url: 'player',
onbeforeload: function (cb) { onbeforeload: function (cb) {
play()
openPlayer(args[0] /* infoHash */, args[1] /* index */, cb) openPlayer(args[0] /* infoHash */, args[1] /* index */, cb)
}, },
onbeforeunload: closePlayer onbeforeunload: closePlayer
}) })
play()
} }
if (action === 'playbackJump') { if (action === 'playbackJump') {
jumpToTime(args[0] /* seconds */) jumpToTime(args[0] /* seconds */)
@@ -306,7 +304,7 @@ function dispatch (action, ...args) {
state.playing.isStalled = true state.playing.isStalled = true
} }
if (action === 'mediaError') { if (action === 'mediaError') {
if (state.location.current().url === 'player') { if (state.location.url() === 'player') {
state.playing.location = 'error' state.playing.location = 'error'
ipcRenderer.send('checkForVLC') ipcRenderer.send('checkForVLC')
ipcRenderer.once('checkForVLC', function (e, isInstalled) { ipcRenderer.once('checkForVLC', function (e, isInstalled) {
@@ -386,7 +384,7 @@ function pause () {
} }
function playPause () { function playPause () {
if (state.location.current().url !== 'player') return if (state.location.url() !== 'player') return
if (state.playing.isPaused) { if (state.playing.isPaused) {
play() play()
} else { } else {
@@ -432,18 +430,18 @@ function openSubtitles () {
function backToList () { function backToList () {
// Exit any modals and screens with a back button // Exit any modals and screens with a back button
state.modal = null state.modal = null
while (state.location.hasBack()) state.location.back() state.location.backToFirst(function () {
// If we were already on the torrent list, scroll to the top
var contentTag = document.querySelector('.content')
if (contentTag) contentTag.scrollTop = 0
// If we were already on the torrent list, scroll to the top // Work around virtual-dom issue: it doesn't expose its redraw function,
var contentTag = document.querySelector('.content') // and only redraws on requestAnimationFrame(). That means when the user
if (contentTag) contentTag.scrollTop = 0 // closes the window (hide window / minimize to tray) and we want to pause
// the video, we update the vdom but it keeps playing until you reopen!
// Work around virtual-dom issue: it doesn't expose its redraw function, var mediaTag = document.querySelector('video,audio')
// and only redraws on requestAnimationFrame(). That means when the user if (mediaTag) mediaTag.pause()
// closes the window (hide window / minimize to tray) and we want to pause })
// the video, we update the vdom but it keeps playing until you reopen!
var mediaTag = document.querySelector('video,audio')
if (mediaTag) mediaTag.pause()
} }
// Checks whether we are connected and already casting // Checks whether we are connected and already casting
@@ -556,41 +554,29 @@ function saveState () {
update() update()
} }
// Called when the user clicks a magnet link or torrent, or uses the Open dialog // Called when the user drag-drops files onto the app
function onOpen (files) { function onOpen (files) {
if (!Array.isArray(files)) files = [ files ] if (!Array.isArray(files)) files = [ files ]
// Return to the home screen if (state.modal) {
backToList() state.modal = null
if (files.every(isTorrent)) {
// All .torrent files? Start downloading
files.forEach(addTorrent)
} else {
// Show the Create Torrent screen. Let's seed those files.
showCreateTorrent(files)
} }
}
// Called when the user drag-drops files onto the app var subtitles = files.filter(isSubtitle)
function onDrag (files) {
if (!Array.isArray(files)) files = [ files ]
var isInPlayer = state.location.current().url === 'player' if (state.location.url() === 'home' || subtitles.length === 0) {
var isHome = state.location.current().url === 'home' && !state.modal
if (isInPlayer) {
// In the player, the only drag-drop function is adding subtitles
addSubtitles(files.filter(isSubtitle), true)
} else if (isHome) {
// Otherwise, you can only drag-drop onto the home screen
if (files.every(isTorrent)) { if (files.every(isTorrent)) {
// All .torrent files? Start downloading if (state.location.url() !== 'home') {
backToList()
}
// All .torrent files? Add them.
files.forEach(addTorrent) files.forEach(addTorrent)
} else { } else {
// Show the Create Torrent screen. Let's seed those files. // Show the Create Torrent screen. Let's seed those files.
showCreateTorrent(files) showCreateTorrent(files)
} }
} else if (state.location.url() === 'player') {
addSubtitles(subtitles, true)
} }
update() update()
@@ -620,6 +606,7 @@ function getTorrentSummary (torrentKey) {
// Adds a torrent to the list, starts downloading/seeding. TorrentID can be a // Adds a torrent to the list, starts downloading/seeding. TorrentID can be a
// magnet URI, infohash, or torrent file: https://github.com/feross/webtorrent#clientaddtorrentid-opts-function-ontorrent-torrent- // magnet URI, infohash, or torrent file: https://github.com/feross/webtorrent#clientaddtorrentid-opts-function-ontorrent-torrent-
function addTorrent (torrentId) { function addTorrent (torrentId) {
backToList()
var torrentKey = state.nextTorrentKey++ var torrentKey = state.nextTorrentKey++
var path = state.saved.downloadPath var path = state.saved.downloadPath
if (torrentId.path) { if (torrentId.path) {
@@ -762,7 +749,6 @@ function startTorrentingSummary (torrentSummary) {
// Shows the Create Torrent page with options to seed a given file or folder // Shows the Create Torrent page with options to seed a given file or folder
function showCreateTorrent (files) { function showCreateTorrent (files) {
if (Array.isArray(files)) { if (Array.isArray(files)) {
if (state.location.pending() || state.location.current().url !== 'home') return
state.location.go({ state.location.go({
url: 'create-torrent', url: 'create-torrent',
files: files files: files
@@ -812,6 +798,9 @@ function findFilesRecursive (fileOrFolder, cb) {
function createTorrent (options) { function createTorrent (options) {
var torrentKey = state.nextTorrentKey++ var torrentKey = state.nextTorrentKey++
ipcRenderer.send('wt-create-torrent', torrentKey, options) ipcRenderer.send('wt-create-torrent', torrentKey, options)
state.location.backToFirst(function () {
state.location.clearForward('create-torrent')
})
} }
function torrentInfoHash (torrentKey, infoHash) { function torrentInfoHash (torrentKey, infoHash) {
@@ -1093,7 +1082,7 @@ function deleteTorrent (infoHash) {
var index = state.saved.torrents.findIndex((x) => x.infoHash === infoHash) var index = state.saved.torrents.findIndex((x) => x.infoHash === infoHash)
if (index > -1) state.saved.torrents.splice(index, 1) if (index > -1) state.saved.torrents.splice(index, 1)
saveStateThrottled() saveStateThrottled()
state.location.clearForward() // prevent user from going forward to a deleted torrent state.location.clearForward('player') // prevent user from going forward to a deleted torrent
sound.play('DELETE') sound.play('DELETE')
} }
@@ -1241,7 +1230,7 @@ function showDoneNotification (torrent) {
// * The video is paused // * The video is paused
// * The video is playing remotely on Chromecast or Airplay // * The video is playing remotely on Chromecast or Airplay
function showOrHidePlayerControls () { function showOrHidePlayerControls () {
var hideControls = state.location.current().url === 'player' && var hideControls = state.location.url() === 'player' &&
state.playing.mouseStationarySince !== 0 && state.playing.mouseStationarySince !== 0 &&
new Date().getTime() - state.playing.mouseStationarySince > 2000 && new Date().getTime() - state.playing.mouseStationarySince > 2000 &&
!state.playing.isPaused && !state.playing.isPaused &&

View File

@@ -4,81 +4,123 @@ function LocationHistory () {
if (!new.target) return new LocationHistory() if (!new.target) return new LocationHistory()
this._history = [] this._history = []
this._forward = [] this._forward = []
this._pending = null this._pending = false
} }
LocationHistory.prototype.go = function (page, cb) { LocationHistory.prototype.url = function () {
console.log('go', page) return this.current() && this.current().url
this.clearForward()
this._go(page, cb)
}
LocationHistory.prototype._go = function (page, cb) {
if (this._pending) return
if (page.onbeforeload) {
this._pending = page
page.onbeforeload((err) => {
if (this._pending !== page) return /* navigation was cancelled */
this._pending = null
if (err) {
if (cb) cb(err)
return
}
this._history.push(page)
if (cb) cb()
})
} else {
this._history.push(page)
if (cb) cb()
}
}
LocationHistory.prototype.back = function (cb) {
if (this._history.length <= 1) return
var page = this._history.pop()
if (page.onbeforeunload) {
// TODO: this is buggy. If the user clicks back twice, then those pages
// may end up in _forward in the wrong order depending on which onbeforeunload
// call finishes first.
page.onbeforeunload(() => {
this._forward.push(page)
if (cb) cb()
})
} else {
this._forward.push(page)
if (cb) cb()
}
}
LocationHistory.prototype.forward = function (cb) {
if (this._forward.length === 0) return
var page = this._forward.pop()
this._go(page, cb)
}
LocationHistory.prototype.clearForward = function () {
this._forward = []
} }
LocationHistory.prototype.current = function () { LocationHistory.prototype.current = function () {
return this._history[this._history.length - 1] return this._history[this._history.length - 1]
} }
LocationHistory.prototype.go = function (page, cb) {
if (!cb) cb = noop
if (this._pending) return cb(null)
console.log('go', page)
this.clearForward()
this._go(page, cb)
}
LocationHistory.prototype.back = function (cb) {
var self = this
if (!cb) cb = noop
if (self._history.length <= 1 || self._pending) return cb(null)
var page = self._history.pop()
self._unload(page, done)
function done (err) {
if (err) return cb(err)
self._forward.push(page)
self._load(self.current(), cb)
}
}
LocationHistory.prototype.hasBack = function () { LocationHistory.prototype.hasBack = function () {
return this._history.length > 1 return this._history.length > 1
} }
LocationHistory.prototype.forward = function (cb) {
if (!cb) cb = noop
if (this._forward.length === 0 || this._pending) return cb(null)
var page = this._forward.pop()
this._go(page, cb)
}
LocationHistory.prototype.hasForward = function () { LocationHistory.prototype.hasForward = function () {
return this._forward.length > 0 return this._forward.length > 0
} }
LocationHistory.prototype.pending = function () { LocationHistory.prototype.clearForward = function (url) {
return this._pending if (url == null) {
this._forward = []
} else {
console.log(this._forward)
console.log(url)
this._forward = this._forward.filter(function (page) {
return page.url !== url
})
}
} }
LocationHistory.prototype.clearPending = function () { LocationHistory.prototype.backToFirst = function (cb) {
this._pending = null var self = this
if (!cb) cb = noop
if (self._history.length <= 1) return cb(null)
self.back(function (err) {
if (err) return cb(err)
self.backToFirst(cb)
})
} }
LocationHistory.prototype._go = function (page, cb) {
var self = this
if (!cb) cb = noop
self._unload(self.current(), done1)
function done1 (err) {
if (err) return cb(err)
self._load(page, done2)
}
function done2 (err) {
if (err) return cb(err)
self._history.push(page)
cb(null)
}
}
LocationHistory.prototype._load = function (page, cb) {
var self = this
self._pending = true
if (page && page.onbeforeload) page.onbeforeload(done)
else done(null)
function done (err) {
self._pending = false
cb(err)
}
}
LocationHistory.prototype._unload = function (page, cb) {
var self = this
self._pending = true
if (page && page.onbeforeunload) page.onbeforeunload(done)
else done(null)
function done (err) {
self._pending = false
cb(err)
}
}
function noop () {}

View File

@@ -22,7 +22,7 @@ function App (state) {
// * The mouse is over the controls or we're scrubbing (see CSS) // * The mouse is over the controls or we're scrubbing (see CSS)
// * The video is paused // * The video is paused
// * The video is playing remotely on Chromecast or Airplay // * The video is playing remotely on Chromecast or Airplay
var hideControls = state.location.current().url === 'player' && var hideControls = state.location.url() === 'player' &&
state.playing.mouseStationarySince !== 0 && state.playing.mouseStationarySince !== 0 &&
new Date().getTime() - state.playing.mouseStationarySince > 2000 && new Date().getTime() - state.playing.mouseStationarySince > 2000 &&
!state.playing.isPaused && !state.playing.isPaused &&
@@ -30,10 +30,10 @@ function App (state) {
// Hide the header on Windows/Linux when in the player // Hide the header on Windows/Linux when in the player
// On OSX, the header appears as part of the title bar // On OSX, the header appears as part of the title bar
var hideHeader = process.platform !== 'darwin' && state.location.current().url === 'player' var hideHeader = process.platform !== 'darwin' && state.location.url() === 'player'
var cls = [ var cls = [
'view-' + state.location.current().url, /* e.g. view-home, view-player */ 'view-' + state.location.url(), /* e.g. view-home, view-player */
'is-' + process.platform /* e.g. is-darwin, is-win32, is-linux */ 'is-' + process.platform /* e.g. is-darwin, is-win32, is-linux */
] ]
if (state.window.isFullScreen) cls.push('is-fullscreen') if (state.window.isFullScreen) cls.push('is-fullscreen')
@@ -81,6 +81,6 @@ function getModal (state) {
} }
function getView (state) { function getView (state) {
var url = state.location.current().url var url = state.location.url()
return Views[url](state) return Views[url](state)
} }

View File

@@ -119,11 +119,10 @@ function CreateTorrentPage (state) {
comment: comment comment: comment
} }
dispatch('createTorrent', options) dispatch('createTorrent', options)
dispatch('backToList')
} }
function handleCancel () { function handleCancel () {
dispatch('backToList') dispatch('back')
} }
function handleToggleShowAdvanced () { function handleToggleShowAdvanced () {

View File

@@ -37,7 +37,7 @@ function Header (state) {
} }
function getAddButton () { function getAddButton () {
if (state.location.current().url !== 'player') { if (state.location.url() !== 'player') {
return hx` return hx`
<i <i
class='icon add' class='icon add'