Torrent list styling

* Download button is a lot easier to read: white down arrow when off, animated and pulsating green down arrow when downloading, solid green up arrow when seeding

* Play button shows a spinner if you click play before a torrent is ready, then an exclamation point if the torrent still isn't ready after 10 seconds

* Drop target shows up always, not just when the torrent list is empty. Lights up when you drag something

* Fixed alignment, the Xs to delete torrents are now aligned with the + to add a new torrent
This commit is contained in:
DC
2016-03-08 03:48:47 -08:00
parent 4c03e98a19
commit ff56d818f6
4 changed files with 71 additions and 45 deletions

View File

@@ -26,7 +26,7 @@ function createMainWindow (menu) {
title: config.APP_NAME, title: config.APP_NAME,
titleBarStyle: 'hidden-inset', // Hide OS chrome, except traffic light buttons (OS X) titleBarStyle: 'hidden-inset', // Hide OS chrome, except traffic light buttons (OS X)
width: 450, width: 450,
height: 450 height: 600
}) })
win.loadURL(config.INDEX) win.loadURL(config.INDEX)

View File

@@ -215,17 +215,6 @@ a:not(.disabled):hover, i:not(.disabled):hover {
flex: 1 1 auto; flex: 1 1 auto;
} }
body.drag::before {
content: '';
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: rgba(255, 0, 0, 0.3);
border: 5px #f00 dashed;
}
/* /*
* PLAYER * PLAYER
*/ */
@@ -256,15 +245,21 @@ body.drag::before {
padding-top: 37px; padding-top: 37px;
} }
.get-started { .drop-target {
opacity: .5; padding: 20px;
padding: 24px; margin: 10px;
margin: 6px;
text-align: center; text-align: center;
border: 5px #444 dashed; border: 5px #444 dashed;
color: #666;
font-size: 16px;
flex: 1 1 auto; flex: 1 1 auto;
} }
body.drag .drop-target {
border-color: #def;
color: #def;
}
.torrent { .torrent {
height: 120px; height: 120px;
background: linear-gradient(to bottom right, #4B79A1, #283E51); background: linear-gradient(to bottom right, #4B79A1, #283E51);
@@ -305,8 +300,8 @@ body.drag::before {
display: flex; display: flex;
} }
.torrent .buttons > :not(:first-child) { .torrent .buttons > * {
margin-left: 10px; /* space buttons by 10px */ margin-right: 6px; /* space buttons apart, align the Xs under the + */
} }
.torrent .buttons .download { .torrent .buttons .download {
@@ -316,6 +311,7 @@ body.drag::before {
border-radius: 14px; border-radius: 14px;
font-size: 18px; font-size: 18px;
padding-top: 6px; padding-top: 6px;
margin-right: 10px; /* download and play btns need more space to look good */
} }
.torrent .buttons .download.downloading { .torrent .buttons .download.downloading {
@@ -326,8 +322,14 @@ body.drag::before {
} }
@keyframes greenpulse { @keyframes greenpulse {
0% { color: #ffffff } 0% {
100% { color: #44dd44 } color: #ffffff;
padding-top: 4px;
}
100% {
color: #44dd44;
padding-top: 6px;
}
} }
.torrent .buttons .download.seeding { .torrent .buttons .download.seeding {
@@ -339,6 +341,25 @@ body.drag::before {
background-color: #F44336; background-color: #F44336;
} }
.torrent.requested .play {
border-top: 6px solid rgba(255, 255, 255, 0.2);
border-right: 6px solid rgba(255, 255, 255, 0.2);
border-bottom: 6px solid rgba(255, 255, 255, 0.2);
border-left: 6px solid #ffffff;
border-radius: 50%;
color: transparent;
animation: load8 1.1s infinite linear;
}
@keyframes load8 {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.torrent .buttons .delete { .torrent .buttons .delete {
opacity: 0.5; opacity: 0.5;
} }

View File

@@ -100,7 +100,7 @@ function init () {
} else { } else {
dispatch('back') dispatch('back')
} }
} else if (e.which === 32) { } else if (e.which === 32) { /* spacebar pauses or plays the video */
dispatch('playPause') dispatch('playPause')
} }
}) })
@@ -434,10 +434,21 @@ function stopServer () {
state.server = null state.server = null
} }
function openPlayer (infoHash) { function openPlayer (torrentSummary) {
startServer(infoHash, function () { torrentSummary.playStatus = 'requested'
var timeout = setTimeout(function () {
torrentSummary.playStatus = 'timeout' /* no seeders available? */
}, 10000) /* give it a few seconds */
startServer(torrentSummary.infoHash, function () {
// if we timed out (user clicked play a long time ago), don't autoplay
clearTimeout(timeout)
var timedOut = torrentSummary.playStatus === 'timeout'
delete torrentSummary.playStatus
if (timedOut) return
// otherwise, play the video
state.url = '/player' state.url = '/player'
/* TODO: set state.title to the clean name of the torrent */ state.title = torrentSummary.name
update() update()
}) })
} }
@@ -455,9 +466,7 @@ function closePlayer () {
update() update()
} }
function toggleTorrent (infoHash) { function toggleTorrent (torrentSummary) {
var torrentSummary = getTorrentSummary(infoHash)
if (!torrentSummary) return
if (torrentSummary.status === 'paused') { if (torrentSummary.status === 'paused') {
torrentSummary.status = 'new' torrentSummary.status = 'new'
startTorrenting(torrentSummary.infoHash) startTorrenting(torrentSummary.infoHash)
@@ -467,7 +476,8 @@ function toggleTorrent (infoHash) {
} }
} }
function deleteTorrent (infoHash) { function deleteTorrent (torrentSummary) {
var infoHash = torrentSummary.infoHash
var torrent = getTorrent(infoHash) var torrent = getTorrent(infoHash)
torrent.destroy() torrent.destroy()

View File

@@ -17,17 +17,13 @@ function prettyBytes (b) {
function TorrentList (state, dispatch) { function TorrentList (state, dispatch) {
var list = state.saved.torrents.map( var list = state.saved.torrents.map(
(torrentSummary) => renderTorrent(torrentSummary, state, dispatch)) (torrentSummary) => renderTorrent(torrentSummary, state, dispatch))
if (list.length === 0) list = emptyList()
return hx`<div class='torrent-list'>${list}</div>`
}
function emptyList () {
return hx` return hx`
<div class="get-started"> <div class='torrent-list'>
<p>No torrents here yet.</p> ${list}
<p>Drop a file or paste an address to get started!</p> <div class='drop-target'>
<p>Drop a torrent file here or paste a magnet link</p>
</div> </div>
` </div>`
} }
// Renders a torrent in the torrent list // Renders a torrent in the torrent list
@@ -48,7 +44,7 @@ function renderTorrent (torrentSummary, state, dispatch) {
// Foreground: name of the torrent, basic info like size, play button, // Foreground: name of the torrent, basic info like size, play button,
// cast buttons if available, and delete // cast buttons if available, and delete
return hx` return hx`
<div class='torrent' style=${style}> <div class='torrent ${torrentSummary.playStatus || ''}' style=${style}>
${renderTorrentMetadata(torrent, torrentSummary)} ${renderTorrentMetadata(torrent, torrentSummary)}
${renderTorrentButtons(torrentSummary, dispatch)} ${renderTorrentButtons(torrentSummary, dispatch)}
</div> </div>
@@ -103,21 +99,20 @@ function renderTorrentMetadata (torrent, torrentSummary) {
// Download button toggles between torrenting (DL/seed) and paused // Download button toggles between torrenting (DL/seed) and paused
// Play button starts streaming the torrent immediately, unpausing if needed // Play button starts streaming the torrent immediately, unpausing if needed
function renderTorrentButtons (torrentSummary, dispatch) { function renderTorrentButtons (torrentSummary, dispatch) {
var infoHash = torrentSummary.infoHash
return hx` return hx`
<div class="buttons"> <div class="buttons">
<i.btn.icon.download <i.btn.icon.download
class='${torrentSummary.status}' class='${torrentSummary.status}'
onclick=${() => dispatch('toggleTorrent', infoHash)}> onclick=${() => dispatch('toggleTorrent', torrentSummary)}>
file_download ${torrentSummary.status === 'seeding' ? 'file_upload' : 'file_download'}
</i> </i>
<i.btn.icon.play <i.btn.icon.play
onclick=${() => dispatch('openPlayer', infoHash)}> onclick=${() => dispatch('openPlayer', torrentSummary)}>
play_arrow ${torrentSummary.playStatus === 'timeout' ? 'warning' : 'play_arrow'}
</i> </i>
<i <i
class='icon delete' class='icon delete'
onclick=${() => dispatch('deleteTorrent', infoHash)}> onclick=${() => dispatch('deleteTorrent', torrentSummary)}>
close close
</i> </i>
</div> </div>