Design: torrent list
This commit is contained in:
@@ -88,25 +88,50 @@ module.exports = class TorrentList extends React.Component {
|
|||||||
if (torrentSummary.error) {
|
if (torrentSummary.error) {
|
||||||
elements.push(
|
elements.push(
|
||||||
<div key='progress-info' className='ellipsis'>
|
<div key='progress-info' className='ellipsis'>
|
||||||
|
{renderDownloadCheckbox()}
|
||||||
|
{renderTorrentStatus()}
|
||||||
{getErrorMessage(torrentSummary)}
|
{getErrorMessage(torrentSummary)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
} else if (torrentSummary.status !== 'paused' && prog) {
|
} else if (torrentSummary.status !== 'paused' && prog) {
|
||||||
elements.push(
|
elements.push(
|
||||||
<div key='progress-info' className='ellipsis'>
|
<div key='progress-info' className='ellipsis'>
|
||||||
|
{renderDownloadCheckbox()}
|
||||||
|
{renderTorrentStatus()}
|
||||||
{renderProgressBar()}
|
{renderProgressBar()}
|
||||||
{renderPercentProgress()}
|
{renderPercentProgress()}
|
||||||
{renderTotalProgress()}
|
{renderTotalProgress()}
|
||||||
{renderPeers()}
|
{renderPeers()}
|
||||||
{renderDownloadSpeed()}
|
{renderSpeeds()}
|
||||||
{renderUploadSpeed()}
|
|
||||||
{renderEta()}
|
{renderEta()}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
elements.push(
|
||||||
|
<div key='progress-info' className='ellipsis'>
|
||||||
|
{renderDownloadCheckbox()}
|
||||||
|
{renderTorrentStatus()}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (<div key='metadata' className='metadata'>{elements}</div>)
|
return (<div key='metadata' className='metadata'>{elements}</div>)
|
||||||
|
|
||||||
|
function renderDownloadCheckbox () {
|
||||||
|
const infoHash = torrentSummary.infoHash
|
||||||
|
const isActive = ['downloading', 'seeding'].includes(torrentSummary.status)
|
||||||
|
return (
|
||||||
|
<Checkbox
|
||||||
|
key='download-button'
|
||||||
|
className={'download ' + torrentSummary.status}
|
||||||
|
style={{display: 'inline-block', width: '32px'}}
|
||||||
|
iconStyle={{width: '20px', height: '20px'}}
|
||||||
|
checked={isActive}
|
||||||
|
onClick={stopPropagation}
|
||||||
|
onCheck={dispatcher('toggleTorrent', infoHash)} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function renderProgressBar () {
|
function renderProgressBar () {
|
||||||
const progress = Math.floor(100 * prog.progress)
|
const progress = Math.floor(100 * prog.progress)
|
||||||
return (<progress value={progress} max='100'>{progress}%</progress>)
|
return (<progress value={progress} max='100'>{progress}%</progress>)
|
||||||
@@ -133,14 +158,12 @@ module.exports = class TorrentList extends React.Component {
|
|||||||
return (<span key='peers'>{prog.numPeers} {count}</span>)
|
return (<span key='peers'>{prog.numPeers} {count}</span>)
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderDownloadSpeed () {
|
function renderSpeeds () {
|
||||||
if (prog.downloadSpeed === 0) return
|
let str = ''
|
||||||
return (<span key='download'>↓ {prettyBytes(prog.downloadSpeed)}/s</span>)
|
if (prog.downloadSpeed > 0) str += prettySpeed(prog.downloadSpeed) + ' ↓ '
|
||||||
}
|
if (prog.uploadSpeed > 0) str += prettySpeed(prog.uploadSpeed) + ' ↑ '
|
||||||
|
if (str === '') return
|
||||||
function renderUploadSpeed () {
|
return (<span key='download'>{str}</span>)
|
||||||
if (prog.uploadSpeed === 0) return
|
|
||||||
return (<span key='upload'>↑ {prettyBytes(prog.uploadSpeed)}/s</span>)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderEta () {
|
function renderEta () {
|
||||||
@@ -161,7 +184,11 @@ module.exports = class TorrentList extends React.Component {
|
|||||||
const minutesStr = (hours || minutes) ? minutes + 'm' : ''
|
const minutesStr = (hours || minutes) ? minutes + 'm' : ''
|
||||||
const secondsStr = seconds + 's'
|
const secondsStr = seconds + 's'
|
||||||
|
|
||||||
return (<span>ETA: {hoursStr} {minutesStr} {secondsStr}</span>)
|
return (<span>{hoursStr} {minutesStr} {secondsStr} remaining</span>)
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderTorrentStatus () {
|
||||||
|
return (<span>{capitalize(torrentSummary.status)}</span>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,53 +196,28 @@ module.exports = class TorrentList extends React.Component {
|
|||||||
// Play button starts streaming the torrent immediately, unpausing if needed
|
// Play button starts streaming the torrent immediately, unpausing if needed
|
||||||
renderTorrentButtons (torrentSummary) {
|
renderTorrentButtons (torrentSummary) {
|
||||||
const infoHash = torrentSummary.infoHash
|
const infoHash = torrentSummary.infoHash
|
||||||
const isActive = ['downloading', 'seeding'].includes(torrentSummary.status)
|
|
||||||
|
|
||||||
let playIcon, playTooltip, playClass
|
let playIcon, playTooltip
|
||||||
if (torrentSummary.playStatus === 'timeout') {
|
if (torrentSummary.playStatus === 'timeout') {
|
||||||
playIcon = 'warning'
|
playIcon = 'warning'
|
||||||
playTooltip = 'Playback timed out. No seeds? No internet? Click to try again.'
|
playTooltip = 'Playback timed out. No seeds? No internet? Click to try again.'
|
||||||
} else {
|
} else {
|
||||||
playIcon = 'play_arrow'
|
playIcon = 'play_circle_outline'
|
||||||
playTooltip = 'Start streaming'
|
playTooltip = 'Start streaming'
|
||||||
}
|
}
|
||||||
|
|
||||||
let downloadTooltip = uppercase(torrentSummary.status)
|
|
||||||
|
|
||||||
// Only show the play/dowload buttons for torrents that contain playable media
|
// Only show the play/dowload buttons for torrents that contain playable media
|
||||||
let playButton, downloadCheckbox, positionElem
|
let playButton
|
||||||
if (!torrentSummary.error) {
|
if (!torrentSummary.error && TorrentPlayer.isPlayableTorrentSummary(torrentSummary)) {
|
||||||
downloadCheckbox = (
|
playButton = (
|
||||||
<Checkbox
|
<i
|
||||||
key='download-button'
|
key='play-button'
|
||||||
className={'download ' + torrentSummary.status}
|
title={playTooltip}
|
||||||
label={downloadTooltip}
|
className={'icon play'}
|
||||||
checked={isActive}
|
onClick={dispatcher('playFile', infoHash)}>
|
||||||
onCheck={dispatcher('toggleTorrent', infoHash)} />
|
{playIcon}
|
||||||
|
</i>
|
||||||
)
|
)
|
||||||
|
|
||||||
// Do we have a saved position? Show it using a radial progress bar on top
|
|
||||||
// of the play button, unless already showing a spinner there:
|
|
||||||
const willShowSpinner = torrentSummary.playStatus === 'requested'
|
|
||||||
const mostRecentFile = torrentSummary.files &&
|
|
||||||
torrentSummary.files[torrentSummary.mostRecentFileIndex]
|
|
||||||
if (mostRecentFile && mostRecentFile.currentTime && !willShowSpinner) {
|
|
||||||
const fraction = mostRecentFile.currentTime / mostRecentFile.duration
|
|
||||||
positionElem = this.renderRadialProgressBar(fraction, 'radial-progress-large')
|
|
||||||
playClass = 'resume-position'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TorrentPlayer.isPlayableTorrentSummary(torrentSummary)) {
|
|
||||||
playButton = (
|
|
||||||
<i
|
|
||||||
key='play-button'
|
|
||||||
title={playTooltip}
|
|
||||||
className={'icon play ' + playClass}
|
|
||||||
onClick={dispatcher('playFile', infoHash)}>
|
|
||||||
{playIcon}
|
|
||||||
</i>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -228,7 +230,6 @@ module.exports = class TorrentList extends React.Component {
|
|||||||
onClick={dispatcher('confirmDeleteTorrent', infoHash, false)}>
|
onClick={dispatcher('confirmDeleteTorrent', infoHash, false)}>
|
||||||
close
|
close
|
||||||
</i>
|
</i>
|
||||||
{downloadCheckbox}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -365,8 +366,12 @@ module.exports = class TorrentList extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function stopPropagation (e) {
|
||||||
|
e.stopPropagation()
|
||||||
|
}
|
||||||
|
|
||||||
// Takes "foo bar", returns "Foo bar"
|
// Takes "foo bar", returns "Foo bar"
|
||||||
function uppercase (str) {
|
function capitalize (str) {
|
||||||
return str.slice(0, 1).toUpperCase() + str.slice(1)
|
return str.slice(0, 1).toUpperCase() + str.slice(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,3 +387,8 @@ function getErrorMessage (torrentSummary) {
|
|||||||
}
|
}
|
||||||
return 'Error'
|
return 'Error'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns '1.9m', '205k', etc
|
||||||
|
function prettySpeed (n) {
|
||||||
|
return prettyBytes(n).replace('B', '').toLowerCase()
|
||||||
|
}
|
||||||
|
|||||||
@@ -338,22 +338,18 @@ textarea,
|
|||||||
}
|
}
|
||||||
|
|
||||||
.torrent:not(:last-child) {
|
.torrent:not(:last-child) {
|
||||||
border-bottom: 1px solid rgb(20, 20, 20);
|
border-bottom: 1px solid #282828;
|
||||||
}
|
}
|
||||||
|
|
||||||
.torrent .metadata {
|
.torrent .metadata {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 25px;
|
top: 20px;
|
||||||
left: 15px;
|
left: 15px;
|
||||||
right: 15px;
|
right: 15px;
|
||||||
width: calc(100% - 40px);
|
width: calc(100% - 30px);
|
||||||
text-shadow: rgba(0, 0, 0, 0.5) 0 0 4px;
|
text-shadow: rgba(0, 0, 0, 0.5) 0 0 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.torrent:hover .metadata {
|
|
||||||
width: calc(100% - 150px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.torrent .metadata span:not(:last-child)::after {
|
.torrent .metadata span:not(:last-child)::after {
|
||||||
content: ' • ';
|
content: ' • ';
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
@@ -361,28 +357,34 @@ textarea,
|
|||||||
padding-right: 4px;
|
padding-right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.torrent:hover .metadata {
|
||||||
|
width: calc(100% - 120px);
|
||||||
|
}
|
||||||
|
|
||||||
.torrent .torrent-controls {
|
.torrent .torrent-controls {
|
||||||
position: absolute;
|
display: none;
|
||||||
top: 29px;
|
|
||||||
right: 10px;
|
|
||||||
align-items: center;
|
|
||||||
display: block /* TODO: none */;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.torrent:hover .torrent-controls {
|
.torrent:hover .torrent-controls {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.torrent .buttons {
|
.torrent .play {
|
||||||
display: flex;
|
position: absolute;
|
||||||
|
top: 23px;
|
||||||
|
right: 45px;
|
||||||
|
font-size: 55px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.torrent .buttons > * {
|
.torrent .delete {
|
||||||
margin-left: 6px; /* space buttons apart */
|
position: absolute;
|
||||||
|
top: 38px;
|
||||||
|
right: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.torrent .buttons .play {
|
.torrent .download {
|
||||||
font-size: 40px;
|
vertical-align: -0.4em;
|
||||||
|
margin-left: -2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.torrent .buttons .radial-progress {
|
.torrent .buttons .radial-progress {
|
||||||
@@ -393,16 +395,19 @@ textarea,
|
|||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
line-height: 1.5em;
|
line-height: 1.5em;
|
||||||
|
margin-bottom: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
progress {
|
progress {
|
||||||
width: 60px;
|
width: 60px;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
progress::-webkit-progress-bar {
|
progress::-webkit-progress-bar {
|
||||||
background-color: #888;
|
background-color: #888;
|
||||||
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
progress::-webkit-progress-value {
|
progress::-webkit-progress-value {
|
||||||
|
|||||||
Reference in New Issue
Block a user