React/Redux - publish component #323
6 changed files with 173 additions and 73 deletions
|
@ -7,7 +7,7 @@ function getRequest (url) {
|
||||||
if (xhttp.readyState == 4 ) {
|
if (xhttp.readyState == 4 ) {
|
||||||
if ( xhttp.status == 200) {
|
if ( xhttp.status == 200) {
|
||||||
resolve(xhttp.response);
|
resolve(xhttp.response);
|
||||||
} else if (xhttp.status == 401) {
|
} else if (xhttp.status == 403) {
|
||||||
reject('Wrong channel name or password');
|
reject('Wrong channel name or password');
|
||||||
} else {
|
} else {
|
||||||
reject('request failed with status:' + xhttp.status);
|
reject('request failed with status:' + xhttp.status);
|
||||||
|
@ -48,7 +48,7 @@ function toggleSection(event){
|
||||||
var slaveElement = document.getElementById(dataSet.slaveelementid);
|
var slaveElement = document.getElementById(dataSet.slaveelementid);
|
||||||
var closedLabel = dataSet.closedlabel;
|
var closedLabel = dataSet.closedlabel;
|
||||||
var openLabel = dataSet.openlabel;
|
var openLabel = dataSet.openlabel;
|
||||||
|
|
||||||
if (status === "false") {
|
if (status === "false") {
|
||||||
slaveElement.hidden = false;
|
slaveElement.hidden = false;
|
||||||
masterElement.innerText = openLabel;
|
masterElement.innerText = openLabel;
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||||
import PreviewDropzone from './previewDropzone.jsx';
|
import PreviewDropzone from './previewDropzone.jsx';
|
||||||
import TitleInput from './titleInput.jsx';
|
import TitleInput from './titleInput.jsx';
|
||||||
import ChannelSelector from './channelSelector.jsx';
|
import ChannelSelector from './channelSelector.jsx';
|
||||||
import UrlInput from './urlInput.jsx';
|
import UrlChooser from './urlChooser.jsx';
|
||||||
import ThumbnailInput from './thumbnailInput.jsx';
|
import ThumbnailInput from './thumbnailInput.jsx';
|
||||||
import MetadataInputs from './metadataInputs.jsx';
|
import MetadataInputs from './metadataInputs.jsx';
|
||||||
|
|
||||||
|
@ -79,6 +79,16 @@ class PublishForm extends React.Component {
|
||||||
<div className="column column--5 column--sml-10 align-content-top">
|
<div className="column column--5 column--sml-10 align-content-top">
|
||||||
<div id="publish-active-area" className="row row--padded">
|
<div id="publish-active-area" className="row row--padded">
|
||||||
|
|
||||||
|
<UrlChooser
|
||||||
|
fileName={this.props.file.name}
|
||||||
|
claim={this.props.claim}
|
||||||
|
publishToChannel={this.props.publishToChannel}
|
||||||
|
loggedInChannelName={this.props.loggedInChannelName}
|
||||||
|
loggedInChannelShortId={this.props.loggedInChannelShortId}
|
||||||
|
updateUploaderState={this.updateUploaderState}
|
||||||
|
makeGetRequest={this.props.makeGetRequest}
|
||||||
|
/>
|
||||||
|
|
||||||
<AnonymousOrChannelSelect publishToChannel={this.props.publishToChannel} updateUploaderState={this.props.updateUploaderState}/>
|
<AnonymousOrChannelSelect publishToChannel={this.props.publishToChannel} updateUploaderState={this.props.updateUploaderState}/>
|
||||||
|
|
||||||
<ChannelSelector
|
<ChannelSelector
|
||||||
|
@ -89,8 +99,6 @@ class PublishForm extends React.Component {
|
||||||
channelError={this.state.channelError}
|
channelError={this.state.channelError}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<UrlInput file={this.props.file}/>
|
|
||||||
|
|
||||||
{ (this.props.file.type === 'video/mp4') && <ThumbnailInput thumbnail={this.props.thumbnail}/> }
|
{ (this.props.file.type === 'video/mp4') && <ThumbnailInput thumbnail={this.props.thumbnail}/> }
|
||||||
|
|
||||||
<MetadataInputs />
|
<MetadataInputs />
|
||||||
|
|
99
react/components/urlChooser.jsx
Normal file
99
react/components/urlChooser.jsx
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
function UrlMiddle ({publishToChannel, loggedInChannelName, loggedInChannelShortId}) {
|
||||||
|
if (publishToChannel) {
|
||||||
|
if (loggedInChannelName) {
|
||||||
|
return <span id="url-channel" className="url-text--secondary">{loggedInChannelName}:{loggedInChannelShortId} /</span>;
|
||||||
|
}
|
||||||
|
return <span id="url-channel-placeholder" className="url-text--secondary tooltip">@channel<span
|
||||||
|
className="tooltip-text">Select a channel below</span> /</span>;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<span id="url-no-channel-placeholder" className="url-text--secondary tooltip">xyz<span className="tooltip-text">This will be a random id</span> /</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class UrlInput extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
urlError : null,
|
||||||
|
urlBeginning: 'spee.ch',
|
||||||
|
urlMiddle : null,
|
||||||
|
};
|
||||||
|
this.handleInput = this.handleInput.bind(this);
|
||||||
|
this.validateClaimName = this.validateClaimName.bind(this);
|
||||||
|
this.cleanseClaimName = this.cleanseClaimName.bind(this);
|
||||||
|
this.checkClaimIsValidAndAvailable = this.checkClaimIsValidAndAvailable.bind(this);
|
||||||
|
}
|
||||||
|
handleInput (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
let value = event.target.value;
|
||||||
|
const name = event.target.name;
|
||||||
|
value = this.cleanseClaimName(value);
|
||||||
|
this.props.updateUploaderState(name, value);
|
||||||
|
this.checkClaimIsValidAndAvailable(value);
|
||||||
|
}
|
||||||
|
validateClaimName (claim) {
|
||||||
|
// ensure a name was entered
|
||||||
|
if (!claim || claim.length < 1) {
|
||||||
|
throw new Error('You must enter a name for your url');
|
||||||
|
}
|
||||||
|
// validate the characters in the 'name' field
|
||||||
|
const invalidCharacters = /[^A-Za-z0-9,-]/g.exec(claim);
|
||||||
|
if (invalidCharacters) {
|
||||||
|
throw new Error('"' + invalidCharacters + '" characters are not allowed');
|
||||||
|
}
|
||||||
|
return claim;
|
||||||
|
}
|
||||||
|
cleanseClaimName (name) {
|
||||||
|
name = name.replace(/\s+/g, '-'); // replace spaces with dashes
|
||||||
|
name = name.replace(/[^A-Za-z0-9-]/g, ''); // remove all characters that are not A-Z, a-z, 0-9, or '-'
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
checkClaimIsValidAndAvailable (claim) {
|
||||||
|
// validationFunctions.checkClaimName(event.target.value)
|
||||||
|
try {
|
||||||
|
claim = this.validateClaimName(claim);
|
||||||
|
} catch (error) {
|
||||||
|
this.setState({urlError: error.message});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const that = this;
|
||||||
|
this.props.makeGetRequest(`/api/claim-is-available/${claim}`)
|
||||||
|
.then(() => {
|
||||||
|
that.setState({urlError: null});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
that.setState({urlError: error.message});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="row row--padded row--no-top row--wide">
|
||||||
|
|
||||||
|
<p id="input-error-claim-name" className="info-message-placeholder info-message--failure">{this.state.urlError}</p>
|
||||||
|
|
||||||
|
<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">{this.state.urlBeginning} / </span>
|
||||||
|
|
||||||
|
<UrlMiddle publishToChannel={this.props.publishToChannel} loggedInChannelName={this.props.loggedInChannelName} loggedInChannelShortId={this.props.loggedInChannelShortId}/>
|
||||||
|
|
||||||
|
<input type="text" id="claim-name-input" className="input-text" name='claim' placeholder="your-url-here" onInput={this.handleInput} value={this.props.claim}/>
|
||||||
|
{ (this.props.claim && !this.state.urlError) && (
|
||||||
|
<span id="input-success-claim-name" className="info-message--success span--absolute">{'\u2713'}</span>
|
||||||
|
)}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = UrlInput;
|
|
@ -1,39 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
class UrlInput extends React.Component {
|
|
||||||
constructor (props) {
|
|
||||||
super(props);
|
|
||||||
this.updateUrl = this.updateUrl.bind(this);
|
|
||||||
}
|
|
||||||
updateUrl (selectedOption) {
|
|
||||||
const urlChannel = document.getElementById('url-channel');
|
|
||||||
const urlNoChannelPlaceholder = document.getElementById('url-no-channel-placeholder');
|
|
||||||
const urlChannelPlaceholder = document.getElementById('url-channel-placeholder');
|
|
||||||
if (selectedOption === 'new' || selectedOption === 'login' || selectedOption === ''){
|
|
||||||
urlChannel.hidden = true;
|
|
||||||
urlNoChannelPlaceholder.hidden = true;
|
|
||||||
urlChannelPlaceholder.hidden = false;
|
|
||||||
} else if (selectedOption === 'anonymous'){
|
|
||||||
urlChannel.hidden = true;
|
|
||||||
urlNoChannelPlaceholder.hidden = false;
|
|
||||||
urlChannelPlaceholder.hidden = true;
|
|
||||||
} else {
|
|
||||||
urlChannel.hidden = false;
|
|
||||||
// show channel and short id
|
|
||||||
const selectedChannel = getCookie('channel_name');
|
|
||||||
const shortChannelId = getCookie('short_channel_id');
|
|
||||||
urlChannel.innerText = `${selectedChannel}:${shortChannelId}`;
|
|
||||||
urlNoChannelPlaceholder.hidden = true;
|
|
||||||
urlChannelPlaceholder.hidden = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
render () {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h3>url component</h3>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = UrlInput;
|
|
|
@ -8,17 +8,18 @@ const DROPZONE = 'DROPZONE';
|
||||||
const DETAILS = 'DETAILS';
|
const DETAILS = 'DETAILS';
|
||||||
const STATUS = 'STATUS';
|
const STATUS = 'STATUS';
|
||||||
const initialState = {
|
const initialState = {
|
||||||
showComponent : DROPZONE, // DROPZONE, DETAILS, or PUBLISHING
|
showComponent : DROPZONE, // DROPZONE, DETAILS, or PUBLISHING
|
||||||
loggedInChannel : null,
|
loggedInChannelName : null,
|
||||||
publishToChannel: false,
|
loggedInChannelShortId: null,
|
||||||
file : null,
|
publishToChannel : false,
|
||||||
title : '',
|
file : null,
|
||||||
channel : null,
|
title : '',
|
||||||
url : '',
|
channel : null,
|
||||||
thumbnail : '',
|
claim : '',
|
||||||
description : '',
|
thumbnail : '',
|
||||||
license : '',
|
description : '',
|
||||||
nsfw : '',
|
license : '',
|
||||||
|
nsfw : '',
|
||||||
};
|
};
|
||||||
|
|
||||||
class Uploader extends React.Component {
|
class Uploader extends React.Component {
|
||||||
|
@ -30,10 +31,16 @@ class Uploader extends React.Component {
|
||||||
this.clearUploaderState = this.clearUploaderState.bind(this);
|
this.clearUploaderState = this.clearUploaderState.bind(this);
|
||||||
this.showComponent = this.showComponent.bind(this);
|
this.showComponent = this.showComponent.bind(this);
|
||||||
this.stageFileAndShowDetails = this.stageFileAndShowDetails.bind(this);
|
this.stageFileAndShowDetails = this.stageFileAndShowDetails.bind(this);
|
||||||
|
this.makeGetRequest = this.makeGetRequest.bind(this);
|
||||||
|
this.makePostRequest = this.makePostRequest.bind(this);
|
||||||
}
|
}
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
// check for whether a channel is logged in
|
// check for whether a channel is logged in
|
||||||
// if so, setState loggedInChannel to the channel name
|
// if so, setState loggedInChannel to the channel name
|
||||||
|
// const loggedInChannel = getCookie('channel_name');
|
||||||
|
// this.setState({loggedInChannel})
|
||||||
|
// const loggedInChannelShortId = getCookie('short_channel_id');
|
||||||
|
// this.setState({loggedInChannelShortId})
|
||||||
}
|
}
|
||||||
updateUploaderState (name, value) {
|
updateUploaderState (name, value) {
|
||||||
console.log(`updateUploaderState ${name} ${value}`);
|
console.log(`updateUploaderState ${name} ${value}`);
|
||||||
|
@ -52,6 +59,45 @@ class Uploader extends React.Component {
|
||||||
// hide the dropzone and show the details
|
// hide the dropzone and show the details
|
||||||
this.showComponent(DETAILS);
|
this.showComponent(DETAILS);
|
||||||
}
|
}
|
||||||
|
makeGetRequest (url) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let xhttp = new XMLHttpRequest();
|
||||||
|
xhttp.open('GET', url, true);
|
||||||
|
xhttp.responseType = 'json';
|
||||||
|
xhttp.onreadystatechange = () => {
|
||||||
|
if (xhttp.readyState == 4 ) {
|
||||||
|
if ( xhttp.status == 200) {
|
||||||
|
resolve(xhttp.response);
|
||||||
|
} else if (xhttp.status == 403) {
|
||||||
|
reject('Wrong channel name or password');
|
||||||
|
} else {
|
||||||
|
reject('request failed with status:' + xhttp.status);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xhttp.send();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
makePostRequest (url, params) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let xhttp = new XMLHttpRequest();
|
||||||
|
xhttp.open('POST', url, true);
|
||||||
|
xhttp.responseType = 'json';
|
||||||
|
xhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
||||||
|
xhttp.onreadystatechange = () => {
|
||||||
|
if (xhttp.readyState == 4 ) {
|
||||||
|
if ( xhttp.status == 200) {
|
||||||
|
resolve(xhttp.response);
|
||||||
|
} else if (xhttp.status == 403) {
|
||||||
|
reject('Wrong channel name or password');
|
||||||
|
} else {
|
||||||
|
reject('request failed with status:' + xhttp.status);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xhttp.send(params);
|
||||||
|
});
|
||||||
|
}
|
||||||
render () {
|
render () {
|
||||||
return (
|
return (
|
||||||
<div className="row row--tall flex-container--column">
|
<div className="row row--tall flex-container--column">
|
||||||
|
@ -62,12 +108,14 @@ class Uploader extends React.Component {
|
||||||
<PublishForm
|
<PublishForm
|
||||||
updateUploaderState={this.updateUploaderState}
|
updateUploaderState={this.updateUploaderState}
|
||||||
clearUploaderState={this.clearUploaderState}
|
clearUploaderState={this.clearUploaderState}
|
||||||
loggedInChannel={this.state.loggedInChannel}
|
makeGetRequest={this.makeGetRequest}
|
||||||
|
loggedInChannelName={this.state.loggedInChannelName}
|
||||||
|
loggedInChannelShortId={this.state.loggedInChannelShortId}
|
||||||
publishToChannel={this.state.publishToChannel}
|
publishToChannel={this.state.publishToChannel}
|
||||||
file={this.state.file}
|
file={this.state.file}
|
||||||
title={this.state.title}
|
title={this.state.title}
|
||||||
channel={this.state.channel}
|
channel={this.state.channel}
|
||||||
url={this.state.url}
|
claim={this.state.claim}
|
||||||
thumbnail={this.state.thumbnail}
|
thumbnail={this.state.thumbnail}
|
||||||
description={this.state.description}
|
description={this.state.description}
|
||||||
license={this.state.license}
|
license={this.state.license}
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
<div class="row row--padded row--wide">
|
|
||||||
<!--error display-->
|
|
||||||
<p id="input-error-claim-name" class="info-message-placeholder info-message--failure" hidden="true"></p>
|
|
||||||
<!--url selection-->
|
|
||||||
<div class="column column--3 column--sml-10">
|
|
||||||
<label class="label">URL:</label>
|
|
||||||
</div><div class="column column--7 column--sml-10 input-text--primary span--relative">
|
|
||||||
<span class="url-text--secondary">spee.ch /</span>
|
|
||||||
<span id="url-channel" class="url-text--secondary" {{#if user}}{{else}}hidden="true"{{/if}}>{{user.channelName}}:{{user.shortChannelId}}</span>
|
|
||||||
<span id="url-no-channel-placeholder" class="url-text--secondary tooltip" {{#if user}}hidden="true"{{else}}{{/if}}>xyz<span class="tooltip-text">This will be a random id</span></span>
|
|
||||||
<span id="url-channel-placeholder" class="url-text--secondary tooltip" hidden="true">@channel<span class="tooltip-text">Select a channel above</span></span> /
|
|
||||||
<input type="text" id="claim-name-input" class="input-text" placeholder="your-url-here" oninput="validationFunctions.checkClaimName(event.target.value)">
|
|
||||||
<span id="input-success-claim-name" class="info-message--success span--absolute"></span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
Loading…
Add table
Reference in a new issue