React: fix warnings

This commit is contained in:
DC
2016-07-20 13:49:14 -07:00
parent 946bba19a9
commit a4a31d0860
11 changed files with 148 additions and 110 deletions

View File

@@ -83,7 +83,7 @@
"clean": "node ./bin/clean.js",
"open-config": "node ./bin/open-config.js",
"package": "node ./bin/package.js",
"start": "electron .",
"start": "jsx --es6module src/ build/ && electron .",
"test": "standard && node ./bin/check-deps.js",
"update-authors": "./bin/update-authors.sh"
}

View File

@@ -82,6 +82,7 @@ function getDefaultPlayState () {
lastTimeUpdate: 0, /* Unix time in ms */
mouseStationarySince: 0, /* Unix time in ms */
playbackRate: 1,
volume: 1,
subtitles: {
tracks: [], /* subtitle tracks, each {label, language, ...} */
selectedIndex: -1, /* current subtitle track */

View File

@@ -82,7 +82,7 @@ function onState (err, _state) {
// a progress bar and to keep the cursor in sync when playing a video
setInterval(update, 1000)
window.requestAnimationFrame(renderIfNecessary)
app = ReactDOM.render(<App state={state} />, document.querySelector('body'))
app = ReactDOM.render(<App state={state} />, document.querySelector('#body'))
// OS integrations:
// ...drag and drop a torrent or video file to play or seed
@@ -240,8 +240,8 @@ const dispatchHandlers = {
// Events from the UI never modify state directly. Instead they call dispatch()
function dispatch (action, ...args) {
// Log dispatch calls, for debugging
if (!['mediaMouseMoved', 'mediaTimeUpdate'].includes(action)) {
// Log dispatch calls, for debugging, but don't spam
if (!['mediaMouseMoved', 'mediaTimeUpdate', 'update'].includes(action)) {
console.log('dispatch: %s %o', action, args)
}

View File

@@ -24,7 +24,6 @@ module.exports = class App extends React.Component {
}
render () {
console.time('render app')
var state = this.state
// Hide player controls while playing video, if the mouse stays still for a while
@@ -55,7 +54,7 @@ module.exports = class App extends React.Component {
{getModal(state)}
</div>
)
console.timeEnd('render app')
return vdom
}
}

View File

@@ -0,0 +1,26 @@
const React = require('react')
const {dispatcher} = require('../lib/dispatcher')
module.exports = class CreateTorrentErrorPage extends React.Component {
render () {
return (
<div className='create-torrent'>
<h2>Create torrent</h2>
<p className='torrent-info'>
<p>
Sorry, you must select at least one file that is not a hidden file.
</p>
<p>
Hidden files, starting with a . character, are not included.
</p>
</p>
<p className='float-right'>
<button className='button-flat light' onClick={dispatcher('back')}>
Cancel
</button>
</p>
</div>
)
}
}

View File

@@ -46,12 +46,12 @@ module.exports = class CreateTorrentPage extends React.Component {
basePath = path.dirname(pathPrefix)
}
var maxFileElems = 100
var fileElems = files.slice(0, maxFileElems).map(function (file) {
var fileElems = files.slice(0, maxFileElems).map(function (file, i) {
var relativePath = files.length === 0 ? file.name : path.relative(pathPrefix, file.path)
return (<div>{relativePath}</div>)
return (<div key={i}>{relativePath}</div>)
})
if (files.length > maxFileElems) {
fileElems.push(<div>+ {maxFileElems - files.length} more</div>)
fileElems.push(<div key='more'>+ {maxFileElems - files.length} more</div>)
}
var trackers = createTorrent.announceList.join('\n')
var collapsedClass = info.showAdvanced ? 'expanded' : 'collapsed'
@@ -59,39 +59,39 @@ module.exports = class CreateTorrentPage extends React.Component {
return (
<div className='create-torrent'>
<h2>Create torrent {defaultName}</h2>
<p className='torrent-info'>
<div key='info' className='torrent-info'>
{torrentInfo}
</p>
<p className='torrent-attribute'>
</div>
<div key='path-prefix' className='torrent-attribute'>
<label>Path:</label>
<div className='torrent-attribute'>{pathPrefix}</div>
</p>
<div className={'expand-collapse ' + collapsedClass}
</div>
<div key='toggle' className={'expand-collapse ' + collapsedClass}
onClick={dispatcher('toggleCreateTorrentAdvanced')}>
{info.showAdvanced ? 'Basic' : 'Advanced'}
</div>
<div className={'create-torrent-advanced ' + collapsedClass}>
<p className='torrent-attribute'>
<div key='advanced' className={'create-torrent-advanced ' + collapsedClass}>
<div key='comment' className='torrent-attribute'>
<label>Comment:</label>
<textarea className='torrent-attribute torrent-comment'></textarea>
</p>
<p className='torrent-attribute'>
</div>
<div key='trackers' className='torrent-attribute'>
<label>Trackers:</label>
<textarea className='torrent-attribute torrent-trackers'>{trackers}</textarea>
</p>
<p className='torrent-attribute'>
<textarea className='torrent-attribute torrent-trackers' value={trackers}></textarea>
</div>
<div key='private' className='torrent-attribute'>
<label>Private:</label>
<input type='checkbox' className='torrent-is-private' value='torrent-is-private' />
</p>
<p className='torrent-attribute'>
</div>
<div key='files' className='torrent-attribute'>
<label>Files:</label>
<div>{fileElems}</div>
</p>
</div>
</div>
<div key='buttons' className='float-right'>
<button key='cancel' className='button-flat light' onClick={dispatcher('back')}>Cancel</button>
<button key='create' className='button-raised' onClick={handleOK}>Create Torrent</button>
</div>
<p className='float-right'>
<button className='button-flat light' onClick={dispatcher('back')}>Cancel</button>
<button className='button-raised' onClick={handleOK}>Create Torrent</button>
</p>
</div>
)

View File

@@ -94,6 +94,7 @@ function renderMedia (state) {
var isSelected = state.playing.subtitles.selectedIndex === i
trackTags.push(
<track
key={i}
default={isSelected ? 'default' : ''}
label={track.label}
type='subtitles'
@@ -110,7 +111,7 @@ function renderMedia (state) {
onDoubleClick={dispatcher('toggleFullScreen')}
onLoadedMetadata={onLoadedMetadata}
onEnded={onEnded}
onStalling={dispatcher('mediaStalled')}
onStalled={dispatcher('mediaStalled')}
onError={dispatcher('mediaError')}
onTimeUpdate={dispatcher('mediaTimeUpdate')}
onEncrypted={dispatcher('mediaEncrypted')}
@@ -122,6 +123,7 @@ function renderMedia (state) {
// Show the media.
return (
<div
key='letterbox'
className='letterbox'
onMouseMove={dispatcher('mediaMouseMoved')}>
{mediaTag}
@@ -179,7 +181,7 @@ function renderOverlay (state) {
}
return (
<div className='media-overlay-background' style={style}>
<div key='overlay' className='media-overlay-background' style={style}>
<div className='media-overlay'>{elems}</div>
</div>
)
@@ -209,21 +211,21 @@ function renderAudioMetadata (state) {
var elems = []
if (artist) {
elems.push((
<div className='audio-artist'>
<div key='artist' className='audio-artist'>
<label>Artist</label>{artist}
</div>
))
}
if (album) {
elems.push((
<div className='audio-album'>
<div key='album' className='audio-album'>
<label>Album</label>{album}
</div>
))
}
if (track) {
elems.push((
<div className='audio-track'>
<div key='track' className='audio-track'>
<label>Track</label>{track}
</div>
))
@@ -232,12 +234,12 @@ function renderAudioMetadata (state) {
// Align the title with the other info, if available. Otherwise, center title
var emptyLabel = (<label></label>)
elems.unshift((
<div className='audio-title'>
<div key='title' className='audio-title'>
{elems.length ? emptyLabel : undefined}{title}
</div>
))
return (<div className='audio-metadata'>{elems}</div>)
return (<div key='audio-metadata' className='audio-metadata'>{elems}</div>)
}
function renderLoadingSpinner (state) {
@@ -254,9 +256,9 @@ function renderLoadingSpinner (state) {
}
return (
<div className='media-stalled'>
<div className='loading-spinner'>&nbsp;</div>
<div className='loading-status ellipsis'>
<div key='loading' className='media-stalled'>
<div key='loading-spinner' className='loading-spinner'>&nbsp;</div>
<div key='loading-progress' className='loading-status ellipsis'>
<span className='progress'>{fileProgress}%</span> downloaded,
<span> {prettyBytes(prog.downloadSpeed || 0)}/s</span>
<span> {prettyBytes(prog.uploadSpeed || 0)}/s</span>
@@ -302,11 +304,11 @@ function renderCastScreen (state) {
}
return (
<div className='letterbox' style={style}>
<div key='cast' className='letterbox' style={style}>
<div className='cast-screen'>
<i className='icon'>{castIcon}</i>
<div className='cast-type'>{castType}</div>
<div className='cast-status'>{castStatus}</div>
<div key='type' className='cast-type'>{castType}</div>
<div key='status' className='cast-status'>{castStatus}</div>
</div>
</div>
)
@@ -322,7 +324,7 @@ function renderCastOptions (state) {
var isSelected = player.device === device
var name = device.name
return (
<li onClick={dispatcher('selectCastDevice', ix)}>
<li key={ix} onClick={dispatcher('selectCastDevice', ix)}>
<i className='icon'>{isSelected ? 'radio_button_checked' : 'radio_button_unchecked'}</i>
{name}
</li>
@@ -330,20 +332,20 @@ function renderCastOptions (state) {
})
return (
<ul className='options-list'>
<ul key='cast-options' className='options-list'>
{items}
</ul>
)
}
function renderSubtitlesOptions (state) {
function renderSubtitleOptions (state) {
var subtitles = state.playing.subtitles
if (!subtitles.tracks.length || !subtitles.showMenu) return
var items = subtitles.tracks.map(function (track, ix) {
var isSelected = state.playing.subtitles.selectedIndex === ix
return (
<li onClick={dispatcher('selectSubtitle', ix)}>
<li key={ix} onClick={dispatcher('selectSubtitle', ix)}>
<i className='icon'>{'radio_button_' + (isSelected ? 'checked' : 'unchecked')}</i>
{track.label}
</li>
@@ -353,7 +355,7 @@ function renderSubtitlesOptions (state) {
var noneSelected = state.playing.subtitles.selectedIndex === -1
var noneClass = 'radio_button_' + (noneSelected ? 'checked' : 'unchecked')
return (
<ul className='options-list'>
<ul key='subtitle-options' className='options-list'>
{items}
<li onClick={dispatcher('selectSubtitle', -1)}>
<i className='icon'>{noneClass}</i>
@@ -373,13 +375,15 @@ function renderPlayerControls (state) {
: ''
var elements = [
<div className='playback-bar'>
<div key='playback-bar' className='playback-bar'>
{renderLoadingBar(state)}
<div
key='cursor'
className='playback-cursor'
style={playbackCursorStyle}>
</div>
<div
key='scrub-bar'
className='scrub-bar'
draggable='true'
onDragStart={handleDragStart}
@@ -388,11 +392,15 @@ function renderPlayerControls (state) {
</div>
</div>,
<i className='icon play-pause float-left' onClick={dispatcher('playPause')}>
<i
key='play'
className='icon play-pause float-left'
onClick={dispatcher('playPause')}>
{state.playing.isPaused ? 'play_arrow' : 'pause'}
</i>,
<i
key='fullscreen'
className='icon fullscreen float-right'
onClick={dispatcher('toggleFullScreen')}>
{state.window.isFullScreen ? 'fullscreen_exit' : 'fullscreen'}
@@ -403,6 +411,7 @@ function renderPlayerControls (state) {
// show closed captions icon
elements.push((
<i
key='subtitles'
className={'icon closed-caption float-right ' + captionsClass}
onClick={handleSubtitles}>
closed_caption
@@ -446,6 +455,7 @@ function renderPlayerControls (state) {
elements.push((
<i
key={castType}
className={'icon device float-right ' + buttonClass}
onClick={buttonHandler}>
{buttonIcon}
@@ -466,8 +476,10 @@ function renderPlayerControls (state) {
'color-stop(' + (volume * 100) + '%, #727272))'
}
//TODO: dcposch change the range input to use value / onChanged instead of
// "readonly" / onMouse[Down,Move,Up]
elements.push((
<div className='volume float-left'>
<div key='volume' className='volume float-left'>
<i
className='icon volume-icon float-left'
onMouseDown={handleVolumeMute}>
@@ -476,10 +488,8 @@ function renderPlayerControls (state) {
<input
className='volume-slider float-right'
type='range' min='0' max='1' step='0.05'
value={volumeChanging !== false ? volumeChanging : volume}
onMouseDown={handleVolumeScrub}
onMouseUp={handleVolumeScrub}
onMouseMove={handleVolumeScrub}
value={volume}
onChange={handleVolumeScrub}
style={volumeStyle}
/>
</div>
@@ -489,7 +499,7 @@ function renderPlayerControls (state) {
var currentTimeStr = formatTime(state.playing.currentTime)
var durationStr = formatTime(state.playing.duration)
elements.push((
<span className='time float-left'>
<span key='time' className='time float-left'>
{currentTimeStr} / {durationStr}
</span>
))
@@ -497,17 +507,17 @@ function renderPlayerControls (state) {
// render playback rate
if (state.playing.playbackRate !== 1) {
elements.push((
<span className='rate float-left'>
<span key='rate' className='rate float-left'>
{state.playing.playbackRate}x
</span>
))
}
return (
<div className='controls'>
<div key='controls' className='controls'>
{elements}
{renderCastOptions(state)}
{renderSubtitlesOptions(state)}
{renderSubtitleOptions(state)}
</div>
)
@@ -540,21 +550,7 @@ function renderPlayerControls (state) {
// Handles volume slider scrub
function handleVolumeScrub (e) {
switch (e.type) {
case 'mouseup':
volumeChanging = false
dispatch('setVolume', e.nativeEvent.offsetX / 50)
break
case 'mousedown':
volumeChanging = this.value
break
case 'mousemove':
// only change if move was started by click
if (volumeChanging !== false) {
volumeChanging = this.value
}
break
}
dispatch('setVolume', e.target.value)
}
function handleSubtitles (e) {
@@ -567,9 +563,6 @@ function renderPlayerControls (state) {
}
}
// lets scrub without sending to volume backend
var volumeChanging = false
// Renders the loading bar. Shows which parts of the torrent are loaded, which
// can be 'spongey' / non-contiguous
function renderLoadingBar (state) {
@@ -594,15 +587,15 @@ function renderLoadingBar (state) {
}
// Output some bars to show which parts of the file are loaded
var loadingBarElems = parts.map(function (part) {
var loadingBarElems = parts.map(function (part, i) {
var style = {
left: (100 * part.start / fileProg.numPieces) + '%',
width: (100 * part.count / fileProg.numPieces) + '%'
}
return (<div className='loading-bar-part' style={style}></div>)
return (<div key={i} className='loading-bar-part' style={style}></div>)
})
return (<div className='loading-bar'>{loadingBarElems}</div>)
return (<div key='loading-bar' className='loading-bar'>{loadingBarElems}</div>)
}
// Returns the CSS background-image string for a poster image + dark vignette

View File

@@ -17,6 +17,7 @@ module.exports = class Preferences extends React.Component {
function renderGeneralSection (state) {
return renderSection({
key: 'general',
title: 'General',
description: '',
icon: 'settings'
@@ -27,6 +28,7 @@ function renderGeneralSection (state) {
function renderDownloadDirSelector (state) {
return renderFileSelector({
key: 'download-path',
label: 'Download Path',
description: 'Data from torrents will be saved here',
property: 'downloadPath',
@@ -46,18 +48,18 @@ function renderDownloadDirSelector (state) {
// - controls should be an array of vdom elements
function renderSection (definition, controls) {
var helpElem = !definition.description ? null : (
<div className='help text'>
<div key='help' className='help text'>
<i className='icon'>help_outline</i>{definition.description}
</div>
)
return (
<section className='section preferences-panel'>
<section key={definition.key} className='section preferences-panel'>
<div className='section-container'>
<div className='section-heading'>
<div key='heading' className='section-heading'>
<i className='icon'>{definition.icon}</i>{definition.title}
</div>
{helpElem}
<div className='section-body'>
<div key='body' className='section-body'>
{controls}
</div>
</div>
@@ -72,7 +74,7 @@ function renderSection (definition, controls) {
// - callback takes a new file or folder path
function renderFileSelector (definition, value, callback) {
return (
<div className='control-group'>
<div key={definition.key} className='control-group'>
<div className='controls'>
<label className='control-label'>
<div className='preference-title'>{definition.label}</div>

View File

@@ -13,9 +13,9 @@ module.exports = class TorrentList extends React.Component {
)
return (
<div className='torrent-list'>
<div key='torrent-list' className='torrent-list'>
{torrentRows}
<div className='torrent-placeholder'>
<div key='torrent-placeholder' className='torrent-placeholder'>
<span className='ellipsis'>Drop a torrent file here or paste a magnet link</span>
</div>
</div>
@@ -45,7 +45,10 @@ module.exports = class TorrentList extends React.Component {
if (isSelected) classes.push('selected')
if (!infoHash) classes.push('disabled')
return (
<div style={style} className={classes.join(' ')}
<div
key={torrentSummary.torrentKey}
style={style}
className={classes.join(' ')}
onContextMenu={infoHash && dispatcher('openTorrentContextMenu', infoHash)}
onClick={infoHash && dispatcher('toggleSelectTorrent', infoHash)}>
{this.renderTorrentMetadata(torrentSummary)}
@@ -59,14 +62,14 @@ module.exports = class TorrentList extends React.Component {
renderTorrentMetadata (torrentSummary) {
var name = torrentSummary.name || 'Loading torrent...'
var elements = [(
<div className='name ellipsis'>{name}</div>
<div key='name' className='name ellipsis'>{name}</div>
)]
// If it's downloading/seeding then show progress info
var prog = torrentSummary.progress
if (torrentSummary.status !== 'paused' && prog) {
elements.push((
<div className='ellipsis'>
<div key='progress-info' className='ellipsis'>
{renderPercentProgress()}
{renderTotalProgress()}
{renderPeers()}
@@ -77,37 +80,37 @@ module.exports = class TorrentList extends React.Component {
))
}
return (<div className='metadata'>{elements}</div>)
return (<div key='metadata' className='metadata'>{elements}</div>)
function renderPercentProgress () {
var progress = Math.floor(100 * prog.progress)
return (<span>{progress}%</span>)
return (<span key='percent-progress'>{progress}%</span>)
}
function renderTotalProgress () {
var downloaded = prettyBytes(prog.downloaded)
var total = prettyBytes(prog.length || 0)
if (downloaded === total) {
return (<span>{downloaded}</span>)
return (<span key='total-progress'>{downloaded}</span>)
} else {
return (<span>{downloaded} / {total}</span>)
return (<span key='total-progress'>{downloaded} / {total}</span>)
}
}
function renderPeers () {
if (prog.numPeers === 0) return
var count = prog.numPeers === 1 ? 'peer' : 'peers'
return (<span>{prog.numPeers} {count}</span>)
return (<span key='peers'>{prog.numPeers} {count}</span>)
}
function renderDownloadSpeed () {
if (prog.downloadSpeed === 0) return
return (<span> {prettyBytes(prog.downloadSpeed)}/s</span>)
return (<span key='download'> {prettyBytes(prog.downloadSpeed)}/s</span>)
}
function renderUploadSpeed () {
if (prog.uploadSpeed === 0) return
return (<span> {prettyBytes(prog.uploadSpeed)}/s</span>)
return (<span key='upload'> {prettyBytes(prog.uploadSpeed)}/s</span>)
}
function renderEta () {
@@ -175,6 +178,7 @@ module.exports = class TorrentList extends React.Component {
if (TorrentPlayer.isPlayableTorrentSummary(torrentSummary)) {
playButton = (
<i
key='play-button'
title={playTooltip}
className={'button-round icon play ' + playClass}
onClick={dispatcher('playFile', infoHash)}>
@@ -184,16 +188,18 @@ module.exports = class TorrentList extends React.Component {
}
return (
<div className='buttons'>
<div key='buttons' className='buttons'>
{positionElem}
{playButton}
<i
key='download-button'
className={'button-round icon download ' + torrentSummary.status}
title={downloadTooltip}
onClick={dispatcher('toggleTorrent', infoHash)}>
{downloadIcon}
</i>
<i
key='delete-button'
className='icon delete'
title='Remove torrent'
onClick={dispatcher('confirmDeleteTorrent', infoHash, false)}>
@@ -211,7 +217,7 @@ module.exports = class TorrentList extends React.Component {
var message = torrentSummary.status === 'paused'
? 'Failed to load torrent info. Click the download button to try again...'
: 'Downloading torrent info...'
filesElement = (<div className='files warning'>{message}</div>)
filesElement = (<div key='files' className='files warning'>{message}</div>)
} else {
// We do know the files. List them and show download stats for each one
var fileRows = torrentSummary.files
@@ -224,16 +230,18 @@ module.exports = class TorrentList extends React.Component {
.map((object) => this.renderFileRow(torrentSummary, object.file, object.index))
filesElement = (
<div className='files'>
<div key='files' className='files'>
<table>
{fileRows}
<tbody>
{fileRows}
</tbody>
</table>
</div>
)
}
return (
<div className='torrent-details'>
<div key='details' className='torrent-details'>
{filesElement}
</div>
)
@@ -275,7 +283,7 @@ module.exports = class TorrentList extends React.Component {
if (!isSelected) rowClass = 'disabled' // File deselected, not being torrented
if (!isDone && !isPlayable) rowClass = 'disabled' // Can't open yet, can't stream
return (
<tr onClick={handleClick}>
<tr key={index} onClick={handleClick}>
<td className={'col-icon ' + rowClass}>
{positionElem}
<i className='icon'>{icon}</i>
@@ -303,17 +311,17 @@ module.exports = class TorrentList extends React.Component {
var transformFix = {transform: 'rotate(' + rotation + 'deg)'}
return (
<div className={'radial-progress ' + cssClass}>
<div className='circle'>
<div className='mask full' style={transformFill}>
<div className='fill' style={transformFill}></div>
<div key='radial-progress' className={'radial-progress ' + cssClass}>
<div key='circle' className='circle'>
<div key='mask-full' className='mask full' style={transformFill}>
<div key='fill' className='fill' style={transformFill}></div>
</div>
<div className='mask half'>
<div className='fill' style={transformFill}></div>
<div className='fill fix' style={transformFix}></div>
<div key='mask-half' className='mask half'>
<div key='fill' className='fill' style={transformFill}></div>
<div key='fill-fix' className='fill fix' style={transformFix}></div>
</div>
</div>
<div className='inset'></div>
<div key='inset' className='inset'></div>
</div>
)
}

View File

@@ -26,6 +26,13 @@ body {
line-height: 1.5em;
}
#body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
table {
table-layout: fixed;
}

View File

@@ -9,5 +9,7 @@
require('../build/renderer/main.js')
</script>
</head>
<body></body>
<body>
<div id='body'></div>
</body>
</html>