Material UI: finish Create Torrent
This commit is contained in:
@@ -39,14 +39,11 @@ class ShowMore extends React.Component {
|
|||||||
? this.props.hideLabel
|
? this.props.hideLabel
|
||||||
: this.props.showLabel
|
: this.props.showLabel
|
||||||
return (
|
return (
|
||||||
<div
|
<div style={this.props.style}>
|
||||||
style={this.props.style}
|
|
||||||
>
|
|
||||||
{this.state.expanded ? this.props.children : null}
|
{this.state.expanded ? this.props.children : null}
|
||||||
<FlatButton
|
<FlatButton
|
||||||
onClick={this.handleClick}
|
onClick={this.handleClick}
|
||||||
label={label}
|
label={label} />
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ const {dispatch, dispatcher} = require('../lib/dispatcher')
|
|||||||
const FlatButton = require('material-ui/FlatButton').default
|
const FlatButton = require('material-ui/FlatButton').default
|
||||||
const RaisedButton = require('material-ui/RaisedButton').default
|
const RaisedButton = require('material-ui/RaisedButton').default
|
||||||
const TextField = require('material-ui/TextField').default
|
const TextField = require('material-ui/TextField').default
|
||||||
|
const Checkbox = require('material-ui/Checkbox').default
|
||||||
|
|
||||||
const CreateTorrentErrorPage = require('../components/create-torrent-error-page')
|
const CreateTorrentErrorPage = require('../components/create-torrent-error-page')
|
||||||
const Heading = require('../components/Heading')
|
const Heading = require('../components/Heading')
|
||||||
@@ -20,27 +21,20 @@ class CreateTorrentPage extends React.Component {
|
|||||||
var state = this.props.state
|
var state = this.props.state
|
||||||
var info = state.location.current()
|
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 state={state} />)
|
|
||||||
|
|
||||||
// First, extract the base folder that the files are all in
|
// First, extract the base folder that the files are all in
|
||||||
var pathPrefix = info.folderPath
|
var pathPrefix = info.folderPath
|
||||||
if (!pathPrefix) {
|
if (!pathPrefix) {
|
||||||
pathPrefix = files.map((x) => x.path).reduce(findCommonPrefix)
|
pathPrefix = info.files.map((x) => x.path).reduce(findCommonPrefix)
|
||||||
if (!pathPrefix.endsWith('/') && !pathPrefix.endsWith('\\')) {
|
if (!pathPrefix.endsWith('/') && !pathPrefix.endsWith('\\')) {
|
||||||
pathPrefix = path.dirname(pathPrefix)
|
pathPrefix = path.dirname(pathPrefix)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sanity check: show the number of files and total size
|
// Then, exclude .DS_Store and other dotfiles
|
||||||
var numFiles = files.length
|
var files = info.files
|
||||||
var totalBytes = files
|
.filter((f) => !containsDots(f.path, pathPrefix))
|
||||||
.map((f) => f.size)
|
.map((f) => ({name: f.name, path: f.path, size: f.size}))
|
||||||
.reduce((a, b) => a + b, 0)
|
if (files.length === 0) return (<CreateTorrentErrorPage state={state} />)
|
||||||
var torrentInfo = `${numFiles} files, ${prettyBytes(totalBytes)}`
|
|
||||||
|
|
||||||
// Then, use the name of the base folder (or sole file, for a single file torrent)
|
// 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.
|
// as the default name. Show all files relative to the base folder.
|
||||||
@@ -54,34 +48,32 @@ class CreateTorrentPage extends React.Component {
|
|||||||
defaultName = path.basename(pathPrefix)
|
defaultName = path.basename(pathPrefix)
|
||||||
basePath = path.dirname(pathPrefix)
|
basePath = path.dirname(pathPrefix)
|
||||||
}
|
}
|
||||||
var maxFileElems = 100
|
|
||||||
var fileElems = files.slice(0, maxFileElems).map(function (file, i) {
|
// Default trackers
|
||||||
var relativePath = files.length === 0
|
|
||||||
? file.name
|
|
||||||
: path.relative(pathPrefix, file.path)
|
|
||||||
return (<div key={i}>{relativePath}</div>)
|
|
||||||
})
|
|
||||||
if (files.length > maxFileElems) {
|
|
||||||
fileElems.push(<div key='more'>+ {maxFileElems - files.length} more</div>)
|
|
||||||
}
|
|
||||||
var trackers = createTorrent.announceList.join('\n')
|
var trackers = createTorrent.announceList.join('\n')
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
|
comment: '',
|
||||||
|
isPrivate: false,
|
||||||
|
pathPrefix,
|
||||||
basePath,
|
basePath,
|
||||||
defaultName,
|
defaultName,
|
||||||
fileElems,
|
files,
|
||||||
torrentInfo,
|
|
||||||
trackers
|
trackers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create React event handlers only once
|
||||||
|
this.setIsPrivate = (_, isPrivate) => this.setState({isPrivate})
|
||||||
|
this.setComment = (_, comment) => this.setState({comment})
|
||||||
|
this.setTrackers = (_, trackers) => this.setState({trackers})
|
||||||
|
this.handleSubmit = () => this.handleSubmit
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit () {
|
handleSubmit () {
|
||||||
var announceList = document.querySelector('.torrent-trackers').value
|
var announceList = this.state.trackers
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.map((s) => s.trim())
|
.map((s) => s.trim())
|
||||||
.filter((s) => s !== '')
|
.filter((s) => s !== '')
|
||||||
var isPrivate = document.querySelector('.torrent-is-private').checked
|
|
||||||
var comment = document.querySelector('.torrent-comment').value.trim()
|
|
||||||
var options = {
|
var options = {
|
||||||
// We can't let the user choose their own name if we want WebTorrent
|
// 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.
|
// to use the files in place rather than creating a new folder.
|
||||||
@@ -89,55 +81,37 @@ class CreateTorrentPage extends React.Component {
|
|||||||
path: this.state.basePath,
|
path: this.state.basePath,
|
||||||
files: this.state.files,
|
files: this.state.files,
|
||||||
announce: announceList,
|
announce: announceList,
|
||||||
private: isPrivate,
|
private: this.state.isPrivate,
|
||||||
comment: comment
|
comment: this.state.comment.trim()
|
||||||
}
|
}
|
||||||
dispatch('createTorrent', options)
|
dispatch('createTorrent', options)
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
|
var files = this.state.files
|
||||||
|
|
||||||
|
// 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)}`
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='create-torrent'>
|
<div className='create-torrent'>
|
||||||
<Heading level={1}>
|
<Heading level={1}>Create torrent {this.state.defaultName}</Heading>
|
||||||
Create torrent "{this.state.defaultName}"
|
<div className='torrent-info'>{torrentInfo}</div>
|
||||||
</Heading>
|
|
||||||
<div className='torrent-info'>
|
|
||||||
{this.state.torrentInfo}
|
|
||||||
</div>
|
|
||||||
<div className='torrent-attribute'>
|
<div className='torrent-attribute'>
|
||||||
<label>Path:</label>
|
<label>Path:</label>
|
||||||
<div className='torrent-attribute'>{this.state.pathPrefix}</div>
|
<div>{this.state.pathPrefix}</div>
|
||||||
</div>
|
</div>
|
||||||
<ShowMore
|
<ShowMore
|
||||||
style={{
|
style={{
|
||||||
marginBottom: 10
|
marginBottom: 10
|
||||||
}}
|
}}
|
||||||
hideLabel='Hide advanced settings...'
|
hideLabel='Hide advanced settings...'
|
||||||
showLabel='Show advanced settings...'
|
showLabel='Show advanced settings...' >
|
||||||
>
|
{this.renderAdvanced()}
|
||||||
<div key='advanced' className='create-torrent-advanced'>
|
|
||||||
<div key='private' className='torrent-attribute'>
|
|
||||||
<label>Private:</label>
|
|
||||||
<input type='checkbox' className='torrent-is-private' value='torrent-is-private' />
|
|
||||||
</div>
|
|
||||||
<Heading level={2}>Trackers:</Heading>
|
|
||||||
<TextField
|
|
||||||
className='torrent-trackers'
|
|
||||||
hintText='Tracker'
|
|
||||||
multiLine
|
|
||||||
rows={2}
|
|
||||||
rowsMax={10}
|
|
||||||
defaultValue={this.state.trackers}
|
|
||||||
/>
|
|
||||||
<div key='comment' className='torrent-attribute'>
|
|
||||||
<label>Comment:</label>
|
|
||||||
<textarea className='torrent-attribute torrent-comment' />
|
|
||||||
</div>
|
|
||||||
<div key='files' className='torrent-attribute'>
|
|
||||||
<label>Files:</label>
|
|
||||||
<div>{this.state.fileElems}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ShowMore>
|
</ShowMore>
|
||||||
<div className='float-right'>
|
<div className='float-right'>
|
||||||
<FlatButton
|
<FlatButton
|
||||||
@@ -156,6 +130,65 @@ class CreateTorrentPage extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderAdvanced () {
|
||||||
|
// Create file list
|
||||||
|
var maxFileElems = 100
|
||||||
|
var files = this.state.files
|
||||||
|
var fileElems = files.slice(0, maxFileElems).map((file, i) => {
|
||||||
|
var relativePath = path.relative(this.state.pathPrefix, file.path)
|
||||||
|
return (<div key={i}>{relativePath}</div>)
|
||||||
|
})
|
||||||
|
if (files.length > maxFileElems) {
|
||||||
|
fileElems.push(<div key='more'>+ {maxFileElems - files.length} more</div>)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Align the text fields
|
||||||
|
var textFieldStyle = { width: '' }
|
||||||
|
var textareaStyle = { margin: 0 }
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key='advanced' className='create-torrent-advanced'>
|
||||||
|
<div key='private' className='torrent-attribute'>
|
||||||
|
<label>Private:</label>
|
||||||
|
<Checkbox
|
||||||
|
className='torrent-is-private'
|
||||||
|
style={{display: ''}}
|
||||||
|
value={this.state.isPrivate}
|
||||||
|
onChange={this.setIsPrivate} />
|
||||||
|
</div>
|
||||||
|
<div key='trackers' className='torrent-attribute'>
|
||||||
|
<label>Trackers:</label>
|
||||||
|
<TextField
|
||||||
|
className='torrent-trackers'
|
||||||
|
style={textFieldStyle}
|
||||||
|
textareaStyle={textareaStyle}
|
||||||
|
multiLine
|
||||||
|
rows={2}
|
||||||
|
rowsMax={10}
|
||||||
|
value={this.state.trackers}
|
||||||
|
onChange={this.setTrackers} />
|
||||||
|
</div>
|
||||||
|
<div key='comment' className='torrent-attribute'>
|
||||||
|
<label>Comment:</label>
|
||||||
|
<TextField
|
||||||
|
className='torrent-comment'
|
||||||
|
style={textFieldStyle}
|
||||||
|
textareaStyle={textareaStyle}
|
||||||
|
hintText='Optionally describe your torrent...'
|
||||||
|
multiLine
|
||||||
|
rows={2}
|
||||||
|
rowsMax={10}
|
||||||
|
value={this.state.comment}
|
||||||
|
onChange={this.setComment} />
|
||||||
|
</div>
|
||||||
|
<div key='files' className='torrent-attribute'>
|
||||||
|
<label>Files:</label>
|
||||||
|
<div>{fileElems}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finds the longest common prefix
|
// Finds the longest common prefix
|
||||||
@@ -168,4 +201,10 @@ function findCommonPrefix (a, b) {
|
|||||||
return a.substring(0, i)
|
return a.substring(0, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function containsDots (path, pathPrefix) {
|
||||||
|
var suffix = path.substring(pathPrefix.length).replace(/\\/g, '/')
|
||||||
|
console.log('SUFFIX ' + suffix)
|
||||||
|
return ('/' + suffix).includes('/.')
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = CreateTorrentPage
|
module.exports = CreateTorrentPage
|
||||||
|
|||||||
@@ -272,10 +272,12 @@ table {
|
|||||||
.create-torrent {
|
.create-torrent {
|
||||||
padding: 10px 25px;
|
padding: 10px 25px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
font: 16px/24px BlinkMacSystemFont, "Helvetica Neue", Helvetica, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
.create-torrent .torrent-attribute {
|
.create-torrent .torrent-attribute {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
margin: 8px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.create-torrent .torrent-attribute>* {
|
.create-torrent .torrent-attribute>* {
|
||||||
@@ -283,13 +285,12 @@ table {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.create-torrent .torrent-attribute label {
|
.create-torrent .torrent-attribute label {
|
||||||
width: 60px;
|
width: 100px;
|
||||||
margin-right: 10px;
|
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
.create-torrent .torrent-attribute>div {
|
.create-torrent .torrent-attribute>div {
|
||||||
width: calc(100% - 90px);
|
width: calc(100% - 100px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.create-torrent .torrent-attribute div {
|
.create-torrent .torrent-attribute div {
|
||||||
@@ -298,18 +299,6 @@ table {
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.create-torrent .torrent-attribute textarea {
|
|
||||||
width: calc(100% - 80px);
|
|
||||||
height: 80px;
|
|
||||||
color: #eee;
|
|
||||||
background-color: transparent;
|
|
||||||
line-height: 1.5;
|
|
||||||
font-size: 14px;
|
|
||||||
font-family: inherit;
|
|
||||||
border-radius: 2px;
|
|
||||||
padding: 4px 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* BUTTONS
|
* BUTTONS
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user