From 946bba19a966b34d2735d2c9a1aee981b18d0a67 Mon Sep 17 00:00:00 2001 From: DC Date: Wed, 20 Jul 2016 01:27:40 -0700 Subject: [PATCH] React: convert functions to controls --- src/renderer/views/app.js | 27 +-- src/renderer/views/create-torrent.js | 216 ++++++++---------- src/renderer/views/header.js | 55 ++--- .../views/open-torrent-address-modal.js | 34 +-- src/renderer/views/player.js | 31 +-- src/renderer/views/preferences.js | 17 +- src/renderer/views/remove-torrent-modal.js | 39 ++-- src/renderer/views/torrent-list.js | 84 +++---- src/renderer/views/unsupported-media-modal.js | 60 ++--- src/renderer/views/update-available-modal.js | 41 ++-- 10 files changed, 299 insertions(+), 305 deletions(-) diff --git a/src/renderer/views/app.js b/src/renderer/views/app.js index e451c3a6..e2224e99 100644 --- a/src/renderer/views/app.js +++ b/src/renderer/views/app.js @@ -49,9 +49,9 @@ module.exports = class App extends React.Component { var vdom = (
- {Header(state)} +
{getErrorPopover(state)} -
{getView(state)}
+
{getView(state)}
{getModal(state)}
) @@ -65,12 +65,13 @@ function getErrorPopover (state) { var recentErrors = state.errors.filter((x) => now - x.time < 5000) var hasErrors = recentErrors.length > 0 - var errorElems = recentErrors.map(function (error) { - return (
{error.message}
) + var errorElems = recentErrors.map(function (error, i) { + return (
{error.message}
) }) return ( -
-
Error
+
+
Error
{errorElems}
) @@ -78,18 +79,18 @@ function getErrorPopover (state) { function getModal (state) { if (!state.modal) return - var contents = Modals[state.modal.id](state) + var ModalContents = Modals[state.modal.id] return ( -
-
-
- {contents} +
+
+
+
) } function getView (state) { - var url = state.location.url() - return Views[url](state) + var View = Views[state.location.url()] + return () } diff --git a/src/renderer/views/create-torrent.js b/src/renderer/views/create-torrent.js index 5eab4215..9deafb7c 100644 --- a/src/renderer/views/create-torrent.js +++ b/src/renderer/views/create-torrent.js @@ -1,143 +1,125 @@ -module.exports = CreateTorrentPage - const React = require('react') const createTorrent = require('create-torrent') const path = require('path') const prettyBytes = require('prettier-bytes') const {dispatch, dispatcher} = require('../lib/dispatcher') +const CreateTorrentErrorPage = require('./create-torrent-error-page') -function CreateTorrentPage (state) { - var info = state.location.current() +module.exports = class CreateTorrentPage extends React.Component { + render () { + var state = this.props.state + var info = state.location.current() - // Preprocess: exclude .DS_Store and other dotfiles - var files = info.files - .filter((f) => !f.name.startsWith('.')) - .map((f) => ({name: f.name, path: f.path, size: f.size})) - if (files.length === 0) return CreateTorrentErrorPage() + // Preprocess: exclude .DS_Store and other dotfiles + var files = info.files + .filter((f) => !f.name.startsWith('.')) + .map((f) => ({name: f.name, path: f.path, size: f.size})) + if (files.length === 0) return () - // First, extract the base folder that the files are all in - var pathPrefix = info.folderPath - if (!pathPrefix) { - pathPrefix = files.map((x) => x.path).reduce(findCommonPrefix) - if (!pathPrefix.endsWith('/') && !pathPrefix.endsWith('\\')) { - pathPrefix = path.dirname(pathPrefix) + // First, extract the base folder that the files are all in + var pathPrefix = info.folderPath + if (!pathPrefix) { + pathPrefix = files.map((x) => x.path).reduce(findCommonPrefix) + if (!pathPrefix.endsWith('/') && !pathPrefix.endsWith('\\')) { + pathPrefix = path.dirname(pathPrefix) + } } - } - // Sanity check: show the number of files and total size - var numFiles = files.length - var totalBytes = files - .map((f) => f.size) - .reduce((a, b) => a + b, 0) - var torrentInfo = `${numFiles} files, ${prettyBytes(totalBytes)}` + // Sanity check: show the number of files and total size + var numFiles = files.length + var totalBytes = files + .map((f) => f.size) + .reduce((a, b) => a + b, 0) + var torrentInfo = `${numFiles} files, ${prettyBytes(totalBytes)}` - // Then, use the name of the base folder (or sole file, for a single file torrent) - // as the default name. Show all files relative to the base folder. - var defaultName, basePath - if (files.length === 1) { - // Single file torrent: /a/b/foo.jpg -> torrent name 'foo.jpg', path '/a/b' - defaultName = files[0].name - basePath = pathPrefix - } else { - // Multi file torrent: /a/b/{foo, bar}.jpg -> torrent name 'b', path '/a' - defaultName = path.basename(pathPrefix) - basePath = path.dirname(pathPrefix) - } - var maxFileElems = 100 - var fileElems = files.slice(0, maxFileElems).map(function (file) { - var relativePath = files.length === 0 ? file.name : path.relative(pathPrefix, file.path) - return (
{relativePath}
) - }) - if (files.length > maxFileElems) { - fileElems.push(
+ {maxFileElems - files.length} more
) - } - var trackers = createTorrent.announceList.join('\n') - var collapsedClass = info.showAdvanced ? 'expanded' : 'collapsed' + // Then, use the name of the base folder (or sole file, for a single file torrent) + // as the default name. Show all files relative to the base folder. + var defaultName, basePath + if (files.length === 1) { + // Single file torrent: /a/b/foo.jpg -> torrent name 'foo.jpg', path '/a/b' + defaultName = files[0].name + basePath = pathPrefix + } else { + // Multi file torrent: /a/b/{foo, bar}.jpg -> torrent name 'b', path '/a' + defaultName = path.basename(pathPrefix) + basePath = path.dirname(pathPrefix) + } + var maxFileElems = 100 + var fileElems = files.slice(0, maxFileElems).map(function (file) { + var relativePath = files.length === 0 ? file.name : path.relative(pathPrefix, file.path) + return (
{relativePath}
) + }) + if (files.length > maxFileElems) { + fileElems.push(
+ {maxFileElems - files.length} more
) + } + var trackers = createTorrent.announceList.join('\n') + var collapsedClass = info.showAdvanced ? 'expanded' : 'collapsed' - return ( -
-

Create torrent {defaultName}

-

- {torrentInfo} -

-

- -

{pathPrefix}
-

-
- {info.showAdvanced ? 'Basic' : 'Advanced'} -
-
-

- - + return ( +

+

Create torrent {defaultName}

+

+ {torrentInfo}

- - + +

{pathPrefix}

-

- - -

-

- -

{fileElems}
+
+ {info.showAdvanced ? 'Basic' : 'Advanced'} +
+
+

+ + +

+

+ + +

+

+ + +

+

+ +

{fileElems}
+

+
+

+ +

-

- - -

-
- ) + ) - function handleOK () { - var announceList = document.querySelector('.torrent-trackers').value - .split('\n') - .map((s) => s.trim()) - .filter((s) => s !== '') - var isPrivate = document.querySelector('.torrent-is-private').checked - var comment = document.querySelector('.torrent-comment').value.trim() - var options = { - // We can't let the user choose their own name if we want WebTorrent - // to use the files in place rather than creating a new folder. - // If we ever want to add support for that: - // name: document.querySelector('.torrent-name').value - name: defaultName, - path: basePath, - files: files, - announce: announceList, - private: isPrivate, - comment: comment + function handleOK () { + // TODO: dcposch use React refs instead + var announceList = document.querySelector('.torrent-trackers').value + .split('\n') + .map((s) => s.trim()) + .filter((s) => s !== '') + var isPrivate = document.querySelector('.torrent-is-private').checked + var comment = document.querySelector('.torrent-comment').value.trim() + var options = { + // We can't let the user choose their own name if we want WebTorrent + // to use the files in place rather than creating a new folder. + // If we ever want to add support for that: + // name: document.querySelector('.torrent-name').value + name: defaultName, + path: basePath, + files: files, + announce: announceList, + private: isPrivate, + comment: comment + } + dispatch('createTorrent', options) } - dispatch('createTorrent', options) } } -function CreateTorrentErrorPage () { - return ( -
-

Create torrent

-

-

- Sorry, you must select at least one file that is not a hidden file. -

-

- Hidden files, starting with a . character, are not included. -

-

-

- -

-
- ) -} - // Finds the longest common prefix function findCommonPrefix (a, b) { for (var i = 0; i < a.length && i < b.length; i++) { diff --git a/src/renderer/views/header.js b/src/renderer/views/header.js index 75a49878..22fb83db 100644 --- a/src/renderer/views/header.js +++ b/src/renderer/views/header.js @@ -1,39 +1,42 @@ -module.exports = Header - const React = require('react') const {dispatcher} = require('../lib/dispatcher') -function Header (state) { - return ( -
- {getTitle()} -
- - chevron_left - - - chevron_right - +module.exports = class Header extends React.Component { + render () { + var loc = this.props.state.location + return ( +
+ {this.getTitle()} +
+ + chevron_left + + + chevron_right + +
+
+ {this.getAddButton()} +
-
- {getAddButton()} -
-
- ) + ) + } - function getTitle () { + getTitle () { if (process.platform !== 'darwin') return null + var state = this.props.state return (
{state.window.title}
) } - function getAddButton () { + getAddButton () { + var state = this.props.state if (state.location.url() !== 'home') return null return ( -

-

- -

-

- - -

- -
- ) +module.exports = class OpenTorrentAddressModal extends React.Component { + render () { + // TODO: dcposch remove janky inline +
+ ) + } } function handleKeyPress (e) { @@ -26,5 +27,6 @@ function handleKeyPress (e) { function handleOK () { dispatch('exitModal') + // TODO: dcposch use React refs instead dispatch('addTorrent', document.querySelector('#add-torrent-url').value) } diff --git a/src/renderer/views/player.js b/src/renderer/views/player.js index 295b9866..94e43d57 100644 --- a/src/renderer/views/player.js +++ b/src/renderer/views/player.js @@ -1,5 +1,3 @@ -module.exports = Player - const React = require('react') const Bitfield = require('bitfield') const prettyBytes = require('prettier-bytes') @@ -9,19 +7,22 @@ const TorrentSummary = require('../lib/torrent-summary') const {dispatch, dispatcher} = require('../lib/dispatcher') // Shows a streaming video player. Standard features + Chromecast + Airplay -function Player (state) { - // Show the video as large as will fit in the window, play immediately - // If the video is on Chromecast or Airplay, show a title screen instead - var showVideo = state.playing.location === 'local' - return ( -
- {showVideo ? renderMedia(state) : renderCastScreen(state)} - {renderPlayerControls(state)} -
- ) +module.exports = class Player extends React.Component { + render () { + // Show the video as large as will fit in the window, play immediately + // If the video is on Chromecast or Airplay, show a title screen instead + var state = this.props.state + var showVideo = state.playing.location === 'local' + return ( +
+ {showVideo ? renderMedia(state) : renderCastScreen(state)} + {renderPlayerControls(state)} +
+ ) + } } // Handles volume change by wheel diff --git a/src/renderer/views/preferences.js b/src/renderer/views/preferences.js index 2e9c32ec..20be7258 100644 --- a/src/renderer/views/preferences.js +++ b/src/renderer/views/preferences.js @@ -1,17 +1,18 @@ -module.exports = Preferences - const React = require('react') const remote = require('electron').remote const dialog = remote.dialog const {dispatch} = require('../lib/dispatcher') -function Preferences (state) { - return ( -
- {renderGeneralSection(state)} -
- ) +module.exports = class Preferences extends React.Component { + render () { + var state = this.props.state + return ( +
+ {renderGeneralSection(state)} +
+ ) + } } function renderGeneralSection (state) { diff --git a/src/renderer/views/remove-torrent-modal.js b/src/renderer/views/remove-torrent-modal.js index b22720b4..b1fda057 100644 --- a/src/renderer/views/remove-torrent-modal.js +++ b/src/renderer/views/remove-torrent-modal.js @@ -1,27 +1,28 @@ -module.exports = RemoveTorrentModal - const React = require('react') const {dispatch, dispatcher} = require('../lib/dispatcher') -function RemoveTorrentModal (state) { - var message = state.modal.deleteData - ? 'Are you sure you want to remove this torrent from the list and delete the data file?' - : 'Are you sure you want to remove this torrent from the list?' - var buttonText = state.modal.deleteData ? 'Remove Data' : 'Remove' +module.exports = class RemoveTorrentModal extends React.Component { + render () { + var state = this.props.state + var message = state.modal.deleteData + ? 'Are you sure you want to remove this torrent from the list and delete the data file?' + : 'Are you sure you want to remove this torrent from the list?' + var buttonText = state.modal.deleteData ? 'Remove Data' : 'Remove' - return ( -
-

{message}

-

- - -

-
- ) + return ( +
+

{message}

+

+ + +

+
+ ) - function handleRemove () { - dispatch('deleteTorrent', state.modal.infoHash, state.modal.deleteData) - dispatch('exitModal') + function handleRemove () { + dispatch('deleteTorrent', state.modal.infoHash, state.modal.deleteData) + dispatch('exitModal') + } } } diff --git a/src/renderer/views/torrent-list.js b/src/renderer/views/torrent-list.js index d50d0958..d5b95f86 100644 --- a/src/renderer/views/torrent-list.js +++ b/src/renderer/views/torrent-list.js @@ -1,5 +1,3 @@ -module.exports = TorrentList - const React = require('react') const prettyBytes = require('prettier-bytes') @@ -7,21 +5,25 @@ const TorrentSummary = require('../lib/torrent-summary') const TorrentPlayer = require('../lib/torrent-player') const {dispatcher} = require('../lib/dispatcher') -function TorrentList (state) { - var torrentRows = state.saved.torrents.map( - (torrentSummary) => renderTorrent(torrentSummary) - ) +module.exports = class TorrentList extends React.Component { + render () { + var state = this.props.state + var torrentRows = state.saved.torrents.map( + (torrentSummary) => this.renderTorrent(torrentSummary) + ) - return ( -
- {torrentRows} -
- Drop a torrent file here or paste a magnet link + return ( +
+ {torrentRows} +
+ Drop a torrent file here or paste a magnet link +
-
- ) + ) + } - function renderTorrent (torrentSummary) { + renderTorrent (torrentSummary) { + var state = this.props.state var infoHash = torrentSummary.infoHash var isSelected = infoHash && state.selectedInfoHash === infoHash @@ -46,15 +48,15 @@ function TorrentList (state) {
- {renderTorrentMetadata(torrentSummary)} - {infoHash ? renderTorrentButtons(torrentSummary) : ''} - {isSelected ? renderTorrentDetails(torrentSummary) : ''} + {this.renderTorrentMetadata(torrentSummary)} + {infoHash ? this.renderTorrentButtons(torrentSummary) : null} + {isSelected ? this.renderTorrentDetails(torrentSummary) : null}
) } // Show name, download status, % complete - function renderTorrentMetadata (torrentSummary) { + renderTorrentMetadata (torrentSummary) { var name = torrentSummary.name || 'Loading torrent...' var elements = [(
{name}
@@ -132,7 +134,7 @@ function TorrentList (state) { // Download button toggles between torrenting (DL/seed) and paused // Play button starts streaming the torrent immediately, unpausing if needed - function renderTorrentButtons (torrentSummary) { + renderTorrentButtons (torrentSummary) { var infoHash = torrentSummary.infoHash var playIcon, playTooltip, playClass @@ -164,7 +166,7 @@ function TorrentList (state) { torrentSummary.files[torrentSummary.defaultPlayFileIndex] if (defaultFile && defaultFile.currentTime && !willShowSpinner) { var fraction = defaultFile.currentTime / defaultFile.duration - positionElem = renderRadialProgressBar(fraction, 'radial-progress-large') + positionElem = this.renderRadialProgressBar(fraction, 'radial-progress-large') playClass = 'resume-position' } @@ -202,7 +204,7 @@ function TorrentList (state) { } // Show files, per-file download status and play buttons, and so on - function renderTorrentDetails (torrentSummary) { + renderTorrentDetails (torrentSummary) { var filesElement if (!torrentSummary.files) { // We don't know what files this torrent contains @@ -219,7 +221,7 @@ function TorrentList (state) { if (b.file.name < a.file.name) return 1 return 0 }) - .map((object) => renderFileRow(torrentSummary, object.file, object.index)) + .map((object) => this.renderFileRow(torrentSummary, object.file, object.index)) filesElement = (
@@ -238,7 +240,7 @@ function TorrentList (state) { } // Show a single torrentSummary file in the details view for a single torrent - function renderFileRow (torrentSummary, file, index) { + renderFileRow (torrentSummary, file, index) { // First, find out how much of the file we've downloaded // Are we even torrenting it? var isSelected = torrentSummary.selections && torrentSummary.selections[index] @@ -254,7 +256,7 @@ function TorrentList (state) { var positionElem if (file.currentTime) { // Radial progress bar. 0% = start from 0:00, 270% = 3/4 of the way thru - positionElem = renderRadialProgressBar(file.currentTime / file.duration) + positionElem = this.renderRadialProgressBar(file.currentTime / file.duration) } // Finally, render the file as a table row @@ -294,25 +296,25 @@ function TorrentList (state) { ) } -} -function renderRadialProgressBar (fraction, cssClass) { - var rotation = 360 * fraction - var transformFill = {transform: 'rotate(' + (rotation / 2) + 'deg)'} - var transformFix = {transform: 'rotate(' + rotation + 'deg)'} + renderRadialProgressBar (fraction, cssClass) { + var rotation = 360 * fraction + var transformFill = {transform: 'rotate(' + (rotation / 2) + 'deg)'} + var transformFix = {transform: 'rotate(' + rotation + 'deg)'} - return ( -
-
-
-
-
-
-
-
+ return ( +
+
+
+
+
+
+
+
+
+
-
-
- ) + ) + } } diff --git a/src/renderer/views/unsupported-media-modal.js b/src/renderer/views/unsupported-media-modal.js index e49bbbe3..52654c32 100644 --- a/src/renderer/views/unsupported-media-modal.js +++ b/src/renderer/views/unsupported-media-modal.js @@ -1,39 +1,39 @@ -module.exports = UnsupportedMediaModal - const React = require('react') const electron = require('electron') -const {dispatch, dispatcher} = require('../lib/dispatcher') +const {dispatcher} = require('../lib/dispatcher') -function UnsupportedMediaModal (state) { - var err = state.modal.error - var message = (err && err.getMessage) - ? err.getMessage() - : err - var actionButton = state.modal.vlcInstalled - ? () - : () - var vlcMessage = state.modal.vlcNotFound - ? 'Couldn\'t run VLC. Please make sure it\'s installed.' - : '' - return ( -
-

Sorry, we can't play that file.

-

{message}

-

- - {actionButton} -

-

{vlcMessage}

-
- ) +module.exports = class UnsupportedMediaModal extends React.Component { + render () { + var state = this.props.state + var err = state.modal.error + var message = (err && err.getMessage) + ? err.getMessage() + : err + var actionButton = state.modal.vlcInstalled + ? () + : () + var vlcMessage = state.modal.vlcNotFound + ? 'Couldn\'t run VLC. Please make sure it\'s installed.' + : '' + return ( +
+

Sorry, we can't play that file.

+

{message}

+

+ + {actionButton} +

+

{vlcMessage}

+
+ ) + } - function onInstall () { + onInstall () { electron.shell.openExternal('http://www.videolan.org/vlc/') + + // TODO: dcposch send a dispatch rather than modifying state directly + var state = this.props.state state.modal.vlcInstalled = true // Assume they'll install it successfully } - - function onPlay () { - dispatch('vlcPlay') - } } diff --git a/src/renderer/views/update-available-modal.js b/src/renderer/views/update-available-modal.js index 718e3ce8..885ba253 100644 --- a/src/renderer/views/update-available-modal.js +++ b/src/renderer/views/update-available-modal.js @@ -1,29 +1,30 @@ -module.exports = UpdateAvailableModal - const React = require('react') const electron = require('electron') const {dispatch} = require('../lib/dispatcher') -function UpdateAvailableModal (state) { - return ( -
-

A new version of WebTorrent is available: v{state.modal.version}

-

We have an auto-updater for Windows and Mac. We don't have one for Linux yet, so you'll have to download the new version manually.

-

- - -

-
- ) +module.exports = class UpdateAvailableModal extends React.Component { + render () { + var state = this.props.state + return ( +
+

A new version of WebTorrent is available: v{state.modal.version}

+

We have an auto-updater for Windows and Mac. We don't have one for Linux yet, so you'll have to download the new version manually.

+

+ + +

+
+ ) - function handleOK () { - electron.shell.openExternal('https://github.com/feross/webtorrent-desktop/releases') - dispatch('exitModal') - } + function handleOK () { + electron.shell.openExternal('https://github.com/feross/webtorrent-desktop/releases') + dispatch('exitModal') + } - function handleCancel () { - dispatch('skipVersion', state.modal.version) - dispatch('exitModal') + function handleCancel () { + dispatch('skipVersion', state.modal.version) + dispatch('exitModal') + } } }