updated publish ux/ui
This commit is contained in:
parent
b7b3a78730
commit
4da04d833c
8 changed files with 137 additions and 109 deletions
|
@ -1,7 +1,7 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { updateLoggedInChannel } from 'actions/channel';
|
import { updateLoggedInChannel } from 'actions/channel';
|
||||||
import View from './view';
|
import View from './view';
|
||||||
import {updateSelectedChannel} from '../../actions/publish';
|
import {updateSelectedChannel} from 'actions/publish';
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => {
|
const mapDispatchToProps = dispatch => {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -119,38 +119,41 @@ class ChannelCreateForm extends React.Component {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{ !this.state.status ? (
|
{ !this.state.status ? (
|
||||||
<form id="publish-channel-form">
|
<form id='publish-channel-form'>
|
||||||
<p id="input-error-channel-name" className="info-message-placeholder info-message--failure">{this.state.error}</p>
|
<div className='row row--wide row--short'>
|
||||||
<div className="row row--wide row--short">
|
<div className='column column--3 column--sml-10'>
|
||||||
<div className="column column--3 column--sml-10">
|
<label className='label' htmlFor='new-channel-name'>Name:</label>
|
||||||
<label className="label" htmlFor="new-channel-name">Name:</label>
|
</div><div className='column column--6 column--sml-10'>
|
||||||
</div><div className="column column--6 column--sml-10">
|
<div className='input-text--primary flex-container--row flex-container--left-bottom span--relative'>
|
||||||
<div className="input-text--primary flex-container--row flex-container--left-bottom span--relative">
|
|
||||||
<span>@</span>
|
<span>@</span>
|
||||||
<input type="text" name="channel" id="new-channel-name" className="input-text" placeholder="exampleChannelName" value={this.state.channel} onChange={this.handleChannelInput} />
|
<input type='text' name='channel' id='new-channel-name' className='input-text' placeholder='exampleChannelName' value={this.state.channel} onChange={this.handleChannelInput} />
|
||||||
{ (this.state.channel && !this.state.error) && <span id="input-success-channel-name" className="info-message--success span--absolute">{'\u2713'}</span> }
|
{ (this.state.channel && !this.state.error) && <span id='input-success-channel-name' className='info-message--success span--absolute'>{'\u2713'}</span> }
|
||||||
{ this.state.error && <span id="input-success-channel-name" className="info-message--failure span--absolute">{'\u2716'}</span> }
|
{ this.state.error && <span id='input-success-channel-name' className='info-message--failure span--absolute'>{'\u2716'}</span> }
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="row row--wide row--short">
|
<div className='row row--wide row--short'>
|
||||||
<div className="column column--3 column--sml-10">
|
<div className='column column--3 column--sml-10'>
|
||||||
<label className="label" htmlFor="new-channel-password">Password:</label>
|
<label className='label' htmlFor='new-channel-password'>Password:</label>
|
||||||
</div><div className="column column--6 column--sml-10">
|
</div><div className='column column--6 column--sml-10'>
|
||||||
<div className="input-text--primary">
|
<div className='input-text--primary'>
|
||||||
<input type="password" name="password" id="new-channel-password" className="input-text" placeholder="" value={this.state.password} onChange={this.handleInput} />
|
<input type='password' name='password' id='new-channel-password' className='input-text' placeholder='' value={this.state.password} onChange={this.handleInput} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{this.state.error ? (
|
||||||
<div className="row row--wide">
|
<p className='info-message--failure'>{this.state.error}</p>
|
||||||
<button className="button--primary" onClick={this.createChannel}>Create Channel</button>
|
) : (
|
||||||
|
<p className='info-message'>Choose a name and password for your channel</p>
|
||||||
|
)}
|
||||||
|
<div className='row row--wide'>
|
||||||
|
<button className='button--primary' onClick={this.createChannel}>Create Channel</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div>
|
||||||
<p className="fine-print">{this.state.status}</p>
|
<p className='fine-print'>{this.state.status}</p>
|
||||||
<ProgressBar size={12}/>
|
<ProgressBar size={12} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -26,7 +26,7 @@ class ChannelLoginForm extends React.Component {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
}),
|
}),
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
}
|
};
|
||||||
request('login', params)
|
request('login', params)
|
||||||
.then(({success, channelName, shortChannelId, channelClaimId, message}) => {
|
.then(({success, channelName, shortChannelId, channelClaimId, message}) => {
|
||||||
console.log('loginToChannel success:', success);
|
console.log('loginToChannel success:', success);
|
||||||
|
@ -47,30 +47,33 @@ class ChannelLoginForm extends React.Component {
|
||||||
}
|
}
|
||||||
render () {
|
render () {
|
||||||
return (
|
return (
|
||||||
<form id="channel-login-form">
|
<form id='channel-login-form'>
|
||||||
<p id="login-error-display-element" className="info-message-placeholder info-message--failure">{this.state.error}</p>
|
<div className='row row--wide row--short'>
|
||||||
<div className="row row--wide row--short">
|
<div className='column column--3 column--sml-10'>
|
||||||
<div className="column column--3 column--sml-10">
|
<label className='label' htmlFor='channel-login-name-input'>Name:</label>
|
||||||
<label className="label" htmlFor="channel-login-name-input">Name:</label>
|
</div><div className='column column--6 column--sml-10'>
|
||||||
</div><div className="column column--6 column--sml-10">
|
<div className='input-text--primary flex-container--row flex-container--left-bottom'>
|
||||||
<div className="input-text--primary flex-container--row flex-container--left-bottom">
|
<span>@</span>
|
||||||
<span>@</span>
|
<input type='text' id='channel-login-name-input' className='input-text' name='name' placeholder='Your Channel Name' value={this.state.channelName} onChange={this.handleInput} />
|
||||||
<input type="text" id="channel-login-name-input" className="input-text" name="name" placeholder="Your Channel Name" value={this.state.channelName} onChange={this.handleInput}/>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className='row row--wide row--short'>
|
||||||
<div className="row row--wide row--short">
|
<div className='column column--3 column--sml-10'>
|
||||||
<div className="column column--3 column--sml-10">
|
<label className='label' htmlFor='channel-login-password-input' >Password:</label>
|
||||||
<label className="label" htmlFor="channel-login-password-input" >Password:</label>
|
</div><div className='column column--6 column--sml-10'>
|
||||||
</div><div className="column column--6 column--sml-10">
|
<div className='input-text--primary'>
|
||||||
<div className="input-text--primary">
|
<input type='password' id='channel-login-password-input' name='password' className='input-text' placeholder='' value={this.state.channelPassword} onChange={this.handleInput} />
|
||||||
<input type="password" id="channel-login-password-input" name="password" className="input-text" placeholder="" value={this.state.channelPassword} onChange={this.handleInput}/>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{ this.state.error ? (
|
||||||
|
<p className='info-message--failure'>{this.state.error}</p>
|
||||||
<div className="row row--wide">
|
) : (
|
||||||
<button className="button--primary" onClick={this.loginToChannel}>Authenticate</button>
|
<p className='info-message'>Enter the name and password for your channel</p>
|
||||||
|
)}
|
||||||
|
<div className='row row--wide'>
|
||||||
|
<button className='button--primary' onClick={this.loginToChannel}>Authenticate</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
|
|
@ -24,28 +24,32 @@ class ChannelSelect extends React.Component {
|
||||||
render () {
|
render () {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p id="input-error-channel-select" className="info-message-placeholder info-message--failure">{this.props.channelError}</p>
|
|
||||||
<form>
|
<form>
|
||||||
<div className="column column--3 column--med-10">
|
<div className='column column--3 column--med-10'>
|
||||||
<input type="radio" name="anonymous-or-channel" id="anonymous-radio" className="input-radio" value="anonymous" checked={!this.props.publishInChannel} onChange={this.toggleAnonymousPublish}/>
|
<input type='radio' name='anonymous-or-channel' id='anonymous-radio' className='input-radio' value='anonymous' checked={!this.props.publishInChannel} onChange={this.toggleAnonymousPublish} />
|
||||||
<label className="label label--pointer" htmlFor="anonymous-radio">Anonymous</label>
|
<label className='label label--pointer' htmlFor='anonymous-radio'>Anonymous</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="column column--7 column--med-10">
|
<div className='column column--7 column--med-10'>
|
||||||
<input type="radio" name="anonymous-or-channel" id="channel-radio" className="input-radio" value="in a channel" checked={this.props.publishInChannel} onChange={this.toggleAnonymousPublish}/>
|
<input type='radio' name='anonymous-or-channel' id='channel-radio' className='input-radio' value='in a channel' checked={this.props.publishInChannel} onChange={this.toggleAnonymousPublish} />
|
||||||
<label className="label label--pointer" htmlFor="channel-radio">In a channel</label>
|
<label className='label label--pointer' htmlFor='channel-radio'>In a channel</label>
|
||||||
</div>
|
</div>
|
||||||
|
{ this.props.channelError ? (
|
||||||
|
<p className='info-message--failure'>{this.props.channelError}</p>
|
||||||
|
) : (
|
||||||
|
<p className='info-message'>Publish anonymously or in a channel</p>
|
||||||
|
)}
|
||||||
</form>
|
</form>
|
||||||
{ this.props.publishInChannel && (
|
{ this.props.publishInChannel && (
|
||||||
<div>
|
<div>
|
||||||
<div className="column column--3">
|
<div className='column column--3'>
|
||||||
<label className="label" htmlFor="channel-name-select">Channel:</label>
|
<label className='label' htmlFor='channel-name-select'>Channel:</label>
|
||||||
</div><div className="column column--7">
|
</div><div className='column column--7'>
|
||||||
<select type="text" id="channel-name-select" className="select select--arrow" value={this.props.selectedChannel} onChange={this.handleSelection}>
|
<select type='text' id='channel-name-select' className='select select--arrow' value={this.props.selectedChannel} onChange={this.handleSelection}>
|
||||||
{ this.props.loggedInChannelName && <option value={this.props.loggedInChannelName} id="publish-channel-select-channel-option">{this.props.loggedInChannelName}</option> }
|
{ this.props.loggedInChannelName && <option value={this.props.loggedInChannelName} id='publish-channel-select-channel-option'>{this.props.loggedInChannelName}</option> }
|
||||||
<option value={states.LOGIN}>Existing</option>
|
<option value={states.LOGIN}>Existing</option>
|
||||||
<option value={states.CREATE}>New</option>
|
<option value={states.CREATE}>New</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
{ (this.props.selectedChannel === states.LOGIN) && <ChannelLoginForm /> }
|
{ (this.props.selectedChannel === states.LOGIN) && <ChannelLoginForm /> }
|
||||||
{ (this.props.selectedChannel === states.CREATE) && <ChannelCreateForm /> }
|
{ (this.props.selectedChannel === states.CREATE) && <ChannelCreateForm /> }
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -51,10 +51,14 @@ class PublishThumbnailInput extends React.Component {
|
||||||
}
|
}
|
||||||
handleVideoLoadedData (event) {
|
handleVideoLoadedData (event) {
|
||||||
const duration = event.target.duration;
|
const duration = event.target.duration;
|
||||||
|
const totalMinutes = Math.floor(duration / 60);
|
||||||
|
const totalSeconds = Math.floor(duration % 60);
|
||||||
// set the slider
|
// set the slider
|
||||||
this.setState({
|
this.setState({
|
||||||
sliderMaxRange: duration * 100,
|
sliderMaxRange: duration * 100,
|
||||||
sliderValue : duration * 100 / 2,
|
sliderValue : duration * 100 / 2,
|
||||||
|
totalMinutes,
|
||||||
|
totalSeconds,
|
||||||
});
|
});
|
||||||
// update the current time of the video
|
// update the current time of the video
|
||||||
let video = document.getElementById('video-thumb-player');
|
let video = document.getElementById('video-thumb-player');
|
||||||
|
@ -88,28 +92,28 @@ class PublishThumbnailInput extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
render () {
|
render () {
|
||||||
const { error, videoSource, sliderMinRange, sliderMaxRange, sliderValue } = this.state;
|
const { error, videoSource, sliderMinRange, sliderMaxRange, sliderValue, totalMinutes, totalSeconds } = this.state;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<label className='label'>Thumbnail:</label>
|
||||||
{ error ? (
|
<video
|
||||||
<p className='info-message--failure'>{error}</p>
|
id='video-thumb-player'
|
||||||
) : (
|
preload='metadata'
|
||||||
<p className='info-message'>Use slider to set thumbnail:</p>
|
muted
|
||||||
)}
|
style={{display: 'none'}}
|
||||||
<video
|
playsInline
|
||||||
id='video-thumb-player'
|
onLoadedData={this.handleVideoLoadedData}
|
||||||
preload='metadata'
|
src={videoSource}
|
||||||
muted
|
onSeeked={this.createThumbnail}
|
||||||
style={{display: 'none'}}
|
/>
|
||||||
playsInline
|
{
|
||||||
onLoadedData={this.handleVideoLoadedData}
|
sliderValue ? (
|
||||||
src={videoSource}
|
<div>
|
||||||
onSeeked={this.createThumbnail}
|
<div className='flex-container--row flex-container--space-between-center' style={{width: '100%'}}>
|
||||||
/>
|
<span className='info-message'>0'00"</span>
|
||||||
{
|
<span className='info-message'>{totalMinutes}'{totalSeconds}"</span>
|
||||||
sliderValue ? (
|
</div>
|
||||||
<div className='slide-container'>
|
<div>
|
||||||
<input
|
<input
|
||||||
type='range'
|
type='range'
|
||||||
min={sliderMinRange}
|
min={sliderMinRange}
|
||||||
|
@ -119,11 +123,16 @@ class PublishThumbnailInput extends React.Component {
|
||||||
onChange={this.handleSliderChange}
|
onChange={this.handleSliderChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
</div>
|
||||||
<p className='info-message' >loading... </p>
|
) : (
|
||||||
)
|
<p className='info-message' >loading... </p>
|
||||||
}
|
)
|
||||||
</div>
|
}
|
||||||
|
{ error ? (
|
||||||
|
<p className='info-message--failure'>{error}</p>
|
||||||
|
) : (
|
||||||
|
<p className='info-message'>Use slider to set thumbnail</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,10 +14,14 @@ class PublishUrlInput extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
componentWillReceiveProps ({ claim, fileName }) {
|
componentWillReceiveProps ({ claim, fileName }) {
|
||||||
if (!claim) {
|
// if a new file was chosen, update the claim name
|
||||||
|
if (fileName !== this.props.fileName) {
|
||||||
return this.setClaimName(fileName);
|
return this.setClaimName(fileName);
|
||||||
}
|
}
|
||||||
this.checkClaimIsAvailable(claim);
|
// if the claim has updated, check its availability
|
||||||
|
if (claim !== this.props.claim) {
|
||||||
|
this.validateClaim(claim);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
handleInput (event) {
|
handleInput (event) {
|
||||||
let value = event.target.value;
|
let value = event.target.value;
|
||||||
|
@ -35,35 +39,42 @@ class PublishUrlInput extends React.Component {
|
||||||
const cleanClaimName = this.cleanseInput(fileNameWithoutEnding);
|
const cleanClaimName = this.cleanseInput(fileNameWithoutEnding);
|
||||||
this.props.onClaimChange(cleanClaimName);
|
this.props.onClaimChange(cleanClaimName);
|
||||||
}
|
}
|
||||||
checkClaimIsAvailable (claim) {
|
validateClaim (claim) {
|
||||||
|
if (!claim) {
|
||||||
|
return this.props.onUrlError('Enter a url above');
|
||||||
|
}
|
||||||
request(`/api/claim/availability/${claim}`)
|
request(`/api/claim/availability/${claim}`)
|
||||||
.then(() => {
|
.then(response => {
|
||||||
|
console.log('api/claim/availability response:', response);
|
||||||
this.props.onUrlError(null);
|
this.props.onUrlError(null);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
console.log('api/claim/availability error:', error);
|
||||||
this.props.onUrlError(error.message);
|
this.props.onUrlError(error.message);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
render () {
|
render () {
|
||||||
|
const { claim, loggedInChannelName, loggedInChannelShortId, publishInChannel, selectedChannel, urlError } = this.props;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className='column column--10 column--sml-10'>
|
||||||
<p id='input-error-claim-name' className='info-message-placeholder info-message--failure'>{this.props.urlError}</p>
|
<div className='input-text--primary span--relative'>
|
||||||
<div className='column column--3 column--sml-10'>
|
|
||||||
<label className='label'>URL:</label>
|
|
||||||
</div><div className='column column--7 column--sml-10 input-text--primary span--relative'>
|
|
||||||
|
|
||||||
<span className='url-text--secondary'>spee.ch / </span>
|
<span className='url-text--secondary'>spee.ch / </span>
|
||||||
|
|
||||||
<UrlMiddle
|
<UrlMiddle
|
||||||
publishInChannel={this.props.publishInChannel}
|
publishInChannel={publishInChannel}
|
||||||
selectedChannel={this.props.selectedChannel}
|
selectedChannel={selectedChannel}
|
||||||
loggedInChannelName={this.props.loggedInChannelName}
|
loggedInChannelName={loggedInChannelName}
|
||||||
loggedInChannelShortId={this.props.loggedInChannelShortId}
|
loggedInChannelShortId={loggedInChannelShortId}
|
||||||
/>
|
/>
|
||||||
|
<input type='text' id='claim-name-input' className='input-text' name='claim' placeholder='your-url-here' onChange={this.handleInput} value={claim} />
|
||||||
<input type='text' id='claim-name-input' className='input-text' name='claim' placeholder='your-url-here' onChange={this.handleInput} value={this.props.claim} />
|
{ (claim && !urlError) && <span id='input-success-claim-name' className='info-message--success span--absolute'>{'\u2713'}</span> }
|
||||||
{ (this.props.claim && !this.props.urlError) && <span id='input-success-claim-name' className='info-message--success span--absolute'>{'\u2713'}</span> }
|
{ urlError && <span id='input-success-channel-name' className='info-message--failure span--absolute'>{'\u2716'}</span> }
|
||||||
{ this.props.urlError && <span id='input-success-channel-name' className='info-message--failure span--absolute'>{'\u2716'}</span> }
|
</div>
|
||||||
|
<div>
|
||||||
|
{ urlError ? (
|
||||||
|
<p id='input-error-claim-name' className='info-message--failure'>{urlError}</p>
|
||||||
|
) : (
|
||||||
|
<p className='info-message'>Choose a custom url</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -19,15 +19,15 @@ module.exports = {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'image/gif':
|
case 'image/gif':
|
||||||
if (file.size > 50000000) {
|
if (file.size > 30000000) {
|
||||||
console.log('file was too big');
|
console.log('file was too big');
|
||||||
throw new Error('Sorry, GIFs are limited to 50 megabytes.');
|
throw new Error('Sorry, GIFs are limited to 30 megabytes.');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'video/mp4':
|
case 'video/mp4':
|
||||||
if (file.size > 50000000) {
|
if (file.size > 20000000) {
|
||||||
console.log('file was too big');
|
console.log('file was too big');
|
||||||
throw new Error('Sorry, videos are limited to 50 megabytes.');
|
throw new Error('Sorry, videos are limited to 20 megabytes.');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -5,9 +5,7 @@ const multipartMiddleware = multipart({uploadDir: files.uploadDirectory});
|
||||||
const db = require('../models');
|
const db = require('../models');
|
||||||
const { claimNameIsAvailable, checkChannelAvailability, publish } = require('../controllers/publishController.js');
|
const { claimNameIsAvailable, checkChannelAvailability, publish } = require('../controllers/publishController.js');
|
||||||
const { getClaimList, resolveUri, getClaim } = require('../helpers/lbryApi.js');
|
const { getClaimList, resolveUri, getClaim } = require('../helpers/lbryApi.js');
|
||||||
|
|
||||||
const { addGetResultsToFileData, createBasicPublishParams, createThumbnailPublishParams, parsePublishApiRequestBody, parsePublishApiRequestFiles, createFileData } = require('../helpers/publishHelpers.js');
|
const { addGetResultsToFileData, createBasicPublishParams, createThumbnailPublishParams, parsePublishApiRequestBody, parsePublishApiRequestFiles, createFileData } = require('../helpers/publishHelpers.js');
|
||||||
|
|
||||||
const errorHandlers = require('../helpers/errorHandlers.js');
|
const errorHandlers = require('../helpers/errorHandlers.js');
|
||||||
const { sendGAAnonymousPublishTiming, sendGAChannelPublishTiming } = require('../helpers/googleAnalytics.js');
|
const { sendGAAnonymousPublishTiming, sendGAChannelPublishTiming } = require('../helpers/googleAnalytics.js');
|
||||||
const { authenticateUser } = require('../auth/authentication.js');
|
const { authenticateUser } = require('../auth/authentication.js');
|
||||||
|
|
Loading…
Add table
Reference in a new issue