Implement back/forward buttons (fix #180)
This commit is contained in:
@@ -17,10 +17,10 @@ var diff = require('virtual-dom/diff')
|
|||||||
var patch = require('virtual-dom/patch')
|
var patch = require('virtual-dom/patch')
|
||||||
|
|
||||||
var App = require('./views/app')
|
var App = require('./views/app')
|
||||||
var config = require('../config')
|
|
||||||
var torrentPoster = require('./lib/torrent-poster')
|
|
||||||
var TorrentPlayer = require('./lib/torrent-player')
|
|
||||||
var Cast = require('./lib/cast')
|
var Cast = require('./lib/cast')
|
||||||
|
var config = require('../config')
|
||||||
|
var TorrentPlayer = require('./lib/torrent-player')
|
||||||
|
var torrentPoster = require('./lib/torrent-poster')
|
||||||
|
|
||||||
// Electron apps have two processes: a main process (node) runs first and starts
|
// 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,
|
// a renderer process (essentially a Chrome window). We're in the renderer process,
|
||||||
@@ -48,6 +48,8 @@ loadState(init)
|
|||||||
* the dock icon and drag+drop.
|
* the dock icon and drag+drop.
|
||||||
*/
|
*/
|
||||||
function init () {
|
function init () {
|
||||||
|
state.location.go({ url: 'home' })
|
||||||
|
|
||||||
// Connect to the WebTorrent and BitTorrent networks
|
// Connect to the WebTorrent and BitTorrent networks
|
||||||
// WebTorrent.app is a hybrid client, as explained here: https://webtorrent.io/faq
|
// WebTorrent.app is a hybrid client, as explained here: https://webtorrent.io/faq
|
||||||
state.client = new WebTorrent()
|
state.client = new WebTorrent()
|
||||||
@@ -158,7 +160,7 @@ function updateElectron () {
|
|||||||
|
|
||||||
// Events from the UI never modify state directly. Instead they call dispatch()
|
// Events from the UI never modify state directly. Instead they call dispatch()
|
||||||
function dispatch (action, ...args) {
|
function dispatch (action, ...args) {
|
||||||
if (['videoMouseMoved', 'playbackJump'].indexOf(action) < 0) {
|
if (['videoMouseMoved', 'playbackJump'].indexOf(action) === -1) {
|
||||||
console.log('dispatch: %s %o', action, args) /* log user interactions, but don't spam */
|
console.log('dispatch: %s %o', action, args) /* log user interactions, but don't spam */
|
||||||
}
|
}
|
||||||
if (action === 'onOpen') {
|
if (action === 'onOpen') {
|
||||||
@@ -174,8 +176,14 @@ function dispatch (action, ...args) {
|
|||||||
seed(args[0] /* files */)
|
seed(args[0] /* files */)
|
||||||
}
|
}
|
||||||
if (action === 'play') {
|
if (action === 'play') {
|
||||||
// TODO: handle audio. video only for now.
|
state.location.go({
|
||||||
openPlayer(args[0] /* torrentSummary */, args[1] /* index */)
|
url: 'player',
|
||||||
|
onbeforeload: function (cb) {
|
||||||
|
// TODO: handle audio. video only for now.
|
||||||
|
openPlayer(args[0] /* torrentSummary */, args[1] /* index */, cb)
|
||||||
|
},
|
||||||
|
onbeforeunload: closePlayer
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if (action === 'openFile') {
|
if (action === 'openFile') {
|
||||||
openFile(args[0] /* torrentSummary */, args[1] /* index */)
|
openFile(args[0] /* torrentSummary */, args[1] /* index */)
|
||||||
@@ -205,14 +213,12 @@ function dispatch (action, ...args) {
|
|||||||
setDimensions(args[0] /* dimensions */)
|
setDimensions(args[0] /* dimensions */)
|
||||||
}
|
}
|
||||||
if (action === 'back') {
|
if (action === 'back') {
|
||||||
// TODO
|
state.location.back()
|
||||||
// window.history.back()
|
update()
|
||||||
ipcRenderer.send('unblockPowerSave')
|
|
||||||
closePlayer()
|
|
||||||
}
|
}
|
||||||
if (action === 'forward') {
|
if (action === 'forward') {
|
||||||
// TODO
|
state.location.forward()
|
||||||
// window.history.forward()
|
update()
|
||||||
}
|
}
|
||||||
if (action === 'playPause') {
|
if (action === 'playPause') {
|
||||||
playPause()
|
playPause()
|
||||||
@@ -567,7 +573,7 @@ function stopServer () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Opens the video player
|
// Opens the video player
|
||||||
function openPlayer (torrentSummary, index) {
|
function openPlayer (torrentSummary, index, cb) {
|
||||||
var torrent = state.client.get(torrentSummary.infoHash)
|
var torrent = state.client.get(torrentSummary.infoHash)
|
||||||
if (!torrent || !torrent.done) playInterfaceSound(config.SOUND_PLAY)
|
if (!torrent || !torrent.done) playInterfaceSound(config.SOUND_PLAY)
|
||||||
torrentSummary.playStatus = 'requested'
|
torrentSummary.playStatus = 'requested'
|
||||||
@@ -589,9 +595,9 @@ function openPlayer (torrentSummary, index) {
|
|||||||
if (timedOut) return
|
if (timedOut) return
|
||||||
|
|
||||||
// otherwise, play the video
|
// otherwise, play the video
|
||||||
state.url = 'player'
|
|
||||||
state.window.title = torrentSummary.name
|
state.window.title = torrentSummary.name
|
||||||
update()
|
update()
|
||||||
|
cb()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -618,18 +624,20 @@ function openFolder (torrentSummary) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function closePlayer () {
|
function closePlayer (cb) {
|
||||||
state.url = 'home'
|
|
||||||
state.window.title = config.APP_NAME
|
state.window.title = config.APP_NAME
|
||||||
update()
|
update()
|
||||||
|
|
||||||
if (state.window.isFullScreen) {
|
if (state.window.isFullScreen) {
|
||||||
dispatch('toggleFullScreen', false)
|
dispatch('toggleFullScreen', false)
|
||||||
}
|
}
|
||||||
|
|
||||||
restoreBounds()
|
restoreBounds()
|
||||||
stopServer()
|
stopServer()
|
||||||
update()
|
update()
|
||||||
|
|
||||||
|
ipcRenderer.send('unblockPowerSave')
|
||||||
|
|
||||||
|
cb()
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleTorrent (torrentSummary) {
|
function toggleTorrent (torrentSummary) {
|
||||||
@@ -652,6 +660,7 @@ function deleteTorrent (torrentSummary) {
|
|||||||
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)
|
||||||
saveState()
|
saveState()
|
||||||
|
state.location.clearForward() // prevent user from going forward to a deleted torrent
|
||||||
playInterfaceSound(config.SOUND_DELETE)
|
playInterfaceSound(config.SOUND_DELETE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
61
renderer/lib/location-history.js
Normal file
61
renderer/lib/location-history.js
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
module.exports = LocationHistory
|
||||||
|
|
||||||
|
function LocationHistory () {
|
||||||
|
if (!new.target) return new LocationHistory()
|
||||||
|
this._history = []
|
||||||
|
this._forward = []
|
||||||
|
}
|
||||||
|
|
||||||
|
LocationHistory.prototype.go = function (page) {
|
||||||
|
console.log('go', page)
|
||||||
|
this.clearForward()
|
||||||
|
this._go(page)
|
||||||
|
}
|
||||||
|
|
||||||
|
LocationHistory.prototype._go = function (page) {
|
||||||
|
if (page.onbeforeload) {
|
||||||
|
page.onbeforeload((err) => {
|
||||||
|
if (err) return
|
||||||
|
this._history.push(page)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this._history.push(page)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LocationHistory.prototype.back = function () {
|
||||||
|
if (this._history.length <= 1) return
|
||||||
|
|
||||||
|
var page = this._history.pop()
|
||||||
|
|
||||||
|
if (page.onbeforeunload) {
|
||||||
|
page.onbeforeunload(() => {
|
||||||
|
this._forward.push(page)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this._forward.push(page)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LocationHistory.prototype.forward = function () {
|
||||||
|
if (this._forward.length === 0) return
|
||||||
|
|
||||||
|
var page = this._forward.pop()
|
||||||
|
this._go(page)
|
||||||
|
}
|
||||||
|
|
||||||
|
LocationHistory.prototype.clearForward = function () {
|
||||||
|
this._forward = []
|
||||||
|
}
|
||||||
|
|
||||||
|
LocationHistory.prototype.current = function () {
|
||||||
|
return this._history[this._history.length - 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
LocationHistory.prototype.hasBack = function () {
|
||||||
|
return this._history.length > 1
|
||||||
|
}
|
||||||
|
|
||||||
|
LocationHistory.prototype.hasForward = function () {
|
||||||
|
return this._forward.length > 0
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ var os = require('os')
|
|||||||
var path = require('path')
|
var path = require('path')
|
||||||
|
|
||||||
var config = require('../config')
|
var config = require('../config')
|
||||||
|
var LocationHistory = require('./lib/location-history')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
/*
|
/*
|
||||||
@@ -11,9 +12,9 @@ module.exports = {
|
|||||||
client: null, /* the WebTorrent client */
|
client: null, /* the WebTorrent client */
|
||||||
server: null, /* local WebTorrent-to-HTTP server */
|
server: null, /* local WebTorrent-to-HTTP server */
|
||||||
prev: {}, /* used for state diffing in updateElectron() */
|
prev: {}, /* used for state diffing in updateElectron() */
|
||||||
url: 'home',
|
location: new LocationHistory(),
|
||||||
window: {
|
window: {
|
||||||
bounds: null, /* x y width height */
|
bounds: null, /* {x, y, width, height } */
|
||||||
isFocused: true,
|
isFocused: true,
|
||||||
isFullScreen: false,
|
isFullScreen: false,
|
||||||
title: config.APP_NAME /* current window title */
|
title: config.APP_NAME /* current window title */
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ function App (state, dispatch) {
|
|||||||
// * 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.url === 'player' &&
|
var hideControls = state.location.current().url === 'player' &&
|
||||||
state.video.mouseStationarySince !== 0 &&
|
state.video.mouseStationarySince !== 0 &&
|
||||||
new Date().getTime() - state.video.mouseStationarySince > 2000 &&
|
new Date().getTime() - state.video.mouseStationarySince > 2000 &&
|
||||||
!state.video.isPaused &&
|
!state.video.isPaused &&
|
||||||
@@ -25,10 +25,10 @@ function App (state, dispatch) {
|
|||||||
|
|
||||||
// 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.url === 'player'
|
var hideHeader = process.platform !== 'darwin' && state.location.current().url === 'player'
|
||||||
|
|
||||||
var cls = [
|
var cls = [
|
||||||
'view-' + state.url, /* e.g. view-home, view-player */
|
'view-' + state.location.current().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')
|
||||||
@@ -75,9 +75,9 @@ function App (state, dispatch) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getView () {
|
function getView () {
|
||||||
if (state.url === 'home') {
|
if (state.location.current().url === 'home') {
|
||||||
return TorrentList(state, dispatch)
|
return TorrentList(state, dispatch)
|
||||||
} else if (state.url === 'player') {
|
} else if (state.location.current().url === 'player') {
|
||||||
return Player(state, dispatch)
|
return Player(state, dispatch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,14 +9,14 @@ function Header (state, dispatch) {
|
|||||||
<div class='header'>
|
<div class='header'>
|
||||||
${getTitle()}
|
${getTitle()}
|
||||||
<div class='nav left'>
|
<div class='nav left'>
|
||||||
<i
|
<i.icon.back
|
||||||
class='icon back'
|
class=${state.location.hasBack() ? '' : 'disabled'}
|
||||||
title='back'
|
title='back'
|
||||||
onclick=${() => dispatch('back')}>
|
onclick=${() => dispatch('back')}>
|
||||||
chevron_left
|
chevron_left
|
||||||
</i>
|
</i>
|
||||||
<i
|
<i.icon.forward
|
||||||
class='icon forward'
|
class=${state.location.hasForward() ? '' : 'disabled'}
|
||||||
title='forward'
|
title='forward'
|
||||||
onclick=${() => dispatch('forward')}>
|
onclick=${() => dispatch('forward')}>
|
||||||
chevron_right
|
chevron_right
|
||||||
@@ -35,7 +35,7 @@ function Header (state, dispatch) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getAddButton () {
|
function getAddButton () {
|
||||||
if (state.url !== 'player') {
|
if (state.location.current().url !== 'player') {
|
||||||
return hx`
|
return hx`
|
||||||
<i
|
<i
|
||||||
class='icon add'
|
class='icon add'
|
||||||
|
|||||||
Reference in New Issue
Block a user